From 920aa5e02bb5a988b1f9fdde29de629dbfa883db Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 3 Dec 2023 15:05:13 -0500 Subject: [PATCH] tpm2: Use Carmichael function for RSA priv. exponent D (>= 2048 bits) Like OpenSSL use the Carmichael function for the RSA private exponent D when an RSA key has >= 2048 bits and public exponent e uses more than 2 bytes. Otherwise use the Euler totient function. The main difference is that by TPM 2 using the Carmichael function OpenSSL now behaves the same way as when it is used by other programs that for example load keys from PEM files where the private exponent D was calculated with this function. The difference is seen when for example blobs cannot be decrypted where newer versions of OpenSSL (with implicit rejection enabled) returned results of 48 bytes every time rather than a deterministic (for same input blob) but varying number of bytes (Euler totient). Switching to the Carmichael function does not have any negative impact on interoperatibility with OpenSSL nor does it affect interoperability between versions of TPM 2 code that did not use it. This means that data encrypted or signed by OpenSSL or TPM 2 can be decrypted or verified by TPM 2 or OpenSSL and that data encrypted or signed with either new or old code in TPM 2 can be decrypted or verified with either old or new code in TPM 2. Signed-off-by: Stefan Berger --- src/tpm2/crypto/openssl/Helpers.c | 74 ++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/tpm2/crypto/openssl/Helpers.c b/src/tpm2/crypto/openssl/Helpers.c index 66b6fa48..17bea746 100644 --- a/src/tpm2/crypto/openssl/Helpers.c +++ b/src/tpm2/crypto/openssl/Helpers.c @@ -471,7 +471,7 @@ GetDigestNameByHashAlg(const TPM_ALG_ID hashAlg) } static BOOL -ComputePrivateExponentD( +ComputePrivateExponentD_Euler( const BIGNUM *P, // IN: first prime (size is 1/2 of bnN) const BIGNUM *Q, // IN: second prime (size is 1/2 of bnN) const BIGNUM *E, // IN: the public exponent @@ -500,6 +500,78 @@ ComputePrivateExponentD( return pOK; } +static BOOL +ComputePrivateExponentD_Carmichael( + const BIGNUM *P, // IN: first prime (size is 1/2 of bnN) + const BIGNUM *Q, // IN: second prime (size is 1/2 of bnN) + const BIGNUM *E, // IN: the public exponent + const BIGNUM *N, // IN: the public modulus + BIGNUM **D // OUT: + ) +{ + BOOL pOK = FALSE; + BN_CTX *ctx; + BIGNUM *pm1, *qm1, *pm1qm1, *gcd, *lcm; + + ctx = BN_CTX_new(); + if (!ctx) + return FALSE; + + BN_CTX_start(ctx); + pm1 = BN_CTX_get(ctx); + qm1 = BN_CTX_get(ctx); + pm1qm1 = BN_CTX_get(ctx); + gcd = BN_CTX_get(ctx); + lcm = BN_CTX_get(ctx); + if (pm1 && qm1 && pm1qm1 && gcd && lcm) { + BN_set_flags(pm1, BN_FLG_CONSTTIME); + BN_set_flags(qm1, BN_FLG_CONSTTIME); + BN_set_flags(pm1qm1, BN_FLG_CONSTTIME); + BN_set_flags(gcd, BN_FLG_CONSTTIME); + BN_set_flags(lcm, BN_FLG_CONSTTIME); + + /* Carmichael */ + pOK = BN_sub(pm1, P, BN_value_one()); + pOK = pOK && BN_sub(qm1, Q, BN_value_one()); + pOK = pOK && BN_mul(pm1qm1, pm1, qm1, ctx); + pOK = pOK && BN_gcd(gcd, pm1, qm1, ctx); + pOK = pOK && BN_div(lcm, NULL, pm1qm1, gcd, ctx); + pOK = pOK && (*D = BN_mod_inverse(NULL, E, lcm, ctx)) != NULL; + } + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return pOK; +} + +static BOOL +ComputePrivateExponentD( + const BIGNUM *P, // IN: first prime (size is 1/2 of bnN) + const BIGNUM *Q, // IN: second prime (size is 1/2 of bnN) + const BIGNUM *E, // IN: the public exponent + const BIGNUM *N, // IN: the public modulus + BIGNUM **D // OUT: + ) +{ + int nbits = BN_num_bits(N); + /* like OpenSSL: + * < 2048 bits : Euler totient function + * >= 2048 bits & e >= 0x10000: Carmichael function + */ + if (nbits >= 2048 && BN_num_bits(E) > 16) { + if (ComputePrivateExponentD_Carmichael(P, Q, E, N, D) == FALSE) + return FALSE; + /* D too small? A key generated following SP800-56B rev 1 + * 6.3.1.1 step 3 should not exist -> fall back to Euler + */ + if (BN_num_bits(*D) <= (nbits >> 1)) + return ComputePrivateExponentD_Euler(P, Q, E, N, D); + return TRUE; + } else { + return ComputePrivateExponentD_Euler(P, Q, E, N, D); + } +} + #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* Build an RSA key from the given BIGUMs. The caller must always free