pub/rsa-gen.c, progs/key.c: Overhaul RSA key generation.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 11 May 2017 09:42:15 +0000 (10:42 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 13:58:40 +0000 (14:58 +0100)
Rewrite the key-generation code from scratch.  The new version seems
simpler to me, and allows the caller to choose the public exponent.  It
also retries repeatedly until it finds acceptable values unless told to
stop within a finite number of steps.

Add an option to `key' to allow the user to select a different
exponent.  Recommend e = 3 in the manpage.

progs/key.1
progs/key.c
pub/rsa-gen.c
pub/rsa.h

index 95eb0de..0ffd076 100644 (file)
@@ -472,6 +472,14 @@ using a passphrase.
 Suppresses the progress indication which is usually generated while
 time-consuming key generation tasks are being performed.
 .TP
+.BI "\-E, \-\-public-exponent"
+Set the public exponent for RSA keys.
+The default is 65537,
+because this seems to be the overwhelmingly popular choice
+among practitioners
+and because it was the exponent used before this option was introduced.
+The value 3 is fine unless you use a completely terrible padding scheme.
+.TP
 .BI "\-L, \-\-lim-lee"
 When generating Diffie\(enHellman parameters, generate a Lim\(enLee
 prime rather than a random (or safe) prime.  See the details on
index 26c67ee..e85107a 100644 (file)
@@ -63,6 +63,7 @@
 #include "gfreduce.h"
 #include "key.h"
 #include "mp.h"
+#include "mpint.h"
 #include "mpmont.h"
 #include "mprand.h"
 #include "mptext.h"
@@ -196,6 +197,7 @@ typedef struct keyopts {
   unsigned bits, qbits;                        /* Bit length for the new key */
   const char *curve;                   /* Elliptic curve name/info */
   grand *r;                            /* Random number source */
+  mp *e;                               /* Public exponent */
   key *p;                              /* Parameters key-data */
 } keyopts;
 
@@ -443,11 +445,15 @@ static void alg_rsa(keyopts *k)
   copyparam(k, 0);
   if (!k->bits)
     k->bits = 1024;
+  if (k->bits < 64)
+    die(EXIT_FAILURE, "RSA key too tiny");
+  if (!k->e)
+    k->e = mp_fromulong(MP_NEW, 65537);
 
   /* --- Generate the RSA parameters --- */
 
-  if (rsa_gen(&rp, k->bits, k->r, 0,
-             (k->f & f_quiet) ? 0 : pgen_ev, 0))
+  if (rsa_gen_e(&rp, k->bits, k->e, k->r, 0,
+               (k->f & f_quiet) ? 0 : pgen_ev, 0))
     die(EXIT_FAILURE, "RSA key generation failed");
 
   /* --- Run a test encryption --- */
@@ -1033,7 +1039,7 @@ static int cmd_add(int argc, char *argv[])
   keyalg *alg = algtab;
   const char *rtag = 0;
   const struct seedalg *sa = SEEDALG_DEFAULT;
-  keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0 };
+  keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0, 0 };
   const char *seed = 0;
   k.r = &rand_global;
 
@@ -1054,6 +1060,7 @@ static int cmd_add(int argc, char *argv[])
       { "seedalg",     OPTF_ARGREQ,    0,      'A' },
       { "seed",                OPTF_ARGREQ,    0,      's' },
       { "newseed",     OPTF_ARGREQ,    0,      'n' },
+      { "public-exponent", OPTF_ARGREQ, 0,     'E' },
       { "lock",                0,              0,      'l' },
       { "quiet",       0,              0,      'q' },
       { "lim-lee",     0,              0,      'L' },
@@ -1061,7 +1068,7 @@ static int cmd_add(int argc, char *argv[])
       { "kcdsa",       0,              0,      'K' },
       { 0,             0,              0,      0 }
     };
-    int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:I:C:A:s:n:lqrLKS",
+    int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:I:C:A:s:n:E:lqrLKS",
                   opt, 0, 0, 0);
     if (i < 0)
       break;
@@ -1223,6 +1230,15 @@ static int cmd_add(int argc, char *argv[])
        kid = id;
       } break;
 
+      /* --- Public exponent --- */
+
+      case 'E': {
+       char *p;
+       k.e = mp_readstring(k.e, optarg, &p, 0);
+       if (!k.e || *p || MP_CMP(k.e, <, MP_THREE) || MP_EVENP(k.e))
+         die(EXIT_FAILURE, "bad exponent `%s'", optarg);
+      } break;
+
       /* --- Other flags --- */
 
       case 'R':
