Skip to content

Switch to new RSA key generation algorithm

Stefan Berger edited this page Apr 12, 2020 · 4 revisions

Background

The update to TPM 2 revision 159 included a fixed RSA key generation algorithm. The older version created keys with a few bits less strength than the requested amount of bits.

The TPM 2 derives keys from seeds. If the key generation algorithm changes (due to a fix), the derived keys will also change and they will not be able to decrypt data previously encrypted before the fix was applied. To work around this problem, libtpms introduced a SEED_COMPAT_LEVEL that identifies a seed's compatibility level and this SEED_COMPAT_LEVEL chooses which key generation algorithm to use, the old one or the new one. By default the old one is used and only once a key hierarchy was cleared (TPM2_Clear for the SP Seed, TPM2_ChangePPS for PP Seed, TPM2_ChangeEPS for EP Seed) it will use the new key generation algorithm for a hierarchy with the new seed. The rationale is that after a hierarchy clear operation old keys are not expected to work anymore, so we don't loose any keys or decryption/signing functionality that we don't expect to loose anyway, and we can switch the user to have better keys.

The changes to libtpms's TPM 2 code provide backwards compatibility for RSA key derivation when upgrading from an older version of the code to a newer one (downgrades are not possible!) while keeping the functionality associated with the keys and being able to get better keys as well.

So the following is true for TPM 2: Each key hierarchy completely changes when its seed changed. Once a seed is changed, new primary keys will be created and all other derived and deriving keys in this hierarchy will also be different than before the seed was reset. TPM2_ChangeEPS() [never to be exposed since it would invalidate EK certs] changes EPSeed and ehPRoof. TPM2_ChangePPS() changes PPSeed and phProof. TPM2_Clear() changes SPSeed and shProof and ehProof. A reset of the TPM2 changes nullSeed and nullProof [HierachyStartup()].

Affected RSA keys:

  • NULL hierarchy; due to nullSeed; regenerated upon every reset of the TPM 2
  • KDF-derived RSA keys in 3 normal hierarchies
  • OBJECTS inherit COMPAT_LEVEL from seed or parent; COMPAT_LEVEL is part of swapped-out state

Unaffected RSA keys:

  • It is not possible to derive keys from an external (e.g., OpenSSL) key. "Child keys require a parent that is fixedTPM, so imported keys cannot be a parent." [KG]

Other unaffected keys -- anything non RSA related:

  • Ellicptic Curve
  • AES
  • TDES

APIs to create keys

  • TPM2_Create(): only random RSA keys are created; when a key is loaded using TPM2_Load() it gets the seedCompatLevel of the parent object, which must be the seedCompatLevel at the time of the TPM2_Create() call
  • TPM2_CreateLoaded(): if the parent key is a derivation key then RSA keys cannot be derived from it; if there is no parent key then a primary object is created derived from a seed of its hierarchy, otherwise it's a purely random key (rand = NULL)
  • TPM2_CreatePrimary(): primary RSA keys are derived from the seed of their hierarchy

Files of interest:

  • src/tpm2/BackwardsCompatibility.h: SEED_COMPAT_LEVEL definition
  • src/tpm2/crypto/openssl/CryptPrime.c: old and new key generation code
  • src/tpm2/crypto/openssl/CryptRand.c: DRBG_InstantiateSeeded gets SEED_COMPAT_LEVEL
  • src/tpm2/Global.h: seedCompatLevel is part of OBJECT
  • src/tpm2/Object.c: Calls ObjectSetLoadedAttributes() with SEED_COMPAT_LEVEL
  • src/tpm2/ObjectCommands.c: TPM2_Load(), TPM2_LoadExternal(), and TPM2_CreateLoaded() call ObjectSetLoadedAttributes() with SEED_COMPAT_LEVEL which then stores it in an OBJECT
  • src/tpm2/Hierarchy.c: seed compatibility level associated with NULL, EP, SP, and PP Seeds
  • src/tpm2/HierarchyCommands.c: TPM2_CreatePrimary() calls ObjectSetLoadedAttributes() with SEED_COMPAT_LEVEL
  • src/tpm2/NVMarshal.c: Object writes and reads seedCompatLevel