Summary
writeDeviceCert() stores the device certificate in TPM NV as PEM, and
writes it with a single un-chunked TPM2_NV_Write. On an Infineon SLB9665 the
per-write ceiling (TPM_PT_NV_BUFFER_MAX) is 768 bytes and the stored
certificate is 660, so there are 108 bytes of headroom before the write
starts failing — and that failure is currently silent and permanent (#6344).
Storing DER instead of PEM recovers ~33% immediately: the same certificate is 446
bytes in DER.
Detail
pkg/pillar/cmd/tpmmgr/tpmmgr.go:
deviceCertBytes, err := os.ReadFile(types.DeviceCertName) // PEM
...
tpm2.NVDefineSpace(rw, tpm2.HandleOwner, etpm.TpmDeviceCertHdl,
etpm.EmptyPassword, etpm.EmptyPassword, nil,
tpm2.AttrOwnerWrite|tpm2.AttrOwnerRead,
uint16(len(deviceCertBytes)))
...
tpm2.NVWrite(rw, tpm2.HandleOwner, etpm.TpmDeviceCertHdl,
etpm.EmptyPassword, deviceCertBytes, 0)
go-tpm's NVWrite issues one TPM2_NV_Write with the whole payload; it does
not chunk. (Its NVReadEx counterpart does chunk, querying
TPM_PT_NV_BUFFER_MAX first — so reads are already correct.)
Measured on an EVE device with an Infineon SLB9665:
|
Value |
TPM_PT_NV_BUFFER_MAX (per-write ceiling) |
0x300 = 768 |
TPM_PT_NV_INDEX_MAX (per-index ceiling) |
0x680 = 1664 |
NV index 0x1500000 actual size |
660 |
| Same certificate encoded as DER |
446 |
The TCG PC Client Platform TPM Profile sets the minimum NV_BUFFER_MAX at 512,
so a conforming part could be tighter than the 660 bytes EVE writes today. Worth
checking tpm2 getcap properties-fixed across the qualified platform list.
Why the headroom matters
Any addition to the certificate template consumes it. Concretely, adding a
device-specific serialNumber attribute to the subject — a change worth making
for other reasons, and only ~90 bytes of DER — takes the stored PEM to roughly
786 bytes. That is over the 768-byte ceiling, so NVDefineSpace succeeds (786 <
1664) and NVWrite then fails, triggering #6344.
Suggested fix
- Store DER, not PEM. TCG's "TPM 2.0 Keys for Device Identity and
Attestation" §7.3.3 recommends exactly this for LDevID/LAK certificates in NV:
"A certificate chain SHOULD be stored as X.509 DER-encoded certificates, as
this is the smallest form and requires no conversion for use by networked
applications." (It is a SHOULD, inside a section that is RECOMMENDED as a
whole; the MUST form is §7.3.2, which governs only OEM-provisioned IDevID/IAK
chains. The binding constraint for EVE is the measured TPM_PT_NV_BUFFER_MAX,
not the spec.) Accept either encoding on read so existing devices keep
working.
- Chunk the write against
TPM_PT_NV_BUFFER_MAX, mirroring what NVReadEx
already does for reads. Without this, nothing larger than one buffer can ever
be stored even in DER — which also blocks storing a certificate chain, where
a leaf plus one issuing CA runs about 1150 bytes in DER (under the 1664-byte
index limit, over the 768-byte write limit).
Two adjacent observations from the same NV dump, worth folding in if these
indices are ever revised:
- All five EVE NV indices use a SHA-1
nameAlg, where the TPM vendor's own
indices use SHA-256. EVE never chooses this — NVDefineSpace is called with
nil for the policy and the default applies — so it is an artifact rather than
a decision.
- The indices are
ownerwrite|ownerread with an empty auth value and no policy,
so anything with owner access can rewrite the device-certificate backup. That
is consistent with its documented role as a backup rather than an anchor, but
it is worth being explicit about if that role ever changes.
Summary
writeDeviceCert()stores the device certificate in TPM NV as PEM, andwrites it with a single un-chunked
TPM2_NV_Write. On an Infineon SLB9665 theper-write ceiling (
TPM_PT_NV_BUFFER_MAX) is 768 bytes and the storedcertificate is 660, so there are 108 bytes of headroom before the write
starts failing — and that failure is currently silent and permanent (#6344).
Storing DER instead of PEM recovers ~33% immediately: the same certificate is 446
bytes in DER.
Detail
pkg/pillar/cmd/tpmmgr/tpmmgr.go:go-tpm'sNVWriteissues oneTPM2_NV_Writewith the whole payload; it doesnot chunk. (Its
NVReadExcounterpart does chunk, queryingTPM_PT_NV_BUFFER_MAXfirst — so reads are already correct.)Measured on an EVE device with an Infineon SLB9665:
TPM_PT_NV_BUFFER_MAX(per-write ceiling)TPM_PT_NV_INDEX_MAX(per-index ceiling)0x1500000actual sizeThe TCG PC Client Platform TPM Profile sets the minimum
NV_BUFFER_MAXat 512,so a conforming part could be tighter than the 660 bytes EVE writes today. Worth
checking
tpm2 getcap properties-fixedacross the qualified platform list.Why the headroom matters
Any addition to the certificate template consumes it. Concretely, adding a
device-specific
serialNumberattribute to the subject — a change worth makingfor other reasons, and only ~90 bytes of DER — takes the stored PEM to roughly
786 bytes. That is over the 768-byte ceiling, so
NVDefineSpacesucceeds (786 <1664) and
NVWritethen fails, triggering #6344.Suggested fix
Attestation" §7.3.3 recommends exactly this for LDevID/LAK certificates in NV:
"A certificate chain SHOULD be stored as X.509 DER-encoded certificates, as
this is the smallest form and requires no conversion for use by networked
applications." (It is a SHOULD, inside a section that is RECOMMENDED as a
whole; the MUST form is §7.3.2, which governs only OEM-provisioned IDevID/IAK
chains. The binding constraint for EVE is the measured
TPM_PT_NV_BUFFER_MAX,not the spec.) Accept either encoding on read so existing devices keep
working.
TPM_PT_NV_BUFFER_MAX, mirroring whatNVReadExalready does for reads. Without this, nothing larger than one buffer can ever
be stored even in DER — which also blocks storing a certificate chain, where
a leaf plus one issuing CA runs about 1150 bytes in DER (under the 1664-byte
index limit, over the 768-byte write limit).
Two adjacent observations from the same NV dump, worth folding in if these
indices are ever revised:
nameAlg, where the TPM vendor's ownindices use SHA-256. EVE never chooses this —
NVDefineSpaceis called withnilfor the policy and the default applies — so it is an artifact rather thana decision.
ownerwrite|ownerreadwith an empty auth value and no policy,so anything with owner access can rewrite the device-certificate backup. That
is consistent with its documented role as a backup rather than an anchor, but
it is worth being explicit about if that role ever changes.