PageRenderTime 52ms CodeModel.GetById 14ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/lib/freebl/dsa.c

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