| /* |
| * |
| * 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 "prerror.h" |
| #include "secerr.h" |
| |
| #include "prtypes.h" |
| #include "prinit.h" |
| #include "blapi.h" |
| #include "nssilock.h" |
| #include "secitem.h" |
| #include "blapit.h" |
| #include "mpi.h" |
| #include "secmpi.h" |
| #include "pqg.h" |
| |
| /* |
| * FIPS 186-2 requires result from random output to be reduced mod q when |
| * generating random numbers for DSA. |
| * |
| * Input: w, 2*qLen bytes |
| * q, qLen bytes |
| * Output: xj, qLen bytes |
| */ |
| static SECStatus |
| fips186Change_ReduceModQForDSA(const PRUint8 *w, const PRUint8 *q, |
| unsigned int qLen, PRUint8 *xj) |
| { |
| mp_int W, Q, Xj; |
| mp_err err; |
| SECStatus rv = SECSuccess; |
| |
| /* Initialize MPI integers. */ |
| MP_DIGITS(&W) = 0; |
| MP_DIGITS(&Q) = 0; |
| MP_DIGITS(&Xj) = 0; |
| CHECK_MPI_OK(mp_init(&W)); |
| CHECK_MPI_OK(mp_init(&Q)); |
| CHECK_MPI_OK(mp_init(&Xj)); |
| /* |
| * Convert input arguments into MPI integers. |
| */ |
| CHECK_MPI_OK(mp_read_unsigned_octets(&W, w, 2 * qLen)); |
| CHECK_MPI_OK(mp_read_unsigned_octets(&Q, q, qLen)); |
| |
| /* |
| * Algorithm 1 of FIPS 186-2 Change Notice 1, Step 3.3 |
| * |
| * xj = (w0 || w1) mod q |
| */ |
| CHECK_MPI_OK(mp_mod(&W, &Q, &Xj)); |
| CHECK_MPI_OK(mp_to_fixlen_octets(&Xj, xj, qLen)); |
| cleanup: |
| mp_clear(&W); |
| mp_clear(&Q); |
| mp_clear(&Xj); |
| if (err) { |
| MP_TO_SEC_ERROR(err); |
| rv = SECFailure; |
| } |
| return rv; |
| } |
| |
| /* |
| * FIPS 186-2 requires result from random output to be reduced mod q when |
| * generating random numbers for DSA. |
| */ |
| SECStatus |
| FIPS186Change_ReduceModQForDSA(const unsigned char *w, |
| const unsigned char *q, |
| unsigned char *xj) |
| { |
| return fips186Change_ReduceModQForDSA(w, q, DSA1_SUBPRIME_LEN, xj); |
| } |
| |
| /* |
| * The core of Algorithm 1 of FIPS 186-2 Change Notice 1. |
| * |
| * We no longer support FIPS 186-2 RNG. This function was exported |
| * for power-up self tests and FIPS tests. Keep this stub, which fails, |
| * to prevent crashes, but also to signal to test code that FIPS 186-2 |
| * RNG is no longer supported. |
| */ |
| SECStatus |
| FIPS186Change_GenerateX(PRUint8 *XKEY, const PRUint8 *XSEEDj, |
| PRUint8 *x_j) |
| { |
| PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
| return SECFailure; |
| } |
| |
| /* |
| * Specialized RNG for DSA |
| * |
| * As per Algorithm 1 of FIPS 186-2 Change Notice 1, in step 3.3 the value |
| * Xj should be reduced mod q, a 160-bit prime number. Since this parameter |
| * is only meaningful in the context of DSA, the above RNG functions |
| * were implemented without it. They are re-implemented below for use |
| * with DSA. |
| */ |
| |
| /* |
| ** Generate some random bytes, using the global random number generator |
| ** object. In DSA mode, so there is a q. |
| */ |
| static SECStatus |
| dsa_GenerateGlobalRandomBytes(const SECItem *qItem, PRUint8 *dest, |
| unsigned int *destLen, unsigned int maxDestLen) |
| { |
| SECStatus rv; |
| SECItem w; |
| const PRUint8 *q = qItem->data; |
| unsigned int qLen = qItem->len; |
| |
| if (*q == 0) { |
| ++q; |
| --qLen; |
| } |
| if (maxDestLen < qLen) { |
| /* This condition can occur when DSA_SignDigest is passed a group |
| with a subprime that is larger than DSA_MAX_SUBPRIME_LEN. */ |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| w.data = NULL; /* otherwise SECITEM_AllocItem asserts */ |
| if (!SECITEM_AllocItem(NULL, &w, 2 * qLen)) { |
| return SECFailure; |
| } |
| *destLen = qLen; |
| |
| rv = RNG_GenerateGlobalRandomBytes(w.data, w.len); |
| if (rv == SECSuccess) { |
| rv = fips186Change_ReduceModQForDSA(w.data, q, qLen, dest); |
| } |
| |
| SECITEM_FreeItem(&w, PR_FALSE); |
| return rv; |
| } |
| |
| static void |
| translate_mpi_error(mp_err err) |
| { |
| MP_TO_SEC_ERROR(err); |
| } |
| |
| static SECStatus |
| dsa_NewKeyExtended(const PQGParams *params, const SECItem *seed, |
| DSAPrivateKey **privKey) |
| { |
| mp_int p, g; |
| mp_int x, y; |
| mp_err err; |
| PLArenaPool *arena; |
| DSAPrivateKey *key; |
| /* Check args. */ |
| if (!params || !privKey || !seed || !seed->data) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| /* Initialize an arena for the DSA key. */ |
| arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE); |
| if (!arena) { |
| PORT_SetError(SEC_ERROR_NO_MEMORY); |
| return SECFailure; |
| } |
| key = (DSAPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DSAPrivateKey)); |
| if (!key) { |
| PORT_SetError(SEC_ERROR_NO_MEMORY); |
| PORT_FreeArena(arena, PR_TRUE); |
| return SECFailure; |
| } |
| key->params.arena = arena; |
| /* Initialize MPI integers. */ |
| MP_DIGITS(&p) = 0; |
| MP_DIGITS(&g) = 0; |
| MP_DIGITS(&x) = 0; |
| MP_DIGITS(&y) = 0; |
| CHECK_MPI_OK(mp_init(&p)); |
| CHECK_MPI_OK(mp_init(&g)); |
| CHECK_MPI_OK(mp_init(&x)); |
| CHECK_MPI_OK(mp_init(&y)); |
| /* Copy over the PQG params */ |
| CHECK_MPI_OK(SECITEM_CopyItem(arena, &key->params.prime, |
| ¶ms->prime)); |
| CHECK_MPI_OK(SECITEM_CopyItem(arena, &key->params.subPrime, |
| ¶ms->subPrime)); |
| CHECK_MPI_OK(SECITEM_CopyItem(arena, &key->params.base, ¶ms->base)); |
| /* Convert stored p, g, and received x into MPI integers. */ |
| SECITEM_TO_MPINT(params->prime, &p); |
| SECITEM_TO_MPINT(params->base, &g); |
| OCTETS_TO_MPINT(seed->data, &x, seed->len); |
| /* Store x in private key */ |
| SECITEM_AllocItem(arena, &key->privateValue, seed->len); |
| PORT_Memcpy(key->privateValue.data, seed->data, seed->len); |
| /* Compute public key y = g**x mod p */ |
| CHECK_MPI_OK(mp_exptmod(&g, &x, &p, &y)); |
| /* Store y in public key */ |
| MPINT_TO_SECITEM(&y, &key->publicValue, arena); |
| *privKey = key; |
| key = NULL; |
| cleanup: |
| mp_clear(&p); |
| mp_clear(&g); |
| mp_clear(&x); |
| mp_clear(&y); |
| if (key) { |
| PORT_FreeArena(key->params.arena, PR_TRUE); |
| } |
| if (err) { |
| translate_mpi_error(err); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| SECStatus |
| DSA_NewRandom(PLArenaPool *arena, const SECItem *q, SECItem *seed) |
| { |
| int retries = 10; |
| unsigned int i; |
| PRBool good; |
| |
| if (q == NULL || q->data == NULL || q->len == 0 || |
| (q->data[0] == 0 && q->len == 1)) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| if (!SECITEM_AllocItem(arena, seed, q->len)) { |
| return SECFailure; |
| } |
| |
| do { |
| /* Generate seed bytes for x according to FIPS 186-1 appendix 3 */ |
| if (dsa_GenerateGlobalRandomBytes(q, seed->data, &seed->len, |
| seed->len)) { |
| goto loser; |
| } |
| /* Disallow values of 0 and 1 for x. */ |
| good = PR_FALSE; |
| for (i = 0; i < seed->len - 1; i++) { |
| if (seed->data[i] != 0) { |
| good = PR_TRUE; |
| break; |
| } |
| } |
| if (!good && seed->data[i] > 1) { |
| good = PR_TRUE; |
| } |
| } while (!good && --retries > 0); |
| |
| if (!good) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| loser: |
| if (arena != NULL) { |
| SECITEM_ZfreeItem(seed, PR_FALSE); |
| } |
| return SECFailure; |
| } |
| |
| return SECSuccess; |
| } |
| |
| /* |
| ** Generate and return a new DSA public and private key pair, |
| ** both of which are encoded into a single DSAPrivateKey struct. |
| ** "params" is a pointer to the PQG parameters for the domain |
| ** Uses a random seed. |
| */ |
| SECStatus |
| DSA_NewKey(const PQGParams *params, DSAPrivateKey **privKey) |
| { |
| SECItem seed; |
| SECStatus rv; |
| |
| rv = PQG_Check(params); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| seed.data = NULL; |
| |
| rv = DSA_NewRandom(NULL, ¶ms->subPrime, &seed); |
| if (rv == SECSuccess) { |
| if (seed.len != PQG_GetLength(¶ms->subPrime)) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| rv = SECFailure; |
| } else { |
| rv = dsa_NewKeyExtended(params, &seed, privKey); |
| } |
| } |
| SECITEM_ZfreeItem(&seed, PR_FALSE); |
| return rv; |
| } |
| |
| /* For FIPS compliance testing. Seed must be exactly the size of subPrime */ |
| SECStatus |
| DSA_NewKeyFromSeed(const PQGParams *params, |
| const unsigned char *seed, |
| DSAPrivateKey **privKey) |
| { |
| SECItem seedItem; |
| seedItem.data = (unsigned char *)seed; |
| seedItem.len = PQG_GetLength(¶ms->subPrime); |
| return dsa_NewKeyExtended(params, &seedItem, privKey); |
| } |
| |
| static SECStatus |
| dsa_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest, |
| const unsigned char *kbytes) |
| { |
| mp_int p, q, g; /* PQG parameters */ |
| mp_int x, k; /* private key & pseudo-random integer */ |
| mp_int r, s; /* tuple (r, s) is signature) */ |
| mp_int t; /* holding tmp values */ |
| mp_int ar; /* holding blinding values */ |
| mp_digit fuzz; /* blinding multiplier for q */ |
| mp_err err = MP_OKAY; |
| SECStatus rv = SECSuccess; |
| unsigned int dsa_subprime_len, dsa_signature_len, offset; |
| SECItem localDigest; |
| unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN]; |
| SECItem t2 = { siBuffer, NULL, 0 }; |
| |
| /* FIPS-compliance dictates that digest is a SHA hash. */ |
| /* Check args. */ |
| if (!key || !signature || !digest) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| dsa_subprime_len = PQG_GetLength(&key->params.subPrime); |
| dsa_signature_len = dsa_subprime_len * 2; |
| if ((signature->len < dsa_signature_len) || |
| (digest->len > HASH_LENGTH_MAX) || |
| (digest->len < SHA1_LENGTH)) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* DSA accepts digests not equal to dsa_subprime_len, if the |
| * digests are greater, then they are truncated to the size of |
| * dsa_subprime_len, using the left most bits. If they are less |
| * then they are padded on the left.*/ |
| PORT_Memset(localDigestData, 0, dsa_subprime_len); |
| offset = (digest->len < dsa_subprime_len) ? (dsa_subprime_len - digest->len) : 0; |
| PORT_Memcpy(localDigestData + offset, digest->data, |
| dsa_subprime_len - offset); |
| localDigest.data = localDigestData; |
| localDigest.len = dsa_subprime_len; |
| |
| /* Initialize MPI integers. */ |
| MP_DIGITS(&p) = 0; |
| MP_DIGITS(&q) = 0; |
| MP_DIGITS(&g) = 0; |
| MP_DIGITS(&x) = 0; |
| MP_DIGITS(&k) = 0; |
| MP_DIGITS(&r) = 0; |
| MP_DIGITS(&s) = 0; |
| MP_DIGITS(&t) = 0; |
| MP_DIGITS(&ar) = 0; |
| CHECK_MPI_OK(mp_init(&p)); |
| CHECK_MPI_OK(mp_init(&q)); |
| CHECK_MPI_OK(mp_init(&g)); |
| CHECK_MPI_OK(mp_init(&x)); |
| CHECK_MPI_OK(mp_init(&k)); |
| CHECK_MPI_OK(mp_init(&r)); |
| CHECK_MPI_OK(mp_init(&s)); |
| CHECK_MPI_OK(mp_init(&t)); |
| CHECK_MPI_OK(mp_init(&ar)); |
| |
| /* |
| ** Convert stored PQG and private key into MPI integers. |
| */ |
| SECITEM_TO_MPINT(key->params.prime, &p); |
| SECITEM_TO_MPINT(key->params.subPrime, &q); |
| SECITEM_TO_MPINT(key->params.base, &g); |
| SECITEM_TO_MPINT(key->privateValue, &x); |
| OCTETS_TO_MPINT(kbytes, &k, dsa_subprime_len); |
| |
| /* k blinding create a single value that has the high bit set in |
| * the mp_digit*/ |
| if (RNG_GenerateGlobalRandomBytes(&fuzz, sizeof(mp_digit)) != SECSuccess) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| rv = SECFailure; |
| goto cleanup; |
| } |
| fuzz |= 1ULL << ((sizeof(mp_digit) * PR_BITS_PER_BYTE - 1)); |
| /* |
| ** FIPS 186-1, Section 5, Step 1 |
| ** |
| ** r = (g**k mod p) mod q |
| */ |
| CHECK_MPI_OK(mp_mul_d(&q, fuzz, &t)); /* t = q*fuzz */ |
| CHECK_MPI_OK(mp_add(&k, &t, &t)); /* t = k+q*fuzz */ |
| /* length of t is now fixed, bits in k have been blinded */ |
| CHECK_MPI_OK(mp_exptmod(&g, &t, &p, &r)); /* r = g**t mod p */ |
| /* r is now g**(k+q*fuzz) == g**k mod p */ |
| CHECK_MPI_OK(mp_mod(&r, &q, &r)); /* r = r mod q */ |
| /* make sure fuzz is cleared off the stack and not optimized away */ |
| *(volatile mp_digit *)&fuzz = 0; |
| |
| /* |
| ** FIPS 186-1, Section 5, Step 2 |
| ** |
| ** s = (k**-1 * (HASH(M) + x*r)) mod q |
| */ |
| if (DSA_NewRandom(NULL, &key->params.subPrime, &t2) != SECSuccess) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| rv = SECFailure; |
| goto cleanup; |
| } |
| SECITEM_TO_MPINT(t2, &t); /* t <-$ Zq */ |
| SECITEM_ZfreeItem(&t2, PR_FALSE); |
| if (DSA_NewRandom(NULL, &key->params.subPrime, &t2) != SECSuccess) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| rv = SECFailure; |
| goto cleanup; |
| } |
| SECITEM_TO_MPINT(t2, &ar); /* ar <-$ Zq */ |
| SECITEM_ZfreeItem(&t2, PR_FALSE); |
| |
| /* 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, &q, &k)); /* k = k * t mod q */ |
| /* k is now k*t*ar */ |
| CHECK_MPI_OK(mp_invmod(&k, &q, &k)); /* k = k**-1 mod q */ |
| /* k is now (k*t*ar)**-1 */ |
| CHECK_MPI_OK(mp_mulmod(&k, &t, &q, &k)); /* k = k * t mod q */ |
| /* k is now (k*ar)**-1 */ |
| SECITEM_TO_MPINT(localDigest, &s); /* s = HASH(M) */ |
| /* To avoid leaking secret bits here the addition is blinded. */ |
| CHECK_MPI_OK(mp_mul(&x, &ar, &x)); /* x = x * ar */ |
| /* x is now x*ar */ |
| CHECK_MPI_OK(mp_mulmod(&x, &r, &q, &x)); /* x = x * r mod q */ |
| /* x is now x*r*ar */ |
| CHECK_MPI_OK(mp_mulmod(&s, &ar, &q, &t)); /* t = s * ar mod q */ |
| /* t is now hash(M)*ar */ |
| CHECK_MPI_OK(mp_add(&t, &x, &s)); /* s = t + x */ |
| /* s is now (HASH(M)+x*r)*ar */ |
| CHECK_MPI_OK(mp_mulmod(&s, &k, &q, &s)); /* s = s * k mod q */ |
| /* s is now (HASH(M)+x*r)*ar*(k*ar)**-1 = (k**-1)*(HASH(M)+x*r) */ |
| |
| /* |
| ** verify r != 0 and s != 0 |
| ** mentioned as optional in FIPS 186-1. |
| */ |
| if (mp_cmp_z(&r) == 0 || mp_cmp_z(&s) == 0) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| rv = SECFailure; |
| goto cleanup; |
| } |
| /* |
| ** Step 4 |
| ** |
| ** Signature is tuple (r, s) |
| */ |
| err = mp_to_fixlen_octets(&r, signature->data, dsa_subprime_len); |
| if (err < 0) |
| goto cleanup; |
| err = mp_to_fixlen_octets(&s, signature->data + dsa_subprime_len, |
| dsa_subprime_len); |
| if (err < 0) |
| goto cleanup; |
| err = MP_OKAY; |
| signature->len = dsa_signature_len; |
| cleanup: |
| PORT_Memset(localDigestData, 0, DSA_MAX_SUBPRIME_LEN); |
| mp_clear(&p); |
| mp_clear(&q); |
| mp_clear(&g); |
| mp_clear(&x); |
| mp_clear(&k); |
| mp_clear(&r); |
| mp_clear(&s); |
| mp_clear(&t); |
| mp_clear(&ar); |
| if (err) { |
| translate_mpi_error(err); |
| rv = SECFailure; |
| } |
| return rv; |
| } |
| |
| /* signature is caller-supplied buffer of at least 40 bytes. |
| ** On input, signature->len == size of buffer to hold signature. |
| ** digest->len == size of digest. |
| ** On output, signature->len == size of signature in buffer. |
| ** Uses a random seed. |
| */ |
| SECStatus |
| DSA_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest) |
| { |
| SECStatus rv; |
| int retries = 10; |
| unsigned char kSeed[DSA_MAX_SUBPRIME_LEN]; |
| unsigned int kSeedLen = 0; |
| unsigned int i; |
| unsigned int dsa_subprime_len = PQG_GetLength(&key->params.subPrime); |
| PRBool good; |
| |
| PORT_SetError(0); |
| do { |
| rv = dsa_GenerateGlobalRandomBytes(&key->params.subPrime, |
| kSeed, &kSeedLen, sizeof kSeed); |
| if (rv != SECSuccess) |
| break; |
| if (kSeedLen != dsa_subprime_len) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| rv = SECFailure; |
| break; |
| } |
| /* Disallow a value of 0 for k. */ |
| good = PR_FALSE; |
| for (i = 0; i < kSeedLen; i++) { |
| if (kSeed[i] != 0) { |
| good = PR_TRUE; |
| break; |
| } |
| } |
| if (!good) { |
| PORT_SetError(SEC_ERROR_NEED_RANDOM); |
| rv = SECFailure; |
| continue; |
| } |
| rv = dsa_SignDigest(key, signature, digest, kSeed); |
| } while (rv != SECSuccess && PORT_GetError() == SEC_ERROR_NEED_RANDOM && |
| --retries > 0); |
| PORT_Memset(kSeed, 0, sizeof kSeed); |
| return rv; |
| } |
| |
| /* For FIPS compliance testing. Seed must be exactly 20 bytes. */ |
| SECStatus |
| DSA_SignDigestWithSeed(DSAPrivateKey *key, |
| SECItem *signature, |
| const SECItem *digest, |
| const unsigned char *seed) |
| { |
| SECStatus rv; |
| rv = dsa_SignDigest(key, signature, digest, seed); |
| return rv; |
| } |
| |
| /* signature is caller-supplied buffer of at least 20 bytes. |
| ** On input, signature->len == size of buffer to hold signature. |
| ** digest->len == size of digest. |
| */ |
| SECStatus |
| DSA_VerifyDigest(DSAPublicKey *key, const SECItem *signature, |
| const SECItem *digest) |
| { |
| /* FIPS-compliance dictates that digest is a SHA hash. */ |
| mp_int p, q, g; /* PQG parameters */ |
| mp_int r_, s_; /* tuple (r', s') is received signature) */ |
| mp_int u1, u2, v, w; /* intermediate values used in verification */ |
| mp_int y; /* public key */ |
| mp_err err; |
| unsigned int dsa_subprime_len, dsa_signature_len, offset; |
| SECItem localDigest; |
| unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN]; |
| SECStatus verified = SECFailure; |
| |
| /* Check args. */ |
| if (!key || !signature || !digest) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| dsa_subprime_len = PQG_GetLength(&key->params.subPrime); |
| dsa_signature_len = dsa_subprime_len * 2; |
| if ((signature->len != dsa_signature_len) || |
| (digest->len > HASH_LENGTH_MAX) || |
| (digest->len < SHA1_LENGTH)) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| /* DSA accepts digests not equal to dsa_subprime_len, if the |
| * digests are greater, than they are truncated to the size of |
| * dsa_subprime_len, using the left most bits. If they are less |
| * then they are padded on the left.*/ |
| PORT_Memset(localDigestData, 0, dsa_subprime_len); |
| offset = (digest->len < dsa_subprime_len) ? (dsa_subprime_len - digest->len) : 0; |
| PORT_Memcpy(localDigestData + offset, digest->data, |
| dsa_subprime_len - offset); |
| localDigest.data = localDigestData; |
| localDigest.len = dsa_subprime_len; |
| |
| /* Initialize MPI integers. */ |
| MP_DIGITS(&p) = 0; |
| MP_DIGITS(&q) = 0; |
| MP_DIGITS(&g) = 0; |
| MP_DIGITS(&y) = 0; |
| MP_DIGITS(&r_) = 0; |
| MP_DIGITS(&s_) = 0; |
| MP_DIGITS(&u1) = 0; |
| MP_DIGITS(&u2) = 0; |
| MP_DIGITS(&v) = 0; |
| MP_DIGITS(&w) = 0; |
| CHECK_MPI_OK(mp_init(&p)); |
| CHECK_MPI_OK(mp_init(&q)); |
| CHECK_MPI_OK(mp_init(&g)); |
| CHECK_MPI_OK(mp_init(&y)); |
| CHECK_MPI_OK(mp_init(&r_)); |
| CHECK_MPI_OK(mp_init(&s_)); |
| CHECK_MPI_OK(mp_init(&u1)); |
| CHECK_MPI_OK(mp_init(&u2)); |
| CHECK_MPI_OK(mp_init(&v)); |
| CHECK_MPI_OK(mp_init(&w)); |
| /* |
| ** Convert stored PQG and public key into MPI integers. |
| */ |
| SECITEM_TO_MPINT(key->params.prime, &p); |
| SECITEM_TO_MPINT(key->params.subPrime, &q); |
| SECITEM_TO_MPINT(key->params.base, &g); |
| SECITEM_TO_MPINT(key->publicValue, &y); |
| /* |
| ** Convert received signature (r', s') into MPI integers. |
| */ |
| OCTETS_TO_MPINT(signature->data, &r_, dsa_subprime_len); |
| OCTETS_TO_MPINT(signature->data + dsa_subprime_len, &s_, dsa_subprime_len); |
| /* |
| ** Verify that 0 < r' < q and 0 < s' < q |
| */ |
| if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || |
| mp_cmp(&r_, &q) >= 0 || mp_cmp(&s_, &q) >= 0) { |
| /* err is zero here. */ |
| PORT_SetError(SEC_ERROR_BAD_SIGNATURE); |
| goto cleanup; /* will return verified == SECFailure */ |
| } |
| /* |
| ** FIPS 186-1, Section 6, Step 1 |
| ** |
| ** w = (s')**-1 mod q |
| */ |
| CHECK_MPI_OK(mp_invmod(&s_, &q, &w)); /* w = (s')**-1 mod q */ |
| /* |
| ** FIPS 186-1, Section 6, Step 2 |
| ** |
| ** u1 = ((Hash(M')) * w) mod q |
| */ |
| SECITEM_TO_MPINT(localDigest, &u1); /* u1 = HASH(M') */ |
| CHECK_MPI_OK(mp_mulmod(&u1, &w, &q, &u1)); /* u1 = u1 * w mod q */ |
| /* |
| ** FIPS 186-1, Section 6, Step 3 |
| ** |
| ** u2 = ((r') * w) mod q |
| */ |
| CHECK_MPI_OK(mp_mulmod(&r_, &w, &q, &u2)); |
| /* |
| ** FIPS 186-1, Section 6, Step 4 |
| ** |
| ** v = ((g**u1 * y**u2) mod p) mod q |
| */ |
| CHECK_MPI_OK(mp_exptmod(&g, &u1, &p, &g)); /* g = g**u1 mod p */ |
| CHECK_MPI_OK(mp_exptmod(&y, &u2, &p, &y)); /* y = y**u2 mod p */ |
| CHECK_MPI_OK(mp_mulmod(&g, &y, &p, &v)); /* v = g * y mod p */ |
| CHECK_MPI_OK(mp_mod(&v, &q, &v)); /* v = v mod q */ |
| /* |
| ** Verification: v == r' |
| */ |
| if (mp_cmp(&v, &r_)) { |
| PORT_SetError(SEC_ERROR_BAD_SIGNATURE); |
| verified = SECFailure; /* Signature failed to verify. */ |
| } else { |
| verified = SECSuccess; /* Signature verified. */ |
| } |
| cleanup: |
| PORT_Memset(localDigestData, 0, sizeof localDigestData); |
| mp_clear(&p); |
| mp_clear(&q); |
| mp_clear(&g); |
| mp_clear(&y); |
| mp_clear(&r_); |
| mp_clear(&s_); |
| mp_clear(&u1); |
| mp_clear(&u2); |
| mp_clear(&v); |
| mp_clear(&w); |
| if (err) { |
| translate_mpi_error(err); |
| } |
| return verified; |
| } |