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