Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
Move invent_firstbits() into sshrsag.c.
Browse files Browse the repository at this point in the history
It's now a subroutine specific to RSA key generation, because the
reworked PrimeCandidateSource system can handle the requirements of
DSA generation automatically.

The difference is that in DSA, one of the primes you generate is used
as a factor in the generation of the other, so you can just pass q as
a factor to pcs_require_residue_1, and it can get the range right by
itself. But in RSA, neither prime is generated with the other one in
mind; they're conceptually generated separately and independently,
apart from that single joint restriction on their product.

(I _could_ have added a feature to PrimeCandidateSource to specify a
range for the prime more specifically than a few initial bits. But I
didn't want to, because it would have been more complicated than doing
it this way, and also slightly less good: if you invent one prime
first and then constrain the range of the other one once you know the
first, then you're not getting an even probability distribution of the
possible _pairs_ of primes - you're privileging one over the other and
skewing the distribution.)
  • Loading branch information
sgtatham committed Feb 23, 2020
1 parent da3bc3d commit 13f594f
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 112 deletions.
1 change: 0 additions & 1 deletion ssh.h
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,6 @@ int eddsa_generate(struct eddsa_key *key, int bits, progfn_t pfn,
mp_int *primegen(
int bits, int modulus, int residue, mp_int *factor,
int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation);

/*
* Connection-sharing API provided by platforms. This function must
Expand Down
111 changes: 0 additions & 111 deletions sshprime.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,114 +239,3 @@ mp_int *primegen(
*/
return p;
}

/*
* Invent a pair of values suitable for use as 'firstbits' in the
* above function, such that their product is at least 2, and such
* that their difference is also at least min_separation.
*
* 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!'
*/
static inline unsigned firstbits_b_min(
unsigned a, unsigned lo, unsigned hi, unsigned min_separation)
{
/* To get a large enough product, b must be at least this much */
unsigned b_min = (2*lo*lo + a - 1) / a;
/* Now enforce a<b, optionally with minimum separation */
if (b_min < a + min_separation)
b_min = a + min_separation;
/* And cap at the upper limit */
if (b_min > hi)
b_min = hi;
return b_min;
}

void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation)
{
/*
* We'll pick 12 initial bits (number selected at random) for each
* prime, not counting the leading 1. So we want to return two
* values in the range [2^12,2^13) whose product is at least 2^25.
*
* Strategy: count up all the viable pairs, then select a random
* number in that range and use it to pick a pair.
*
* To keep things simple, we'll ensure a < b, and randomly swap
* them at the end.
*/
const unsigned lo = 1<<12, hi = 1<<13, minproduct = 2*lo*lo;
unsigned a, b;

/*
* Count up the number of prefixes of b that would be valid for
* each prefix of a.
*/
mp_int *total = mp_new(32);
for (a = lo; a < hi; a++) {
unsigned b_min = firstbits_b_min(a, lo, hi, min_separation);
mp_add_integer_into(total, total, hi - b_min);
}

/*
* Make up a random number in the range [0,2*total).
*/
mp_int *mlo = mp_from_integer(0), *mhi = mp_new(32);
mp_lshift_fixed_into(mhi, total, 1);
mp_int *randval = mp_random_in_range(mlo, mhi);
mp_free(mlo);
mp_free(mhi);

/*
* Use the low bit of randval as our swap indicator, leaving the
* rest of it in the range [0,total).
*/
unsigned swap = mp_get_bit(randval, 0);
mp_rshift_fixed_into(randval, randval, 1);

/*
* Now do the same counting loop again to make the actual choice.
*/
a = b = 0;
for (unsigned a_candidate = lo; a_candidate < hi; a_candidate++) {
unsigned b_min = firstbits_b_min(a_candidate, lo, hi, min_separation);
unsigned limit = hi - b_min;

unsigned b_candidate = b_min + mp_get_integer(randval);
unsigned use_it = 1 ^ mp_hs_integer(randval, limit);
a ^= (a ^ a_candidate) & -use_it;
b ^= (b ^ b_candidate) & -use_it;

mp_sub_integer_into(randval, randval, limit);
}

mp_free(randval);
mp_free(total);

/*
* Check everything came out right.
*/
assert(lo <= a);
assert(a < hi);
assert(lo <= b);
assert(b < hi);
assert(a * b >= minproduct);
assert(b >= a + min_separation);

/*
* Last-minute optional swap of a and b.
*/
unsigned diff = (a ^ b) & (-swap);
a ^= diff;
b ^= diff;

*one = a;
*two = b;
}
115 changes: 115 additions & 0 deletions sshrsag.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

