chiark / gitweb /
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 95eb0ded54031227b7fa6ee9007dca068e479069..0ffd0769a21dc5ca78acae3aab64511c87d1fb2b 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 26c67eeccb5dc43430ffcbd9130b87e0f8b08e5e..e85107ae5fd7788b91beda5009164ffcdda8cccc 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 3b5334b8e3189a488c86d5e127ba482f548d55ab..b381bcd008eca7867e0ecd8c9d6af2e16e62d94a 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 cd910b7722e8c5fdede59f62326978a2a6f266b6..df046ce80a8e3942fe57f3b642583d29712b793f 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