diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /security/nss/lib/freebl/ec.c | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/freebl/ec.c')
-rw-r--r-- | security/nss/lib/freebl/ec.c | 1319 |
1 files changed, 1319 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/ec.c b/security/nss/lib/freebl/ec.c new file mode 100644 index 0000000000..35a848395c --- /dev/null +++ b/security/nss/lib/freebl/ec.c @@ -0,0 +1,1319 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef FREEBL_NO_DEPEND +#include "stubs.h" +#endif + +#include "blapi.h" +#include "blapii.h" +#include "prerr.h" +#include "secerr.h" +#include "secmpi.h" +#include "secitem.h" +#include "mplogic.h" +#include "ec.h" +#include "ecl.h" +#include "verified/Hacl_P384.h" +#include "verified/Hacl_P521.h" +#include "secport.h" + +#define EC_DOUBLECHECK PR_FALSE + +SECStatus +ec_secp384r1_scalar_validate(const SECItem *scalar) +{ + if (!scalar || !scalar->data) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (scalar->len != 48) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + bool b = Hacl_P384_validate_private_key(scalar->data); + + if (!b) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +ec_secp521r1_scalar_validate(const SECItem *scalar) +{ + if (!scalar || !scalar->data) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (scalar->len != 66) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + bool b = Hacl_P521_validate_private_key(scalar->data); + + if (!b) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + return SECSuccess; +} + +static const ECMethod kMethods[] = { + { ECCurve25519, + ec_Curve25519_pt_mul, + ec_Curve25519_pt_validate, + ec_Curve25519_scalar_validate, + NULL, + NULL }, + { + ECCurve_NIST_P256, + ec_secp256r1_pt_mul, + ec_secp256r1_pt_validate, + ec_secp256r1_scalar_validate, + ec_secp256r1_sign_digest, + ec_secp256r1_verify_digest, + }, + { + ECCurve_NIST_P384, + NULL, + NULL, + ec_secp384r1_scalar_validate, + NULL, + NULL, + }, + { + ECCurve_NIST_P521, + NULL, + NULL, + ec_secp521r1_scalar_validate, + NULL, + NULL, + }, +}; + +static const ECMethod * +ec_get_method_from_name(ECCurveName name) +{ + unsigned long i; + for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); ++i) { + if (kMethods[i].name == name) { + return &kMethods[i]; + } + } + return NULL; +} + +/* + * Returns true if pointP is the point at infinity, false otherwise + */ +PRBool +ec_point_at_infinity(SECItem *pointP) +{ + unsigned int i; + + for (i = 1; i < pointP->len; i++) { + if (pointP->data[i] != 0x00) + return PR_FALSE; + } + + return PR_TRUE; +} + +/* + * Computes scalar point multiplication pointQ = k1 * G + k2 * pointP for + * the curve whose parameters are encoded in params with base point G. + */ +SECStatus +ec_points_mul(const ECParams *params, const mp_int *k1, const mp_int *k2, + const SECItem *pointP, SECItem *pointQ) +{ + mp_int Px, Py, Qx, Qy; + mp_int Gx, Gy, order, irreducible, a, b; + ECGroup *group = NULL; + SECStatus rv = SECFailure; + mp_err err = MP_OKAY; + unsigned int len; + +#if EC_DEBUG + int i; + char mpstr[256]; + + printf("ec_points_mul: params [len=%d]:", params->DEREncoding.len); + for (i = 0; i < params->DEREncoding.len; i++) + printf("%02x:", params->DEREncoding.data[i]); + printf("\n"); + + if (k1 != NULL) { + mp_tohex((mp_int *)k1, mpstr); + printf("ec_points_mul: scalar k1: %s\n", mpstr); + mp_todecimal((mp_int *)k1, mpstr); + printf("ec_points_mul: scalar k1: %s (dec)\n", mpstr); + } + + if (k2 != NULL) { + mp_tohex((mp_int *)k2, mpstr); + printf("ec_points_mul: scalar k2: %s\n", mpstr); + mp_todecimal((mp_int *)k2, mpstr); + printf("ec_points_mul: scalar k2: %s (dec)\n", mpstr); + } + + if (pointP != NULL) { + printf("ec_points_mul: pointP [len=%d]:", pointP->len); + for (i = 0; i < pointP->len; i++) + printf("%02x:", pointP->data[i]); + printf("\n"); + } +#endif + + /* NOTE: We only support uncompressed points for now */ + len = (((unsigned int)params->fieldID.size) + 7) >> 3; + if (pointP != NULL) { + if ((pointP->data[0] != EC_POINT_FORM_UNCOMPRESSED) || + (pointP->len != (2 * len + 1))) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); + return SECFailure; + }; + } + + MP_DIGITS(&Px) = 0; + MP_DIGITS(&Py) = 0; + MP_DIGITS(&Qx) = 0; + MP_DIGITS(&Qy) = 0; + MP_DIGITS(&Gx) = 0; + MP_DIGITS(&Gy) = 0; + MP_DIGITS(&order) = 0; + MP_DIGITS(&irreducible) = 0; + MP_DIGITS(&a) = 0; + MP_DIGITS(&b) = 0; + CHECK_MPI_OK(mp_init(&Px)); + CHECK_MPI_OK(mp_init(&Py)); + CHECK_MPI_OK(mp_init(&Qx)); + CHECK_MPI_OK(mp_init(&Qy)); + CHECK_MPI_OK(mp_init(&Gx)); + CHECK_MPI_OK(mp_init(&Gy)); + CHECK_MPI_OK(mp_init(&order)); + CHECK_MPI_OK(mp_init(&irreducible)); + CHECK_MPI_OK(mp_init(&a)); + CHECK_MPI_OK(mp_init(&b)); + + if ((k2 != NULL) && (pointP != NULL)) { + /* Initialize Px and Py */ + CHECK_MPI_OK(mp_read_unsigned_octets(&Px, pointP->data + 1, (mp_size)len)); + CHECK_MPI_OK(mp_read_unsigned_octets(&Py, pointP->data + 1 + len, (mp_size)len)); + } + + /* construct from named params, if possible */ + if (params->name != ECCurve_noName) { + group = ECGroup_fromName(params->name); + } + + if (group == NULL) + goto cleanup; + + if ((k2 != NULL) && (pointP != NULL)) { + CHECK_MPI_OK(ECPoints_mul(group, k1, k2, &Px, &Py, &Qx, &Qy)); + } else { + CHECK_MPI_OK(ECPoints_mul(group, k1, NULL, NULL, NULL, &Qx, &Qy)); + } + + /* our ECC codes uses large stack variables to store intermediate results, + * clear our stack before returning to prevent CSP leakage */ + BLAPI_CLEAR_STACK(2048) + + /* Construct the SECItem representation of point Q */ + pointQ->data[0] = EC_POINT_FORM_UNCOMPRESSED; + CHECK_MPI_OK(mp_to_fixlen_octets(&Qx, pointQ->data + 1, + (mp_size)len)); + CHECK_MPI_OK(mp_to_fixlen_octets(&Qy, pointQ->data + 1 + len, + (mp_size)len)); + + rv = SECSuccess; + +#if EC_DEBUG + printf("ec_points_mul: pointQ [len=%d]:", pointQ->len); + for (i = 0; i < pointQ->len; i++) + printf("%02x:", pointQ->data[i]); + printf("\n"); +#endif + +cleanup: + ECGroup_free(group); + mp_clear(&Px); + mp_clear(&Py); + mp_clear(&Qx); + mp_clear(&Qy); + mp_clear(&Gx); + mp_clear(&Gy); + mp_clear(&order); + mp_clear(&irreducible); + mp_clear(&a); + mp_clear(&b); + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + + return rv; +} + +/* Generates a new EC key pair. The private key is a supplied + * value and the public key is the result of performing a scalar + * point multiplication of that value with the curve's base point. + */ +SECStatus +ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey, + const unsigned char *privKeyBytes, int privKeyLen) +{ + SECStatus rv = SECFailure; + PLArenaPool *arena; + ECPrivateKey *key; + mp_int k; + mp_err err = MP_OKAY; + int len; + +#if EC_DEBUG + printf("ec_NewKey called\n"); +#endif + MP_DIGITS(&k) = 0; + + if (!ecParams || ecParams->name == ECCurve_noName || + !privKey || !privKeyBytes || privKeyLen <= 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Initialize an arena for the EC key. */ + if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) + return SECFailure; + + key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey)); + if (!key) { + PORT_FreeArena(arena, PR_TRUE); + return SECFailure; + } + + /* Set the version number (SEC 1 section C.4 says it should be 1) */ + SECITEM_AllocItem(arena, &key->version, 1); + key->version.data[0] = 1; + + /* Copy all of the fields from the ECParams argument to the + * ECParams structure within the private key. + */ + key->ecParams.arena = arena; + key->ecParams.type = ecParams->type; + key->ecParams.fieldID.size = ecParams->fieldID.size; + key->ecParams.fieldID.type = ecParams->fieldID.type; + if (ecParams->fieldID.type == ec_field_GFp || + ecParams->fieldID.type == ec_field_plain) { + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime, + &ecParams->fieldID.u.prime)); + } else { + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.poly, + &ecParams->fieldID.u.poly)); + } + key->ecParams.fieldID.k1 = ecParams->fieldID.k1; + key->ecParams.fieldID.k2 = ecParams->fieldID.k2; + key->ecParams.fieldID.k3 = ecParams->fieldID.k3; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a, + &ecParams->curve.a)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b, + &ecParams->curve.b)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed, + &ecParams->curve.seed)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base, + &ecParams->base)); + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order, + &ecParams->order)); + key->ecParams.cofactor = ecParams->cofactor; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding, + &ecParams->DEREncoding)); + key->ecParams.name = ecParams->name; + CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID, + &ecParams->curveOID)); + + SECITEM_AllocItem(arena, &key->publicValue, EC_GetPointSize(ecParams)); + len = ecParams->order.len; + SECITEM_AllocItem(arena, &key->privateValue, len); + + /* Copy private key */ + if (privKeyLen >= len) { + memcpy(key->privateValue.data, privKeyBytes, len); + } else { + memset(key->privateValue.data, 0, (len - privKeyLen)); + memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen); + } + + /* Compute corresponding public key */ + + /* 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->pt_mul == NULL) { + /* unknown curve */ + rv = SECFailure; + goto cleanup; + } + rv = method->pt_mul(&key->publicValue, &key->privateValue, NULL); + NSS_DECLASSIFY(key->publicValue.data, key->publicValue.len); /* Declassifying public key to avoid false positive */ + if (rv != SECSuccess) { + goto cleanup; + } else { + goto done; + } + } + + CHECK_MPI_OK(mp_init(&k)); + CHECK_MPI_OK(mp_read_unsigned_octets(&k, key->privateValue.data, + (mp_size)len)); + + rv = ec_points_mul(ecParams, &k, NULL, NULL, &(key->publicValue)); + NSS_DECLASSIFY(key->publicValue.data, key->publicValue.len); /* Declassifying public key to avoid false positive */ + if (rv != SECSuccess) { + goto cleanup; + } + +done: + *privKey = key; + +cleanup: + mp_clear(&k); + if (rv) { + PORT_FreeArena(arena, PR_TRUE); + } + +#if EC_DEBUG + printf("ec_NewKey returning %s\n", + (rv == SECSuccess) ? "success" : "failure"); +#endif + + return rv; +} + +/* Generates a new EC key pair. The private key is a supplied + * random value (in seed) and the public key is the result of + * performing a scalar point multiplication of that value with + * the curve's base point. + */ +SECStatus +EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey, + const unsigned char *seed, int seedlen) +{ + SECStatus rv = SECFailure; + rv = ec_NewKey(ecParams, privKey, seed, seedlen); + return rv; +} + +/* 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. + */ + +SECStatus +ec_GenerateRandomPrivateKey(ECParams *ecParams, SECItem *privKey) +{ + SECStatus rv = SECFailure; + + unsigned int len = EC_GetScalarSize(ecParams); + + if (privKey->len != len || privKey->data == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + const ECMethod *method = ec_get_method_from_name(ecParams->name); + if (method == NULL || method->scalar_validate == NULL) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return SECFailure; + } + + uint8_t leading_coeff_mask; + switch (ecParams->name) { + case ECCurve25519: + case ECCurve_NIST_P256: + case ECCurve_NIST_P384: + leading_coeff_mask = 0xff; + break; + case ECCurve_NIST_P521: + leading_coeff_mask = 0x01; + break; + default: + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return SECFailure; + } + + /* The rejection sampling method from FIPS 186-5 A.4.2 */ + int count = 100; + do { + rv = RNG_GenerateGlobalRandomBytes(privKey->data, len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + return SECFailure; + } + privKey->data[0] &= leading_coeff_mask; + NSS_CLASSIFY(privKey->data, privKey->len); + rv = method->scalar_validate(privKey); + } while (rv != SECSuccess && --count > 0); + + if (rv != SECSuccess) { // implies count == 0 + PORT_SetError(SEC_ERROR_BAD_KEY); + } + + return rv; +} + +/* Generates a new EC key pair. The private key is a random value and + * the public key is the result of performing a scalar point multiplication + * of that value with the curve's base point. + */ +SECStatus +EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey) +{ + SECStatus rv = SECFailure; + SECItem privKeyRand = { siBuffer, NULL, 0 }; + + if (!ecParams || ecParams->name == ECCurve_noName || !privKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + 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, privKeyRand.data, privKeyRand.len)); + +cleanup: + if (privKeyRand.data) { + SECITEM_ZfreeItem(&privKeyRand, PR_FALSE); + } +#if EC_DEBUG + printf("EC_NewKey returning %s\n", + (rv == SECSuccess) ? "success" : "failure"); +#endif + + return rv; +} + +/* Validates an EC public key as described in Section 5.2.2 of + * X9.62. The ECDH primitive when used without the cofactor does + * not address small subgroup attacks, which may occur when the + * public key is not valid. These attacks can be prevented by + * validating the public key before using ECDH. + */ +SECStatus +EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue) +{ + mp_int Px, Py; + ECGroup *group = NULL; + SECStatus rv = SECFailure; + mp_err err = MP_OKAY; + unsigned int len; + + if (!ecParams || ecParams->name == ECCurve_noName || + !publicValue || !publicValue->len) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + return rv; + } + + /* 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->pt_validate == NULL) { + /* unknown curve */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + return rv; + } + rv = method->pt_validate(publicValue); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_KEY); + } + return rv; + } + + /* NOTE: We only support uncompressed points for now */ + len = (((unsigned int)ecParams->fieldID.size) + 7) >> 3; + if (publicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); + return SECFailure; + } else if (publicValue->len != (2 * len + 1)) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + MP_DIGITS(&Px) = 0; + MP_DIGITS(&Py) = 0; + CHECK_MPI_OK(mp_init(&Px)); + CHECK_MPI_OK(mp_init(&Py)); + + /* Initialize Px and Py */ + CHECK_MPI_OK(mp_read_unsigned_octets(&Px, publicValue->data + 1, (mp_size)len)); + CHECK_MPI_OK(mp_read_unsigned_octets(&Py, publicValue->data + 1 + len, (mp_size)len)); + + /* construct from named params */ + group = ECGroup_fromName(ecParams->name); + if (group == NULL) { + /* + * ECGroup_fromName fails if ecParams->name is not a valid + * ECCurveName value, or if we run out of memory, or perhaps + * for other reasons. Unfortunately if ecParams->name is a + * valid ECCurveName value, we don't know what the right error + * code should be because ECGroup_fromName doesn't return an + * error code to the caller. Set err to MP_UNDEF because + * that's what ECGroup_fromName uses internally. + */ + if ((ecParams->name <= ECCurve_noName) || + (ecParams->name >= ECCurve_pastLastCurve)) { + err = MP_BADARG; + } else { + err = MP_UNDEF; + } + goto cleanup; + } + + /* validate public point */ + if ((err = ECPoint_validate(group, &Px, &Py)) < MP_YES) { + if (err == MP_NO) { + PORT_SetError(SEC_ERROR_BAD_KEY); + rv = SECFailure; + err = MP_OKAY; /* don't change the error code */ + } + goto cleanup; + } + + rv = SECSuccess; + +cleanup: + ECGroup_free(group); + mp_clear(&Px); + mp_clear(&Py); + + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + return rv; +} + +/* +** Performs an ECDH key derivation by computing the scalar point +** multiplication of privateValue and publicValue (with or without the +** cofactor) and returns the x-coordinate of the resulting elliptic +** curve point in derived secret. If successful, derivedSecret->data +** is set to the address of the newly allocated buffer containing the +** derived secret, and derivedSecret->len is the size of the secret +** produced. It is the caller's responsibility to free the allocated +** buffer containing the derived secret. +*/ +SECStatus +ECDH_Derive(SECItem *publicValue, + ECParams *ecParams, + SECItem *privateValue, + PRBool withCofactor, + SECItem *derivedSecret) +{ + SECStatus rv = SECFailure; + unsigned int len = 0; + mp_err err = MP_OKAY; + + if (!publicValue || !publicValue->len || + !ecParams || ecParams->name == ECCurve_noName || + !privateValue || !privateValue->len || !derivedSecret) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + return rv; + } + + /* + * Make sure the point is on the requested curve to avoid + * certain small subgroup attacks. + */ + if (EC_ValidatePublicKey(ecParams, publicValue) != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_KEY); + rv = SECFailure; + return rv; + } + + /* Perform curve specific multiplication using ECMethod */ + if (ecParams->fieldID.type == ec_field_plain) { + const ECMethod *method; + memset(derivedSecret, 0, sizeof(*derivedSecret)); + derivedSecret = SECITEM_AllocItem(NULL, derivedSecret, EC_GetScalarSize(ecParams)); + if (derivedSecret == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + return rv; + } + method = ec_get_method_from_name(ecParams->name); + if (method == NULL || method->pt_validate == NULL || + method->pt_mul == NULL) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + rv = SECFailure; + goto done; + } + rv = method->pt_mul(derivedSecret, privateValue, publicValue); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_KEY); + } + goto done; + } + + SECItem pointQ = { siBuffer, NULL, 0 }; + mp_int k; /* to hold the private value */ +#if EC_DEBUG + int i; +#endif + + /* + * We fail if the public value is the point at infinity, since + * this produces predictable results. + */ + if (ec_point_at_infinity(publicValue)) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + + MP_DIGITS(&k) = 0; + memset(derivedSecret, 0, sizeof *derivedSecret); + len = (ecParams->fieldID.size + 7) >> 3; + pointQ.len = EC_GetPointSize(ecParams); + if ((pointQ.data = PORT_Alloc(pointQ.len)) == NULL) + goto cleanup; + + CHECK_MPI_OK(mp_init(&k)); + CHECK_MPI_OK(mp_read_unsigned_octets(&k, privateValue->data, + (mp_size)privateValue->len)); + + if (withCofactor && (ecParams->cofactor != 1)) { + mp_int cofactor; + /* multiply k with the cofactor */ + MP_DIGITS(&cofactor) = 0; + CHECK_MPI_OK(mp_init(&cofactor)); + mp_set(&cofactor, ecParams->cofactor); + CHECK_MPI_OK(mp_mul(&k, &cofactor, &k)); + mp_clear(&cofactor); + } + + /* Multiply our private key and peer's public point */ + if (ec_points_mul(ecParams, NULL, &k, publicValue, &pointQ) != SECSuccess) { + goto cleanup; + } + if (ec_point_at_infinity(&pointQ)) { + PORT_SetError(SEC_ERROR_BAD_KEY); /* XXX better error code? */ + goto cleanup; + } + + /* Allocate memory for the derived secret and copy + * the x co-ordinate of pointQ into it. + */ + SECITEM_AllocItem(NULL, derivedSecret, len); + memcpy(derivedSecret->data, pointQ.data + 1, len); + + rv = SECSuccess; + +#if EC_DEBUG + printf("derived_secret:\n"); + for (i = 0; i < derivedSecret->len; i++) + printf("%02x:", derivedSecret->data[i]); + printf("\n"); +#endif + +cleanup: + mp_clear(&k); + + if (pointQ.data) { + PORT_ZFree(pointQ.data, pointQ.len); + } + +done: + + if (err) { + MP_TO_SEC_ERROR(err); + } + if (rv != SECSuccess) { + SECITEM_ZfreeItem(derivedSecret, PR_FALSE); + } + return rv; +} + +/* Computes the ECDSA signature (a concatenation of two values r and s) + * on the digest using the given key and the random value kb (used in + * computing s). + */ + +static SECStatus +ec_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, + const SECItem *digest, const unsigned char *kb, const int kblen) +{ + SECStatus rv = SECFailure; + ECParams *ecParams = NULL; + mp_err err = MP_OKAY; + int flen = 0; /* length in bytes of the field size */ + unsigned olen; /* length in bytes of the base point order */ + + /* Check args */ + if (!key || !signature || !digest || !kb || (kblen <= 0)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + goto done; + } + + ecParams = &(key->ecParams); + flen = (ecParams->fieldID.size + 7) >> 3; + olen = ecParams->order.len; + if (signature->data == NULL) { + /* a call to get the signature length only */ + signature->len = 2 * olen; + rv = SECSuccess; + goto done; + } + if (signature->len < 2 * olen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + rv = SECFailure; + goto done; + } + + /* Perform curve specific signature using ECMethod */ + if (ecParams->fieldID.type == ec_field_plain) { + const ECMethod *method = ec_get_method_from_name(ecParams->name); + if (method == NULL || method->sign_digest == NULL) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + rv = SECFailure; + goto done; + } + rv = method->sign_digest(key, signature, digest, kb, kblen); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + } + goto done; + } + + mp_int x1; + mp_int d, k; /* private key, random integer */ + mp_int r, s; /* tuple (r, s) is the signature */ + mp_int t; /* holding tmp values */ + mp_int n; + mp_int ar; /* blinding value */ + SECItem kGpoint = { siBuffer, NULL, 0 }; + unsigned char *t2 = NULL; + unsigned obits; /* length in bits of the base point order */ + +#if EC_DEBUG + char mpstr[256]; +#endif + + /* Initialize MPI integers. */ + /* must happen before the first potential call to cleanup */ + MP_DIGITS(&x1) = 0; + MP_DIGITS(&d) = 0; + MP_DIGITS(&k) = 0; + MP_DIGITS(&r) = 0; + MP_DIGITS(&s) = 0; + MP_DIGITS(&n) = 0; + MP_DIGITS(&t) = 0; + MP_DIGITS(&ar) = 0; + + CHECK_MPI_OK(mp_init(&x1)); + CHECK_MPI_OK(mp_init(&d)); + CHECK_MPI_OK(mp_init(&k)); + CHECK_MPI_OK(mp_init(&r)); + CHECK_MPI_OK(mp_init(&s)); + CHECK_MPI_OK(mp_init(&n)); + CHECK_MPI_OK(mp_init(&t)); + CHECK_MPI_OK(mp_init(&ar)); + + SECITEM_TO_MPINT(ecParams->order, &n); + SECITEM_TO_MPINT(key->privateValue, &d); + + CHECK_MPI_OK(mp_read_unsigned_octets(&k, kb, kblen)); + /* Make sure k is in the interval [1, n-1] */ + if ((mp_cmp_z(&k) <= 0) || (mp_cmp(&k, &n) >= 0)) { +#if EC_DEBUG + printf("k is outside [1, n-1]\n"); + mp_tohex(&k, mpstr); + printf("k : %s \n", mpstr); + mp_tohex(&n, mpstr); + printf("n : %s \n", mpstr); +#endif + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** ANSI X9.62, Section 5.3.2, Step 2 + ** + ** Compute kG + */ + kGpoint.len = EC_GetPointSize(ecParams); + kGpoint.data = PORT_Alloc(kGpoint.len); + if ((kGpoint.data == NULL) || + (ec_points_mul(ecParams, &k, NULL, NULL, &kGpoint) != SECSuccess)) + goto cleanup; + NSS_DECLASSIFY(kGpoint.data, kGpoint.len); /* Declassifying the r component */ + /* + ** ANSI X9.62, Section 5.3.3, Step 1 + ** + ** Extract the x co-ordinate of kG into x1 + */ + CHECK_MPI_OK(mp_read_unsigned_octets(&x1, kGpoint.data + 1, + (mp_size)flen)); + + /* + ** ANSI X9.62, Section 5.3.3, Step 2 + ** + ** r = x1 mod n NOTE: n is the order of the curve + */ + CHECK_MPI_OK(mp_mod(&x1, &n, &r)); + + /* + ** ANSI X9.62, Section 5.3.3, Step 3 + ** + ** verify r != 0 + */ + if (mp_cmp_z(&r) == 0) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** ANSI X9.62, Section 5.3.3, Step 4 + ** + ** s = (k**-1 * (HASH(M) + d*r)) mod n + */ + SECITEM_TO_MPINT(*digest, &s); /* s = HASH(M) */ + + /* In the definition of EC signing, digests are truncated + * to the length of n in bits. + * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ + CHECK_MPI_OK((obits = mpl_significant_bits(&n))); + if (digest->len * 8 > obits) { + mpl_rsh(&s, &s, digest->len * 8 - obits); + } + +#if EC_DEBUG + mp_todecimal(&n, mpstr); + printf("n : %s (dec)\n", mpstr); + mp_todecimal(&d, mpstr); + printf("d : %s (dec)\n", mpstr); + mp_tohex(&x1, mpstr); + printf("x1: %s\n", mpstr); + mp_todecimal(&s, mpstr); + printf("digest: %s (decimal)\n", mpstr); + mp_todecimal(&r, mpstr); + printf("r : %s (dec)\n", mpstr); + mp_tohex(&r, mpstr); + printf("r : %s\n", mpstr); +#endif + + if ((t2 = PORT_Alloc(2 * ecParams->order.len)) == NULL) { + rv = SECFailure; + goto cleanup; + } + if (RNG_GenerateGlobalRandomBytes(t2, 2 * ecParams->order.len) != SECSuccess) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + rv = SECFailure; + goto cleanup; + } + CHECK_MPI_OK(mp_read_unsigned_octets(&t, t2, 2 * ecParams->order.len)); /* t <-$ Zn */ + PORT_Memset(t2, 0, 2 * ecParams->order.len); + if (RNG_GenerateGlobalRandomBytes(t2, 2 * ecParams->order.len) != SECSuccess) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + rv = SECFailure; + goto cleanup; + } + CHECK_MPI_OK(mp_read_unsigned_octets(&ar, t2, 2 * ecParams->order.len)); /* ar <-$ Zn */ + + /* Using mp_invmod on k directly would leak bits from k. */ + CHECK_MPI_OK(mp_mul(&k, &ar, &k)); /* k = k * ar */ + NSS_DECLASSIFY(MP_DIGITS(&k), MP_ALLOC(&k) * sizeof(mp_digit)); /* declassifying k here because it is masked by multiplying with ar */ + CHECK_MPI_OK(mp_mulmod(&k, &t, &n, &k)); /* k = k * t mod n */ + CHECK_MPI_OK(mp_invmod(&k, &n, &k)); /* k = k**-1 mod n */ + CHECK_MPI_OK(mp_mulmod(&k, &t, &n, &k)); /* k = k * t mod n */ + /* To avoid leaking secret bits here the addition is blinded. */ + CHECK_MPI_OK(mp_mul(&d, &ar, &t)); /* t = d * ar */ + NSS_DECLASSIFY(MP_DIGITS(&t), MP_ALLOC(&t) * sizeof(mp_digit)); /* declassifying d here because it is masked by multiplying with ar */ + CHECK_MPI_OK(mp_mulmod(&t, &r, &n, &d)); /* d = t * r mod n */ + CHECK_MPI_OK(mp_mulmod(&s, &ar, &n, &t)); /* t = s * ar mod n */ + CHECK_MPI_OK(mp_add(&t, &d, &s)); /* s = t + d */ + CHECK_MPI_OK(mp_mulmod(&s, &k, &n, &s)); /* s = s * k mod n */ + +#if EC_DEBUG + mp_todecimal(&s, mpstr); + printf("s : %s (dec)\n", mpstr); + mp_tohex(&s, mpstr); + printf("s : %s\n", mpstr); +#endif + + /* + ** ANSI X9.62, Section 5.3.3, Step 5 + ** + ** verify s != 0 + */ + if (mp_cmp_z(&s) == 0) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + goto cleanup; + } + + /* + ** + ** Signature is tuple (r, s) + */ + CHECK_MPI_OK(mp_to_fixlen_octets(&r, signature->data, olen)); + CHECK_MPI_OK(mp_to_fixlen_octets(&s, signature->data + olen, olen)); + + signature->len = 2 * olen; + rv = SECSuccess; + err = MP_OKAY; + +cleanup: + mp_clear(&x1); + mp_clear(&d); + mp_clear(&k); + mp_clear(&r); + mp_clear(&s); + mp_clear(&n); + mp_clear(&t); + mp_clear(&ar); + + if (t2) { + PORT_ZFree(t2, 2 * ecParams->order.len); + } + + if (kGpoint.data) { + PORT_ZFree(kGpoint.data, kGpoint.len); + } + +done: + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + +#if EC_DEBUG + printf("ECDSA signing with seed %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} + +SECStatus +ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, + const SECItem *digest, const unsigned char *kb, const int kblen) +{ +#if EC_DEBUG || EC_DOUBLECHECK + SECItem *signature2 = SECITEM_AllocItem(NULL, NULL, signature->len); + SECStatus signSuccess = ec_SignDigestWithSeed(key, signature, digest, kb, kblen); + SECStatus signSuccessDouble = ec_SignDigestWithSeed(key, signature2, digest, kb, kblen); + int signaturesEqual = NSS_SecureMemcmp(signature->data, signature2->data, signature->len); + SECStatus rv; + + if ((signaturesEqual == 0) && (signSuccess == SECSuccess) && (signSuccessDouble == SECSuccess)) { + rv = SECSuccess; + } else { + rv = SECFailure; + } + +#if EC_DEBUG + printf("ECDSA signing with seed %s after signing twice\n", (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + SECITEM_FreeItem(signature2, PR_TRUE); + return rv; +#else + return ec_SignDigestWithSeed(key, signature, digest, kb, kblen); +#endif +} + +/* +** Computes the ECDSA signature on the digest using the given key +** and a random seed. +*/ +SECStatus +ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest) +{ + SECStatus rv = SECFailure; + SECItem nonceRand = { siBuffer, NULL, 0 }; + + if (!key) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Generate random value k */ + 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, nonceRand.data, nonceRand.len); + NSS_DECLASSIFY(signature->data, signature->len); + +cleanup: + if (nonceRand.data) { + SECITEM_ZfreeItem(&nonceRand, PR_FALSE); + } + +#if EC_DEBUG + printf("ECDSA signing %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} + +/* +** Checks the signature on the given digest using the key provided. +** +** The key argument must represent a valid EC public key (a point on +** the relevant curve). If it is not a valid point, then the behavior +** of this function is undefined. In cases where a public key might +** not be valid, use EC_ValidatePublicKey to check. +*/ +SECStatus +ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, + const SECItem *digest) +{ + SECStatus rv = SECFailure; + ECParams *ecParams = NULL; + mp_err err = MP_OKAY; + + /* Check args */ + if (!key || !signature || !digest) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + goto done; + } + + ecParams = &(key->ecParams); + + /* Perform curve specific signature verification using ECMethod */ + if (ecParams->fieldID.type == ec_field_plain) { + const ECMethod *method = ec_get_method_from_name(ecParams->name); + if (method == NULL || method->verify_digest == NULL) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + rv = SECFailure; + goto done; + } + rv = method->verify_digest(key, signature, digest); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } + goto done; + } + + mp_int r_, s_; /* tuple (r', s') is received signature) */ + mp_int c, u1, u2, v; /* intermediate values used in verification */ + mp_int x1; + mp_int n; + SECItem pointC = { siBuffer, NULL, 0 }; + int slen; /* length in bytes of a half signature (r or s) */ + int flen; /* length in bytes of the field size */ + unsigned olen; /* length in bytes of the base point order */ + unsigned obits; /* length in bits of the base point order */ + +#if EC_DEBUG + char mpstr[256]; + printf("ECDSA verification called\n"); +#endif + + /* Initialize MPI integers. */ + /* must happen before the first potential call to cleanup */ + MP_DIGITS(&r_) = 0; + MP_DIGITS(&s_) = 0; + MP_DIGITS(&c) = 0; + MP_DIGITS(&u1) = 0; + MP_DIGITS(&u2) = 0; + MP_DIGITS(&x1) = 0; + MP_DIGITS(&v) = 0; + MP_DIGITS(&n) = 0; + + CHECK_MPI_OK(mp_init(&r_)); + CHECK_MPI_OK(mp_init(&s_)); + CHECK_MPI_OK(mp_init(&c)); + CHECK_MPI_OK(mp_init(&u1)); + CHECK_MPI_OK(mp_init(&u2)); + CHECK_MPI_OK(mp_init(&x1)); + CHECK_MPI_OK(mp_init(&v)); + CHECK_MPI_OK(mp_init(&n)); + + flen = (ecParams->fieldID.size + 7) >> 3; + olen = ecParams->order.len; + if (signature->len == 0 || signature->len % 2 != 0 || + signature->len > 2 * olen) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + goto cleanup; + } + slen = signature->len / 2; + + /* + * The incoming point has been verified in sftk_handlePublicKeyObject. + */ + + SECITEM_AllocItem(NULL, &pointC, EC_GetPointSize(ecParams)); + if (pointC.data == NULL) { + goto cleanup; + } + + /* + ** Convert received signature (r', s') into MPI integers. + */ + CHECK_MPI_OK(mp_read_unsigned_octets(&r_, signature->data, slen)); + CHECK_MPI_OK(mp_read_unsigned_octets(&s_, signature->data + slen, slen)); + + /* + ** ANSI X9.62, Section 5.4.2, Steps 1 and 2 + ** + ** Verify that 0 < r' < n and 0 < s' < n + */ + SECITEM_TO_MPINT(ecParams->order, &n); + if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || + mp_cmp(&r_, &n) >= 0 || mp_cmp(&s_, &n) >= 0) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + goto cleanup; /* will return rv == SECFailure */ + } + + /* + ** ANSI X9.62, Section 5.4.2, Step 3 + ** + ** c = (s')**-1 mod n + */ + CHECK_MPI_OK(mp_invmod(&s_, &n, &c)); /* c = (s')**-1 mod n */ + + /* + ** ANSI X9.62, Section 5.4.2, Step 4 + ** + ** u1 = ((HASH(M')) * c) mod n + */ + SECITEM_TO_MPINT(*digest, &u1); /* u1 = HASH(M) */ + + /* In the definition of EC signing, digests are truncated + * to the length of n in bits. + * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ + CHECK_MPI_OK((obits = mpl_significant_bits(&n))); + if (digest->len * 8 > obits) { /* u1 = HASH(M') */ + mpl_rsh(&u1, &u1, digest->len * 8 - obits); + } + +#if EC_DEBUG + mp_todecimal(&r_, mpstr); + printf("r_: %s (dec)\n", mpstr); + mp_todecimal(&s_, mpstr); + printf("s_: %s (dec)\n", mpstr); + mp_todecimal(&c, mpstr); + printf("c : %s (dec)\n", mpstr); + mp_todecimal(&u1, mpstr); + printf("digest: %s (dec)\n", mpstr); +#endif + + CHECK_MPI_OK(mp_mulmod(&u1, &c, &n, &u1)); /* u1 = u1 * c mod n */ + + /* + ** ANSI X9.62, Section 5.4.2, Step 4 + ** + ** u2 = ((r') * c) mod n + */ + CHECK_MPI_OK(mp_mulmod(&r_, &c, &n, &u2)); + + /* + ** ANSI X9.62, Section 5.4.3, Step 1 + ** + ** Compute u1*G + u2*Q + ** Here, A = u1.G B = u2.Q and C = A + B + ** If the result, C, is the point at infinity, reject the signature + */ + if (ec_points_mul(ecParams, &u1, &u2, &key->publicValue, &pointC) != SECSuccess) { + rv = SECFailure; + goto cleanup; + } + if (ec_point_at_infinity(&pointC)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + goto cleanup; + } + + CHECK_MPI_OK(mp_read_unsigned_octets(&x1, pointC.data + 1, flen)); + + /* + ** ANSI X9.62, Section 5.4.4, Step 2 + ** + ** v = x1 mod n + */ + CHECK_MPI_OK(mp_mod(&x1, &n, &v)); + +#if EC_DEBUG + mp_todecimal(&r_, mpstr); + printf("r_: %s (dec)\n", mpstr); + mp_todecimal(&v, mpstr); + printf("v : %s (dec)\n", mpstr); +#endif + + /* + ** ANSI X9.62, Section 5.4.4, Step 3 + ** + ** Verification: v == r' + */ + if (mp_cmp(&v, &r_)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; /* Signature failed to verify. */ + } else { + rv = SECSuccess; /* Signature verified. */ + } + +#if EC_DEBUG + mp_todecimal(&u1, mpstr); + printf("u1: %s (dec)\n", mpstr); + mp_todecimal(&u2, mpstr); + printf("u2: %s (dec)\n", mpstr); + mp_tohex(&x1, mpstr); + printf("x1: %s\n", mpstr); + mp_todecimal(&v, mpstr); + printf("v : %s (dec)\n", mpstr); +#endif + +cleanup: + mp_clear(&r_); + mp_clear(&s_); + mp_clear(&c); + mp_clear(&u1); + mp_clear(&u2); + mp_clear(&x1); + mp_clear(&v); + mp_clear(&n); + + if (pointC.data) + SECITEM_ZfreeItem(&pointC, PR_FALSE); + +done: + if (err) { + MP_TO_SEC_ERROR(err); + rv = SECFailure; + } + +#if EC_DEBUG + printf("ECDSA verification %s\n", + (rv == SECSuccess) ? "succeeded" : "failed"); +#endif + + return rv; +} |