#define RSA_EXPONENT 37 /* we like this prime */

static void invent_firstbits(unsigned *one, unsigned *two,
unsigned min_separation);

int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
void *pfnparam)
{
Expand Down Expand Up @@ -129,3 +132,115 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn,

return 1;
}

/*
* Invent a pair of values suitable for use as the 'firstbits' values
* for the two RSA primes, such that their product is at least 2, and
* such that their difference is also at least min_separation.
*
* This is used for generating RSA 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!'
*/
static inline unsigned firstbits_b_min(
unsigned a, unsigned lo, unsigned hi, unsigned min_separation)
{
/* To get a large enough product, b must be at least this much */
unsigned b_min = (2*lo*lo + a - 1) / a;
/* Now enforce a<b, optionally with minimum separation */
if (b_min < a + min_separation)
b_min = a + min_separation;
/* And cap at the upper limit */
if (b_min > hi)
b_min = hi;
return b_min;
}

static void invent_firstbits(unsigned *one, unsigned *two,
unsigned min_separation)
{
/*
* We'll pick 12 initial bits (number selected at random) for each
* prime, not counting the leading 1. So we want to return two
* values in the range [2^12,2^13) whose product is at least 2^25.
*
* Strategy: count up all the viable pairs, then select a random
* number in that range and use it to pick a pair.
*
* To keep things simple, we'll ensure a < b, and randomly swap
* them at the end.
*/
const unsigned lo = 1<<12, hi = 1<<13, minproduct = 2*lo*lo;
unsigned a, b;

/*
* Count up the number of prefixes of b that would be valid for
* each prefix of a.
*/
mp_int *total = mp_new(32);
for (a = lo; a < hi; a++) {
unsigned b_min = firstbits_b_min(a, lo, hi, min_separation);
mp_add_integer_into(total, total, hi - b_min);
}

/*
* Make up a random number in the range [0,2*total).
*/
mp_int *mlo = mp_from_integer(0), *mhi = mp_new(32);
mp_lshift_fixed_into(mhi, total, 1);
mp_int *randval = mp_random_in_range(mlo, mhi);
mp_free(mlo);
mp_free(mhi);

/*
* Use the low bit of randval as our swap indicator, leaving the
* rest of it in the range [0,total).
*/
unsigned swap = mp_get_bit(randval, 0);
mp_rshift_fixed_into(randval, randval, 1);

/*
* Now do the same counting loop again to make the actual choice.
*/
a = b = 0;
for (unsigned a_candidate = lo; a_candidate < hi; a_candidate++) {
unsigned b_min = firstbits_b_min(a_candidate, lo, hi, min_separation);
unsigned limit = hi - b_min;

unsigned b_candidate = b_min + mp_get_integer(randval);
unsigned use_it = 1 ^ mp_hs_integer(randval, limit);
a ^= (a ^ a_candidate) & -use_it;
b ^= (b ^ b_candidate) & -use_it;

mp_sub_integer_into(randval, randval, limit);
}

mp_free(randval);
mp_free(total);

/*
* Check everything came out right.
*/
assert(lo <= a);
assert(a < hi);
assert(lo <= b);
assert(b < hi);
assert(a * b >= minproduct);
assert(b >= a + min_separation);

/*
* Last-minute optional swap of a and b.
*/
unsigned diff = (a ^ b) & (-swap);
a ^= diff;
b ^= diff;

*one = a;
*two = b;
}

0 comments on commit 13f594f

Please sign in to comment.