diff options
Diffstat (limited to 'programs/rsasigkey/rsasigkey.c')
-rw-r--r-- | programs/rsasigkey/rsasigkey.c | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/programs/rsasigkey/rsasigkey.c b/programs/rsasigkey/rsasigkey.c new file mode 100644 index 000000000..b55dbb889 --- /dev/null +++ b/programs/rsasigkey/rsasigkey.c @@ -0,0 +1,573 @@ +/* + * RSA signature key generation + * Copyright (C) 1999, 2000, 2001 Henry Spencer. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * RCSID $Id: rsasigkey.c,v 1.2 2005/08/11 10:35:58 as Exp $ + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <time.h> +#include <limits.h> +#include <errno.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> +#include <freeswan.h> +#include "gmp.h" + +#ifndef DEVICE +#define DEVICE "/dev/random" +#endif +#ifndef MAXBITS +#define MAXBITS 20000 +#endif + +/* the code in getoldkey() knows about this */ +#define E 3 /* standard public exponent */ + +char usage[] = "rsasigkey [--verbose] [--random device] nbits"; +char usage2[] = "rsasigkey [--verbose] --oldkey filename"; +struct option opts[] = { + {"verbose", 0, NULL, 'v',}, + {"random", 1, NULL, 'r',}, + {"rounds", 1, NULL, 'p',}, + {"oldkey", 1, NULL, 'o',}, + {"hostname", 1, NULL, 'H',}, + {"noopt", 0, NULL, 'n',}, + {"help", 0, NULL, 'h',}, + {"version", 0, NULL, 'V',}, + {0, 0, NULL, 0,} +}; +int verbose = 0; /* narrate the action? */ +char *device = DEVICE; /* where to get randomness */ +int nrounds = 30; /* rounds of prime checking; 25 is good */ +mpz_t prime1; /* old key's prime1 */ +mpz_t prime2; /* old key's prime2 */ +char outputhostname[1024]; /* hostname for output */ +int do_lcm = 1; /* use lcm(p-1, q-1), not (p-1)*(q-1) */ + +char me[] = "ipsec rsasigkey"; /* for messages */ + +/* forwards */ +int getoldkey(char *filename); +void rsasigkey(int nbits, int useoldkey); +void initprime(mpz_t var, int nbits, int eval); +void initrandom(mpz_t var, int nbits); +void getrandom(size_t nbytes, char *buf); +char *bundle(int e, mpz_t n, size_t *sizep); +char *conv(char *bits, size_t nbytes, int format); +char *hexout(mpz_t var); +void report(char *msg); + +/* + - main - mostly argument parsing + */ +int main(int argc, char *argv[]) +{ + int opt; + extern int optind; + extern char *optarg; + int errflg = 0; + int i; + int nbits; + char *oldkeyfile = NULL; + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) + switch (opt) { + case 'v': /* verbose description */ + verbose = 1; + break; + case 'r': /* nonstandard /dev/random */ + device = optarg; + break; + case 'p': /* number of prime-check rounds */ + nrounds = atoi(optarg); + if (nrounds <= 0) { + fprintf(stderr, "%s: rounds must be > 0\n", me); + exit(2); + } + break; + case 'o': /* reformat old key */ + oldkeyfile = optarg; + break; + case 'H': /* set hostname for output */ + strcpy(outputhostname, optarg); + break; + case 'n': /* don't optimize the private key */ + do_lcm = 0; + break; + case 'h': /* help */ + printf("Usage:\t%s\n", usage); + printf("\tor\n"); + printf("\t%s\n", usage2); + exit(0); + break; + case 'V': /* version */ + printf("%s %s\n", me, ipsec_version_code()); + exit(0); + break; + case '?': + default: + errflg = 1; + break; + } + if (errflg || optind != ((oldkeyfile != NULL) ? argc : argc-1)) { + printf("Usage:\t%s\n", usage); + printf("\tor\n"); + printf("\t%s\n", usage2); + exit(2); + } + + if (outputhostname[0] == '\0') { + i = gethostname(outputhostname, sizeof(outputhostname)); + if (i < 0) { + fprintf(stderr, "%s: gethostname failed (%s)\n", + me, + strerror(errno)); + exit(1); + } + } + + if (oldkeyfile == NULL) { + assert(argv[optind] != NULL); + nbits = atoi(argv[optind]); + } else + nbits = getoldkey(oldkeyfile); + + if (nbits <= 0) { + fprintf(stderr, "%s: invalid bit count (%d)\n", me, nbits); + exit(1); + } else if (nbits > MAXBITS) { + fprintf(stderr, "%s: overlarge bit count (max %d)\n", me, + MAXBITS); + exit(1); + } else if (nbits % (CHAR_BIT*2) != 0) { /* *2 for nbits/2-bit primes */ + fprintf(stderr, "%s: bit count (%d) not multiple of %d\n", me, + nbits, (int)CHAR_BIT*2); + exit(1); + } + + rsasigkey(nbits, (oldkeyfile == NULL) ? 0 : 1); + exit(0); +} + +/* + - getoldkey - fetch an old key's primes + */ +int /* nbits */ +getoldkey(filename) +char *filename; +{ + FILE *f; + char line[MAXBITS/2]; + char *p; + char *value; + static char pube[] = "PublicExponent:"; + static char pubevalue[] = "0x03"; + static char pr1[] = "Prime1:"; + static char pr2[] = "Prime2:"; +# define STREQ(a, b) (strcmp(a, b) == 0) + int sawpube = 0; + int sawpr1 = 0; + int sawpr2 = 0; + int nbits; + + nbits = 0; + + if (STREQ(filename, "-")) + f = stdin; + else + f = fopen(filename, "r"); + if (f == NULL) { + fprintf(stderr, "%s: unable to open file `%s' (%s)\n", me, + filename, strerror(errno)); + exit(1); + } + if (verbose) + fprintf(stderr, "getting old key from %s...\n", filename); + + while (fgets(line, sizeof(line), f) != NULL) { + p = line + strlen(line) - 1; + if (*p != '\n') { + fprintf(stderr, "%s: over-long line in file `%s'\n", + me, filename); + exit(1); + } + *p = '\0'; + + p = line + strspn(line, " \t"); /* p -> first word */ + value = strpbrk(p, " \t"); /* value -> after it */ + if (value != NULL) { + *value++ = '\0'; + value += strspn(value, " \t"); + /* value -> second word if any */ + } + + if (value == NULL || *value == '\0') { + /* wrong format */ + } else if (STREQ(p, pube)) { + sawpube = 1; + if (!STREQ(value, pubevalue)) { + fprintf(stderr, "%s: wrong public exponent (`%s') in old key\n", + me, value); + exit(1); + } + } else if (STREQ(p, pr1)) { + if (sawpr1) { + fprintf(stderr, "%s: duplicate `%s' lines in `%s'\n", + me, pr1, filename); + exit(1); + } + sawpr1 = 1; + nbits = (strlen(value) - 2) * 4 * 2; + if (mpz_init_set_str(prime1, value, 0) < 0) { + fprintf(stderr, "%s: conversion error in reading old prime1\n", + me); + exit(1); + } + } else if (STREQ(p, pr2)) { + if (sawpr2) { + fprintf(stderr, "%s: duplicate `%s' lines in `%s'\n", + me, pr2, filename); + exit(1); + } + sawpr2 = 1; + if (mpz_init_set_str(prime2, value, 0) < 0) { + fprintf(stderr, "%s: conversion error in reading old prime2\n", + me); + exit(1); + } + } + } + + if (f != stdin) + fclose(f); + + if (!sawpube || !sawpr1 || !sawpr2) { + fprintf(stderr, "%s: old key missing or incomplete\n", me); + exit(1); + } + + assert(sawpr1); /* and thus nbits is known */ + return(nbits); +} + +/* + - rsasigkey - generate an RSA signature key + * e is fixed at 3, without discussion. That would not be wise if these + * keys were to be used for encryption, but for signatures there are some + * real speed advantages. + */ +void +rsasigkey(nbits, useoldkey) +int nbits; +int useoldkey; /* take primes from old key? */ +{ + mpz_t p; + mpz_t q; + mpz_t n; + mpz_t e; + mpz_t d; + mpz_t q1; /* temporary */ + mpz_t m; /* internal modulus, (p-1)*(q-1) */ + mpz_t t; /* temporary */ + mpz_t exp1; + mpz_t exp2; + mpz_t coeff; + char *bundp; + size_t bs; + int success; + time_t now = time((time_t *)NULL); + + /* the easy stuff */ + if (useoldkey) { + mpz_init_set(p, prime1); + mpz_init_set(q, prime2); + } else { + initprime(p, nbits/2, E); + initprime(q, nbits/2, E); + } + mpz_init(t); + if (mpz_cmp(p, q) < 0) { + report("swapping primes so p is the larger..."); + mpz_set(t, p); + mpz_set(p, q); + mpz_set(q, t); + } + report("computing modulus..."); + mpz_init(n); + mpz_mul(n, p, q); /* n = p*q */ + mpz_init_set_ui(e, E); + + /* internal modulus */ + report("computing lcm(p-1, q-1)..."); + mpz_init_set(m, p); + mpz_sub_ui(m, m, 1); + mpz_init_set(q1, q); + mpz_sub_ui(q1, q1, 1); + mpz_gcd(t, m, q1); /* t = gcd(p-1, q-1) */ + mpz_mul(m, m, q1); /* m = (p-1)*(q-1) */ + if (do_lcm) + mpz_divexact(m, m, t); /* m = lcm(p-1, q-1) */ + mpz_gcd(t, m, e); + assert(mpz_cmp_ui(t, 1) == 0); /* m and e relatively prime */ + + /* decryption key */ + report("computing d..."); + mpz_init(d); + success = mpz_invert(d, e, m); + assert(success); /* e has an inverse mod m */ + if (mpz_cmp_ui(d, 0) < 0) + mpz_add(d, d, m); + assert(mpz_cmp(d, m) < 0); + + /* the speedup hacks */ + report("computing exp1, exp1, coeff..."); + mpz_init(exp1); + mpz_sub_ui(t, p, 1); + mpz_mod(exp1, d, t); /* exp1 = d mod p-1 */ + mpz_init(exp2); + mpz_sub_ui(t, q, 1); + mpz_mod(exp2, d, t); /* exp2 = d mod q-1 */ + mpz_init(coeff); + mpz_invert(coeff, q, p); /* coeff = q^-1 mod p */ + if (mpz_cmp_ui(coeff, 0) < 0) + mpz_add(coeff, coeff, p); + assert(mpz_cmp(coeff, p) < 0); + + /* and the output */ + /* note, getoldkey() knows about some of this */ + report("output...\n"); /* deliberate extra newline */ + printf("\t# RSA %d bits %s %s", nbits, outputhostname, ctime(&now)); + /* ctime provides \n */ + printf("\t# for signatures only, UNSAFE FOR ENCRYPTION\n"); + bundp = bundle(E, n, &bs); + printf("\t#pubkey=%s\n", conv(bundp, bs, 's')); /* RFC2537ish format */ + printf("\tModulus: %s\n", hexout(n)); + printf("\tPublicExponent: %s\n", hexout(e)); + printf("\t# everything after this point is secret\n"); + printf("\tPrivateExponent: %s\n", hexout(d)); + printf("\tPrime1: %s\n", hexout(p)); + printf("\tPrime2: %s\n", hexout(q)); + printf("\tExponent1: %s\n", hexout(exp1)); + printf("\tExponent2: %s\n", hexout(exp2)); + printf("\tCoefficient: %s\n", hexout(coeff)); +} + +/* + - initprime - initialize an mpz_t to a random prime of specified size + * Efficiency tweak: we reject candidates that are 1 higher than a multiple + * of e, since they will make the internal modulus not relatively prime to e. + */ +void +initprime(var, nbits, eval) +mpz_t var; +int nbits; /* known to be a multiple of CHAR_BIT */ +int eval; /* value of e; 0 means don't bother w. tweak */ +{ + unsigned long tries; + size_t len; +# define OKAY(p) (eval == 0 || mpz_fdiv_ui(p, eval) != 1) + + initrandom(var, nbits); + assert(mpz_fdiv_ui(var, 2) == 1); /* odd number */ + + report("looking for a prime starting there (can take a while)..."); + tries = 1; + while (!( OKAY(var) && mpz_probab_prime_p(var, nrounds) )) { + mpz_add_ui(var, var, 2); + tries++; + } + + len = mpz_sizeinbase(var, 2); + assert(len == (size_t)nbits || len == (size_t)(nbits+1)); + if (len == (size_t)(nbits+1)) { + report("carry out occurred (!), retrying..."); + mpz_clear(var); + initprime(var, nbits, eval); + return; + } + if (verbose) + fprintf(stderr, "found it after %lu tries.\n", tries); +} + +/* + - initrandom - initialize an mpz_t to a random number, specified bit count + * Converting via hex is a bit weird, but it's the best route GMP gives us. + * Note that highmost and lowmost bits are forced on -- highmost to give a + * number of exactly the specified length, lowmost so it is an odd number. + */ +void +initrandom(var, nbits) +mpz_t var; +int nbits; /* known to be a multiple of CHAR_BIT */ +{ + size_t nbytes = (size_t)(nbits / CHAR_BIT); + static char bitbuf[MAXBITS/CHAR_BIT]; + static char hexbuf[2 + MAXBITS/4 + 1]; + size_t hsize = sizeof(hexbuf); + + assert(nbytes <= sizeof(bitbuf)); + getrandom(nbytes, bitbuf); + bitbuf[0] |= 01 << (CHAR_BIT-1); /* force high bit on */ + bitbuf[nbytes-1] |= 01; /* force low bit on */ + if (datatot(bitbuf, nbytes, 'x', hexbuf, hsize) > hsize) { + fprintf(stderr, "%s: can't-happen buffer overflow\n", me); + exit(1); + } + if (mpz_init_set_str(var, hexbuf, 0) < 0) { + fprintf(stderr, "%s: can't-happen hex conversion error\n", me); + exit(1); + } +} + +/* + - getrandom - get some random bytes from /dev/random (or wherever) + */ +void +getrandom(nbytes, buf) +size_t nbytes; +char *buf; /* known to be big enough */ +{ + size_t ndone; + int dev; + size_t got; + + dev = open(device, 0); + if (dev < 0) { + fprintf(stderr, "%s: could not open %s (%s)\n", me, + device, strerror(errno)); + exit(1); + } + + ndone = 0; + if (verbose) + fprintf(stderr, "getting %d random bytes from %s...\n", (int) nbytes, + device); + while (ndone < nbytes) { + got = read(dev, buf + ndone, nbytes - ndone); + if (got < 0) { + fprintf(stderr, "%s: read error on %s (%s)\n", me, + device, strerror(errno)); + exit(1); + } + if (got == 0) { + fprintf(stderr, "%s: eof on %s!?!\n", me, device); + exit(1); + } + ndone += got; + } + + close(dev); +} + +/* + - hexout - prepare hex output, guaranteeing even number of digits + * (The current FreeS/WAN conversion routines want an even digit count, + * but mpz_get_str doesn't promise one.) + */ +char * /* pointer to static buffer (ick) */ +hexout(var) +mpz_t var; +{ + static char hexbuf[3 + MAXBITS/4 + 1]; + char *hexp; + + mpz_get_str(hexbuf+3, 16, var); + if (strlen(hexbuf+3)%2 == 0) /* even number of hex digits */ + hexp = hexbuf+1; + else { /* odd, must pad */ + hexp = hexbuf; + hexp[2] = '0'; + } + hexp[0] = '0'; + hexp[1] = 'x'; + + return hexp; +} + +/* + - bundle - bundle e and n into an RFC2537-format lump + * Note, calls hexout. + */ +char * /* pointer to static buffer (ick) */ +bundle(e, n, sizep) +int e; +mpz_t n; +size_t *sizep; +{ + char *hexp = hexout(n); + static char bundbuf[2 + MAXBITS/8]; + const char *er; + size_t size; + + assert(e <= 255); + bundbuf[0] = 1; + bundbuf[1] = e; + er = ttodata(hexp, 0, 0, bundbuf+2, sizeof(bundbuf)-2, &size); + if (er != NULL) { + fprintf(stderr, "%s: can't-happen bundle convert error `%s'\n", + me, er); + exit(1); + } + if (size > sizeof(bundbuf)-2) { + fprintf(stderr, "%s: can't-happen bundle overflow (need %d)\n", + me, (int) size); + exit(1); + } + if (sizep != NULL) + *sizep = size + 2; + return bundbuf; +} + +/* + - conv - convert bits to output in specified format + */ +char * /* pointer to static buffer (ick) */ +conv(bits, nbytes, format) +char *bits; +size_t nbytes; +int format; /* datatot() code */ +{ + static char convbuf[MAXBITS/4 + 50]; /* enough for hex */ + size_t n; + + n = datatot(bits, nbytes, format, convbuf, sizeof(convbuf)); + if (n == 0) { + fprintf(stderr, "%s: can't-happen convert error\n", me); + exit(1); + } + if (n > sizeof(convbuf)) { + fprintf(stderr, "%s: can't-happen convert overflow (need %d)\n", + me, (int) n); + exit(1); + } + return convbuf; +} + +/* + - report - report progress, if indicated + */ +void +report(msg) +char *msg; +{ + if (!verbose) + return; + fprintf(stderr, "%s\n", msg); +} |