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