SECStatus DH_NewKey(DHParams *params, DHPrivateKey **privKey) { PLArenaPool *arena; DHPrivateKey *key; mp_int g, xa, p, Ya; mp_err err = MP_OKAY; SECStatus rv = SECSuccess; if (!params || !privKey) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE); if (!arena) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } key = (DHPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DHPrivateKey)); if (!key) { PORT_SetError(SEC_ERROR_NO_MEMORY); PORT_FreeArena(arena, PR_TRUE); return SECFailure; } key->arena = arena; MP_DIGITS(&g) = 0; MP_DIGITS(&xa) = 0; MP_DIGITS(&p) = 0; MP_DIGITS(&Ya) = 0; CHECK_MPI_OK( mp_init(&g) ); CHECK_MPI_OK( mp_init(&xa) ); CHECK_MPI_OK( mp_init(&p) ); CHECK_MPI_OK( mp_init(&Ya) ); /* Set private key's p */ CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->prime, ¶ms->prime) ); SECITEM_TO_MPINT(key->prime, &p); /* Set private key's g */ CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->base, ¶ms->base) ); SECITEM_TO_MPINT(key->base, &g); /* Generate private key xa */ SECITEM_AllocItem(arena, &key->privateValue, dh_GetSecretKeyLen(params->prime.len)); CHECK_SEC_OK(RNG_GenerateGlobalRandomBytes(key->privateValue.data, key->privateValue.len)); SECITEM_TO_MPINT( key->privateValue, &xa ); /* xa < p */ CHECK_MPI_OK( mp_mod(&xa, &p, &xa) ); /* Compute public key Ya = g ** xa mod p */ CHECK_MPI_OK( mp_exptmod(&g, &xa, &p, &Ya) ); MPINT_TO_SECITEM(&Ya, &key->publicValue, key->arena); *privKey = key; cleanup: mp_clear(&g); mp_clear(&xa); mp_clear(&p); mp_clear(&Ya); if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } if (rv) { *privKey = NULL; PORT_FreeArena(arena, PR_TRUE); } return rv; }
SECStatus DH_GenParam(int primeLen, DHParams **params) { PLArenaPool *arena; DHParams *dhparams; unsigned char *pb = NULL; unsigned char *ab = NULL; unsigned long counter = 0; mp_int p, q, a, h, psub1, test; mp_err err = MP_OKAY; SECStatus rv = SECSuccess; if (!params || primeLen < 0) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE); if (!arena) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } dhparams = (DHParams *)PORT_ArenaZAlloc(arena, sizeof(DHParams)); if (!dhparams) { PORT_SetError(SEC_ERROR_NO_MEMORY); PORT_FreeArena(arena, PR_TRUE); return SECFailure; } dhparams->arena = arena; MP_DIGITS(&p) = 0; MP_DIGITS(&q) = 0; MP_DIGITS(&a) = 0; MP_DIGITS(&h) = 0; MP_DIGITS(&psub1) = 0; MP_DIGITS(&test) = 0; CHECK_MPI_OK( mp_init(&p) ); CHECK_MPI_OK( mp_init(&q) ); CHECK_MPI_OK( mp_init(&a) ); CHECK_MPI_OK( mp_init(&h) ); CHECK_MPI_OK( mp_init(&psub1) ); CHECK_MPI_OK( mp_init(&test) ); /* generate prime with MPI, uses Miller-Rabin to generate strong prime. */ pb = PORT_Alloc(primeLen); CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(pb, primeLen) ); pb[0] |= 0x80; /* set high-order bit */ pb[primeLen-1] |= 0x01; /* set low-order bit */ CHECK_MPI_OK( mp_read_unsigned_octets(&p, pb, primeLen) ); CHECK_MPI_OK( mpp_make_prime(&p, primeLen * 8, PR_TRUE, &counter) ); /* construct Sophie-Germain prime q = (p-1)/2. */ CHECK_MPI_OK( mp_sub_d(&p, 1, &psub1) ); CHECK_MPI_OK( mp_div_2(&psub1, &q) ); /* construct a generator from the prime. */ ab = PORT_Alloc(primeLen); /* generate a candidate number a in p's field */ CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(ab, primeLen) ); CHECK_MPI_OK( mp_read_unsigned_octets(&a, ab, primeLen) ); /* force a < p (note that quot(a/p) <= 1) */ if ( mp_cmp(&a, &p) > 0 ) CHECK_MPI_OK( mp_sub(&a, &p, &a) ); do { /* check that a is in the range [2..p-1] */ if ( mp_cmp_d(&a, 2) < 0 || mp_cmp(&a, &psub1) >= 0) { /* a is outside of the allowed range. Set a=3 and keep going. */ mp_set(&a, 3); } /* if a**q mod p != 1 then a is a generator */ CHECK_MPI_OK( mp_exptmod(&a, &q, &p, &test) ); if ( mp_cmp_d(&test, 1) != 0 ) break; /* increment the candidate and try again. */ CHECK_MPI_OK( mp_add_d(&a, 1, &a) ); } while (PR_TRUE); MPINT_TO_SECITEM(&p, &dhparams->prime, arena); MPINT_TO_SECITEM(&a, &dhparams->base, arena); *params = dhparams; cleanup: mp_clear(&p); mp_clear(&q); mp_clear(&a); mp_clear(&h); mp_clear(&psub1); mp_clear(&test); if (pb) PORT_ZFree(pb, primeLen); if (ab) PORT_ZFree(ab, primeLen); if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } if (rv) PORT_FreeArena(arena, PR_TRUE); 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). */ SECStatus ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, const SECItem *digest, const unsigned char *kb, const int kblen) { SECStatus rv = SECFailure; #ifndef NSS_DISABLE_ECC 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_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; /* 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)); 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; } /* ** We do not want timing information to leak the length of k, ** so we compute k*G using an equivalent scalar of fixed ** bit-length. ** Fix based on patch for ECDSA timing attack in the paper ** by Billy Bob Brumley and Nicola Tuveri at ** http://eprint.iacr.org/2011/232 ** ** How do we convert k to a value of a fixed bit-length? ** k starts off as an integer satisfying 0 <= k < n. Hence, ** n <= k+n < 2n, which means k+n has either the same number ** of bits as n or one more bit than n. If k+n has the same ** number of bits as n, the second addition ensures that the ** final value has exactly one more bit than n. Thus, we ** always end up with a value that exactly one more bit than n. */ CHECK_MPI_OK(mp_add(&k, &n, &k)); if (mpl_significant_bits(&k) <= mpl_significant_bits(&n)) { CHECK_MPI_OK(mp_add(&k, &n, &k)); } /* ** ANSI X9.62, Section 5.3.2, Step 2 ** ** Compute kG */ kGpoint.len = ecParams->pointSize; kGpoint.data = PORT_Alloc(ecParams->pointSize); 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 */ 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 */ CHECK_MPI_OK(mp_mulmod(&d, &r, &n, &d)); /* d = d * r mod n */ CHECK_MPI_OK(mp_addmod(&s, &d, &n, &s)); /* s = s + d mod n */ 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); if (t2) { PORT_Free(t2); } if (kGpoint.data) { PORT_ZFree(kGpoint.data, ecParams->pointSize); } if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } #if EC_DEBUG printf("ECDSA signing with seed %s\n", (rv == SECSuccess) ? "succeeded" : "failed"); #endif #else PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); #endif /* NSS_DISABLE_ECC */ return rv; }