Coverage Report

Created: 2026-10-13 06:23

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