blob: af906635a987f05ddc930decbdea008df6ca5048 [file] [log] [blame]
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;
+}
+