index 3b5334b..b381bcd 100644 (file)
 
 /*----- Main code ---------------------------------------------------------*/
 
-/* --- @rsa_gen@ --- *
+/* --- @rsa_gen@, @rsa_gen_e --- *
  *
  * Arguments:  @rsa_priv *rp@ = pointer to block to be filled in
  *             @unsigned nbits@ = required modulus size in bits
+ *             @mp *e@ = public exponent
  *             @grand *r@ = random number source
  *             @unsigned n@ = number of attempts to make
  *             @pgen_proc *event@ = event handler function
  *             possible.
  */
 
-int rsa_gen(rsa_priv *rp, unsigned nbits, grand *r, unsigned n,
-           pgen_proc *event, void *ectx)
+static int genprime(mp **pp, mp **dd, const char *name,
+                   unsigned nbits, mp *e,
+                   grand *r, unsigned nsteps, pgen_proc *event, void *ectx)
 {
-  pgen_gcdstepctx g;
-  mp *phi = MP_NEW;
-
-  /* --- Bits of initialization --- */
-
-  rp->e = mp_fromulong(MP_NEW, 0x10001);
-  rp->d = MP_NEW;
-
-  /* --- Generate strong primes %$p$% and %$q$% --- *
-   *
-   * Constrain the GCD of @q@ to ensure that overly small private exponents
-   * are impossible.  Current results suggest that if %$d < n^{0.29}$% then
-   * it can be guessed fairly easily.  This implementation is rather more
-   * conservative about that sort of thing.
-   */
-
-  if ((rp->p = strongprime("p", MP_NEWSEC, nbits/2, r, n, event, ectx)) == 0)
-    goto fail_p;
-
-  /* --- Do painful fiddling with GCD steppers --- *
-   *
-   * Also, arrange that %$q \ge \lceil 2^{N-1}/p \rceil$%, so that %$p q$%
-   * has the right length.
-   */
-
-  {
-    mp *q;
-    rabin rb;
-
-    if ((q = strongprime_setup("q", MP_NEWSEC, &g.jp, nbits / 2,
-                              r, n, event, ectx)) == 0)
-      goto fail_q;
-
-    g.r = mp_lsr(MP_NEW, rp->p, 1);
-    g.g = MP_NEW;
-    g.max = MP_256;
-    q = pgen("q", q, q, event, ectx, n, pgen_gcdstep, &g,
-            rabin_iters(nbits/2), pgen_test, &rb);
-    pfilt_destroy(&g.jp);
-    mp_drop(g.r);
-    if (!q) {
-      mp_drop(g.g);
-      goto fail_q;
-    }
-    rp->q = q;
+  pgen_jumpctx jctx; pfilt j;
+  mp *p = MP_NEWSEC, *t = MP_NEW, *u = MP_NEW;
+  rabin rb;
+  mpw p3, j3, a;
+  int rc = -1;
+
+  j.m = MP_NEW;
+
+  /* Find a start position for the search. */
+  p = strongprime_setup(name, p, &j, nbits, r, nsteps, event, ectx);
+  if (!p) goto end;
+
+  /* Special handling for e = 3. */
+  if (MP_EQ(e, MP_THREE)) {
+    /* We must have p == 2 (mod 3): if p == 0 (mod 3) then p is not prime; if
+     * p == 1 (mod 3) then e|(p - 1).  So fiddle the start position and jump
+     * context to allow for this.
+     */
+
+    /* Figure out the residues of the start position and jump. */
+    mp_div(0, &t, p, MP_THREE); p3 = t->v[0];
+    mp_div(0, &u, j.m, MP_THREE); j3 = u->v[0];
+
+    /* If the jump is a multiple of three already, then we're stuffed unless
+     * the start position is 2 (mod 3).  This shouldn't happen, since the
+     * jump should be twice a multiple of two large primes, which will be
+     * nonzero (mod 3).
+     *
+     * Set a = (2 - p) j mod 3.  Then p' = p + a j == p (mod j), and p' ==
+     * p + (2 - p) j^2 == 2 (mod 3).
+     */
+    assert(j3 != 0);
+    a = ((2 - p3)*j3)%3;
+    if (a == 1) p = mp_add(p, p, j.m);
+    else if (a == 2) { t = mp_lsl(t, j.m, 1); p = mp_add(p, p, t); }
+
+    /* Finally, multiply j by three to make sure it preserves this
+     * congruence.
+     */
+    pfilt_muladd(&j, &j, 3, 0);
   }
 
-  /* --- Ensure that %$p > q$% --- *
-   *
-   * Also ensure that %$p$% and %$q$% are sufficiently different to deter
-   * square-root-based factoring methods.
-   */
-
-  phi = mp_sub(phi, rp->p, rp->q);
-  if (MP_LEN(phi) * 4 < MP_LEN(rp->p) * 3 ||
-      MP_LEN(phi) * 4 < MP_LEN(rp->q) * 3) {
-    mp_drop(rp->p);
-    mp_drop(g.g);
-    goto fail_q;
-  }
+  /* Set the search going. */
+  jctx.j = &j;
+  p = pgen(name, p, p, event, ectx,
+          nsteps, pgen_jump, &jctx,
+          rabin_iters(nbits), pgen_test, &rb);
+
+  if (!p) goto end;
+
+  /* Check the GCD constraint. */
+  t = mp_sub(t, p, MP_ONE);
+  mp_gcd(&t, &u, 0, e, t);
+  if (!MP_EQ(t, MP_ONE)) goto end;
+
+  /* All is good. */
+  mp_drop(*pp); *pp = p; p = 0;
+  mp_drop(*dd); *dd = u; u = 0;
+  rc = 0;
+end:
+  mp_drop(p); mp_drop(t); mp_drop(u);
+  mp_drop(j.m);
+  return (rc);
+}
 
-  if (MP_NEGP(phi)) {
-    mp *z = rp->p;
-    rp->p = rp->q;
-    rp->q = z;
+int rsa_gen_e(rsa_priv *rp, unsigned nbits, mp *e,
+             grand *r, unsigned nsteps, pgen_proc *event, void *ectx)
+{
+  mp *p = MP_NEWSEC, *q = MP_NEWSEC, *n = MP_NEW;
+  mp *dp = MP_NEWSEC, *dq = MP_NEWSEC;
+  mp *t = MP_NEW, *u = MP_NEW, *v = MP_NEW;
+  mp *tt;
+  int rc = -1;
+
+#define RETRY(what) \
+  do { if (nsteps) goto fail; else goto retry_##what; } while (0)
+
+  /* Find the first prime. */
+retry_all:
+retry_p:
+  if (genprime(&p, &dp, "p", nbits/2, e, r, nsteps, event, ectx))
+    RETRY(p);
+
+  /* Find the second prime. */
+retry_q:
+  if (genprime(&q, &dq, "q", nbits/2, e, r, nsteps, event, ectx))
+    RETRY(q);
+
+  /* Check that gcd(p - 1, q - 1) is sufficiently large. */
+  u = mp_sub(u, p, MP_ONE);
+  v = mp_sub(v, q, MP_ONE);
+  mp_gcd(&t, 0, 0, u, v);
+  if (mp_bits(t) >= 8) RETRY(all);
+
+  /* Arrange for p > q. */
+  if (MP_CMP(p, <, q)) {
+    tt = p;  p  = q;  q  = tt;
+    tt = dp; dp = dq; dq = tt;
   }
 
-  /* --- Work out the modulus and the CRT coefficient --- */
+  /* Check that the modulus is the right size. */
+  n = mp_mul(n, p, q);
+  if (mp_bits(n) != nbits) RETRY(all);
 
-  rp->n = mp_mul(MP_NEW, rp->p, rp->q);
-  rp->q_inv = mp_modinv(MP_NEW, rp->q, rp->p);
-
-  /* --- Work out %$\varphi(n) = (p - 1)(q - 1)$% --- *
-   *
-   * Save on further multiplications by noting that %$n = pq$% is known and
-   * that %$(p - 1)(q - 1) = pq - p - q + 1$%.  To minimize the size of @d@
-   * (useful for performance reasons, although not very because an overly
-   * small @d@ will be rejected for security reasons) this is then divided by
-   * %$\gcd(p - 1, q - 1)$%.
-   */
-
-  phi = mp_sub(phi, rp->n, rp->p);
-  phi = mp_sub(phi, phi, rp->q);
-  phi = mp_add(phi, phi, MP_ONE);
-  phi = mp_lsr(phi, phi, 1);
-  mp_div(&phi, 0, phi, g.g);
-
-  /* --- Decide on a public exponent --- *
-   *
-   * Simultaneously compute the private exponent.
+  /* Now we want to calculate d.  The unit-group exponent is λ = lcm(p - 1,
+   * q - 1), so d == e^-1 (mod λ).
    */
+  u = mp_mul(u, u, v);
+  mp_div(&t, 0, u, t);
+  rp->d = mp_modinv(MP_NEW, e, t);
+
+  /* All done. */
+  rp->e = MP_COPY(e);
+  rp->q_inv = mp_modinv(MP_NEW, q, p);
+  rp->p = p; p = 0; rp->dp = dp; dp = 0;
+  rp->q = q; q = 0; rp->dq = dq; dq = 0;
+  rp->n = n; n = 0;
+  rc = 0;
+
+fail:
+  mp_drop(p); mp_drop(dp);
+  mp_drop(q); mp_drop(dq);
+  mp_drop(n);
+  mp_drop(t); mp_drop(u); mp_drop(v);
+  return (rc);
+}
 
-  mp_gcd(&g.g, 0, &rp->d, phi, rp->e);
-  if (!MP_EQ(g.g, MP_ONE) && MP_LEN(rp->d) * 4 > MP_LEN(rp->n) * 3)
-    goto fail_e;
-  if (mp_bits(rp->n) != nbits)
-    goto fail_e;
-
-  /* --- Work out exponent residues --- */
-
-  rp->dp = MP_NEW; phi = mp_sub(phi, rp->p, MP_ONE);
-  mp_div(0, &rp->dp, rp->d, phi);
-
-  rp->dq = MP_NEW; phi = mp_sub(phi, rp->q, MP_ONE);
-  mp_div(0, &rp->dq, rp->d, phi);
-
-  /* --- Done --- */
-
-  mp_drop(phi);
-  mp_drop(g.g);
-  return (0);
-
-  /* --- Tidy up when something goes wrong --- */
-
-fail_e:
-  mp_drop(g.g);
-  mp_drop(phi);
-  mp_drop(rp->n);
-  mp_drop(rp->q_inv);
-  mp_drop(rp->q);
-fail_q:
-  mp_drop(rp->p);
-fail_p:
-  mp_drop(rp->e);
-  if (rp->d)
-    mp_drop(rp->d);
-  return (-1);
+int rsa_gen(rsa_priv *rp, unsigned nbits,
+           grand *r, unsigned nsteps, pgen_proc *event, void *ectx)
+{
+  mp *f4 = mp_fromulong(MP_NEW, 65537);
+  int rc = rsa_gen_e(rp, nbits, f4, r, nsteps, event, ectx);
+  mp_drop(f4); return (rc);
 }
 
 /*----- That's all, folks -------------------------------------------------*/
index cd910b7..df046ce 100644 (file)
--- a/pub/rsa.h
+++ b/pub/rsa.h
@@ -325,6 +325,7 @@ extern int rsa_verify(rsa_pubctx */*rp*/, mp */*s*/,
  *
  * Arguments:  @rsa_priv *rp@ = pointer to block to be filled in
  *             @unsigned nbits@ = required modulus size in bits
+ *             @mp *e@ = public exponent
  *             @grand *r@ = random number source
  *             @unsigned n@ = number of attempts to make
  *             @pgen_proc *event@ = event handler function
@@ -341,6 +342,10 @@ extern int rsa_gen(rsa_priv */*rp*/, unsigned /*nbits*/,
                   grand */*r*/, unsigned /*n*/,
                   pgen_proc */*event*/, void */*ectx*/);
 
+extern int rsa_gen_e(rsa_priv */*rp*/, unsigned /*nbits*/, mp */*e*/,
+                    grand */*r*/, unsigned /*nsteps*/,
+                    pgen_proc */*event*/, void */*ectx*/);
+
 /* --- @rsa_recover@ --- *
  *
  * Arguments:  @rsa_priv *rp@ = pointer to parameter block