/* 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" #define EC_DOUBLECHECK PR_FALSE static const ECMethod kMethods[] = { { ECCurve25519, ec_Curve25519_pt_mul, ec_Curve25519_pt_validate } }; 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->mul == NULL) { /* unknown curve */ rv = SECFailure; goto cleanup; } rv = method->mul(&key->publicValue, &key->privateValue, NULL); 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)); 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 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 rv = SECSuccess; mp_err err; unsigned char *privKeyBytes = NULL; mp_int privKeyVal, order_1, one; MP_DIGITS(&privKeyVal) = 0; MP_DIGITS(&order_1) = 0; MP_DIGITS(&one) = 0; CHECK_MPI_OK(mp_init(&privKeyVal)); CHECK_MPI_OK(mp_init(&order_1)); CHECK_MPI_OK(mp_init(&one)); /* Generates 2*len random bytes using the global random bit generator * (which implements Algorithm 1 of FIPS 186-2 Change Notice 1) then * reduces modulo the group order. */ if ((privKeyBytes = PORT_Alloc(2 * len)) == NULL) 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)); CHECK_MPI_OK(mp_set_int(&one, 1)); CHECK_MPI_OK(mp_sub(&order_1, &one, &order_1)); 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); cleanup: mp_clear(&privKeyVal); mp_clear(&order_1); mp_clear(&one); if (err < MP_OKAY) { MP_TO_SEC_ERROR(err); rv = SECFailure; } if (rv != SECSuccess && privKeyBytes) { PORT_ZFree(privKeyBytes, 2 * len); privKeyBytes = NULL; } return privKeyBytes; } /* 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; int len; unsigned char *privKeyBytes = NULL; 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) goto cleanup; /* generate public key */ CHECK_SEC_OK(ec_NewKey(ecParams, privKey, privKeyBytes, len)); cleanup: if (privKeyBytes) { PORT_ZFree(privKeyBytes, len); } #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); return SECFailure; } /* 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) { /* unknown curve */ PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } return method->validate(publicValue); } /* 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; SECItem pointQ = { siBuffer, NULL, 0 }; mp_int k; /* to hold the private value */ mp_err err = MP_OKAY; #if EC_DEBUG int i; #endif if (!publicValue || !publicValue->len || !ecParams || ecParams->name == ECCurve_noName || !privateValue || !privateValue->len || !derivedSecret) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* * 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); return SECFailure; } /* 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_GetPointSize(ecParams)); if (derivedSecret == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } method = ec_get_method_from_name(ecParams->name); if (method == NULL || method->validate == NULL || method->mul == NULL) { PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); return SECFailure; } rv = method->mul(derivedSecret, privateValue, publicValue); if (rv != SECSuccess) { SECITEM_ZfreeItem(derivedSecret, PR_FALSE); } return rv; } /* * 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 (err) { MP_TO_SEC_ERROR(err); } if (pointQ.data) { PORT_ZFree(pointQ.data, pointQ.len); } 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; 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 */ mp_err err = MP_OKAY; ECParams *ecParams = NULL; SECItem kGpoint = { siBuffer, NULL, 0 }; int flen = 0; /* 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 */ unsigned char *t2 = NULL; #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 args */ if (!key || !signature || !digest || !kb || (kblen < 0)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); goto cleanup; } 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 */ goto finish; } if (signature->len < 2 * olen) { PORT_SetError(SEC_ERROR_OUTPUT_LEN); goto cleanup; } 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; /* ** 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 */ 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 */ 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)); finish: 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); } 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, signature2, 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; int len; unsigned char *kBytes = NULL; if (!key) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* Generate random value k */ len = key->ecParams.order.len; kBytes = ec_GenerateRandomPrivateKey(key->ecParams.order.data, len); if (kBytes == NULL) goto cleanup; /* Generate ECDSA signature with the specified k value */ rv = ECDSA_SignDigestWithSeed(key, signature, digest, kBytes, len); cleanup: if (kBytes) { PORT_ZFree(kBytes, len); } #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; 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; mp_err err = MP_OKAY; ECParams *ecParams = NULL; 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 args */ if (!key || !signature || !digest) { PORT_SetError(SEC_ERROR_INVALID_ARGS); goto cleanup; } ecParams = &(key->ecParams); 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; } 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)); /* ** 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); if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } #if EC_DEBUG printf("ECDSA verification %s\n", (rv == SECSuccess) ? "succeeded" : "failed"); #endif return rv; }