Skip to main content

rsa/algorithms/
generate.rs

1//! Generate prime components for the RSA Private Key
2
3use alloc::vec::Vec;
4use crypto_bigint::{BoxedUint, Odd};
5use crypto_primes::{
6    hazmat::{SetBits, SmallFactorsSieveFactory},
7    is_prime, sieve_and_find, Flavor,
8};
9use rand_core::CryptoRng;
10
11use crate::{
12    algorithms::rsa::{compute_modulus, compute_private_exponent_euler_totient},
13    errors::{Error, Result},
14};
15
16pub struct RsaPrivateKeyComponents {
17    pub n: Odd<BoxedUint>,
18    pub e: BoxedUint,
19    pub d: BoxedUint,
20    pub primes: Vec<BoxedUint>,
21}
22
23/// Generates a multi-prime RSA keypair of the given bit size, public exponent,
24/// and the given random source, as suggested in [1]. Although the public
25/// keys are compatible (actually, indistinguishable) from the 2-prime case,
26/// the private keys are not. Thus it may not be possible to export multi-prime
27/// private keys in certain formats or to subsequently import them into other
28/// code.
29///
30/// Table 1 in [2] suggests maximum numbers of primes for a given size.
31///
32/// [1]: https://patents.google.com/patent/US4405829A/en
33/// [2]: http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
34pub(crate) fn generate_multi_prime_key_with_exp<R: CryptoRng + ?Sized>(
35    rng: &mut R,
36    nprimes: usize,
37    bit_size: usize,
38    exp: BoxedUint,
39) -> Result<RsaPrivateKeyComponents> {
40    if nprimes < 2 {
41        return Err(Error::NprimesTooSmall);
42    }
43
44    if bit_size < 64 {
45        let prime_limit = (1u64 << (bit_size / nprimes) as u64) as f64;
46
47        // pi approximates the number of primes less than prime_limit
48
49        // Calculate `log(prime_limit)` as `log(x) = log2(x) / log2(e) = log2(x) * log(2)`.
50        let mut pi = prime_limit / ((bit_size / nprimes) as f64 * core::f64::consts::LN_2 - 1.);
51
52        // Generated primes start with 0b11, so we can only use a quarter of them.
53        pi /= 4f64;
54        // Use a factor of two to ensure that key generation terminates in a
55        // reasonable amount of time.
56        pi /= 2f64;
57
58        if pi < nprimes as f64 {
59            return Err(Error::TooFewPrimes);
60        }
61    }
62
63    let mut primes = vec![BoxedUint::zero(); nprimes];
64    let n_final: Odd<BoxedUint>;
65    let d_final: BoxedUint;
66
67    'next: loop {
68        let mut todo = bit_size;
69        // `generate_prime_with_rng` should set the top two bits in each prime.
70        // Thus each prime has the form
71        //   p_i = 2^bitlen(p_i) × 0.11... (in base 2).
72        // And the product is:
73        //   P = 2^todo × α
74        // where α is the product of nprimes numbers of the form 0.11...
75        //
76        // If α < 1/2 (which can happen for nprimes > 2), we need to
77        // shift todo to compensate for lost bits: the mean value of 0.11...
78        // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
79        // will give good results.
80        if nprimes >= 7 {
81            todo += (nprimes - 2) / 5;
82        }
83
84        for (i, prime) in primes.iter_mut().enumerate() {
85            let bits = (todo / (nprimes - i)) as u32;
86            *prime = generate_prime_with_rng(rng, bits);
87            todo -= prime.bits() as usize;
88        }
89
90        // Makes sure that primes is pairwise unequal.
91        for (i, prime1) in primes.iter().enumerate() {
92            for prime2 in primes.iter().take(i) {
93                if prime1 == prime2 {
94                    continue 'next;
95                }
96            }
97        }
98
99        let n = compute_modulus(&primes);
100
101        if n.bits() as usize != bit_size {
102            // This should never happen for nprimes == 2 because
103            // generate_prime_with_rng should set the top two bits in each prime.
104            // For nprimes > 2 we hope it does not happen often.
105            continue 'next;
106        }
107
108        if let Ok(d) = compute_private_exponent_euler_totient(&primes, &exp) {
109            n_final = n;
110            d_final = d;
111            break;
112        }
113    }
114
115    Ok(RsaPrivateKeyComponents {
116        n: n_final,
117        e: exp,
118        d: d_final,
119        primes,
120    })
121}
122
123fn generate_prime_with_rng<R: CryptoRng + ?Sized>(rng: &mut R, bit_length: u32) -> BoxedUint {
124    let factory = SmallFactorsSieveFactory::new(Flavor::Any, bit_length, SetBits::TwoMsb)
125        .unwrap_or_else(|err| panic!("Error creating the sieve: {err}"));
126
127    sieve_and_find(rng, factory, |_rng, candidate| {
128        is_prime(Flavor::Any, candidate)
129    })
130    .unwrap_or_else(|err| panic!("Error generating random candidates: {}", err))
131    .expect("will produce a result eventually")
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use rand::rngs::ChaCha8Rng;
138    use rand_core::SeedableRng;
139
140    const EXP: u64 = 65537;
141
142    #[test]
143    fn test_impossible_keys() {
144        let mut rng = ChaCha8Rng::from_seed([42; 32]);
145        let exp = BoxedUint::from(EXP);
146
147        for i in 0..32 {
148            let _ = generate_multi_prime_key_with_exp(&mut rng, 2, i, exp.clone());
149            let _ = generate_multi_prime_key_with_exp(&mut rng, 3, i, exp.clone());
150            let _ = generate_multi_prime_key_with_exp(&mut rng, 4, i, exp.clone());
151            let _ = generate_multi_prime_key_with_exp(&mut rng, 5, i, exp.clone());
152        }
153    }
154
155    macro_rules! key_generation {
156        ($name:ident, $multi:expr, $size:expr) => {
157            #[test]
158            fn $name() {
159                let mut rng = ChaCha8Rng::from_seed([42; 32]);
160                let exp = BoxedUint::from(EXP);
161                for _ in 0..10 {
162                    let components =
163                        generate_multi_prime_key_with_exp(&mut rng, $multi, $size, exp.clone())
164                            .unwrap();
165                    assert_eq!(components.n.bits(), $size);
166                    assert_eq!(components.primes.len(), $multi);
167                }
168            }
169        };
170    }
171
172    key_generation!(key_generation_128, 2, 128);
173    key_generation!(key_generation_1024, 2, 1024);
174
175    key_generation!(key_generation_multi_3_256, 3, 256);
176
177    key_generation!(key_generation_multi_4_64, 4, 64);
178
179    key_generation!(key_generation_multi_5_64, 5, 64);
180    key_generation!(key_generation_multi_8_576, 8, 576);
181    // TODO: reenable, currently slow
182    // key_generation!(key_generation_multi_16_1024, 16, 1024);
183}