summaryrefslogtreecommitdiff
path: root/programs/rsasigkey
diff options
context:
space:
mode:
Diffstat (limited to 'programs/rsasigkey')
-rw-r--r--programs/rsasigkey/.cvsignore1
-rw-r--r--programs/rsasigkey/Makefile39
-rw-r--r--programs/rsasigkey/rsasigkey.8259
-rw-r--r--programs/rsasigkey/rsasigkey.c573
4 files changed, 872 insertions, 0 deletions
diff --git a/programs/rsasigkey/.cvsignore b/programs/rsasigkey/.cvsignore
new file mode 100644
index 000000000..f9e610b4d
--- /dev/null
+++ b/programs/rsasigkey/.cvsignore
@@ -0,0 +1 @@
+rsasigkey
diff --git a/programs/rsasigkey/Makefile b/programs/rsasigkey/Makefile
new file mode 100644
index 000000000..c2b82e5c8
--- /dev/null
+++ b/programs/rsasigkey/Makefile
@@ -0,0 +1,39 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# 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: Makefile,v 1.1 2004/03/15 20:35:30 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=rsasigkey
+LIBS=${FREESWANLIB} -lgmp
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:30 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 mcr
+# changed TOPDIR->FREESWANSRCDIR in all Makefiles.
+# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the
+# kernel sense.)
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
+
diff --git a/programs/rsasigkey/rsasigkey.8 b/programs/rsasigkey/rsasigkey.8
new file mode 100644
index 000000000..c64dd46bd
--- /dev/null
+++ b/programs/rsasigkey/rsasigkey.8
@@ -0,0 +1,259 @@
+.TH IPSEC_RSASIGKEY 8 "22 July 2001"
+.\" RCSID $Id: rsasigkey.8,v 1.1 2004/03/15 20:35:30 as Exp $
+.SH NAME
+ipsec rsasigkey \- generate RSA signature key
+.SH SYNOPSIS
+.B ipsec
+.B rsasigkey
+[
+.B \-\-verbose
+] [
+.B \-\-random
+filename
+]
+.B \e
+.br
+\ \ \ [
+.B \-\-rounds
+nr
+] [
+.B \-\-hostname
+host ] [
+.B \-\-noopt
+] nbits
+.br
+.B ipsec
+.B rsasigkey
+[
+.B \-\-verbose
+] [
+.B \-\-hostname
+host ]
+.B \e
+.br
+\ \ \
+[
+.B \-\-noopt
+]
+.B \-\-oldkey
+file
+.SH DESCRIPTION
+.I Rsasigkey
+generates an RSA public/private key pair,
+suitable for digital signatures,
+of (exactly)
+.I nbits
+bits (that is, two primes each of exactly
+.IR nbits /2
+bits,
+and related numbers)
+and emits it on standard output as ASCII (mostly hex) data.
+.I nbits
+must be a multiple of 16.
+.PP
+The public exponent is forced to the value
+.BR 3 ,
+which has important speed advantages for signature checking.
+Beware that the resulting keys have known weaknesses as encryption keys
+\fIand should not be used for that purpose\fR.
+.PP
+The
+.B \-\-verbose
+option makes
+.I rsasigkey
+give a running commentary on standard error.
+By default, it works in silence until it is ready to generate output.
+.PP
+The
+.B \-\-random
+option specifies a source for random bits.
+The default is
+.I /dev/random
+(see
+.IR random (4)).
+Normally,
+.I rsasigkey
+reads exactly
+.I nbits
+random bits from the source;
+in extremely-rare circumstances it may need more.
+.PP
+The
+.B \-\-rounds
+option specifies the number of rounds to be done by the
+.I mpz_probab_prime_p
+probabilistic primality checker.
+The default, 30, is fairly rigorous and should not normally
+have to be overridden.
+.PP
+The
+.B \-\-hostname
+option specifies what host name to use in
+the first line of the output (see below);
+the default is what
+.IR gethostname (2)
+returns.
+.PP
+The
+.B \-\-noopt
+option suppresses an optimization of the private key
+(to be precise, setting of the decryption exponent to
+.B lcm(p\-1,q\-1)
+rather than
+.BR (p\-1)*(q\-1) )
+which speeds up operations on it slightly
+but can cause it to flunk a validity check in old RSA implementations
+(notably, obsolete versions of
+.IR ipsec_pluto (8)).
+.PP
+The
+.B \-\-oldkey
+option specifies that rather than generate a new key,
+.I rsasigkey
+should read an old key from the
+.I file
+(the name
+.B \-
+means ``standard input'')
+and use that to generate its output.
+Input lines which do not look like
+.I rsasigkey
+output are silently ignored.
+This permits updating old keys to the current format.
+.PP
+The output format looks like this (with long numbers trimmed down
+for clarity):
+.PP
+.ne 15
+.nf
+ # RSA 2048 bits xy.example.com Sat Apr 15 13:53:22 2000
+ # for signatures only, UNSAFE FOR ENCRYPTION
+ #pubkey=0sAQOF8tZ2NZt...Y1P+buFuFn/
+ Modulus: 0xcc2a86fcf440...cf1011abb82d1
+ PublicExponent: 0x03
+ # everything after this point is secret
+ PrivateExponent: 0x881c59fdf8...ab05c8c77d23
+ Prime1: 0xf49fd1f779...46504c7bf3
+ Prime2: 0xd5a9108453...321d43cb2b
+ Exponent1: 0xa31536a4fb...536d98adda7f7
+ Exponent2: 0x8e70b5ad8d...9142168d7dcc7
+ Coefficient: 0xafb761d001...0c13e98d98
+.fi
+.PP
+The first (comment) line,
+indicating the nature and date of the key,
+and giving a host name,
+is used by
+.IR ipsec_showhostkey (8)
+when generating some forms of key output.
+.PP
+The commented-out
+.B pubkey=
+line contains the public key\(emthe public exponent and the modulus\(emcombined
+in approximately RFC 2537 format
+(the one deviation is that the combined value is given with a
+.B 0s
+prefix, rather than in unadorned base-64),
+suitable for use in the
+.I ipsec.conf
+file.
+.PP
+The
+.BR Modulus ,
+.BR PublicExponent ,
+and
+.B PrivateExponent
+lines give the basic signing and verification data.
+.PP
+The
+.B Prime1
+and
+.B Prime2
+lines give the primes themselves (aka
+.I p
+and
+.IR q ),
+largest first.
+The
+.B Exponent1
+and
+.B Exponent2
+lines give
+the private exponent mod
+.IR p\-1
+and
+.IR q\-1
+respectively.
+The
+.B Coefficient
+line gives the Chinese Remainder Theorem coefficient,
+which is the inverse of
+.IR q ,
+mod
+.IR p .
+These additional numbers (which must all be kept as secret as the
+private exponent) are precomputed aids to rapid signature generation.
+.PP
+No attempt is made to break long lines.
+.PP
+The US patent on the RSA algorithm expired 20 Sept 2000.
+.SH EXAMPLES
+.TP
+.B "ipsec rsasigkey \-\-verbose 2192 >mykey"
+generates a 2192-bit signature key and puts it in the file
+.IR mykey ,
+with running commentary on standard error.
+The file contents can be inserted verbatim into a suitable entry in the
+.I ipsec.secrets
+file (see
+.IR ipsec.secrets (5)),
+and the public key can then be extracted and edited into the
+.I ipsec.conf
+file (see
+.IR ipsec.conf (5)).
+.TP
+.B "ipsec rsasigkey \-\-verbose \-\-oldkey oldie >latest"
+takes the old signature key from file
+.I oldie
+and puts a version in the current format into the file
+.IR latest ,
+with running commentary on standard error.
+.SH FILES
+/dev/random
+.SH SEE ALSO
+random(4), ipsec_showhostkey(8)
+.br
+\fIApplied Cryptography\fR, 2nd. ed., by Bruce Schneier, Wiley 1996.
+.br
+RFCs 2537, 2313.
+.br
+\fIGNU MP, the GNU multiple precision arithmetic library, edition 2.0.2\fR,
+by Torbj Granlund.
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+There is an internal limit on
+.IR nbits ,
+currently 20000.
+.PP
+.IR Rsasigkey 's
+run time is difficult to predict,
+since
+.I /dev/random
+output can be arbitrarily delayed if
+the system's entropy pool is low on randomness,
+and the time taken by the search for primes is also somewhat unpredictable.
+A reasonably typical time for a 1024-bit key on a quiet 200MHz Pentium MMX
+with plenty of randomness available is 20 seconds,
+almost all of it in the prime searches.
+Generating a 2192-bit key on the same system usually takes several minutes.
+A 4096-bit key took an hour and a half of CPU time.
+.PP
+The
+.B \-\-oldkey
+option does not check its input format as rigorously as it might.
+Corrupted
+.I rsasigkey
+output may confuse it.
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);
+}