| Description: Upstream patch for CVE-2023-6125, part 1 |
| NOTE: It is unclear what exactly are the fixing parts, and |
| the version in buster has only support for Curve25519 and not |
| for ECCurve_NIST_P256. |
| Therefore this patch is currently not applied, until more |
| information is available, but it is kept for later reference. |
| Origin: (security tracker) https://deb.freexian.com/extended-lts/tracker/CVE-2023-6135 |
| Origin: https://hg.mozilla.org/projects/nss/rev/e68b42b773657000078d104aaccbe26e71a1e0be |
| |
| |
| # HG changeset patch |
| # User Karthikeyan Bhargavan <karthik.bhargavan@gmail.com> |
| # Date 1698703971 0 |
| # Node ID e68b42b773657000078d104aaccbe26e71a1e0be |
| # Parent a69c9f36bb8ac1c47cacb6c6ec9f06309de33951 |
| Bug 1861728 - Include P-256 Scalar Validation from HACL*. r=jschanck |
| |
| Differential Revision: https://phabricator.services.mozilla.com/D192126 |
| |
| --- a/lib/freebl/ec.c |
| +++ b/lib/freebl/ec.c |
| @@ -18,7 +18,8 @@ |
| static const ECMethod kMethods[] = { |
| { ECCurve25519, |
| ec_Curve25519_pt_mul, |
| - ec_Curve25519_pt_validate } |
| + ec_Curve25519_pt_validate, |
| + ec_Curve25519_scalar_validate } |
| }; |
| |
| static const ECMethod * |
| @@ -274,12 +275,12 @@ |
| /* Use curve specific code for point multiplication */ |
| if (ecParams->fieldID.type == ec_field_plain) { |
| const ECMethod *method = ec_get_method_from_name(ecParams->name); |
| - if (method == NULL || method->mul == NULL) { |
| + if (method == NULL || method->pt_mul == NULL) { |
| /* unknown curve */ |
| rv = SECFailure; |
| goto cleanup; |
| } |
| - rv = method->mul(&key->publicValue, &key->privateValue, NULL); |
| + rv = method->pt_mul(&key->publicValue, &key->privateValue, NULL); |
| goto done; |
| } |
| |
| @@ -323,26 +324,56 @@ |
| return rv; |
| } |
| |
| -/* Generate a random private key using the algorithm A.4.1 of ANSI X9.62, |
| +/* Generate a random private key using the algorithm A.4.1 or A.4.2 of ANSI X9.62, |
| * modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the |
| * random number generator. |
| - * |
| - * Parameters |
| - * - order: a buffer that holds the curve's group order |
| - * - len: the length in octets of the order buffer |
| - * |
| - * Return Value |
| - * Returns a buffer of len octets that holds the private key. The caller |
| - * is responsible for freeing the buffer with PORT_ZFree. |
| */ |
| -static unsigned char * |
| -ec_GenerateRandomPrivateKey(const unsigned char *order, int len) |
| + |
| +SECStatus |
| +ec_GenerateRandomPrivateKey(ECParams *ecParams, SECItem *privKey) |
| { |
| SECStatus rv = SECSuccess; |
| mp_err err; |
| - unsigned char *privKeyBytes = NULL; |
| + |
| + unsigned int len = EC_GetScalarSize(ecParams); |
| + |
| + if (privKey->len != len || privKey->data == NULL) { |
| + PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| + return SECFailure; |
| + } |
| + |
| + /* For known curves, use rejection sampling A.4.2 */ |
| + if (ecParams->fieldID.type == ec_field_plain) { |
| + const ECMethod *method = ec_get_method_from_name(ecParams->name); |
| + rv = SECFailure; |
| + if (method == NULL || method->scalar_validate == NULL) { |
| + /* unknown curve */ |
| + PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| + goto done; |
| + } |
| + int count = 100; |
| + while (rv != SECSuccess && count >= 0) { |
| + rv = RNG_GenerateGlobalRandomBytes(privKey->data, len); |
| + if (rv != SECSuccess) { |
| + PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| + goto done; |
| + } |
| + rv = method->scalar_validate(privKey); |
| + count--; |
| + } |
| + if (rv != SECSuccess) { |
| + PORT_SetError(SEC_ERROR_BAD_KEY); |
| + } |
| + goto done; |
| + } |
| + |
| + /* For unknown curves, use algotithm A.4.1 */ |
| + |
| + unsigned char *order = ecParams->order.data; |
| mp_int privKeyVal, order_1, one; |
| |
| + unsigned char *privKeyBytes = NULL; |
| + |
| MP_DIGITS(&privKeyVal) = 0; |
| MP_DIGITS(&order_1) = 0; |
| MP_DIGITS(&one) = 0; |
| @@ -354,8 +385,13 @@ |
| * (which implements Algorithm 1 of FIPS 186-2 Change Notice 1) then |
| * reduces modulo the group order. |
| */ |
| - if ((privKeyBytes = PORT_Alloc(2 * len)) == NULL) |
| + |
| + if ((privKeyBytes = PORT_Alloc(2 * len)) == NULL) { |
| + PORT_SetError(SEC_ERROR_NO_MEMORY); |
| + rv = SECFailure; |
| goto cleanup; |
| + } |
| + |
| CHECK_SEC_OK(RNG_GenerateGlobalRandomBytes(privKeyBytes, 2 * len)); |
| CHECK_MPI_OK(mp_read_unsigned_octets(&privKeyVal, privKeyBytes, 2 * len)); |
| CHECK_MPI_OK(mp_read_unsigned_octets(&order_1, order, len)); |
| @@ -364,20 +400,26 @@ |
| CHECK_MPI_OK(mp_mod(&privKeyVal, &order_1, &privKeyVal)); |
| CHECK_MPI_OK(mp_add(&privKeyVal, &one, &privKeyVal)); |
| CHECK_MPI_OK(mp_to_fixlen_octets(&privKeyVal, privKeyBytes, len)); |
| - memset(privKeyBytes + len, 0, len); |
| + memcpy(privKey->data, privKeyBytes, len); |
| + |
| cleanup: |
| mp_clear(&privKeyVal); |
| mp_clear(&order_1); |
| mp_clear(&one); |
| + if (privKeyBytes) { |
| + PORT_ZFree(privKeyBytes, 2 * len); |
| + } |
| if (err < MP_OKAY) { |
| MP_TO_SEC_ERROR(err); |
| rv = SECFailure; |
| } |
| - if (rv != SECSuccess && privKeyBytes) { |
| - PORT_ZFree(privKeyBytes, 2 * len); |
| - privKeyBytes = NULL; |
| + |
| +done: |
| + if (rv != SECSuccess && privKey->data) { |
| + SECITEM_ZfreeItem(privKey, PR_FALSE); |
| + return rv; |
| } |
| - return privKeyBytes; |
| + return rv; |
| } |
| |
| /* Generates a new EC key pair. The private key is a random value and |
| @@ -388,24 +430,28 @@ |
| EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey) |
| { |
| SECStatus rv = SECFailure; |
| - int len; |
| - unsigned char *privKeyBytes = NULL; |
| + SECItem privKeyRand = { siBuffer, NULL, 0 }; |
| |
| if (!ecParams || ecParams->name == ECCurve_noName || !privKey) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| - len = ecParams->order.len; |
| - privKeyBytes = ec_GenerateRandomPrivateKey(ecParams->order.data, len); |
| - if (privKeyBytes == NULL) |
| + SECITEM_AllocItem(NULL, &privKeyRand, EC_GetScalarSize(ecParams)); |
| + if (privKeyRand.data == NULL) { |
| + PORT_SetError(SEC_ERROR_NO_MEMORY); |
| + rv = SECFailure; |
| + goto cleanup; |
| + } |
| + rv = ec_GenerateRandomPrivateKey(ecParams, &privKeyRand); |
| + if (rv != SECSuccess || privKeyRand.data == NULL) |
| goto cleanup; |
| /* generate public key */ |
| - CHECK_SEC_OK(ec_NewKey(ecParams, privKey, privKeyBytes, len)); |
| + CHECK_SEC_OK(ec_NewKey(ecParams, privKey, privKeyRand.data, privKeyRand.len)); |
| |
| cleanup: |
| - if (privKeyBytes) { |
| - PORT_ZFree(privKeyBytes, len); |
| + if (privKeyRand.data) { |
| + SECITEM_ZfreeItem(&privKeyRand, PR_FALSE); |
| } |
| #if EC_DEBUG |
| printf("EC_NewKey returning %s\n", |
| @@ -439,12 +485,12 @@ |
| /* Uses curve specific code for point validation. */ |
| if (ecParams->fieldID.type == ec_field_plain) { |
| const ECMethod *method = ec_get_method_from_name(ecParams->name); |
| - if (method == NULL || method->validate == NULL) { |
| + if (method == NULL || method->pt_validate == NULL) { |
| /* unknown curve */ |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| - return method->validate(publicValue); |
| + return method->pt_validate(publicValue); |
| } |
| |
| /* NOTE: We only support uncompressed points for now */ |
| @@ -563,12 +609,12 @@ |
| return SECFailure; |
| } |
| method = ec_get_method_from_name(ecParams->name); |
| - if (method == NULL || method->validate == NULL || |
| - method->mul == NULL) { |
| + if (method == NULL || method->pt_validate == NULL || |
| + method->pt_mul == NULL) { |
| PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); |
| return SECFailure; |
| } |
| - rv = method->mul(derivedSecret, privateValue, publicValue); |
| + rv = method->pt_mul(derivedSecret, privateValue, publicValue); |
| if (rv != SECSuccess) { |
| SECITEM_ZfreeItem(derivedSecret, PR_FALSE); |
| } |
| @@ -886,8 +932,7 @@ |
| ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest) |
| { |
| SECStatus rv = SECFailure; |
| - int len; |
| - unsigned char *kBytes = NULL; |
| + SECItem nonceRand = { siBuffer, NULL, 0 }; |
| |
| if (!key) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| @@ -895,17 +940,22 @@ |
| } |
| |
| /* Generate random value k */ |
| - len = key->ecParams.order.len; |
| - kBytes = ec_GenerateRandomPrivateKey(key->ecParams.order.data, len); |
| - if (kBytes == NULL) |
| + SECITEM_AllocItem(NULL, &nonceRand, EC_GetScalarSize(&key->ecParams)); |
| + if (nonceRand.data == NULL) { |
| + PORT_SetError(SEC_ERROR_NO_MEMORY); |
| + rv = SECFailure; |
| + goto cleanup; |
| + } |
| + rv = ec_GenerateRandomPrivateKey(&key->ecParams, &nonceRand); |
| + if (rv != SECSuccess || nonceRand.data == NULL) |
| goto cleanup; |
| |
| /* Generate ECDSA signature with the specified k value */ |
| - rv = ECDSA_SignDigestWithSeed(key, signature, digest, kBytes, len); |
| + rv = ECDSA_SignDigestWithSeed(key, signature, digest, nonceRand.data, nonceRand.len); |
| |
| cleanup: |
| - if (kBytes) { |
| - PORT_ZFree(kBytes, len); |
| + if (nonceRand.data) { |
| + SECITEM_ZfreeItem(&nonceRand, PR_FALSE); |
| } |
| |
| #if EC_DEBUG |
| --- a/lib/freebl/ec.h |
| +++ b/lib/freebl/ec.h |
| @@ -13,8 +13,9 @@ |
| |
| struct ECMethodStr { |
| ECCurveName name; |
| - SECStatus (*mul)(SECItem *result, SECItem *scalar, SECItem *point); |
| - SECStatus (*validate)(const SECItem *point); |
| + SECStatus (*pt_mul)(SECItem *result, SECItem *scalar, SECItem *point); |
| + SECStatus (*pt_validate)(const SECItem *point); |
| + SECStatus (*scalar_validate)(const SECItem *scalar); |
| }; |
| typedef struct ECMethodStr ECMethod; |
| |
| --- a/lib/freebl/ecl/ecl.h |
| +++ b/lib/freebl/ecl/ecl.h |
| @@ -45,5 +45,6 @@ |
| |
| SECStatus ec_Curve25519_pt_mul(SECItem *X, SECItem *k, SECItem *P); |
| SECStatus ec_Curve25519_pt_validate(const SECItem *px); |
| +SECStatus ec_Curve25519_scalar_validate(const SECItem *scalar); |
| |
| #endif /* __ecl_h_ */ |
| --- a/lib/freebl/ecl/ecp_25519.c |
| +++ b/lib/freebl/ecl/ecp_25519.c |
| @@ -15,6 +15,7 @@ |
| #include "mpi-priv.h" |
| #include "secmpi.h" |
| #include "secitem.h" |
| +#include "secerr.h" |
| #include "secport.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| @@ -94,6 +95,24 @@ |
| return SECSuccess; |
| } |
| |
| +/* |
| + * scalar validation is not necessary. |
| + */ |
| +SECStatus |
| +ec_Curve25519_scalar_validate(const SECItem *scalar) |
| +{ |
| + if (!scalar || !scalar->data) { |
| + PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| + return SECFailure; |
| + } |
| + |
| + if (scalar->len != 32) { |
| + PORT_SetError(SEC_ERROR_BAD_KEY); |
| + return SECFailure; |
| + } |
| + return SECSuccess; |
| +} |
| + |
| /* |
| * Scalar multiplication for Curve25519. |
| * If P == NULL, the base point is used. |
| --- a/lib/freebl/blapi.h |
| +++ b/lib/freebl/blapi.h |
| @@ -1695,6 +1695,12 @@ |
| */ |
| extern int EC_GetPointSize(const ECParams *params); |
| |
| +// needed for the patch for CVE-2023-6135 |
| +/* |
| + * use the internal table to get the size in bytes of a single EC coordinate |
| + */ |
| +extern int EC_GetScalarSize(const ECParams *params); |
| + |
| SEC_END_PROTOS |
| |
| #endif /* _BLAPI_H_ */ |
| --- a/lib/freebl/ecdecode.c |
| +++ b/lib/freebl/ecdecode.c |
| @@ -250,3 +250,19 @@ |
| } |
| return curveParams->pointSize - 1; |
| } |
| + |
| +int |
| +EC_GetScalarSize(const ECParams *params) |
| +{ |
| + ECCurveName name = params->name; |
| + const ECCurveBytes *curveParams; |
| + |
| + if ((name < ECCurve_noName) || (name > ECCurve_pastLastCurve) || |
| + ((curveParams = ecCurve_map[name]) == NULL)) { |
| + /* unknown curve, calculate scalar size from field size in params */ |
| + int sizeInBytes = (params->fieldID.size + 7) / 8; |
| + return sizeInBytes; |
| + } |
| + return curveParams->scalarSize; |
| +} |
| + |