mirror of
https://github.com/stefanberger/libtpms
synced 2026-08-10 09:31:02 +00:00
tpm2: EcSchnorr: Enforce that the OpenSSL-generated bnD has no leading zeros
To avoid a potential side channel in the EcSchnorr signing algorithm, enforce that the OpenSSL-generated bnD does not have leading zeros that may then cause a timing side channel in the BnEccModMult() operation. We modified BnEccGenerateKeyPair() so it calls BnEccModMult with a scalar of constant number of bytes (for a particular curve): In this version of BnEccGenerateKeyPair we take a dual approach to constant time requirements: For curves whose order is at the byte boundary, e.g. NIST P224/P256/P384, we make sure that bnD has all bytes set (no leading zeros) so that OpenSSL BIGNUM code will not reduce the number of bytes and the subsequent BnEccModMult() would run faster for a shoter value. For all other curves whose order is not at the byte boundary, e.g. NIST P521, we simply always add the order to bnD and call BnEccModMult() with the result bnD1, which leads to the same result. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> Suggested-by: Charanjit Jutla <csjutla@us.ibm.com> Reviewed-by: Charanjit Jutla <csjutla@us.ibm.com> Tested-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
parent
8583927139
commit
5c224b8d98
@ -169,6 +169,8 @@ BnEccGetPrivate(
|
||||
const ECC_CURVE_DATA *C, // IN: curve for which the private key
|
||||
#if USE_OPENSSL_FUNCTIONS_EC
|
||||
const EC_GROUP *G, // IN: the EC_GROUP to use; must be != NULL for rand == NULL
|
||||
BOOL noLeadingZeros, // IN: require that all bytes in the private key be set
|
||||
// result may not have leading zero bytes
|
||||
#endif
|
||||
// needs to be appropriate
|
||||
RAND_STATE *rand // IN: state for DRBG
|
||||
|
||||
@ -575,6 +575,8 @@ BnEccGetPrivate(
|
||||
bigNum dOut, // OUT: the qualified random value
|
||||
const ECC_CURVE_DATA *C, // IN: curve for which the private key
|
||||
const EC_GROUP *G, // IN: the EC_GROUP to use; must be != NULL for rand == NULL
|
||||
BOOL noLeadingZeros, // IN: require that all bytes in the private key be set
|
||||
// result may not have leading zero bytes
|
||||
// needs to be appropriate
|
||||
RAND_STATE *rand // IN: state for DRBG
|
||||
)
|
||||
@ -583,11 +585,16 @@ BnEccGetPrivate(
|
||||
BOOL OK;
|
||||
UINT32 orderBits = BnSizeInBits(order);
|
||||
UINT32 orderBytes = BITS_TO_BYTES(orderBits);
|
||||
UINT32 requestedBits = 0;
|
||||
BN_VAR(bnExtraBits, MAX_ECC_KEY_BITS + 64);
|
||||
BN_VAR(nMinus1, MAX_ECC_KEY_BITS);
|
||||
|
||||
if (rand == NULL)
|
||||
return OpenSSLEccGetPrivate(dOut, G);
|
||||
if (rand == NULL) {
|
||||
if (noLeadingZeros)
|
||||
requestedBits = orderBits;
|
||||
|
||||
return OpenSSLEccGetPrivate(dOut, G, requestedBits);
|
||||
}
|
||||
|
||||
//
|
||||
OK = BnGetRandomBits(bnExtraBits, (orderBytes * 8) + 64, rand);
|
||||
@ -600,6 +607,8 @@ BnEccGetPrivate(
|
||||
/* 10.2.11.2.21 BnEccGenerateKeyPair() */
|
||||
/* This function gets a private scalar from the source of random bits and does the point multiply to
|
||||
get the public key. */
|
||||
#if !USE_OPENSSL_FUNCTIONS_EC // libtpms added
|
||||
|
||||
BOOL
|
||||
BnEccGenerateKeyPair(
|
||||
bigNum bnD, // OUT: private scalar
|
||||
@ -610,11 +619,7 @@ BnEccGenerateKeyPair(
|
||||
{
|
||||
BOOL OK = FALSE;
|
||||
// Get a private scalar
|
||||
#if USE_OPENSSL_FUNCTIONS_EC // libtpms added beging
|
||||
OK = BnEccGetPrivate(bnD, AccessCurveData(E), E->G, rand);
|
||||
#else // libtpms added end
|
||||
OK = BnEccGetPrivate(bnD, AccessCurveData(E), rand);
|
||||
#endif // libtpms added
|
||||
// Do a point multiply
|
||||
OK = OK && BnEccModMult(ecQ, NULL, bnD, E);
|
||||
if(!OK)
|
||||
@ -623,6 +628,54 @@ BnEccGenerateKeyPair(
|
||||
BnSetWord(ecQ->z, 1);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#else // libtpms added begin
|
||||
|
||||
/* In this version of BnEccGenerateKeyPair we take a dual approach to constant
|
||||
time requirements: For curves whose order is at the byte boundary, e.g.
|
||||
NIST P224/P256/P384, we make sure that bnD has all bytes set (no leading zeros)
|
||||
so that OpenSSL BIGNUM code will not reduce the number of bytes and the
|
||||
subsequent BnEccModMult() would run faster for a shoter value. For all other
|
||||
curves whose order is not at the byte boundary, e.g. NIST P521, we simply
|
||||
always add the order of the curve to bnD and call BnEccModMult() with the
|
||||
result bnD1, which leads to the same result. */
|
||||
BOOL
|
||||
BnEccGenerateKeyPair(
|
||||
bigNum bnD, // OUT: private scalar
|
||||
bn_point_t *ecQ, // OUT: public point
|
||||
bigCurve E, // IN: curve for the point
|
||||
RAND_STATE *rand // IN: DRBG state to use
|
||||
)
|
||||
{
|
||||
BOOL OK = FALSE;
|
||||
bigConst order = CurveGetOrder(AccessCurveData(E));
|
||||
UINT32 orderBits = BnSizeInBits(order);
|
||||
BOOL atByteBoundary = (orderBits & 7) == 0;
|
||||
BOOL noLeadingZeros = atByteBoundary;
|
||||
ECC_NUM(bnD1);
|
||||
|
||||
// We request that bnD not have leading zeros if it is at byte-boundary,
|
||||
// like for example it is the case for NIST P256.
|
||||
OK = BnEccGetPrivate(bnD, AccessCurveData(E), E->G, noLeadingZeros, rand);
|
||||
if (!atByteBoundary) {
|
||||
// for NIST P521 we can add the order to bnD to ensure we have
|
||||
// a constant amount of bytes; the result is the same as if we
|
||||
// were doing the BnEccModMult() calculation with bnD.
|
||||
OK = OK && BnAdd(bnD1, bnD, order);
|
||||
OK = OK && BnEccModMult(ecQ, NULL, bnD1, E);
|
||||
} else {
|
||||
OK = OK && BnEccModMult(ecQ, NULL, bnD, E);
|
||||
}
|
||||
|
||||
if(!OK)
|
||||
BnSetWord(ecQ->z, 0);
|
||||
else
|
||||
BnSetWord(ecQ->z, 1);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif // libtpms added end
|
||||
|
||||
/* 10.2.12.2.21 CryptEccNewKeyPair */
|
||||
/* This function creates an ephemeral ECC. It is ephemeral in that is expected that the private part
|
||||
of the key will be discarded */
|
||||
|
||||
@ -303,7 +303,7 @@ BnSignEcdaa(
|
||||
// generate nonceK such that 0 < nonceK < n
|
||||
// use bnT as a temp.
|
||||
#if USE_OPENSSL_FUNCTIONS_EC // libtpms added begin
|
||||
if(!BnEccGetPrivate(bnT, AccessCurveData(E), E->G, rand))
|
||||
if(!BnEccGetPrivate(bnT, AccessCurveData(E), E->G, false, rand))
|
||||
#else // libtpms added end
|
||||
if(!BnEccGetPrivate(bnT, AccessCurveData(E), rand))
|
||||
#endif // libtpms added
|
||||
|
||||
@ -210,12 +210,17 @@ evpfunc GetEVPCipher(TPM_ALG_ID algorithm, // IN
|
||||
BOOL
|
||||
OpenSSLEccGetPrivate(
|
||||
bigNum dOut, // OUT: the qualified random value
|
||||
const EC_GROUP *G // IN: the EC_GROUP to use
|
||||
const EC_GROUP *G, // IN: the EC_GROUP to use
|
||||
const UINT32 requestedBits // IN: if not 0, then dOut must have that many bits
|
||||
)
|
||||
{
|
||||
BOOL OK = FALSE;
|
||||
const BIGNUM *D;
|
||||
EC_KEY *eckey = EC_KEY_new();
|
||||
UINT32 requestedBytes = BITS_TO_BYTES(requestedBits);
|
||||
int repeats = 0;
|
||||
int maxRepeats;
|
||||
int numBytes;
|
||||
|
||||
pAssert(G != NULL);
|
||||
|
||||
@ -225,10 +230,31 @@ OpenSSLEccGetPrivate(
|
||||
if (EC_KEY_set_group(eckey, G) != 1)
|
||||
goto Exit;
|
||||
|
||||
if (EC_KEY_generate_key(eckey) == 1) {
|
||||
OK = TRUE;
|
||||
D = EC_KEY_get0_private_key(eckey);
|
||||
OsslToTpmBn(dOut, D);
|
||||
maxRepeats = 8;
|
||||
// non-byte boundary order'ed curves, like NIST P521, need more loops to
|
||||
// have a result with topmost byte != 0
|
||||
if (requestedBits & 7)
|
||||
maxRepeats += (9 - (requestedBits & 7));
|
||||
|
||||
while (true) {
|
||||
if (EC_KEY_generate_key(eckey) == 1) {
|
||||
D = EC_KEY_get0_private_key(eckey);
|
||||
// if we need a certain amount of bytes and we are below a threshold
|
||||
// of loops, check the number of bytes we have, otherwise take the
|
||||
// result
|
||||
if ((requestedBytes != 0) && (repeats < maxRepeats)) {
|
||||
numBytes = BN_num_bytes(D);
|
||||
if ((int)requestedBytes != numBytes) {
|
||||
// result does not have enough bytes
|
||||
repeats++;
|
||||
continue;
|
||||
}
|
||||
// result is sufficient
|
||||
}
|
||||
OK = TRUE;
|
||||
OsslToTpmBn(dOut, D);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
@ -85,7 +85,8 @@ evpfunc GetEVPCipher(TPM_ALG_ID algorithm, // IN
|
||||
#if USE_OPENSSL_FUNCTIONS_EC
|
||||
BOOL OpenSSLEccGetPrivate(
|
||||
bigNum dOut, // OUT: the qualified random value
|
||||
const EC_GROUP *G // IN: the EC_GROUP to use
|
||||
const EC_GROUP *G, // IN: the EC_GROUP to use
|
||||
const UINT32 requestedBits // IN: if not 0, then dOut must have that many bits
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user