Travis Cross d2edcad66e Merge Phil Zimmermann's libzrtp as a FreeSWITCH library
Thanks to Phil Zimmermann for the code and for the license exception
we needed to include it.

There remains some build system integration work to be done before
this code will build properly in the FreeSWITCH tree.
2012-03-31 23:42:27 +00:00

673 lines
16 KiB
C

/*
* 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;
}