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 <stefanb@linux.ibm.com>
This commit is contained in:
Stefan Berger 2023-12-03 15:05:13 -05:00 committed by Stefan Berger
parent 6566330177
commit 920aa5e02b

View File

@ -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