1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#![warn(missing_docs, unused_imports)]

//! _This is a part of **scicrypt**. For more information, head to the
//! [scicrypt](https://crates.io/crates/scicrypt) crate homepage._
//!
//! Number theoretic functions, particularly suited for cryptography. Functions include extremely
//! fast (safe) prime generation.

mod primes;

use crate::primes::FIRST_PRIMES;
use scicrypt_bigint::UnsignedInteger;
use scicrypt_traits::randomness::GeneralRng;
use scicrypt_traits::randomness::SecureRng;

/// Generates a uniformly random prime number of a given bit length. So, the number contains
/// `bit_length` bits, of which the first and the last bit are always 1.
pub fn gen_prime<R: SecureRng>(bit_length: u32, rng: &mut GeneralRng<R>) -> UnsignedInteger {
    'outer: loop {
        let mut candidate = UnsignedInteger::random(bit_length, rng);
        candidate.set_bit_leaky(bit_length - 1);
        candidate.set_bit_leaky(0);

        // A heuristic that closely follows OpenSSL (https://github.com/openssl/openssl/blob/4cedf30e995f9789cf6bb103e248d33285a84067/crypto/bn/bn_prime.c)
        let prime_count: usize = bit_length as usize / 3;
        let mods: Vec<u64> = FIRST_PRIMES[..prime_count]
            .iter()
            .map(|p| candidate.mod_u_leaky(*p))
            .collect();

        let mut delta = 0;
        let max_delta = u64::MAX - FIRST_PRIMES.last().unwrap();
        candidate += &'sieve: loop {
            for i in 1..prime_count {
                if (mods[i] + delta) % FIRST_PRIMES[i] == 0 {
                    // For candidate x and prime p, if x % p = 0 then x is not prime
                    // So, we go to the next odd number and try again
                    delta += 2;

                    if delta > max_delta {
                        continue 'outer;
                    }

                    continue 'sieve;
                }
            }

            // If we have passed all prime_count first primes, then we are fairly certain this is a prime!
            break UnsignedInteger::from(delta);
        };

        // Ensure that we have a prime with a stronger primality test
        if candidate.is_probably_prime_leaky() {
            return candidate;
        }
    }
}

/// Generates a uniformly random *safe* prime number of a given bit length. This is a prime $p$ of
/// the form $p = 2q + 1$, where $q$ is a smaller prime.
pub fn gen_safe_prime<R: SecureRng>(bit_length: u32, rng: &mut GeneralRng<R>) -> UnsignedInteger {
    'outer: loop {
        let mut candidate = UnsignedInteger::random(bit_length, rng);
        candidate.set_bit_leaky(bit_length - 1);
        candidate.set_bit_leaky(0);

        // A heuristic that closely follows OpenSSL (https://github.com/openssl/openssl/blob/4cedf30e995f9789cf6bb103e248d33285a84067/crypto/bn/bn_prime.c)
        let prime_count: usize = bit_length as usize / 3;
        let mods: Vec<u64> = FIRST_PRIMES[..prime_count]
            .iter()
            .map(|p| candidate.mod_u_leaky(*p))
            .collect();

        let mut delta = 0;
        let max_delta = u64::MAX - FIRST_PRIMES[prime_count - 1];
        candidate += &'sieve: loop {
            for i in 1..prime_count {
                if (mods[i] + delta) % FIRST_PRIMES[i] <= 1 {
                    // For candidate x and prime p, if x % p = 0 then x is not prime
                    // So, we go to the next odd number and try again
                    delta += 4;

                    if delta > max_delta {
                        continue 'outer;
                    }

                    continue 'sieve;
                }
            }

            // If we have passed all prime_count first primes, then we are fairly certain this is a prime!
            break UnsignedInteger::from(delta);
        };

        // Ensure that we have a prime with a stronger primality test
        if candidate.is_probably_prime_leaky() {
            // Ensure that p for 2p = 1 is also a prime with the stronger primality test
            let candidate_reduced = &candidate >> 1;
            if candidate_reduced.is_probably_prime_leaky() {
                return candidate;
            }
        }
    }
}

/// Generates a uniformly random RSA modulus, which is the product of two safe primes $p$ and $q$.
/// This method returns both the modulus and $\lambda$, which is the least common multiple of
/// $p - 1$ and $q - 1$.
pub fn gen_rsa_modulus<R: SecureRng>(
    bit_length: u32,
    rng: &mut GeneralRng<R>,
) -> (UnsignedInteger, UnsignedInteger) {
    let mut p = gen_safe_prime(bit_length / 2, rng);
    let mut q = gen_safe_prime(bit_length / 2, rng);

    let n = &p * &q;

    p.clear_bit_leaky(0);
    q.clear_bit_leaky(0);

    let lambda = p.lcm_leaky(&q);

    (n, lambda)
}

#[cfg(test)]
mod tests {
    use crate::{gen_prime, gen_safe_prime};
    use rand_core::OsRng;
    use scicrypt_bigint::UnsignedInteger;
    use scicrypt_traits::randomness::GeneralRng;

    fn assert_primality_100_000_factors(integer: &UnsignedInteger) {
        let (_, hi) = primal::estimate_nth_prime(100_000);
        for prime in primal::Sieve::new(hi as usize).primes_from(0) {
            assert!(
                integer.mod_u_leaky(prime as u64) != 0,
                "{} is divisible by {}",
                integer,
                prime
            );
        }
    }

    #[test]
    fn test_gen_prime_for_factors() {
        let mut rng = GeneralRng::new(OsRng);
        let generated_prime = gen_prime(256, &mut rng);

        assert_primality_100_000_factors(&generated_prime);
    }

    #[test]
    fn test_gen_safe_prime_for_factors() {
        let mut rng = GeneralRng::new(OsRng);
        let generated_prime = gen_safe_prime(256, &mut rng);

        assert_primality_100_000_factors(&generated_prime);

        let sophie_germain_prime = &generated_prime >> 1;

        assert_primality_100_000_factors(&sophie_germain_prime);
    }
}