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

376 lines
9.0 KiB
C

/*
* Copyright (c) 1995 Colin Plumb. All rights reserved.
* For licensing and other legal details, see the file legal.c.
*
* dhtest.c - Diffie-Hellman prime generator.
*
* This generates Diffie-Hellman primes using a (hopefully) clearly
* defined algorithm, based on David Kravitz's "kosherizer".
* This takes a seed in the form of a byte string, usually ASCII.
* The byte string is hashed with SHA. This forms the low 160 bits
* of the search start number. If the desired start number is longer
* than this, the byte string is treated as a big-endian number and
* incremented, which increments the last byte, propagating carry.
* (Modulo the size of the seed itself, which is not an issue in
* practice for any seed at least one byte long.)
* This incremented value is hashed to produce the next most significant
* 160 bits, and so on.
* After enough bits have been accumulated, the low bit is set, the extra
* high bits are masked off to zero, and the two high bits of the
* search start number are set. This is used as a starting seed for a
* sequential (increasing) search for a suitable prime.
*
* A suitable prime P is itself prime, and (P-1)/2 is also prime.
*/
#include <stdio.h>
#include <string.h>
#include "bn.h"
#include "germain.h"
#include "sieve.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.
*
* 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 most significant *two* bits are forced to 1, the first to
* ensure that the number is long enough, and the second just to
* place the prime in the high half of the range to make breaking
* it slightly more difficult, since it makes essentially no
* difference to the use of the number.
*/
static int
genRandBn(struct BigNum *bn, unsigned bits, unsigned char high,
unsigned char low, unsigned char *seed, unsigned len)
{
unsigned char buf[SHA_DIGESTSIZE];
unsigned bytes;
unsigned l = 0; /* Current position */
unsigned i;
struct SHAContext sha;
bnSetQ(bn, 0);
bytes = (bits+7) / 8; /* Number of bytes to use */
shaInit(&sha);
shaUpdate(&sha, seed, len);
shaFinal(&sha, buf);
buf[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))
buf[0] |= high << (bits & 7);
if (bnInsertBigBytes(bn, buf, l, SHA_DIGESTSIZE) < 0)
return -1;
l += SHA_DIGESTSIZE;
/* Increment the seed, ignoring carry out. */
i = len;
while (i--) {
if (++seed[i] & 255)
break; /* Didn't wrap; done */
}
shaInit(&sha);
shaUpdate(&sha, seed, len);
shaFinal(&sha, buf);
}
/* Do the final "bytes"-long section, using the tail bytes in buf */
/* Mask off excess high bits */
buf[SHA_DIGESTSIZE-bytes] &= 255 >> (-bits & 7);
/* Merge in specified high bits */
buf[SHA_DIGESTSIZE-bytes] |= high >> (-bits & 7);
if (bytes > 1 && (bits & 7))
buf[SHA_DIGESTSIZE-bytes+1] |= high << (bits & 7);
/* Merge in the appropriate bytes of the buffer */
if (bnInsertBigBytes(bn, buf+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
genDH(struct BigNum *bn, unsigned bits, unsigned char *seed, unsigned len,
FILE *f)
{
#if CLOCK_AVAIL
timetype start, stop;
unsigned long s;
#endif
int i;
unsigned char s1[1024], s2[1024];
unsigned p1, p2;
struct BigNum step;
struct Progress progress;
if (f)
fprintf(f, "Generating a %u-bit D-H prime with \"%.*s\"\n",
bits, (int)len, (char *)seed);
progress.f = f;
progress.column = 0;
progress.wrap = 78;
/* Find p - choose a starting place */
if (genRandBn(bn, bits, 0xC0, 3, seed, len) < 0)
return -1;
#if BNDEBUG /* DEBUG - check that sieve works properly */
bnBegin(&step);
bnSetQ(&step, 2);
sieveBuild(s1, 1024, bn, 2, 0);
sieveBuildBig(s2, 1024, bn, &step, 0);
p1 = p2 = 0;
if (s1[0] != s2[0])
printf("Difference: s1[0] = %x s2[0] = %x\n", s1[0], s2[0]);
do {
p1 = sieveSearch(s1, 1024, p1);
p2 = sieveSearch(s2, 1024, p2);
if (p1 != p2)
printf("Difference: p1 = %u p2 = %u\n", p1, p2);
} while (p1 && p2);
bnEnd(&step);
#endif
/* And search for a prime */
#if CLOCK_AVAIL
gettime(&start);
#endif
i = germainPrimeGen(bn, 1, f ? genProgress : 0, (void *)&progress);
if (i < 0)
return -1;
#if CLOCK_AVAIL
gettime(&stop);
#endif
if (f) {
putc('\n', f);
fprintf(f, "%d modular exponentiations performed.\n", i);
}
#if CLOCK_AVAIL
subtime(stop, start);
s = sec(stop);
bndPrintf("%u-bit time = %lu.%03u sec.", bits, 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("p = ", bn);
return 0;
}
static int
testDH(struct BigNum *bn)
{
struct BigNum pub1, pub2, sec1, sec2;
unsigned bits;
int i = 0;
char buf[4];
bnBegin(&pub1);
bnBegin(&pub2);
bnBegin(&sec1);
bnBegin(&sec2);
/* Bits of secret - add a few to ensure an even distribution */
bits = bnBits(bn)+4;
/* Temporarily decrement bn for some operations */
(void)bnSubQ(bn, 1);
strcpy(buf, "foo");
i = genRandBn(&sec1, bits, 0, 0, (unsigned char *)buf, 4);
if (i < 0)
goto done;
/* Reduce sec1 to the correct range */
i = bnMod(&sec1, &sec1, bn);
if (i < 0)
goto done;
strcpy(buf, "bar");
i = genRandBn(&sec2, bits, 0, 0, (unsigned char *)buf, 4);
if (i < 0)
goto done;
/* Reduce sec2 to the correct range */
i = bnMod(&sec2, &sec2, bn);
if (i < 0)
goto done;
/* Re-increment bn */
(void)bnAddQ(bn, 1);
puts("Doing first half for party 1");
i = bnTwoExpMod(&pub1, &sec1, bn);
if (i < 0)
goto done;
puts("Doing first half for party 2");
i = bnTwoExpMod(&pub2, &sec2, bn);
if (i < 0)
goto done;
/* In a real protocol, pub1 and pub2 are now exchanged */
puts("Doing second half for party 1");
i = bnExpMod(&pub2, &pub2, &sec1, bn);
if (i < 0)
goto done;
bndPut("shared = ", &pub2);
puts("Doing second half for party 2");
i = bnExpMod(&pub1, &pub1, &sec2, bn);
if (i < 0)
goto done;
bndPut("shared = ", &pub1);
if (bnCmp(&pub1, &pub2) != 0) {
puts("Diffie-Hellman failed!");
i = -1;
} else {
puts("Test successful.");
}
done:
bnEnd(&sec2);
bnEnd(&sec1);
bnEnd(&pub2);
bnEnd(&pub1);
return i;
}
/* 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 bn;
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(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x100, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x200, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x300, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x400, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x500, buf, len, stdout);
testDH(&bn);
#if 0
/* These get *really* slow */
len = copy(buf, argc, argv);
genDH(&bn, 0x600, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0x800, buf, len, stdout);
testDH(&bn);
len = copy(buf, argc, argv);
genDH(&bn, 0xc00, buf, len, stdout);
testDH(&bn);
/* Like, plan on a *week* or more for this one. */
len = copy(buf, argc, argv);
genDH(&bn, 0x1000, buf, len, stdout);
testDH(&bn);
#endif
bnEnd(&bn);
return 0;
}