1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/sshdssg.c
Simon Tatham da3bc3d927 Refactor generation of candidate integers in primegen.
I've replaced the random number generation and small delta-finding
loop in primegen() with a much more elaborate system in its own source
file, with unit tests and everything.

Immediate benefits:

 - fixes a theoretical possibility of overflowing the target number of
   bits, if the random number was so close to the top of the range
   that the addition of delta * factor pushed it over. However, this
   only happened with negligible probability.

 - fixes a directional bias in delta-finding. The previous code
   incremented the number repeatedly until it found a value coprime to
   all the right things, which meant that a prime preceded by a
   particularly long sequence of numbers with tiny factors was more
   likely to be chosen. Now we select candidate delta values at
   random, that bias should be eliminated.

 - changes the semantics of the outermost primegen() function to make
   them easier to use, because now the caller specifies the 'bits' and
   'firstbits' values for the actual returned prime, rather than
   having to account for the factor you're multiplying it by in DSA.
   DSA client code is correspondingly adjusted.

Future benefits:

 - having the candidate generation in a separate function makes it
   easy to reuse in alternative prime generation strategies

 - the available constraints support applications such as Maurer's
   algorithm for generating provable primes, or strong primes for RSA
   in which both p-1 and p+1 have a large factor. So those become
   things we could experiment with in future.
2020-02-23 15:47:44 +00:00

112 lines
3.8 KiB
C

/*
* DSS key generation.
*/
#include "misc.h"
#include "ssh.h"
#include "mpint.h"
int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
void *pfnparam)
{
/*
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
*
* For prime generation: our initial filter finds things
* coprime to everything below 2^16. Computing the product of
* (p-1)/p for all prime p below 2^16 gives about 20.33; so
* among B-bit integers, one in every 20.33 will get through
* the initial filter to be a candidate prime.
*
* Meanwhile, we are searching for primes in the region of 2^B;
* since pi(x) ~ x/log(x), when x is in the region of 2^B, the
* prime density will be d/dx pi(x) ~ 1/log(B), i.e. about
* 1/0.6931B. So the chance of any given candidate being prime
* is 20.33/0.6931B, which is roughly 29.34 divided by B.
*
* So now we have this probability P, we're looking at an
* exponential distribution with parameter P: we will manage in
* one attempt with probability P, in two with probability
* P(1-P), in three with probability P(1-P)^2, etc. The
* probability that we have still not managed to find a prime
* after N attempts is (1-P)^N.
*
* We therefore inform the progress indicator of the number B
* (29.34/B), so that it knows how much to increment by each
* time. We do this in 16-bit fixed point, so 29.34 becomes
* 0x1D.57C4.
*/
pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x2800);
pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / 160);
pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x40 * bits);
pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / bits);
/*
* In phase three we are finding an order-q element of the
* multiplicative group of p, by finding an element whose order
* is _divisible_ by q and raising it to the power of (p-1)/q.
* _Most_ elements will have order divisible by q, since for a
* start phi(p) of them will be primitive roots. So
* realistically we don't need to set this much below 1 (64K).
* Still, we'll set it to 1/2 (32K) to be on the safe side.
*/
pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x2000);
pfn(pfnparam, PROGFN_EXP_PHASE, 3, -32768);
pfn(pfnparam, PROGFN_READY, 0, 0);
/*
* Generate q: a prime of length 160.
*/
mp_int *q = primegen(160, 0, 0, NULL, 1, pfn, pfnparam, 1);
/*
* Now generate p: a prime of length `bits', such that p-1 is
* divisible by q.
*/
mp_int *p = primegen(bits, 0, 0, q, 2, pfn, pfnparam, 1);
/*
* Next we need g. Raise 2 to the power (p-1)/q modulo p, and
* if that comes out to one then try 3, then 4 and so on. As
* soon as we hit a non-unit (and non-zero!) one, that'll do
* for g.
*/
mp_int *power = mp_div(p, q); /* this is floor(p/q) == (p-1)/q */
mp_int *h = mp_from_integer(1);
int progress = 0;
mp_int *g;
while (1) {
pfn(pfnparam, PROGFN_PROGRESS, 3, ++progress);
g = mp_modpow(h, power, p);
if (mp_hs_integer(g, 2))
break; /* got one */
mp_free(g);
mp_add_integer_into(h, h, 1);
}
mp_free(h);
mp_free(power);
/*
* Now we're nearly done. All we need now is our private key x,
* which should be a number between 1 and q-1 exclusive, and
* our public key y = g^x mod p.
*/
mp_int *two = mp_from_integer(2);
mp_int *qm1 = mp_copy(q);
mp_sub_integer_into(qm1, qm1, 1);
mp_int *x = mp_random_in_range(two, qm1);
mp_free(two);
mp_free(qm1);
key->sshk.vt = &ssh_dss;
key->p = p;
key->q = q;
key->g = g;
key->x = x;
key->y = mp_modpow(key->g, key->x, key->p);
return 1;
}