Skip to content

feat(tpm): hold an RSA CA key in the TPM #197

Description

@Bugs5382

Summary

RSA CA support (#194) is scoped to the software key path, so on a TPM-backed node the CA key remains ECDSA P-384 and an RSA CA is unavailable. Holding the RSA CA key in the TPM is the remaining gap.

Nothing in the architecture forbids it: the go-tpm version in use exposes the full RSA surface, the ECC storage root key can wrap an RSA child, and TPM2_Sign performs the PKCS#1 padding inside the TPM so the signer stays a pure delegation. What blocks it is that every layer is written to one concrete algorithm:

  • The key template is ECC-only and errors on any other algorithm.
  • The public-area parser rejects a non-ECC key and is called unconditionally on load, so even a hand-created RSA key could not be reloaded.
  • The in-memory key holds a concrete *ecdsa.PublicKey.
  • The signer hardcodes the algorithm guard, a 48-byte digest length, the ECDSA signing scheme, the ECDSA signature extraction, and an ASN.1 r/s wrap. RSA needs the RSASSA extraction and the raw signature returned unwrapped, since ASN.1-wrapping it would corrupt it.
  • The signer discards crypto.SignerOpts. That is harmless for ECDSA but not for RSA, where the opts are the only way to distinguish PKCS#1 v1.5 from PSS and to learn the hash.
  • Capability probing only queries ECC curves, and boot hard-fails unless the TPM advertises P-384. An RSA mode needs algorithm and RSA-key-size probing plus a matching branch in that check.

Acceptance criteria

  • A TPM-resident RSA CA key can be created, reloaded across a reboot, and used to sign.
  • The signer honours crypto.SignerOpts rather than relying on the scheme baked into the key template.
  • Capability probing reports RSA support and the available key sizes, and the boot check fails fast when the configured algorithm is unavailable rather than at first signing.
  • ECDSA P-384 behaviour on the TPM path is unchanged.

Notes

Two decisions to make before starting, both consequences of hardware rather than code:

  • Most TPM 2.0 parts implement only RSA-2048; 3072 is uncommon and 4096 is rare. A TPM-resident RSA CA would in practice be a 2048-bit CA certifying 3072-bit subject keys, since the subject-key floor is already 3072. That inversion should be decided deliberately rather than inherited.
  • RSA key generation on a TPM takes seconds to tens of seconds, which lands in the first-boot ceremony and in CA-key rotation.

Activity

  1. added this to the v1.1.0 milestone on Sep 17, 2026
  2. self-assigned this
    on Sep 17, 2026
  3. Bugs5382 commented on Sep 17, 2026

    @Bugs5382
    ContributorAuthor

    Constraint from #203, now on main: RSA-2048 is no longer an accepted
    pki.root_key_alg, because ca.ValidateSubjectKey will not certify an RSA key
    below 3072 bits and a CA certificate's subject is the CA's own key. SelfSignRoot
    enforces that floor too as of #204.

    tpm.AlgorithmRSA2048 still exists in the TPM layer but is unreachable from
    config. So this issue now has to answer the key-size question head on, rather than
    inheriting it: if the target parts only implement RSA-2048, a TPM-resident RSA CA
    key cannot be certified by CryptOS at all under the current floor, and the work
    needs either parts that do RSA-3072 or a deliberate decision about the floor for
    TPM-resident keys specifically.

  4. added theissue type on Sep 29, 2026
  5. modified the milestones: v1.1.0, v1.0.0 on Sep 30, 2026
  6. linked a pull request that will close this issuefeat(tpm): hold an RSA CA key in the TPM #282on Sep 30, 2026
  7. Bugs5382 commented on Sep 30, 2026

    @Bugs5382
    ContributorAuthor

    Notes from the work in #282:

    • Key size. The 3072-bit floor stays for TPM-held keys too, so there's no 2048-bit CA certifying 3072-bit subjects. On a TPM without the configured size, CreateKey asks the TPM with TPM2_TestParms first and returns tpm.ErrKeyAlgorithmUnsupported. The ceremony and CA-key rotation return that as FailedPrecondition. A subordinate node's first-boot key creation fails closed. Nothing falls back to a smaller size or to software.
    • Test coverage. The in-process simulator (go-tpm-tools' build of the Microsoft reference TPM) implements RSA-2048 only, and enabling RSA-3072 with a define crashes it. The simulator tests cover the refusal and the RSA signing mechanics on a 2048-bit key. A TPM-held RSA-3072 key is tested end to end against swtpm, which ci-go now installs.
    • RSA-4096. swtpm 0.10.2 implements RSA-2048 and RSA-3072 but not RSA-4096, so a TPM-held RSA-4096 key has only been checked on its refusal path.
    • Also fixed in the same PR. CA-key rotation (BeginRotation) got the same FailedPrecondition mapping as the ceremony, since it creates keys through the same backend.
  8. Bugs5382 commented on Sep 30, 2026

    @Bugs5382
    ContributorAuthor

    Done in #282 (7e2b05c).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature (feat). Minor version bump.

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions