Generate keys more carefully, so that when the user asks for an n-bit
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sun, 4 Mar 2012 00:24:49 +0000 (00:24 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sun, 4 Mar 2012 00:24:49 +0000 (00:24 +0000)
key they always get an n-bit number instead of n-1. The latter was
perfectly harmless but kept confusing users.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9421 cda61777-01e9-0310-a592-d414129be87e

ssh.h
sshdssg.c
sshprime.c
sshrsag.c

diff --git a/ssh.h b/ssh.h
index 1ccc074..c604554 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -550,7 +550,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
 int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
                 void *pfnparam);
 Bignum primegen(int bits, int modulus, int residue, Bignum factor,
-               int phase, progfn_t pfn, void *pfnparam);
+               int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
+void invent_firstbits(unsigned *one, unsigned *two);
 
 
 /*
index 3eb68d4..3d7b0ef 100644 (file)
--- a/sshdssg.c
+++ b/sshdssg.c
@@ -9,6 +9,7 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
                 void *pfnparam)
 {
     Bignum qm1, power, g, h, tmp;
+    unsigned pfirst, qfirst;
     int progress;
 
     /*
@@ -70,15 +71,16 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
 
     pfn(pfnparam, PROGFN_READY, 0, 0);
 
+    invent_firstbits(&pfirst, &qfirst);
     /*
      * Generate q: a prime of length 160.
      */
-    key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam);
+    key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam, qfirst);
     /*
      * Now generate p: a prime of length `bits', such that p-1 is
      * divisible by q.
      */
-    key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam);
+    key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam, pfirst);
 
     /*
      * Next we need g. Raise 2 to the power (p-1)/q modulo p, and
index 96fe49c..03b9c72 100644 (file)
@@ -838,11 +838,19 @@ static const unsigned short primes[] = {
  *    more than a multiple of a dirty great bignum. In this case
  *    `bits' gives the size of the factor by which we _multiply_
  *    that bignum, rather than the size of the whole number.
+ *
+ *  - for the basically cosmetic purposes of generating keys of the
+ *    length actually specified rather than off by one bit, we permit
+ *    the caller to provide an unsigned integer 'firstbits' which will
+ *    match the top few bits of the returned prime. (That is, there
+ *    will exist some n such that (returnvalue >> n) == firstbits.) If
+ *    'firstbits' is not needed, specifying it to either 0 or 1 is
+ *    an adequate no-op.
  */
 Bignum primegen(int bits, int modulus, int residue, Bignum factor,
-               int phase, progfn_t pfn, void *pfnparam)
+               int phase, progfn_t pfn, void *pfnparam, unsigned firstbits)
 {
-    int i, k, v, byte, bitsleft, check, checks;
+    int i, k, v, byte, bitsleft, check, checks, fbsize;
     unsigned long delta;
     unsigned long moduli[NPRIMES + 1];
     unsigned long residues[NPRIMES + 1];
@@ -853,6 +861,10 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor,
     byte = 0;
     bitsleft = 0;
 
+    fbsize = 0;
+    while (firstbits >> fbsize)        /* work out how to align this */
+        fbsize++;
+
   STARTOVER:
 
     pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
@@ -865,9 +877,11 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor,
      */
     p = bn_power_2(bits - 1);
     for (i = 0; i < bits; i++) {
-       if (i == 0 || i == bits - 1)
+       if (i == 0 || i == bits - 1) {
            v = (i != 0 || !factor) ? 1 : 0;
-       else {
+        } else if (i >= bits - fbsize) {
+            v = (firstbits >> (i - (bits - fbsize))) & 1;
+        } else {
            if (bitsleft <= 0)
                bitsleft = 8, byte = random_byte();
            v = byte & 1;
@@ -1041,3 +1055,32 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor,
     freebn(pm1);
     return p;
 }
+
+/*
+ * Invent a pair of values suitable for use as 'firstbits' in the
+ * above function, such that their product is at least 2.
+ *
+ * This is used for generating both RSA and DSA keys which have
+ * exactly the specified number of bits rather than one fewer - if you
+ * generate an a-bit and a b-bit number completely at random and
+ * multiply them together, you could end up with either an (ab-1)-bit
+ * number or an (ab)-bit number. The former happens log(2)*2-1 of the
+ * time (about 39%) and, though actually harmless, every time it
+ * occurs it has a non-zero probability of sparking a user email along
+ * the lines of 'Hey, I asked PuTTYgen for a 2048-bit key and I only
+ * got 2047 bits! Bug!'
+ */
+void invent_firstbits(unsigned *one, unsigned *two)
+{
+    /*
+     * Our criterion is that any number in the range [one,one+1)
+     * multiplied by any number in the range [two,two+1) should have
+     * the highest bit set. It should be clear that we can trivially
+     * test this by multiplying the smallest values in each interval,
+     * i.e. the ones we actually invented.
+     */
+    do {
+        *one = 0x100 | random_byte();
+        *two = 0x100 | random_byte();
+    } while (*one * *two < 0x20000);
+}
index eb714ad..dbe8940 100644 (file)
--- a/sshrsag.c
+++ b/sshrsag.c
@@ -10,6 +10,7 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
                 void *pfnparam)
 {
     Bignum pm1, qm1, phi_n;
+    unsigned pfirst, qfirst;
 
     /*
      * Set up the phase limits for the progress report. We do this
@@ -59,10 +60,11 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
      * general that's slightly more fiddly to arrange. By choosing
      * a prime e, we can simplify the criterion.)
      */
+    invent_firstbits(&pfirst, &qfirst);
     key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL,
-                     1, pfn, pfnparam);
+                     1, pfn, pfnparam, pfirst);
     key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL,
-                     2, pfn, pfnparam);
+                     2, pfn, pfnparam, qfirst);
 
     /*
      * Ensure p > q, by swapping them if not.