123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- /*
- * Copyright (c) 1995 Colin Plumb. All rights reserved.
- * For licensing and other legal details, see the file legal.c.
- *
- * dsatest.c - DSA key generator and test driver.
- *
- * This generates DSA primes using a (hopefully) clearly
- * defined algorithm, based on David Kravitz's "kosherizer".
- * It is not, however, identical.
- */
- #include <stdio.h>
- #include <string.h>
- #include "bn.h"
- #include "prime.h"
- #include "cputime.h"
- #include "sha.h"
- #define BNDEBUG 1
- #if BNDEBUG
- #include "bnprint.h"
- #define bndPut(prompt, bn) bnPrint(stdout, prompt, bn, "\n")
- #define bndPrintf printf
- #else
- #define bndPut(prompt, bn) ((void)(prompt),(void)(bn))
- #define bndPrintf (void)
- #endif
- /*
- * Generate a bignum of a specified length, with the given
- * high and low 8 bits. "High" is merged into the high 8 bits of the
- * number. For example, set it to 0x80 to ensure that the number is
- * exactly "bits" bits long (i.e. 2^(bits-1) <= bn < 2^bits).
- * "Low" is merged into the low 8 bits. For example, set it to
- * 1 to ensure that you generate an odd number.
- *
- * Then XOR the result into the input bignum. This is to
- * accomodate the kosherizer in all its generality.
- *
- * The bignum is generated using the given seed string. The
- * technique is from David Kravitz (of the NSA)'s "kosherizer".
- * The string is hashed, and that (with the low bit forced to 1)
- * is used for the low 160 bits of the number. Then the string,
- * considered as a big-endian array of bytes, is incremented
- * and the incremented value is hashed to produce the next most
- * significant 160 bits, and so on. The increment is performed
- * modulo the size of the seed string.
- *
- * The seed is returned incremented so that it may be used to generate
- * subsequent numbers.
- *
- * The most and least significant 8 bits of the returned number are forced
- * to the values passed in "high" and "low", respectively. Typically,
- * high would be set to 0x80 to force the most significant bit to 1.
- */
- static int
- genRandBn(struct BigNum *bn, unsigned bits, unsigned char high,
- unsigned char low, unsigned char *seed, unsigned len)
- {
- unsigned char buf1[SHA_DIGESTSIZE];
- unsigned char buf2[SHA_DIGESTSIZE];
- unsigned bytes = (bits+7)/8;
- unsigned l = 0; /* Current position */
- unsigned i;
- struct SHAContext sha;
- if (!bits)
- return 0;
- /* Generate the first bunch of hashed data */
- shaInit(&sha);
- shaUpdate(&sha, seed, len);
- shaFinal(&sha, buf1);
- /* Increment the seed, ignoring carry out. */
- i = len;
- while (i-- && (++seed[i] & 255) == 0)
- ;
- /* XOR in the existing bytes */
- bnExtractBigBytes(bn, buf2, l, SHA_DIGESTSIZE);
- for (i = 0; i < SHA_DIGESTSIZE; i++)
- buf1[i] ^= buf2[i];
- buf1[SHA_DIGESTSIZE-1] |= low;
- while (bytes > SHA_DIGESTSIZE) {
- bytes -= SHA_DIGESTSIZE;
- /* Merge in low half of high bits, if necessary */
- if (bytes == 1 && (bits & 7))
- buf1[0] |= high << (bits & 7);
- if (bnInsertBigBytes(bn, buf1, l, SHA_DIGESTSIZE) < 0)
- return -1;
- l += SHA_DIGESTSIZE;
- /* Compute the next hash we need */
- shaInit(&sha);
- shaUpdate(&sha, seed, len);
- shaFinal(&sha, buf1);
- /* Increment the seed, ignoring carry out. */
- i = len;
- while (i-- && (++seed[i] & 255) == 0)
- ;
- /* XOR in the existing bytes */
- bnExtractBigBytes(bn, buf2, l, SHA_DIGESTSIZE);
- for (i = 0; i < SHA_DIGESTSIZE; i++)
- buf1[i] ^= buf2[i];
- }
- /* Do the final "bytes"-long section, using the tail bytes in buf1 */
- /* Mask off excess high bits */
- buf1[SHA_DIGESTSIZE-bytes] &= 255 >> (-bits & 7);
- /* Merge in specified high bits */
- buf1[SHA_DIGESTSIZE-bytes] |= high >> (-bits & 7);
- if (bytes > 1 && (bits & 7))
- buf1[SHA_DIGESTSIZE-bytes+1] |= high << (bits & 7);
- /* Merge in the appropriate bytes of the buffer */
- if (bnInsertBigBytes(bn, buf1+SHA_DIGESTSIZE-bytes, l, bytes) < 0)
- return -1;
- return 0;
- }
- struct Progress {
- FILE *f;
- unsigned column;
- unsigned wrap;
- };
- static int
- genProgress(void *arg, int c)
- {
- struct Progress *p = arg;
- if (++p->column > p->wrap) {
- putc('\n', p->f);
- p->column = 1;
- }
- putc(c, p->f);
- fflush(p->f);
- return 0;
- }
- static int
- dsaGen(struct BigNum *p, unsigned pbits, struct BigNum *q, unsigned qbits,
- struct BigNum *g, struct BigNum *x, struct BigNum *y,
- unsigned char *seed, unsigned len, FILE *f)
- {
- struct BigNum h, e;
- int i;
- #if CLOCK_AVAIL
- timetype start, stop;
- unsigned long s;
- #endif
- struct Progress progress;
- if (f)
- fprintf(f,
- "Generating a DSA key pair with %u-bit p and %u-bit q,\n"
- "seed = \"%.*s\"\n", pbits, qbits, (int)len, (char *)seed);
- progress.f = f;
- progress.column = 0;
- progress.wrap = 78;
- #if CLOCK_AVAIL
- gettime(&start);
- #endif
- /*
- * Choose a random starting place for q
- * Starting place is SHA(seed) XOR SHA(seed+1),
- * With the high *8* bits set to 1.
- */
- (void)bnSetQ(q, 0);
- if (genRandBn(q, qbits, 0xFF, 0, seed, len) < 0)
- return -1;
- bndPut("q1 = ", q);
- if (genRandBn(q, qbits, 0xFF, 1, seed, len) < 0)
- return -1;
- bndPut("q2 = ", q);
- /* And search for a prime */
- i = primeGen(q, (unsigned (*)(unsigned))0, f ? genProgress : 0,
- (void *)&progress, 0);
- bndPut("q = ", q);
- if (i < 0)
- return -1;
-
- /* ...and for p */
- (void)bnSetQ(p, 0);
- if (genRandBn(p, pbits, 0xC0, 1, seed, len) < 0)
- return -1;
- bndPut("p1 = ", p);
- /* Temporarily double q */
- if (bnLShift(q, 1) < 0)
- return -1;
- bnBegin(&h);
- bnBegin(&e);
- /* Set p = p - (p mod q) + 1, i.e. congruent to 1 mod 2*q */
- if (bnMod(&e, p, q) < 0)
- goto failed;
- if (bnSub(p, &e) < 0 || bnAddQ(p, 1) < 0)
- goto failed;
- bndPut("p2 = ", p);
- if (f)
- genProgress(&progress, ' ');
- /* And search for a prime */
- i = primeGenStrong(p, q, f ? genProgress : 0, (void *)&progress);
- if (i < 0)
- return -1;
- bndPut("p = ", p);
- /* Reduce q again */
- bnRShift(q, 1);
- /* Now hunt for a suitable g - first, find (p-1)/q */
- if (bnDivMod(&e, &h, p, q) < 0)
- goto failed;
- /* e is now the exponent (p-1)/q, and h is the remainder (one!) */
- if (bnBits(&h) != 1) {
- bndPut("Huh? p % q = ", &h);
- goto failed;
- }
- if (f)
- genProgress(&progress, ' ');
- /* Search for a suitable h */
- if (bnSetQ(&h, 2) < 0 || bnTwoExpMod(g, &e, p) < 0)
- goto failed;
- i++;
- while (bnBits(g) < 2) {
- if (f)
- genProgress(&progress, '.');
- if (bnAddQ(&h, 1) < 0 || bnExpMod(g, &h, &e, p) < 0)
- goto failed;
- i++;
- }
- if (f)
- genProgress(&progress, '*');
- #if CLOCK_AVAIL
- gettime(&stop);
- #endif
- /*
- * Now pick the secret, x. Choose it a bit larger than q and do
- * modular reduction to make it uniformly distributed.
- */
- bnSetQ(x, 0);
- /* XXX SECURITY ALERT Replace with a real RNG! SECURITY ALERT XXX */
- if (genRandBn(x, qbits+8, 0, 0, seed, len) < 0)
- goto failed;
- if (bnMod(x, x, q) < 0 || bnExpMod(y, g, x, p) < 0)
- goto failed;
- i++;
- if (f)
- putc('\n', f);
- printf("%d modular exponentiations performed.\n", i);
- #if CLOCK_AVAIL
- subtime(stop, start);
- s = sec(stop);
- bndPrintf("%u/%u-bit time = %lu.%03u sec.", pbits, qbits,
- s, msec(stop));
- if (s > 60) {
- putchar(' ');
- putchar('(');
- if (s > 3600)
- printf("%u:%02u", (unsigned)(s/3600),
- (unsigned)(s/60%60));
- else
- printf("%u", (unsigned)(s/60));
- printf(":%02u)", (unsigned)(s%60));
- }
- putchar('\n');
- #endif
- bndPut("q = ", q);
- bndPut("p = ", p);
- bndPut("h = ", &h);
- bndPut("g = ", g);
- bndPut("x = ", x);
- bndPut("y = ", y);
- bnEnd(&h);
- bnEnd(&e);
- return 0;
- failed:
- bnEnd(&h);
- bnEnd(&e);
- return -1;
- }
- static int
- dsaSign(struct BigNum const *p, struct BigNum const *q, struct BigNum const *g,
- struct BigNum const *x, struct BigNum const *y,
- struct BigNum const *hash, struct BigNum const *k,
- struct BigNum *r, struct BigNum *s)
- {
- int retval = -1;
- struct BigNum t;
- (void)y;
- bnBegin(&t);
- /* Make the signature... first the precomputation */
- /* Compute r = (g^k mod p) mod q */
- if (bnExpMod(r, g, k, p) < 0 || bnMod(r, r, q) < 0)
- goto failed;
- /* Compute s = k^-1 * (hash + x*r) mod q */
- if (bnInv(&t, k, q) < 0)
- goto failed;
- if (bnMul(s, x, r) < 0 || bnMod(s, s, q) < 0)
- goto failed;
- /* End of precomputation. Steps after this require the hash. */
- if (bnAdd(s, hash) < 0)
- goto failed;
- if (bnCmp(s, q) > 0 && bnSub(s, q) < 0)
- goto failed;
- if (bnMul(s, s, &t) < 0 || bnMod(s, s, q) < 0)
- goto failed;
- /* Okay, r and s are the signature! */
- retval = 0;
- failed:
- bnEnd(&t);
- return retval;
- }
- /* Faster version, using precomputed tables */
- static int
- dsaSignFast(struct BigNum const *p, struct BigNum const *q,
- struct BnBasePrecomp const *pre,
- struct BigNum const *x, struct BigNum const *y,
- struct BigNum const *hash, struct BigNum const *k,
- struct BigNum *r, struct BigNum *s)
- {
- int retval = -1;
- struct BigNum t;
- (void)y;
- bnBegin(&t);
- /* Make the signature... first the precomputation */
- /* Compute r = (g^k mod p) mod q */
- if (bnBasePrecompExpMod(r, pre, k, p) < 0 || bnMod(r, r, q) < 0)
- goto failed;
- /* Compute s = k^-1 * (hash + x*r) mod q */
- if (bnInv(&t, k, q) < 0)
- goto failed;
- if (bnMul(s, x, r) < 0 || bnMod(s, s, q) < 0)
- goto failed;
- /* End of precomputation. Steps after this require the hash. */
- if (bnAdd(s, hash) < 0)
- goto failed;
- if (bnCmp(s, q) > 0 && bnSub(s, q) < 0)
- goto failed;
- if (bnMul(s, s, &t) < 0 || bnMod(s, s, q) < 0)
- goto failed;
- /* Okay, r and s are the signature! */
- retval = 0;
- failed:
- bnEnd(&t);
- return retval;
- }
- /*
- * Returns 1 for a good signature, 0 for bad, and -1 on error.
- */
- static int
- dsaVerify(struct BigNum const *p, struct BigNum const *q,
- struct BigNum const *g, struct BigNum const *y,
- struct BigNum const *r, struct BigNum const *s,
- struct BigNum const *hash)
- {
- struct BigNum w, u1, u2;
- int retval = -1;
- bnBegin(&w);
- bnBegin(&u1);
- bnBegin(&u2);
- if (bnInv(&w, s, q) < 0)
- goto failed;
- if (bnMul(&u1, hash, &w) < 0 || bnMod(&u1, &u1, q) < 0)
- goto failed;
- if (bnMul(&u2, r, &w) < 0 || bnMod(&u2, &u2, q) < 0)
- goto failed;
- /* Now for the expensive part... */
- if (bnDoubleExpMod(&w, g, &u1, y, &u2, p) < 0)
- goto failed;
- if (bnMod(&w, &w, q) < 0)
- goto failed;
- retval = (bnCmp(r, &w) == 0);
- failed:
- bnEnd(&u2);
- bnEnd(&u1);
- bnEnd(&w);
- return retval;
- }
- #define divide_by_n(sec, msec, n) \
- ( msec += 1000 * (sec % n), \
- sec /= n, msec /= n, \
- sec += msec / 1000, \
- msec %= 1000 )
- static int
- dsaTest(struct BigNum const *p, struct BigNum const *q, struct BigNum const *g,
- struct BigNum const *x, struct BigNum const *y)
- {
- struct BigNum hash, r, s, k;
- struct BigNum r1, s1;
- struct BnBasePrecomp pre;
- unsigned bits;
- unsigned i;
- int verified;
- int retval = -1;
- unsigned char foo[4], bar[4];
- #if CLOCK_AVAIL
- timetype start, stop;
- unsigned long cursec, sigsec = 0, sig1sec = 0, versec = 0;
- unsigned curms, sigms = 0, sig1ms = 0, verms = 0;
- unsigned j, n, m = 0;
- #endif
- bnBegin(&hash);
- bnBegin(&r); bnBegin(&r1);
- bnBegin(&s); bnBegin(&s1);
- bnBegin(&k);
- bits = bnBits(q);
- strcpy((char *)foo, "foo");
- strcpy((char *)bar, "bar");
- /* Precompute powers of g */
- if (bnBasePrecompBegin(&pre, g, p, bits) < 0)
- goto failed;
- bndPrintf(" N\tSigning \tSigning1\tVerifying\tStatus\n");
- for (i = 0; i < 25; i++) {
- /* Pick a random hash, the right length. */
- (void)bnSetQ(&k, 0);
- if (genRandBn(&hash, bits, 0, 0, foo, 4) < 0)
- goto failed;
- /* Make the signature... */
- /*
- * XXX SECURITY ALERT XXX
- * XXX Replace with a real RNG! XXX
- * XXX SECURITY ALERT XXX
- */
- (void)bnSetQ(&k, 0);
- if (genRandBn(&k, bnBits(q)+8, 0, 0, bar, 4) < 0)
- goto failed;
- /* Reduce k to the correct range */
- if (bnMod(&k, &k, q) < 0)
- goto failed;
- #if CLOCK_AVAIL
- /* Decide on a number of iterations to perform... */
- m += n = i+1; /* This goes from 1 to 325 */
- bndPrintf("%3d", n);
- gettime(&start);
- for (j = 0; j < n; j++)
- #endif
- if (dsaSign(p, q, g, x, y, &hash, &k, &r, &s) < 0)
- goto failed;
- #if CLOCK_AVAIL
- gettime(&stop);
- subtime(stop, start);
- sigsec += cursec = sec(stop);
- sigms += curms = msec(stop);
- divide_by_n(cursec, curms, n);
- bndPrintf("\t%lu.%03u\t\t", cursec, curms);
- #else
- bndPrintf("\t*\t\t");
- #endif
- fflush(stdout);
- #if CLOCK_AVAIL
- gettime(&start);
- for (j = 0; j < n; j++)
- #endif
- if (dsaSignFast(p, q, &pre, x, y, &hash, &k, &r1, &s1) < 0)
- goto failed;
- #if CLOCK_AVAIL
- gettime(&stop);
- subtime(stop, start);
- sig1sec += cursec = sec(stop);
- sig1ms += curms = msec(stop);
- divide_by_n(cursec, curms, n);
- bndPrintf("%lu.%03u\t\t", cursec, curms);
- #else
- bndPrintf("*\t\t");
- #endif
- fflush(stdout);
- if (bnCmp(&r, &r1) != 0) {
- printf("\a** Error r != r1");
- bndPut("g = ", g);
- bndPut("k = ", &k);
- bndPut("r = ", &r);
- bndPut("r1= ", &r1);
- }
- if (bnCmp(&s, &s1) != 0) {
- printf("\a** Error r != r1");
- bndPut("g = ", g);
- bndPut("k = ", &k);
- bndPut("s = ", &s);
- bndPut("s1= ", &s1);
- }
- /* Okay, r and s are the signature! Now, verify it. */
- #if CLOCK_AVAIL
- gettime(&start);
- verified = 0; /* To silence warning */
- for (j = 0; j < n; j++) {
- #endif
- verified = dsaVerify(p, q, g, y, &r, &s, &hash);
- if (verified <= 0)
- break;
- }
- #if CLOCK_AVAIL
- gettime(&stop);
- subtime(stop, start);
- versec += cursec = sec(stop);
- verms += curms = msec(stop);
- divide_by_n(cursec, curms, j);
- bndPrintf("%lu.%03u\t\t", cursec, curms);
- #else
- bndPrintf("*\t\t");
- #endif
- if (verified > 0) {
- printf("Test successful.\n");
- } else if (verified == 0) {
- printf("\aSignature did NOT check!.\n");
- bndPut("hash = ", &hash);
- bndPut("k = ", &k);
- bndPut("r = ", &r);
- bndPut("s = ", &s);
- getchar();
- } else {
- printf("\a** Error while verifying");
- bndPut("hash = ", &hash);
- bndPut("k = ", &k);
- bndPut("r = ", &r);
- bndPut("s = ", &s);
- getchar();
- goto failed;
- }
- }
- #if CLOCK_AVAIL
- divide_by_n(sigsec, sigms, m);
- divide_by_n(sig1sec, sig1ms, m);
- divide_by_n(versec, verms, m);
- bndPrintf("%3u\t%lu.%03u\t\t%lu.%03u\t\t%lu.%03u\t\tAVERAGE %u/%u\n",
- m, sigsec, sigms, sig1sec, sig1ms, versec, verms,
- bnBits(p), bnBits(q));
- #endif
- /* Success */
- retval = 0;
- failed:
- bnBasePrecompEnd(&pre);
- bnEnd(&k);
- bnEnd(&s1); bnEnd(&s);
- bnEnd(&r1); bnEnd(&r);
- bnEnd(&hash);
- return retval;
- }
- /* Copy the command line to the buffer. */
- static unsigned
- copy(unsigned char *buf, int argc, char **argv)
- {
- unsigned pos, len;
-
- pos = 0;
- while (--argc) {
- len = strlen(*++argv);
- memcpy(buf, *argv, len);
- buf += len;
- pos += len;
- if (argc > 1) {
- *buf++ = ' ';
- pos++;
- }
- }
- return pos;
- }
- int
- main(int argc, char **argv)
- {
- unsigned len;
- struct BigNum p, q, g, x, y;
- unsigned char buf[1024];
- if (argc < 2) {
- fprintf(stderr, "Usage: %s <seed>\n", argv[0]);
- fputs("\
- <seed> should be a a string of bytes to be hashed to seed the prime\n\
- generator. Note that unquoted whitespace between words will be counted\n\
- as a single space. To include multiple spaces, quote them.\n", stderr);
- return 1;
- }
- bnInit();
- bnBegin(&p);
- bnBegin(&q);
- bnBegin(&g);
- bnBegin(&x);
- bnBegin(&y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 512, &q, 160, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 768, &q, 160, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 1024, &q, 160, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 1536, &q, 192, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 2048, &q, 224, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 3072, &q, 256, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- len = copy(buf, argc, argv);
- dsaGen(&p, 4096, &q, 288, &g, &x, &y, buf, len, stdout);
- dsaTest(&p, &q, &g, &x, &y);
- bnEnd(&y);
- bnEnd(&x);
- bnEnd(&g);
- bnEnd(&q);
- bnEnd(&p);
- return 0;
- }
|