|
| 1 | +// Based on the TheAlgorithms/Python |
| 2 | +// RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for |
| 3 | +// Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526 |
| 4 | + |
| 5 | +use lazy_static; |
| 6 | +use num_bigint::BigUint; |
| 7 | +use num_traits::{Num, Zero}; |
| 8 | +use std::{ |
| 9 | + collections::HashMap, |
| 10 | + time::{SystemTime, UNIX_EPOCH}, |
| 11 | +}; |
| 12 | + |
| 13 | +// Using lazy static to initialize statics that require code to be executed at runtime. |
| 14 | +lazy_static! { |
| 15 | + // A map of predefined prime numbers for different bit lengths, as specified in RFC 3526 |
| 16 | + static ref PRIMES: HashMap<u8, BigUint> = { |
| 17 | + let mut m:HashMap<u8, BigUint> = HashMap::new(); |
| 18 | + m.insert( |
| 19 | + // 1536-bit |
| 20 | + 5, |
| 21 | + BigUint::parse_bytes( |
| 22 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 23 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 24 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 25 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 26 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 27 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 28 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 29 | + 670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", |
| 30 | + 16 |
| 31 | + ).unwrap() |
| 32 | + ); |
| 33 | + m.insert( |
| 34 | + // 2048-bit |
| 35 | + 14, |
| 36 | + BigUint::parse_bytes( |
| 37 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 38 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 39 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 40 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 41 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 42 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 43 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 44 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\ |
| 45 | + E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\ |
| 46 | + DE2BCBF6955817183995497CEA956AE515D2261898FA0510\ |
| 47 | + 15728E5A8AACAA68FFFFFFFFFFFFFFFF", |
| 48 | + 16 |
| 49 | + ).unwrap() |
| 50 | + ); |
| 51 | + |
| 52 | + m.insert( |
| 53 | + // 3072-bit |
| 54 | + 15, |
| 55 | + BigUint::parse_bytes( |
| 56 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 57 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 58 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 59 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 60 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 61 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 62 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 63 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\ |
| 64 | + E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\ |
| 65 | + DE2BCBF6955817183995497CEA956AE515D2261898FA0510\ |
| 66 | + 15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\ |
| 67 | + ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\ |
| 68 | + ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\ |
| 69 | + F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ |
| 70 | + BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\ |
| 71 | + 43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", |
| 72 | + 16 |
| 73 | + ).unwrap() |
| 74 | + ); |
| 75 | + m.insert( |
| 76 | + // 4096-bit |
| 77 | + 16, |
| 78 | + BigUint::parse_bytes( |
| 79 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 80 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 81 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 82 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 83 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 84 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 85 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 86 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\ |
| 87 | + E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\ |
| 88 | + DE2BCBF6955817183995497CEA956AE515D2261898FA0510\ |
| 89 | + 15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\ |
| 90 | + ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\ |
| 91 | + ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\ |
| 92 | + F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ |
| 93 | + BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\ |
| 94 | + 43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7\ |
| 95 | + 88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA\ |
| 96 | + 2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6\ |
| 97 | + 287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED\ |
| 98 | + 1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9\ |
| 99 | + 93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ |
| 100 | + FFFFFFFFFFFFFFFF", |
| 101 | + 16 |
| 102 | + ).unwrap() |
| 103 | + ); |
| 104 | + m.insert( |
| 105 | + // 6144-bit |
| 106 | + 17, |
| 107 | + BigUint::parse_bytes( |
| 108 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ |
| 109 | + 8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ |
| 110 | + 302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ |
| 111 | + A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ |
| 112 | + 49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ |
| 113 | + FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 114 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ |
| 115 | + 180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ |
| 116 | + 3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ |
| 117 | + 04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ |
| 118 | + B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ |
| 119 | + 1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ |
| 120 | + BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ |
| 121 | + E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ |
| 122 | + 99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ |
| 123 | + 04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ |
| 124 | + 233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ |
| 125 | + D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ |
| 126 | + 36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ |
| 127 | + AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ |
| 128 | + DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ |
| 129 | + 2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ |
| 130 | + F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ |
| 131 | + BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ |
| 132 | + CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ |
| 133 | + B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ |
| 134 | + 387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ |
| 135 | + 6DCC4024FFFFFFFFFFFFFFFF", |
| 136 | + 16 |
| 137 | + ).unwrap() |
| 138 | + ); |
| 139 | + |
| 140 | + m.insert( |
| 141 | + // 8192-bit |
| 142 | + 18, |
| 143 | + BigUint::parse_bytes( |
| 144 | + b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 145 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 146 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 147 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 148 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 149 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 150 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 151 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\ |
| 152 | + E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\ |
| 153 | + DE2BCBF6955817183995497CEA956AE515D2261898FA0510\ |
| 154 | + 15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\ |
| 155 | + ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\ |
| 156 | + ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\ |
| 157 | + F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ |
| 158 | + BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\ |
| 159 | + 43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7\ |
| 160 | + 88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA\ |
| 161 | + 2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6\ |
| 162 | + 287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED\ |
| 163 | + 1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9\ |
| 164 | + 93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ |
| 165 | + 36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD\ |
| 166 | + F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831\ |
| 167 | + 179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B\ |
| 168 | + DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF\ |
| 169 | + 5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6\ |
| 170 | + D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3\ |
| 171 | + 23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ |
| 172 | + CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328\ |
| 173 | + 06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C\ |
| 174 | + DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE\ |
| 175 | + 12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4\ |
| 176 | + 38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300\ |
| 177 | + 741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568\ |
| 178 | + 3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ |
| 179 | + 22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B\ |
| 180 | + 4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A\ |
| 181 | + 062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36\ |
| 182 | + 4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1\ |
| 183 | + B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92\ |
| 184 | + 4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47\ |
| 185 | + 9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ |
| 186 | + 60C980DD98EDD3DFFFFFFFFFFFFFFFFF", |
| 187 | + 16 |
| 188 | + ).unwrap() |
| 189 | + ); |
| 190 | + m |
| 191 | + }; |
| 192 | +} |
| 193 | + |
| 194 | +/// Generating random number, should use num_bigint::RandomBits if possible. |
| 195 | +fn rand() -> usize { |
| 196 | + SystemTime::now() |
| 197 | + .duration_since(UNIX_EPOCH) |
| 198 | + .unwrap() |
| 199 | + .subsec_nanos() as usize |
| 200 | +} |
| 201 | + |
| 202 | +pub struct DiffieHellman { |
| 203 | + prime: BigUint, |
| 204 | + private_key: BigUint, |
| 205 | + public_key: BigUint, |
| 206 | + generator: u8, |
| 207 | +} |
| 208 | + |
| 209 | +impl DiffieHellman { |
| 210 | + // Diffie-Hellman key exchange algorithm is based on the following mathematical concepts: |
| 211 | + |
| 212 | + // - A large prime number p (known as the prime modulus) is chosen and shared by both parties. |
| 213 | + |
| 214 | + // - A base number g (known as the generator) is chosen and shared by both parties. |
| 215 | + |
| 216 | + // - Each party generates a private key a or b (which are secret and only known to that party) and calculates a corresponding public key A or B using the following formulas: |
| 217 | + // - A = g^a mod p |
| 218 | + // - B = g^b mod p |
| 219 | + |
| 220 | + // - Each party then exchanges their public keys with each other. |
| 221 | + |
| 222 | + // - Each party then calculates the shared secret key s using the following formula: |
| 223 | + // - s = B^a mod p or s = A^b mod p |
| 224 | + |
| 225 | + // Both parties now have the same shared secret key s which can be used for encryption or authentication. |
| 226 | + |
| 227 | + pub fn new(group: Option<u8>) -> Self { |
| 228 | + let mut _group: u8 = 14; |
| 229 | + if let Some(x) = group { |
| 230 | + _group = x; |
| 231 | + } |
| 232 | + |
| 233 | + if !PRIMES.contains_key(&_group) { |
| 234 | + panic!("group not in primes") |
| 235 | + } |
| 236 | + |
| 237 | + // generate private key |
| 238 | + let private_key: BigUint = BigUint::from(rand()); |
| 239 | + |
| 240 | + Self { |
| 241 | + prime: PRIMES[&_group].clone(), |
| 242 | + private_key, |
| 243 | + generator: 2, // the generator is 2 for all the primes if this would not be the case it can be added to hashmap |
| 244 | + public_key: BigUint::default(), |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + /// get private key as hexadecimal String |
| 249 | + pub fn get_private_key(&self) -> String { |
| 250 | + self.private_key.to_str_radix(16) |
| 251 | + } |
| 252 | + |
| 253 | + /// Generate public key A = g**a mod p |
| 254 | + pub fn generate_public_key(&mut self) -> String { |
| 255 | + self.public_key = BigUint::from(self.generator).modpow(&self.private_key, &self.prime); |
| 256 | + self.public_key.to_str_radix(16) |
| 257 | + } |
| 258 | + |
| 259 | + pub fn is_valid_public_key(&self, key_str: &str) -> bool { |
| 260 | + // the unwrap_or_else will make sure it is false, because 2 <= 0 and therefor False is returned |
| 261 | + let key = BigUint::from_str_radix(key_str, 16) |
| 262 | + .unwrap_or_else(|_| BigUint::parse_bytes(b"0", 16).unwrap()); |
| 263 | + |
| 264 | + // Check if the other public key is valid based on NIST SP800-56 |
| 265 | + if BigUint::from(2_u8) <= key |
| 266 | + && key <= &self.prime - BigUint::from(2_u8) |
| 267 | + && !key |
| 268 | + .modpow( |
| 269 | + &((&self.prime - BigUint::from(1_u8)) / BigUint::from(2_u8)), |
| 270 | + &self.prime, |
| 271 | + ) |
| 272 | + .is_zero() |
| 273 | + { |
| 274 | + return true; |
| 275 | + } |
| 276 | + false |
| 277 | + } |
| 278 | + |
| 279 | + /// Generate the shared key |
| 280 | + pub fn generate_shared_key(self, other_key_str: &str) -> Option<String> { |
| 281 | + let other_key = BigUint::from_str_radix(other_key_str, 16) |
| 282 | + .unwrap_or_else(|_| BigUint::parse_bytes(b"0", 16).unwrap()); |
| 283 | + if !self.is_valid_public_key(&other_key.to_str_radix(16)) { |
| 284 | + return None; |
| 285 | + } |
| 286 | + |
| 287 | + let shared_key = other_key.modpow(&self.private_key, &self.prime); |
| 288 | + Some(shared_key.to_str_radix(16)) |
| 289 | + } |
| 290 | +} |
| 291 | + |
| 292 | +#[cfg(test)] |
| 293 | +mod tests { |
| 294 | + use super::*; |
| 295 | + |
| 296 | + #[test] |
| 297 | + fn verify_invalid_pub_key() { |
| 298 | + let diffie = DiffieHellman::new(Some(14)); |
| 299 | + assert_eq!(diffie.is_valid_public_key("0000"), false); |
| 300 | + } |
| 301 | + |
| 302 | + #[test] |
| 303 | + fn verify_valid_pub_key() { |
| 304 | + let diffie = DiffieHellman::new(Some(14)); |
| 305 | + assert_eq!( |
| 306 | + diffie.is_valid_public_key("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"), |
| 307 | + true |
| 308 | + ); |
| 309 | + } |
| 310 | + |
| 311 | + #[test] |
| 312 | + fn verify_invalid_pub_key_same_as_prime() { |
| 313 | + let diffie = DiffieHellman::new(Some(14)); |
| 314 | + assert_eq!( |
| 315 | + diffie.is_valid_public_key( |
| 316 | + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\ |
| 317 | + 29024E088A67CC74020BBEA63B139B22514A08798E3404DD\ |
| 318 | + EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\ |
| 319 | + E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\ |
| 320 | + EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\ |
| 321 | + C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\ |
| 322 | + 83655D23DCA3AD961C62F356208552BB9ED529077096966D\ |
| 323 | + 670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\ |
| 324 | + E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\ |
| 325 | + DE2BCBF6955817183995497CEA956AE515D2261898FA0510\ |
| 326 | + 15728E5A8AACAA68FFFFFFFFFFFFFFFF" |
| 327 | + ), |
| 328 | + false |
| 329 | + ); |
| 330 | + } |
| 331 | + |
| 332 | + #[test] |
| 333 | + fn verify_key_exchange() { |
| 334 | + let mut alice = DiffieHellman::new(Some(16)); |
| 335 | + let mut bob = DiffieHellman::new(Some(16)); |
| 336 | + |
| 337 | + // Private key not used, showed for illustrative purpose |
| 338 | + let _alice_private = alice.get_private_key(); |
| 339 | + let alice_public = alice.generate_public_key(); |
| 340 | + |
| 341 | + // Private key not used, showed for illustrative purpose |
| 342 | + let _bob_private = bob.get_private_key(); |
| 343 | + let bob_public = bob.generate_public_key(); |
| 344 | + |
| 345 | + // generating shared key using the struct implemenations |
| 346 | + let alice_shared = alice.generate_shared_key(bob_public.as_str()).unwrap(); |
| 347 | + let bob_shared = bob.generate_shared_key(alice_public.as_str()).unwrap(); |
| 348 | + assert_eq!(alice_shared, bob_shared); |
| 349 | + } |
| 350 | +} |
0 commit comments