Repository navigation
feat(tpm): hold an RSA CA key in the TPM - #282
Merged
Merged
Conversation
4 tasks
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
machine-config-pki and machine-config said the TPM path holds ECDSA P-384 keys only and quoted the old refusal. With CryptOS-PKI/cryptos-node#282 an RSA-3072 or RSA-4096 CA key is created in the TPM when the TPM implements that size, and key creation fails with FailedPrecondition, never falling back, when it does not. The callouts now say that. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
air-gapped-root said an RSA CA key can't be held in the TPM. With CryptOS-PKI/cryptos-node#282 it can, at a size the TPM implements; the table and a new caution callout cover the RSA-2048-only TPMs and the FailedPrecondition refusal. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
keys-never-leave-tpm said the TPM path was ECC only and listed RSA CA keys in the TPM as not built. With CryptOS-PKI/cryptos-node#282 the page covers the RSA template, the TestParms size check at boot and before creation, RSA signing through the TPM, and the refusal on TPMs without the size. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
marked this pull request as ready for review
September 30, 2026 03:38
This was referenced Sep 30, 2026
An RSA-3072 or RSA-4096 CA key can now be created and held in the TPM in state_key.mode tpm, loaded through the same RootKeyBackend and key loader as the ECDSA key, with the same fixedTPM, fixedParent and sensitiveDataOrigin attributes. - The key template, public-area parser and signer handle RSA. The key carries a null scheme; Sign takes the hash and padding (PKCS#1 v1.5 or PSS) from crypto.SignerOpts, returns the raw signature, and verifies it before returning it. - CreateKey asks the TPM with TPM2_TestParms whether it implements the size first and returns ErrKeyAlgorithmUnsupported, creating nothing, when it does not. The ceremony and CA-key rotation turn that into FailedPrecondition naming the algorithm. Nothing falls back to a smaller size or to software. - RSA-2048 stays refused for a TPM-held CA key, matching the 3072-bit floor. - Probe reports the RSA sizes the TPM accepts, and boot logs them. - Export stays refused for any TPM-held key; the mode check is now a named helper. The in-process simulator implements RSA-2048 only, so the RSA-3072 path is tested end to end against swtpm (ceremony, reboot and reload, leaf, subordinate, CSR, CRL, OCSP), which CI now installs. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
The Linux TPM driver behind /dev/tpmrm0 resends a command the TPM answers with TPM_RC_RETRY or TPM_RC_TESTING. The test talks to swtpm over a raw socket with no driver, and the libtpms on the CI runner answers the RSA signature in the ceremony with TPM_RC_RETRY, so the test transport now resends as the driver does. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
force-pushed
the
feat/197-tpm-rsa-ca-key
branch
from
September 30, 2026 10:09
49156bd to
50b5fe6
Compare
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
air-gapped-root said an RSA CA key can't be held in the TPM. With CryptOS-PKI/cryptos-node#282 it can, at a size the TPM implements; the table and a new caution callout cover the RSA-2048-only TPMs and the FailedPrecondition refusal. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
keys-never-leave-tpm said the TPM path was ECC only and listed RSA CA keys in the TPM as not built. With CryptOS-PKI/cryptos-node#282 the page covers the RSA template, the TestParms size check at boot and before creation, RSA signing through the TPM, and the refusal on TPMs without the size. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
added a commit
to CryptOS-PKI/docs
that referenced
this pull request
Sep 30, 2026
machine-config-pki and machine-config said the TPM path holds ECDSA P-384 keys only and quoted the old refusal. With CryptOS-PKI/cryptos-node#282 an RSA-3072 or RSA-4096 CA key is created in the TPM when the TPM implements that size, and key creation fails with FailedPrecondition, never falling back, when it does not. The callouts now say that. Depends on CryptOS-PKI/cryptos-node#282 and should merge with it. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Contributor
Author
|
Closing summary: an RSA CA key (3072 bits and up) is now created and held in the TPM through the single key loader, and the ceremony fails early when the TPM can't do the requested size. The swtpm transport resends on TPM_RC_RETRY. Rebased onto main after #285; CI green on 50b5fe6, including the swtpm RSA-3072 end-to-end test. Squash-merging with the owner's approval; closes #197. |
This was referenced Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
With
state_key.mode: tpm, an RSApki.root_key_algmade the ceremony fail, because every layer of the TPM path only handled ECC. This PR lets a TPM create and hold an RSA-3072 or RSA-4096 CA key. The key is loaded through the sameRootKeyBackendand key loader as the ECDSA key, and it has the samefixedTPM,fixedParentandsensitiveDataOriginattributes. The crypto path still uses only stdlib and go-tpm, and there are no new Go dependencies.Signtakes the hash (SHA-256, SHA-384 or SHA-512) and the padding (PKCS#1 v1.5, or PSS for*rsa.PSSOptions) fromcrypto.SignerOpts, and returns the raw signature, not ASN.1-wrapped. Every RSA signature is checked against the public key before it's returned. That catches a faulty signature, and it also catches a PSS salt length the TPM picked that differs from the one the caller asked for. ECDSA signing behaves as before.CreateKeyand inLoadKey, to match the 3072-bit subject-key rule.CreateKeyfirst callsTPM2_TestParms. If the TPM doesn't implement the size, it returnstpm.ErrKeyAlgorithmUnsupportedand creates nothing. The root ceremony and CA-key rotation turn that intoFailedPrecondition, and the message names the algorithm. The ceremony can then be run again with another algorithm. An intermediate or issuing node creates its key at first boot, so on that node boot stops with the same error (fail closed, as before). Nothing ever falls back to a smaller size or to a software key.Probenow also reports which RSA sizes the TPM accepts (Capabilities.RSAKeyBits,SupportsRSAKeyBits), and boot logsinit: TPM capabilities: ECC curves [...], RSA key sizes [...]. The P-384 boot check hasn't changed.caKeyExportable, so it can be tested.init.NewTPMRootBackendandtpm.OpenTransportexist so the end-to-end test can drive the production backend against swtpm.Testing against a TPM that has RSA-3072
The in-process simulator the TPM tests use (go-tpm-tools' build of the Microsoft reference TPM) implements RSA-2048 only. Its build profile turns RSA-3072 off, and enabling it with a define crashes the simulator at reset. So the tests are split:
ErrKeyAlgorithmUnsupported, and noTPM2_Createis sent. This is a real refusal from the simulator, not a fake.FailedPreconditionnaming RSA-3072, commits no identity, and then succeeds on a retry with ECDSA-P384.FailedPreconditionand stages nothing.internal/e2e/tpm_rsa_e2e_test.go): the real ceremony in tpm mode with RSA-3072. Then a reboot (swtpm restarts on its persisted state), and a reload through the production backend. The reloaded key then signs a leaf, a subordinate CA, a CSR, a CRL and a delegated OCSP responder. Everything verifies and is SHA-2 RSA signed. The test also checks that the stored key is a TPM RSA object withfixedTPM,fixedParentandsensitiveDataOrigin. It skips whenswtpmisn't installed, but fails under CI, andci-gonow installsswtpmfrom apt, asci-imagealready does for its toolchain.swtpm 0.10.2 (locally) and swtpm 0.7.3 with libtpms 0.9.3 (the Ubuntu 24.04 packages CI installs) implement RSA-3072, and 0.10.2 does not implement RSA-4096. So RSA-4096 has only been checked on its refusal path, not created in a TPM.
Tests changed
TestPublicTemplateRejectsRSAbecameTestPublicTemplateRSAFloor. The behaviour it pinned (every RSA size refused on the TPM path) is what this PR changes. It now pins RSA-2048 refused (the error names the 3072 floor) and RSA-3072 and RSA-4096 accepted. No other existing test was changed.Docs
docs/vmca-subordination.md: the "not supported, see feat(tpm): hold an RSA CA key in the TPM #197" line is replaced with a caution callout. It covers the RSA-2048-only guarantee, how the node fails when the TPM lacks the size, the boot log line, and what to do instead.README.md: the TPM-bound identity bullet, the swtpm test prerequisite, and theci-goline.docs/stub-pages). Merge them together with this PR:docs/reference/machine-config-pki.md,machine-config.md): 89e398bdocs/use-cases/air-gapped-root.md): 720c38edocs/deep-dives/keys-never-leave-tpm.md): b7252c8Overlap with other open PRs
docs/vmca-subordination.md, turning it into a callout. Whichever merges second resolves it: keep this PR's text inside docs: OS-specific commands and risk callouts #277's callout form.README.md, and feat(init): keep the node clock in sync with a client-only SNTP #271 touchesinternal/init/run.go. The hunks don't overlap, so no merge order is needed.Closes #197
Verification
How this was verified
task ci(fmt, golangci-lint, vet,go test ./..., build) passes locally withswtpm0.10.2 on PATH, so the swtpm end-to-end test ran and passed. Locally,TMPDIR=/tmpwas needed because macOS's long default temp path breaks the existinginternal/grpcunix-socket test. That's unrelated to this change, and the test passes with a short path.TPM_RC_RETRY. On a node, the Linux TPM driver behind/dev/tpmrm0resends a command that getsTPM_RC_RETRYorTPM_RC_TESTING. The test's raw swtpm socket has no driver, so the test transport now resends the same way (second commit). Production code is unchanged by that fix. CI is now green, and the end-to-end test ran there against swtpm.actionlintis clean onci-go.yml. The pre-push hook passed.FailedPreconditiontests failed before the implementation. The ceremony test failed withInternal, and the probe and signer tests didn't compile.