scicrypt_numbertheory/
lib.rs1#![warn(missing_docs, unused_imports)]
2
3mod primes;
10
11use crate::primes::FIRST_PRIMES;
12use scicrypt_bigint::UnsignedInteger;
13use scicrypt_traits::randomness::GeneralRng;
14use scicrypt_traits::randomness::SecureRng;
15
16pub fn gen_prime<R: SecureRng>(bit_length: u32, rng: &mut GeneralRng<R>) -> UnsignedInteger {
19 'outer: loop {
20 let mut candidate = UnsignedInteger::random(bit_length, rng);
21 candidate.set_bit_leaky(bit_length - 1);
22 candidate.set_bit_leaky(0);
23
24 let prime_count: usize = bit_length as usize / 3;
26 let mods: Vec<u64> = FIRST_PRIMES[..prime_count]
27 .iter()
28 .map(|p| candidate.mod_u_leaky(*p))
29 .collect();
30
31 let mut delta = 0;
32 let max_delta = u64::MAX - FIRST_PRIMES.last().unwrap();
33 candidate += &'sieve: loop {
34 for i in 1..prime_count {
35 if (mods[i] + delta) % FIRST_PRIMES[i] == 0 {
36 delta += 2;
39
40 if delta > max_delta {
41 continue 'outer;
42 }
43
44 continue 'sieve;
45 }
46 }
47
48 break UnsignedInteger::from(delta);
50 };
51
52 if candidate.is_probably_prime_leaky() {
54 return candidate;
55 }
56 }
57}
58
59pub fn gen_safe_prime<R: SecureRng>(bit_length: u32, rng: &mut GeneralRng<R>) -> UnsignedInteger {
62 'outer: loop {
63 let mut candidate = UnsignedInteger::random(bit_length, rng);
64 candidate.set_bit_leaky(bit_length - 1);
65 candidate.set_bit_leaky(0);
66
67 let prime_count: usize = bit_length as usize / 3;
69 let mods: Vec<u64> = FIRST_PRIMES[..prime_count]
70 .iter()
71 .map(|p| candidate.mod_u_leaky(*p))
72 .collect();
73
74 let mut delta = 0;
75 let max_delta = u64::MAX - FIRST_PRIMES[prime_count - 1];
76 candidate += &'sieve: loop {
77 for i in 1..prime_count {
78 if (mods[i] + delta) % FIRST_PRIMES[i] <= 1 {
79 delta += 4;
82
83 if delta > max_delta {
84 continue 'outer;
85 }
86
87 continue 'sieve;
88 }
89 }
90
91 break UnsignedInteger::from(delta);
93 };
94
95 if candidate.is_probably_prime_leaky() {
97 let candidate_reduced = &candidate >> 1;
99 if candidate_reduced.is_probably_prime_leaky() {
100 return candidate;
101 }
102 }
103 }
104}
105
106pub fn gen_rsa_modulus<R: SecureRng>(
110 bit_length: u32,
111 rng: &mut GeneralRng<R>,
112) -> (UnsignedInteger, UnsignedInteger) {
113 let mut p = gen_safe_prime(bit_length / 2, rng);
114 let mut q = gen_safe_prime(bit_length / 2, rng);
115
116 let n = &p * &q;
117
118 p.clear_bit_leaky(0);
119 q.clear_bit_leaky(0);
120
121 let lambda = p.lcm_leaky(&q);
122
123 (n, lambda)
124}
125
126#[cfg(test)]
127mod tests {
128 use crate::{gen_prime, gen_safe_prime};
129 use rand_core::OsRng;
130 use scicrypt_bigint::UnsignedInteger;
131 use scicrypt_traits::randomness::GeneralRng;
132
133 fn assert_primality_100_000_factors(integer: &UnsignedInteger) {
134 let (_, hi) = primal::estimate_nth_prime(100_000);
135 for prime in primal::Sieve::new(hi as usize).primes_from(0) {
136 assert!(
137 integer.mod_u_leaky(prime as u64) != 0,
138 "{} is divisible by {}",
139 integer,
140 prime
141 );
142 }
143 }
144
145 #[test]
146 fn test_gen_prime_for_factors() {
147 let mut rng = GeneralRng::new(OsRng);
148 let generated_prime = gen_prime(256, &mut rng);
149
150 assert_primality_100_000_factors(&generated_prime);
151 }
152
153 #[test]
154 fn test_gen_safe_prime_for_factors() {
155 let mut rng = GeneralRng::new(OsRng);
156 let generated_prime = gen_safe_prime(256, &mut rng);
157
158 assert_primality_100_000_factors(&generated_prime);
159
160 let sophie_germain_prime = &generated_prime >> 1;
161
162 assert_primality_100_000_factors(&sophie_germain_prime);
163 }
164}