2008-06-06 05:40:11 -07:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Netscape security libraries.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
2009-04-06 18:36:45 -07:00
|
|
|
/* $Id: dsa.c,v 1.20 2009/03/29 16:51:58 wtc%google.com Exp $ */
|
2009-01-20 19:43:31 -08:00
|
|
|
|
|
|
|
#ifdef FREEBL_NO_DEPEND
|
|
|
|
#include "stubs.h"
|
|
|
|
#endif
|
2008-06-06 05:40:11 -07:00
|
|
|
|
2009-04-06 18:36:45 -07:00
|
|
|
#include "prerror.h"
|
2008-06-06 05:40:11 -07:00
|
|
|
#include "secerr.h"
|
|
|
|
|
|
|
|
#include "prtypes.h"
|
|
|
|
#include "prinit.h"
|
|
|
|
#include "blapi.h"
|
|
|
|
#include "nssilock.h"
|
|
|
|
#include "secitem.h"
|
|
|
|
#include "blapi.h"
|
|
|
|
#include "mpi.h"
|
|
|
|
#include "secmpi.h"
|
|
|
|
|
|
|
|
/* XXX to be replaced by define in blapit.h */
|
|
|
|
#define NSS_FREEBL_DSA_DEFAULT_CHUNKSIZE 2048
|
|
|
|
|
2009-04-06 18:36:45 -07:00
|
|
|
#define FIPS_DSA_Q 160
|
|
|
|
#define QSIZE (FIPS_DSA_Q / PR_BITS_PER_BYTE)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIPS 186-2 requires result from random output to be reduced mod q when
|
|
|
|
* generating random numbers for DSA.
|
|
|
|
*
|
|
|
|
* Input: w, 2*QSIZE bytes
|
|
|
|
* q, DSA_SUBPRIME_LEN bytes
|
|
|
|
* Output: xj, DSA_SUBPRIME_LEN bytes
|
|
|
|
*/
|
|
|
|
SECStatus
|
|
|
|
FIPS186Change_ReduceModQForDSA(const PRUint8 *w,
|
|
|
|
const PRUint8 *q,
|
|
|
|
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*QSIZE) );
|
|
|
|
CHECK_MPI_OK( mp_read_unsigned_octets(&Q, q, DSA_SUBPRIME_LEN) );
|
|
|
|
/*
|
|
|
|
* 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, DSA_SUBPRIME_LEN) );
|
|
|
|
cleanup:
|
|
|
|
mp_clear(&W);
|
|
|
|
mp_clear(&Q);
|
|
|
|
mp_clear(&Xj);
|
|
|
|
if (err) {
|
|
|
|
MP_TO_SEC_ERROR(err);
|
|
|
|
rv = SECFailure;
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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(void *dest, size_t len, const PRUint8 *q)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
PRUint8 w[2*QSIZE];
|
|
|
|
|
|
|
|
PORT_Assert(q && len == DSA_SUBPRIME_LEN);
|
|
|
|
if (len != DSA_SUBPRIME_LEN) {
|
|
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
if (*q == 0) {
|
|
|
|
++q;
|
|
|
|
}
|
|
|
|
rv = RNG_GenerateGlobalRandomBytes(w, 2*QSIZE);
|
|
|
|
if (rv != SECSuccess) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
FIPS186Change_ReduceModQForDSA(w, q, (PRUint8 *)dest);
|
|
|
|
return rv;
|
|
|
|
}
|
2008-06-06 05:40:11 -07:00
|
|
|
|
|
|
|
static void translate_mpi_error(mp_err err)
|
|
|
|
{
|
|
|
|
MP_TO_SEC_ERROR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
dsa_NewKey(const PQGParams *params, DSAPrivateKey **privKey,
|
|
|
|
const unsigned char *xb)
|
|
|
|
{
|
|
|
|
mp_int p, g;
|
|
|
|
mp_int x, y;
|
|
|
|
mp_err err;
|
|
|
|
PRArenaPool *arena;
|
|
|
|
DSAPrivateKey *key;
|
|
|
|
/* Check args. */
|
|
|
|
if (!params || !privKey) {
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
/* Initialize an arena for the DSA key. */
|
|
|
|
arena = PORT_NewArena(NSS_FREEBL_DSA_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(xb, &x, DSA_SUBPRIME_LEN);
|
|
|
|
/* Store x in private key */
|
|
|
|
SECITEM_AllocItem(arena, &key->privateValue, DSA_SUBPRIME_LEN);
|
|
|
|
memcpy(key->privateValue.data, xb, DSA_SUBPRIME_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
unsigned char seed[DSA_SUBPRIME_LEN];
|
|
|
|
int retries = 10;
|
|
|
|
int i;
|
|
|
|
PRBool good;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* Generate seed bytes for x according to FIPS 186-1 appendix 3 */
|
2009-04-06 18:36:45 -07:00
|
|
|
if (dsa_GenerateGlobalRandomBytes(seed, DSA_SUBPRIME_LEN,
|
2008-06-06 05:40:11 -07:00
|
|
|
params->subPrime.data))
|
|
|
|
return SECFailure;
|
|
|
|
/* Disallow values of 0 and 1 for x. */
|
|
|
|
good = PR_FALSE;
|
|
|
|
for (i = 0; i < DSA_SUBPRIME_LEN-1; i++) {
|
|
|
|
if (seed[i] != 0) {
|
|
|
|
good = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!good && seed[i] > 1) {
|
|
|
|
good = PR_TRUE;
|
|
|
|
}
|
|
|
|
} while (!good && --retries > 0);
|
|
|
|
|
|
|
|
if (!good) {
|
|
|
|
PORT_SetError(SEC_ERROR_NEED_RANDOM);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate a new DSA key using random seed. */
|
|
|
|
rv = dsa_NewKey(params, privKey, seed);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For FIPS compliance testing. Seed must be exactly 20 bytes long */
|
|
|
|
SECStatus
|
|
|
|
DSA_NewKeyFromSeed(const PQGParams *params,
|
|
|
|
const unsigned char *seed,
|
|
|
|
DSAPrivateKey **privKey)
|
|
|
|
{
|
|
|
|
SECStatus rv;
|
|
|
|
rv = dsa_NewKey(params, privKey, seed);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
|
|
|
dsa_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest,
|
|
|
|
const unsigned char *kb)
|
|
|
|
{
|
|
|
|
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_err err = MP_OKAY;
|
|
|
|
SECStatus rv = SECSuccess;
|
|
|
|
|
|
|
|
/* FIPS-compliance dictates that digest is a SHA1 hash. */
|
|
|
|
/* Check args. */
|
|
|
|
if (!key || !signature || !digest ||
|
|
|
|
(signature->len < DSA_SIGNATURE_LEN) ||
|
|
|
|
(digest->len != SHA1_LENGTH)) {
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
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) );
|
|
|
|
/*
|
|
|
|
** 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(kb, &k, DSA_SUBPRIME_LEN);
|
|
|
|
/*
|
|
|
|
** FIPS 186-1, Section 5, Step 1
|
|
|
|
**
|
|
|
|
** r = (g**k mod p) mod q
|
|
|
|
*/
|
|
|
|
CHECK_MPI_OK( mp_exptmod(&g, &k, &p, &r) ); /* r = g**k mod p */
|
|
|
|
CHECK_MPI_OK( mp_mod(&r, &q, &r) ); /* r = r mod q */
|
|
|
|
/*
|
|
|
|
** FIPS 186-1, Section 5, Step 2
|
|
|
|
**
|
|
|
|
** s = (k**-1 * (SHA1(M) + x*r)) mod q
|
|
|
|
*/
|
|
|
|
SECITEM_TO_MPINT(*digest, &s); /* s = SHA1(M) */
|
|
|
|
CHECK_MPI_OK( mp_invmod(&k, &q, &k) ); /* k = k**-1 mod q */
|
|
|
|
CHECK_MPI_OK( mp_mulmod(&x, &r, &q, &x) ); /* x = x * r mod q */
|
|
|
|
CHECK_MPI_OK( mp_addmod(&s, &x, &q, &s) ); /* s = s + x mod q */
|
|
|
|
CHECK_MPI_OK( mp_mulmod(&s, &k, &q, &s) ); /* s = s * k mod q */
|
|
|
|
/*
|
|
|
|
** 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:
|
|
|
|
mp_clear(&p);
|
|
|
|
mp_clear(&q);
|
|
|
|
mp_clear(&g);
|
|
|
|
mp_clear(&x);
|
|
|
|
mp_clear(&k);
|
|
|
|
mp_clear(&r);
|
|
|
|
mp_clear(&s);
|
|
|
|
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_SUBPRIME_LEN];
|
|
|
|
int i;
|
|
|
|
PRBool good;
|
|
|
|
|
|
|
|
PORT_SetError(0);
|
|
|
|
do {
|
2009-04-06 18:36:45 -07:00
|
|
|
rv = dsa_GenerateGlobalRandomBytes(kSeed, DSA_SUBPRIME_LEN,
|
2008-06-06 05:40:11 -07:00
|
|
|
key->params.subPrime.data);
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
break;
|
|
|
|
/* Disallow a value of 0 for k. */
|
|
|
|
good = PR_FALSE;
|
|
|
|
for (i = 0; i < DSA_SUBPRIME_LEN; 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);
|
|
|
|
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 SHA1 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;
|
|
|
|
SECStatus verified = SECFailure;
|
|
|
|
|
|
|
|
/* Check args. */
|
|
|
|
if (!key || !signature || !digest ||
|
|
|
|
(signature->len != DSA_SIGNATURE_LEN) ||
|
|
|
|
(digest->len != SHA1_LENGTH)) {
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
/* 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 = ((SHA1(M')) * w) mod q
|
|
|
|
*/
|
|
|
|
SECITEM_TO_MPINT(*digest, &u1); /* u1 = SHA1(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:
|
|
|
|
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;
|
|
|
|
}
|