security/nss/lib/freebl/dsa.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifdef FREEBL_NO_DEPEND
michael@0 8 #include "stubs.h"
michael@0 9 #endif
michael@0 10
michael@0 11 #include "prerror.h"
michael@0 12 #include "secerr.h"
michael@0 13
michael@0 14 #include "prtypes.h"
michael@0 15 #include "prinit.h"
michael@0 16 #include "blapi.h"
michael@0 17 #include "nssilock.h"
michael@0 18 #include "secitem.h"
michael@0 19 #include "blapi.h"
michael@0 20 #include "mpi.h"
michael@0 21 #include "secmpi.h"
michael@0 22 #include "pqg.h"
michael@0 23
michael@0 24 /* XXX to be replaced by define in blapit.h */
michael@0 25 #define NSS_FREEBL_DSA_DEFAULT_CHUNKSIZE 2048
michael@0 26
michael@0 27 /*
michael@0 28 * FIPS 186-2 requires result from random output to be reduced mod q when
michael@0 29 * generating random numbers for DSA.
michael@0 30 *
michael@0 31 * Input: w, 2*qLen bytes
michael@0 32 * q, qLen bytes
michael@0 33 * Output: xj, qLen bytes
michael@0 34 */
michael@0 35 static SECStatus
michael@0 36 fips186Change_ReduceModQForDSA(const PRUint8 *w, const PRUint8 *q,
michael@0 37 unsigned int qLen, PRUint8 * xj)
michael@0 38 {
michael@0 39 mp_int W, Q, Xj;
michael@0 40 mp_err err;
michael@0 41 SECStatus rv = SECSuccess;
michael@0 42
michael@0 43 /* Initialize MPI integers. */
michael@0 44 MP_DIGITS(&W) = 0;
michael@0 45 MP_DIGITS(&Q) = 0;
michael@0 46 MP_DIGITS(&Xj) = 0;
michael@0 47 CHECK_MPI_OK( mp_init(&W) );
michael@0 48 CHECK_MPI_OK( mp_init(&Q) );
michael@0 49 CHECK_MPI_OK( mp_init(&Xj) );
michael@0 50 /*
michael@0 51 * Convert input arguments into MPI integers.
michael@0 52 */
michael@0 53 CHECK_MPI_OK( mp_read_unsigned_octets(&W, w, 2*qLen) );
michael@0 54 CHECK_MPI_OK( mp_read_unsigned_octets(&Q, q, qLen) );
michael@0 55
michael@0 56 /*
michael@0 57 * Algorithm 1 of FIPS 186-2 Change Notice 1, Step 3.3
michael@0 58 *
michael@0 59 * xj = (w0 || w1) mod q
michael@0 60 */
michael@0 61 CHECK_MPI_OK( mp_mod(&W, &Q, &Xj) );
michael@0 62 CHECK_MPI_OK( mp_to_fixlen_octets(&Xj, xj, qLen) );
michael@0 63 cleanup:
michael@0 64 mp_clear(&W);
michael@0 65 mp_clear(&Q);
michael@0 66 mp_clear(&Xj);
michael@0 67 if (err) {
michael@0 68 MP_TO_SEC_ERROR(err);
michael@0 69 rv = SECFailure;
michael@0 70 }
michael@0 71 return rv;
michael@0 72 }
michael@0 73
michael@0 74 /*
michael@0 75 * FIPS 186-2 requires result from random output to be reduced mod q when
michael@0 76 * generating random numbers for DSA.
michael@0 77 */
michael@0 78 SECStatus
michael@0 79 FIPS186Change_ReduceModQForDSA(const unsigned char *w,
michael@0 80 const unsigned char *q,
michael@0 81 unsigned char *xj) {
michael@0 82 return fips186Change_ReduceModQForDSA(w, q, DSA1_SUBPRIME_LEN, xj);
michael@0 83 }
michael@0 84
michael@0 85 /*
michael@0 86 * The core of Algorithm 1 of FIPS 186-2 Change Notice 1.
michael@0 87 *
michael@0 88 * We no longer support FIPS 186-2 RNG. This function was exported
michael@0 89 * for power-up self tests and FIPS tests. Keep this stub, which fails,
michael@0 90 * to prevent crashes, but also to signal to test code that FIPS 186-2
michael@0 91 * RNG is no longer supported.
michael@0 92 */
michael@0 93 SECStatus
michael@0 94 FIPS186Change_GenerateX(PRUint8 *XKEY, const PRUint8 *XSEEDj,
michael@0 95 PRUint8 *x_j)
michael@0 96 {
michael@0 97 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
michael@0 98 return SECFailure;
michael@0 99 }
michael@0 100
michael@0 101 /*
michael@0 102 * Specialized RNG for DSA
michael@0 103 *
michael@0 104 * As per Algorithm 1 of FIPS 186-2 Change Notice 1, in step 3.3 the value
michael@0 105 * Xj should be reduced mod q, a 160-bit prime number. Since this parameter
michael@0 106 * is only meaningful in the context of DSA, the above RNG functions
michael@0 107 * were implemented without it. They are re-implemented below for use
michael@0 108 * with DSA.
michael@0 109 */
michael@0 110
michael@0 111 /*
michael@0 112 ** Generate some random bytes, using the global random number generator
michael@0 113 ** object. In DSA mode, so there is a q.
michael@0 114 */
michael@0 115 static SECStatus
michael@0 116 dsa_GenerateGlobalRandomBytes(const SECItem * qItem, PRUint8 * dest,
michael@0 117 unsigned int * destLen, unsigned int maxDestLen)
michael@0 118 {
michael@0 119 SECStatus rv;
michael@0 120 SECItem w;
michael@0 121 const PRUint8 * q = qItem->data;
michael@0 122 unsigned int qLen = qItem->len;
michael@0 123
michael@0 124 if (*q == 0) {
michael@0 125 ++q;
michael@0 126 --qLen;
michael@0 127 }
michael@0 128 if (maxDestLen < qLen) {
michael@0 129 /* This condition can occur when DSA_SignDigest is passed a group
michael@0 130 with a subprime that is larger than DSA_MAX_SUBPRIME_LEN. */
michael@0 131 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 132 return SECFailure;
michael@0 133 }
michael@0 134 w.data = NULL; /* otherwise SECITEM_AllocItem asserts */
michael@0 135 if (!SECITEM_AllocItem(NULL, &w, 2*qLen)) {
michael@0 136 return SECFailure;
michael@0 137 }
michael@0 138 *destLen = qLen;
michael@0 139
michael@0 140 rv = RNG_GenerateGlobalRandomBytes(w.data, w.len);
michael@0 141 if (rv == SECSuccess) {
michael@0 142 rv = fips186Change_ReduceModQForDSA(w.data, q, qLen, dest);
michael@0 143 }
michael@0 144
michael@0 145 SECITEM_FreeItem(&w, PR_FALSE);
michael@0 146 return rv;
michael@0 147 }
michael@0 148
michael@0 149 static void translate_mpi_error(mp_err err)
michael@0 150 {
michael@0 151 MP_TO_SEC_ERROR(err);
michael@0 152 }
michael@0 153
michael@0 154 static SECStatus
michael@0 155 dsa_NewKeyExtended(const PQGParams *params, const SECItem * seed,
michael@0 156 DSAPrivateKey **privKey)
michael@0 157 {
michael@0 158 mp_int p, g;
michael@0 159 mp_int x, y;
michael@0 160 mp_err err;
michael@0 161 PLArenaPool *arena;
michael@0 162 DSAPrivateKey *key;
michael@0 163 /* Check args. */
michael@0 164 if (!params || !privKey || !seed || !seed->data) {
michael@0 165 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 166 return SECFailure;
michael@0 167 }
michael@0 168 /* Initialize an arena for the DSA key. */
michael@0 169 arena = PORT_NewArena(NSS_FREEBL_DSA_DEFAULT_CHUNKSIZE);
michael@0 170 if (!arena) {
michael@0 171 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 172 return SECFailure;
michael@0 173 }
michael@0 174 key = (DSAPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DSAPrivateKey));
michael@0 175 if (!key) {
michael@0 176 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 177 PORT_FreeArena(arena, PR_TRUE);
michael@0 178 return SECFailure;
michael@0 179 }
michael@0 180 key->params.arena = arena;
michael@0 181 /* Initialize MPI integers. */
michael@0 182 MP_DIGITS(&p) = 0;
michael@0 183 MP_DIGITS(&g) = 0;
michael@0 184 MP_DIGITS(&x) = 0;
michael@0 185 MP_DIGITS(&y) = 0;
michael@0 186 CHECK_MPI_OK( mp_init(&p) );
michael@0 187 CHECK_MPI_OK( mp_init(&g) );
michael@0 188 CHECK_MPI_OK( mp_init(&x) );
michael@0 189 CHECK_MPI_OK( mp_init(&y) );
michael@0 190 /* Copy over the PQG params */
michael@0 191 CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.prime,
michael@0 192 &params->prime) );
michael@0 193 CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.subPrime,
michael@0 194 &params->subPrime) );
michael@0 195 CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.base, &params->base) );
michael@0 196 /* Convert stored p, g, and received x into MPI integers. */
michael@0 197 SECITEM_TO_MPINT(params->prime, &p);
michael@0 198 SECITEM_TO_MPINT(params->base, &g);
michael@0 199 OCTETS_TO_MPINT(seed->data, &x, seed->len);
michael@0 200 /* Store x in private key */
michael@0 201 SECITEM_AllocItem(arena, &key->privateValue, seed->len);
michael@0 202 PORT_Memcpy(key->privateValue.data, seed->data, seed->len);
michael@0 203 /* Compute public key y = g**x mod p */
michael@0 204 CHECK_MPI_OK( mp_exptmod(&g, &x, &p, &y) );
michael@0 205 /* Store y in public key */
michael@0 206 MPINT_TO_SECITEM(&y, &key->publicValue, arena);
michael@0 207 *privKey = key;
michael@0 208 key = NULL;
michael@0 209 cleanup:
michael@0 210 mp_clear(&p);
michael@0 211 mp_clear(&g);
michael@0 212 mp_clear(&x);
michael@0 213 mp_clear(&y);
michael@0 214 if (key)
michael@0 215 PORT_FreeArena(key->params.arena, PR_TRUE);
michael@0 216 if (err) {
michael@0 217 translate_mpi_error(err);
michael@0 218 return SECFailure;
michael@0 219 }
michael@0 220 return SECSuccess;
michael@0 221 }
michael@0 222
michael@0 223 SECStatus
michael@0 224 DSA_NewRandom(PLArenaPool * arena, const SECItem * q, SECItem * seed)
michael@0 225 {
michael@0 226 int retries = 10;
michael@0 227 unsigned int i;
michael@0 228 PRBool good;
michael@0 229
michael@0 230 if (q == NULL || q->data == NULL || q->len == 0 ||
michael@0 231 (q->data[0] == 0 && q->len == 1)) {
michael@0 232 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 233 return SECFailure;
michael@0 234 }
michael@0 235
michael@0 236 if (!SECITEM_AllocItem(arena, seed, q->len)) {
michael@0 237 return SECFailure;
michael@0 238 }
michael@0 239
michael@0 240 do {
michael@0 241 /* Generate seed bytes for x according to FIPS 186-1 appendix 3 */
michael@0 242 if (dsa_GenerateGlobalRandomBytes(q, seed->data, &seed->len,
michael@0 243 seed->len)) {
michael@0 244 goto loser;
michael@0 245 }
michael@0 246 /* Disallow values of 0 and 1 for x. */
michael@0 247 good = PR_FALSE;
michael@0 248 for (i = 0; i < seed->len-1; i++) {
michael@0 249 if (seed->data[i] != 0) {
michael@0 250 good = PR_TRUE;
michael@0 251 break;
michael@0 252 }
michael@0 253 }
michael@0 254 if (!good && seed->data[i] > 1) {
michael@0 255 good = PR_TRUE;
michael@0 256 }
michael@0 257 } while (!good && --retries > 0);
michael@0 258
michael@0 259 if (!good) {
michael@0 260 PORT_SetError(SEC_ERROR_NEED_RANDOM);
michael@0 261 loser: if (arena != NULL) {
michael@0 262 SECITEM_FreeItem(seed, PR_FALSE);
michael@0 263 }
michael@0 264 return SECFailure;
michael@0 265 }
michael@0 266
michael@0 267 return SECSuccess;
michael@0 268 }
michael@0 269
michael@0 270 /*
michael@0 271 ** Generate and return a new DSA public and private key pair,
michael@0 272 ** both of which are encoded into a single DSAPrivateKey struct.
michael@0 273 ** "params" is a pointer to the PQG parameters for the domain
michael@0 274 ** Uses a random seed.
michael@0 275 */
michael@0 276 SECStatus
michael@0 277 DSA_NewKey(const PQGParams *params, DSAPrivateKey **privKey)
michael@0 278 {
michael@0 279 SECItem seed;
michael@0 280 SECStatus rv;
michael@0 281
michael@0 282 rv = PQG_Check(params);
michael@0 283 if (rv != SECSuccess) {
michael@0 284 return rv;
michael@0 285 }
michael@0 286 seed.data = NULL;
michael@0 287
michael@0 288 rv = DSA_NewRandom(NULL, &params->subPrime, &seed);
michael@0 289 if (rv == SECSuccess) {
michael@0 290 if (seed.len != PQG_GetLength(&params->subPrime)) {
michael@0 291 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 292 rv = SECFailure;
michael@0 293 } else {
michael@0 294 rv = dsa_NewKeyExtended(params, &seed, privKey);
michael@0 295 }
michael@0 296 }
michael@0 297 SECITEM_FreeItem(&seed, PR_FALSE);
michael@0 298 return rv;
michael@0 299 }
michael@0 300
michael@0 301 /* For FIPS compliance testing. Seed must be exactly the size of subPrime */
michael@0 302 SECStatus
michael@0 303 DSA_NewKeyFromSeed(const PQGParams *params,
michael@0 304 const unsigned char *seed,
michael@0 305 DSAPrivateKey **privKey)
michael@0 306 {
michael@0 307 SECItem seedItem;
michael@0 308 seedItem.data = (unsigned char*) seed;
michael@0 309 seedItem.len = PQG_GetLength(&params->subPrime);
michael@0 310 return dsa_NewKeyExtended(params, &seedItem, privKey);
michael@0 311 }
michael@0 312
michael@0 313 static SECStatus
michael@0 314 dsa_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest,
michael@0 315 const unsigned char *kb)
michael@0 316 {
michael@0 317 mp_int p, q, g; /* PQG parameters */
michael@0 318 mp_int x, k; /* private key & pseudo-random integer */
michael@0 319 mp_int r, s; /* tuple (r, s) is signature) */
michael@0 320 mp_err err = MP_OKAY;
michael@0 321 SECStatus rv = SECSuccess;
michael@0 322 unsigned int dsa_subprime_len, dsa_signature_len, offset;
michael@0 323 SECItem localDigest;
michael@0 324 unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN];
michael@0 325
michael@0 326
michael@0 327 /* FIPS-compliance dictates that digest is a SHA hash. */
michael@0 328 /* Check args. */
michael@0 329 if (!key || !signature || !digest) {
michael@0 330 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 331 return SECFailure;
michael@0 332 }
michael@0 333
michael@0 334 dsa_subprime_len = PQG_GetLength(&key->params.subPrime);
michael@0 335 dsa_signature_len = dsa_subprime_len*2;
michael@0 336 if ((signature->len < dsa_signature_len) ||
michael@0 337 (digest->len > HASH_LENGTH_MAX) ||
michael@0 338 (digest->len < SHA1_LENGTH)) {
michael@0 339 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 340 return SECFailure;
michael@0 341 }
michael@0 342
michael@0 343 /* DSA accepts digests not equal to dsa_subprime_len, if the
michael@0 344 * digests are greater, then they are truncated to the size of
michael@0 345 * dsa_subprime_len, using the left most bits. If they are less
michael@0 346 * then they are padded on the left.*/
michael@0 347 PORT_Memset(localDigestData, 0, dsa_subprime_len);
michael@0 348 offset = (digest->len < dsa_subprime_len) ?
michael@0 349 (dsa_subprime_len - digest->len) : 0;
michael@0 350 PORT_Memcpy(localDigestData+offset, digest->data,
michael@0 351 dsa_subprime_len - offset);
michael@0 352 localDigest.data = localDigestData;
michael@0 353 localDigest.len = dsa_subprime_len;
michael@0 354
michael@0 355 /* Initialize MPI integers. */
michael@0 356 MP_DIGITS(&p) = 0;
michael@0 357 MP_DIGITS(&q) = 0;
michael@0 358 MP_DIGITS(&g) = 0;
michael@0 359 MP_DIGITS(&x) = 0;
michael@0 360 MP_DIGITS(&k) = 0;
michael@0 361 MP_DIGITS(&r) = 0;
michael@0 362 MP_DIGITS(&s) = 0;
michael@0 363 CHECK_MPI_OK( mp_init(&p) );
michael@0 364 CHECK_MPI_OK( mp_init(&q) );
michael@0 365 CHECK_MPI_OK( mp_init(&g) );
michael@0 366 CHECK_MPI_OK( mp_init(&x) );
michael@0 367 CHECK_MPI_OK( mp_init(&k) );
michael@0 368 CHECK_MPI_OK( mp_init(&r) );
michael@0 369 CHECK_MPI_OK( mp_init(&s) );
michael@0 370 /*
michael@0 371 ** Convert stored PQG and private key into MPI integers.
michael@0 372 */
michael@0 373 SECITEM_TO_MPINT(key->params.prime, &p);
michael@0 374 SECITEM_TO_MPINT(key->params.subPrime, &q);
michael@0 375 SECITEM_TO_MPINT(key->params.base, &g);
michael@0 376 SECITEM_TO_MPINT(key->privateValue, &x);
michael@0 377 OCTETS_TO_MPINT(kb, &k, dsa_subprime_len);
michael@0 378 /*
michael@0 379 ** FIPS 186-1, Section 5, Step 1
michael@0 380 **
michael@0 381 ** r = (g**k mod p) mod q
michael@0 382 */
michael@0 383 CHECK_MPI_OK( mp_exptmod(&g, &k, &p, &r) ); /* r = g**k mod p */
michael@0 384 CHECK_MPI_OK( mp_mod(&r, &q, &r) ); /* r = r mod q */
michael@0 385 /*
michael@0 386 ** FIPS 186-1, Section 5, Step 2
michael@0 387 **
michael@0 388 ** s = (k**-1 * (HASH(M) + x*r)) mod q
michael@0 389 */
michael@0 390 SECITEM_TO_MPINT(localDigest, &s); /* s = HASH(M) */
michael@0 391 CHECK_MPI_OK( mp_invmod(&k, &q, &k) ); /* k = k**-1 mod q */
michael@0 392 CHECK_MPI_OK( mp_mulmod(&x, &r, &q, &x) ); /* x = x * r mod q */
michael@0 393 CHECK_MPI_OK( mp_addmod(&s, &x, &q, &s) ); /* s = s + x mod q */
michael@0 394 CHECK_MPI_OK( mp_mulmod(&s, &k, &q, &s) ); /* s = s * k mod q */
michael@0 395 /*
michael@0 396 ** verify r != 0 and s != 0
michael@0 397 ** mentioned as optional in FIPS 186-1.
michael@0 398 */
michael@0 399 if (mp_cmp_z(&r) == 0 || mp_cmp_z(&s) == 0) {
michael@0 400 PORT_SetError(SEC_ERROR_NEED_RANDOM);
michael@0 401 rv = SECFailure;
michael@0 402 goto cleanup;
michael@0 403 }
michael@0 404 /*
michael@0 405 ** Step 4
michael@0 406 **
michael@0 407 ** Signature is tuple (r, s)
michael@0 408 */
michael@0 409 err = mp_to_fixlen_octets(&r, signature->data, dsa_subprime_len);
michael@0 410 if (err < 0) goto cleanup;
michael@0 411 err = mp_to_fixlen_octets(&s, signature->data + dsa_subprime_len,
michael@0 412 dsa_subprime_len);
michael@0 413 if (err < 0) goto cleanup;
michael@0 414 err = MP_OKAY;
michael@0 415 signature->len = dsa_signature_len;
michael@0 416 cleanup:
michael@0 417 PORT_Memset(localDigestData, 0, DSA_MAX_SUBPRIME_LEN);
michael@0 418 mp_clear(&p);
michael@0 419 mp_clear(&q);
michael@0 420 mp_clear(&g);
michael@0 421 mp_clear(&x);
michael@0 422 mp_clear(&k);
michael@0 423 mp_clear(&r);
michael@0 424 mp_clear(&s);
michael@0 425 if (err) {
michael@0 426 translate_mpi_error(err);
michael@0 427 rv = SECFailure;
michael@0 428 }
michael@0 429 return rv;
michael@0 430 }
michael@0 431
michael@0 432 /* signature is caller-supplied buffer of at least 40 bytes.
michael@0 433 ** On input, signature->len == size of buffer to hold signature.
michael@0 434 ** digest->len == size of digest.
michael@0 435 ** On output, signature->len == size of signature in buffer.
michael@0 436 ** Uses a random seed.
michael@0 437 */
michael@0 438 SECStatus
michael@0 439 DSA_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest)
michael@0 440 {
michael@0 441 SECStatus rv;
michael@0 442 int retries = 10;
michael@0 443 unsigned char kSeed[DSA_MAX_SUBPRIME_LEN];
michael@0 444 unsigned int kSeedLen = 0;
michael@0 445 unsigned int i;
michael@0 446 unsigned int dsa_subprime_len = PQG_GetLength(&key->params.subPrime);
michael@0 447 PRBool good;
michael@0 448
michael@0 449 PORT_SetError(0);
michael@0 450 do {
michael@0 451 rv = dsa_GenerateGlobalRandomBytes(&key->params.subPrime,
michael@0 452 kSeed, &kSeedLen, sizeof kSeed);
michael@0 453 if (rv != SECSuccess)
michael@0 454 break;
michael@0 455 if (kSeedLen != dsa_subprime_len) {
michael@0 456 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 457 rv = SECFailure;
michael@0 458 break;
michael@0 459 }
michael@0 460 /* Disallow a value of 0 for k. */
michael@0 461 good = PR_FALSE;
michael@0 462 for (i = 0; i < kSeedLen; i++) {
michael@0 463 if (kSeed[i] != 0) {
michael@0 464 good = PR_TRUE;
michael@0 465 break;
michael@0 466 }
michael@0 467 }
michael@0 468 if (!good) {
michael@0 469 PORT_SetError(SEC_ERROR_NEED_RANDOM);
michael@0 470 rv = SECFailure;
michael@0 471 continue;
michael@0 472 }
michael@0 473 rv = dsa_SignDigest(key, signature, digest, kSeed);
michael@0 474 } while (rv != SECSuccess && PORT_GetError() == SEC_ERROR_NEED_RANDOM &&
michael@0 475 --retries > 0);
michael@0 476 return rv;
michael@0 477 }
michael@0 478
michael@0 479 /* For FIPS compliance testing. Seed must be exactly 20 bytes. */
michael@0 480 SECStatus
michael@0 481 DSA_SignDigestWithSeed(DSAPrivateKey * key,
michael@0 482 SECItem * signature,
michael@0 483 const SECItem * digest,
michael@0 484 const unsigned char * seed)
michael@0 485 {
michael@0 486 SECStatus rv;
michael@0 487 rv = dsa_SignDigest(key, signature, digest, seed);
michael@0 488 return rv;
michael@0 489 }
michael@0 490
michael@0 491 /* signature is caller-supplied buffer of at least 20 bytes.
michael@0 492 ** On input, signature->len == size of buffer to hold signature.
michael@0 493 ** digest->len == size of digest.
michael@0 494 */
michael@0 495 SECStatus
michael@0 496 DSA_VerifyDigest(DSAPublicKey *key, const SECItem *signature,
michael@0 497 const SECItem *digest)
michael@0 498 {
michael@0 499 /* FIPS-compliance dictates that digest is a SHA hash. */
michael@0 500 mp_int p, q, g; /* PQG parameters */
michael@0 501 mp_int r_, s_; /* tuple (r', s') is received signature) */
michael@0 502 mp_int u1, u2, v, w; /* intermediate values used in verification */
michael@0 503 mp_int y; /* public key */
michael@0 504 mp_err err;
michael@0 505 int dsa_subprime_len, dsa_signature_len, offset;
michael@0 506 SECItem localDigest;
michael@0 507 unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN];
michael@0 508 SECStatus verified = SECFailure;
michael@0 509
michael@0 510 /* Check args. */
michael@0 511 if (!key || !signature || !digest ) {
michael@0 512 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 513 return SECFailure;
michael@0 514 }
michael@0 515
michael@0 516 dsa_subprime_len = PQG_GetLength(&key->params.subPrime);
michael@0 517 dsa_signature_len = dsa_subprime_len*2;
michael@0 518 if ((signature->len != dsa_signature_len) ||
michael@0 519 (digest->len > HASH_LENGTH_MAX) ||
michael@0 520 (digest->len < SHA1_LENGTH)) {
michael@0 521 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 522 return SECFailure;
michael@0 523 }
michael@0 524
michael@0 525 /* DSA accepts digests not equal to dsa_subprime_len, if the
michael@0 526 * digests are greater, than they are truncated to the size of
michael@0 527 * dsa_subprime_len, using the left most bits. If they are less
michael@0 528 * then they are padded on the left.*/
michael@0 529 PORT_Memset(localDigestData, 0, dsa_subprime_len);
michael@0 530 offset = (digest->len < dsa_subprime_len) ?
michael@0 531 (dsa_subprime_len - digest->len) : 0;
michael@0 532 PORT_Memcpy(localDigestData+offset, digest->data,
michael@0 533 dsa_subprime_len - offset);
michael@0 534 localDigest.data = localDigestData;
michael@0 535 localDigest.len = dsa_subprime_len;
michael@0 536
michael@0 537 /* Initialize MPI integers. */
michael@0 538 MP_DIGITS(&p) = 0;
michael@0 539 MP_DIGITS(&q) = 0;
michael@0 540 MP_DIGITS(&g) = 0;
michael@0 541 MP_DIGITS(&y) = 0;
michael@0 542 MP_DIGITS(&r_) = 0;
michael@0 543 MP_DIGITS(&s_) = 0;
michael@0 544 MP_DIGITS(&u1) = 0;
michael@0 545 MP_DIGITS(&u2) = 0;
michael@0 546 MP_DIGITS(&v) = 0;
michael@0 547 MP_DIGITS(&w) = 0;
michael@0 548 CHECK_MPI_OK( mp_init(&p) );
michael@0 549 CHECK_MPI_OK( mp_init(&q) );
michael@0 550 CHECK_MPI_OK( mp_init(&g) );
michael@0 551 CHECK_MPI_OK( mp_init(&y) );
michael@0 552 CHECK_MPI_OK( mp_init(&r_) );
michael@0 553 CHECK_MPI_OK( mp_init(&s_) );
michael@0 554 CHECK_MPI_OK( mp_init(&u1) );
michael@0 555 CHECK_MPI_OK( mp_init(&u2) );
michael@0 556 CHECK_MPI_OK( mp_init(&v) );
michael@0 557 CHECK_MPI_OK( mp_init(&w) );
michael@0 558 /*
michael@0 559 ** Convert stored PQG and public key into MPI integers.
michael@0 560 */
michael@0 561 SECITEM_TO_MPINT(key->params.prime, &p);
michael@0 562 SECITEM_TO_MPINT(key->params.subPrime, &q);
michael@0 563 SECITEM_TO_MPINT(key->params.base, &g);
michael@0 564 SECITEM_TO_MPINT(key->publicValue, &y);
michael@0 565 /*
michael@0 566 ** Convert received signature (r', s') into MPI integers.
michael@0 567 */
michael@0 568 OCTETS_TO_MPINT(signature->data, &r_, dsa_subprime_len);
michael@0 569 OCTETS_TO_MPINT(signature->data + dsa_subprime_len, &s_, dsa_subprime_len);
michael@0 570 /*
michael@0 571 ** Verify that 0 < r' < q and 0 < s' < q
michael@0 572 */
michael@0 573 if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 ||
michael@0 574 mp_cmp(&r_, &q) >= 0 || mp_cmp(&s_, &q) >= 0) {
michael@0 575 /* err is zero here. */
michael@0 576 PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
michael@0 577 goto cleanup; /* will return verified == SECFailure */
michael@0 578 }
michael@0 579 /*
michael@0 580 ** FIPS 186-1, Section 6, Step 1
michael@0 581 **
michael@0 582 ** w = (s')**-1 mod q
michael@0 583 */
michael@0 584 CHECK_MPI_OK( mp_invmod(&s_, &q, &w) ); /* w = (s')**-1 mod q */
michael@0 585 /*
michael@0 586 ** FIPS 186-1, Section 6, Step 2
michael@0 587 **
michael@0 588 ** u1 = ((Hash(M')) * w) mod q
michael@0 589 */
michael@0 590 SECITEM_TO_MPINT(localDigest, &u1); /* u1 = HASH(M') */
michael@0 591 CHECK_MPI_OK( mp_mulmod(&u1, &w, &q, &u1) ); /* u1 = u1 * w mod q */
michael@0 592 /*
michael@0 593 ** FIPS 186-1, Section 6, Step 3
michael@0 594 **
michael@0 595 ** u2 = ((r') * w) mod q
michael@0 596 */
michael@0 597 CHECK_MPI_OK( mp_mulmod(&r_, &w, &q, &u2) );
michael@0 598 /*
michael@0 599 ** FIPS 186-1, Section 6, Step 4
michael@0 600 **
michael@0 601 ** v = ((g**u1 * y**u2) mod p) mod q
michael@0 602 */
michael@0 603 CHECK_MPI_OK( mp_exptmod(&g, &u1, &p, &g) ); /* g = g**u1 mod p */
michael@0 604 CHECK_MPI_OK( mp_exptmod(&y, &u2, &p, &y) ); /* y = y**u2 mod p */
michael@0 605 CHECK_MPI_OK( mp_mulmod(&g, &y, &p, &v) ); /* v = g * y mod p */
michael@0 606 CHECK_MPI_OK( mp_mod(&v, &q, &v) ); /* v = v mod q */
michael@0 607 /*
michael@0 608 ** Verification: v == r'
michael@0 609 */
michael@0 610 if (mp_cmp(&v, &r_)) {
michael@0 611 PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
michael@0 612 verified = SECFailure; /* Signature failed to verify. */
michael@0 613 } else {
michael@0 614 verified = SECSuccess; /* Signature verified. */
michael@0 615 }
michael@0 616 cleanup:
michael@0 617 mp_clear(&p);
michael@0 618 mp_clear(&q);
michael@0 619 mp_clear(&g);
michael@0 620 mp_clear(&y);
michael@0 621 mp_clear(&r_);
michael@0 622 mp_clear(&s_);
michael@0 623 mp_clear(&u1);
michael@0 624 mp_clear(&u2);
michael@0 625 mp_clear(&v);
michael@0 626 mp_clear(&w);
michael@0 627 if (err) {
michael@0 628 translate_mpi_error(err);
michael@0 629 }
michael@0 630 return verified;
michael@0 631 }

mercurial