message.rs (12505B)
1 use crate::error::RadrootsSimplexSmpCryptoError; 2 use alloc::vec; 3 use alloc::vec::Vec; 4 use getrandom::getrandom; 5 use hkdf::Hkdf; 6 use poly1305::Poly1305; 7 use poly1305::universal_hash::KeyInit as Poly1305KeyInit; 8 use salsa20::cipher::consts::U10; 9 use salsa20::cipher::{KeyIvInit, StreamCipher}; 10 use salsa20::{Salsa20, hsalsa}; 11 use sha2::{Digest, Sha256, Sha512}; 12 use subtle::ConstantTimeEq; 13 use x25519_dalek::{PublicKey, StaticSecret}; 14 15 pub const RADROOTS_SIMPLEX_SMP_NONCE_LENGTH: usize = 24; 16 pub const RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH: usize = 32; 17 const RADROOTS_SIMPLEX_SMP_AUTH_TAG_LENGTH: usize = 16; 18 const RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INIT_INFO: &[u8] = b"SimpleXSbChainInit"; 19 const RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INFO: &[u8] = b"SimpleXSbChain"; 20 const RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_KEY_LENGTH: usize = 32; 21 const RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INIT_OUTPUT_LENGTH: usize = 64; 22 const RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_STEP_OUTPUT_LENGTH: usize = 88; 23 24 type RadrootsSimplexSmpSecretBoxKeyAndNonce = (Vec<u8>, [u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH]); 25 type RadrootsSimplexSmpSecretBoxChainStep = ( 26 RadrootsSimplexSmpSecretBoxKeyAndNonce, 27 RadrootsSimplexSmpSecretBoxChainKey, 28 ); 29 30 #[derive(Debug, Clone, PartialEq, Eq)] 31 pub struct RadrootsSimplexSmpX25519Keypair { 32 pub public_key: Vec<u8>, 33 pub private_key: Vec<u8>, 34 } 35 36 impl RadrootsSimplexSmpX25519Keypair { 37 pub fn generate() -> Result<Self, RadrootsSimplexSmpCryptoError> { 38 let mut secret = [0_u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH]; 39 getrandom(&mut secret).map_err(|_| RadrootsSimplexSmpCryptoError::EntropyUnavailable)?; 40 Ok(Self::from_secret_bytes(secret)) 41 } 42 43 pub fn from_seed(seed: &[u8]) -> Self { 44 let digest = Sha256::digest(seed); 45 let mut secret = [0_u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH]; 46 secret.copy_from_slice(&digest[..RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH]); 47 Self::from_secret_bytes(secret) 48 } 49 50 pub fn public_key_from_private( 51 private_key: &[u8], 52 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 53 let private: [u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH] = 54 private_key.try_into().map_err(|_| { 55 RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength(private_key.len()) 56 })?; 57 Ok(PublicKey::from(&StaticSecret::from(private)) 58 .as_bytes() 59 .to_vec()) 60 } 61 62 fn from_secret_bytes(secret: [u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH]) -> Self { 63 let private = StaticSecret::from(secret); 64 let public = PublicKey::from(&private); 65 Self { 66 public_key: public.as_bytes().to_vec(), 67 private_key: private.to_bytes().to_vec(), 68 } 69 } 70 } 71 72 #[derive(Debug, Clone, PartialEq, Eq)] 73 pub struct RadrootsSimplexSmpSecretBoxChainKey { 74 bytes: [u8; RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_KEY_LENGTH], 75 } 76 77 impl RadrootsSimplexSmpSecretBoxChainKey { 78 fn from_slice(value: &[u8]) -> Result<Self, RadrootsSimplexSmpCryptoError> { 79 let bytes: [u8; RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_KEY_LENGTH] = 80 value.try_into().map_err(|_| { 81 RadrootsSimplexSmpCryptoError::InvalidSecretBoxChainKeyLength(value.len()) 82 })?; 83 Ok(Self { bytes }) 84 } 85 } 86 87 pub fn init_secretbox_chain( 88 session_identifier: &[u8], 89 shared_secret: &[u8], 90 ) -> Result< 91 ( 92 RadrootsSimplexSmpSecretBoxChainKey, 93 RadrootsSimplexSmpSecretBoxChainKey, 94 ), 95 RadrootsSimplexSmpCryptoError, 96 > { 97 let output = hkdf_expand( 98 session_identifier, 99 shared_secret, 100 RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INIT_INFO, 101 RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INIT_OUTPUT_LENGTH, 102 )?; 103 let (first, second) = output.split_at(RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_KEY_LENGTH); 104 Ok(( 105 RadrootsSimplexSmpSecretBoxChainKey::from_slice(first)?, 106 RadrootsSimplexSmpSecretBoxChainKey::from_slice(second)?, 107 )) 108 } 109 110 pub fn advance_secretbox_chain( 111 chain_key: &RadrootsSimplexSmpSecretBoxChainKey, 112 ) -> Result<RadrootsSimplexSmpSecretBoxChainStep, RadrootsSimplexSmpCryptoError> { 113 let output = hkdf_expand( 114 b"", 115 &chain_key.bytes, 116 RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_INFO, 117 RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_STEP_OUTPUT_LENGTH, 118 )?; 119 let (next_chain_key, remainder) = 120 output.split_at(RADROOTS_SIMPLEX_SMP_SECRETBOX_CHAIN_KEY_LENGTH); 121 let (secretbox_key, nonce_bytes) = 122 remainder.split_at(RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH); 123 Ok(( 124 ( 125 secretbox_key.to_vec(), 126 nonce_bytes.try_into().map_err(|_| { 127 RadrootsSimplexSmpCryptoError::InvalidNonceLength(nonce_bytes.len()) 128 })?, 129 ), 130 RadrootsSimplexSmpSecretBoxChainKey::from_slice(next_chain_key)?, 131 )) 132 } 133 134 pub fn derive_shared_secret( 135 private_key: &[u8], 136 public_key: &[u8], 137 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 138 let private: [u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH] = private_key 139 .try_into() 140 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength(private_key.len()))?; 141 let public: [u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH] = public_key 142 .try_into() 143 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len()))?; 144 let secret = StaticSecret::from(private).diffie_hellman(&PublicKey::from(public)); 145 Ok(secret.as_bytes().to_vec()) 146 } 147 148 pub fn random_nonce() 149 -> Result<[u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH], RadrootsSimplexSmpCryptoError> { 150 let mut nonce = [0_u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH]; 151 getrandom(&mut nonce).map_err(|_| RadrootsSimplexSmpCryptoError::EntropyUnavailable)?; 152 Ok(nonce) 153 } 154 155 pub fn encrypt_padded( 156 shared_secret: &[u8], 157 nonce: &[u8], 158 plaintext: &[u8], 159 padded_len: usize, 160 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 161 if plaintext.len().saturating_add(2) > padded_len { 162 return Err(RadrootsSimplexSmpCryptoError::InvalidMessageLength { 163 actual: plaintext.len(), 164 padded: padded_len, 165 }); 166 } 167 let mut padded = Vec::with_capacity(padded_len); 168 padded.extend_from_slice(&(plaintext.len() as u16).to_be_bytes()); 169 padded.extend_from_slice(plaintext); 170 padded.resize(padded_len, b'#'); 171 encrypt_no_pad(shared_secret, nonce, &padded) 172 } 173 174 pub fn decrypt_padded( 175 shared_secret: &[u8], 176 nonce: &[u8], 177 ciphertext: &[u8], 178 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 179 let padded = decrypt_no_pad(shared_secret, nonce, ciphertext)?; 180 if padded.len() < 2 { 181 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 182 padded.len(), 183 )); 184 } 185 let length = u16::from_be_bytes([padded[0], padded[1]]) as usize; 186 if length > padded.len().saturating_sub(2) { 187 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 188 padded.len(), 189 )); 190 } 191 Ok(padded[2..2 + length].to_vec()) 192 } 193 194 pub fn encrypt_no_pad( 195 shared_secret: &[u8], 196 nonce: &[u8], 197 plaintext: &[u8], 198 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 199 let stream = simplex_secretbox_stream(shared_secret, nonce, plaintext.len())?; 200 let (mac_key, mask) = stream.split_at(RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH); 201 let mut encrypted = Vec::with_capacity(RADROOTS_SIMPLEX_SMP_AUTH_TAG_LENGTH + plaintext.len()); 202 let mut ciphertext = plaintext.to_vec(); 203 xor_in_place(&mut ciphertext, mask); 204 let tag = Poly1305::new(mac_key.into()).compute_unpadded(&ciphertext); 205 encrypted.extend_from_slice(&tag); 206 encrypted.extend_from_slice(&ciphertext); 207 Ok(encrypted) 208 } 209 210 pub fn decrypt_no_pad( 211 shared_secret: &[u8], 212 nonce: &[u8], 213 ciphertext: &[u8], 214 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 215 if ciphertext.len() < RADROOTS_SIMPLEX_SMP_AUTH_TAG_LENGTH { 216 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 217 ciphertext.len(), 218 )); 219 } 220 let (tag_bytes, encrypted) = ciphertext.split_at(RADROOTS_SIMPLEX_SMP_AUTH_TAG_LENGTH); 221 let stream = simplex_secretbox_stream(shared_secret, nonce, encrypted.len())?; 222 let (mac_key, mask) = stream.split_at(RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH); 223 let tag = Poly1305::new(mac_key.into()).compute_unpadded(encrypted); 224 if tag.as_slice().ct_eq(tag_bytes).unwrap_u8() != 1 { 225 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 226 ciphertext.len(), 227 )); 228 } 229 let mut buffer = encrypted.to_vec(); 230 xor_in_place(&mut buffer, mask); 231 Ok(buffer) 232 } 233 234 fn simplex_secretbox_stream( 235 shared_secret: &[u8], 236 nonce: &[u8], 237 plaintext_len: usize, 238 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 239 if shared_secret.len() != RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH { 240 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 241 shared_secret.len(), 242 )); 243 } 244 let nonce: [u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH] = nonce 245 .try_into() 246 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidNonceLength(nonce.len()))?; 247 let first_key = hsalsa::<U10>(shared_secret.into(), (&[0_u8; 16]).into()); 248 let second_key = hsalsa::<U10>(&first_key, (&nonce[..16]).into()); 249 let mut cipher = Salsa20::new(&second_key, (&nonce[16..]).into()); 250 let mut stream = vec![0_u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH + plaintext_len]; 251 cipher.apply_keystream(&mut stream); 252 Ok(stream) 253 } 254 255 fn xor_in_place(value: &mut [u8], mask: &[u8]) { 256 for (byte, mask) in value.iter_mut().zip(mask.iter()) { 257 *byte ^= mask; 258 } 259 } 260 261 fn hkdf_expand( 262 salt: &[u8], 263 ikm: &[u8], 264 info: &[u8], 265 output_len: usize, 266 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 267 let hkdf = Hkdf::<Sha512>::new(Some(salt), ikm); 268 let mut output = vec![0_u8; output_len]; 269 hkdf.expand(info, &mut output) 270 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidKeyDerivationLength(output_len))?; 271 Ok(output) 272 } 273 274 #[cfg(test)] 275 mod tests { 276 use super::*; 277 278 #[test] 279 fn derives_repeatable_keypair_from_seed() { 280 let first = RadrootsSimplexSmpX25519Keypair::from_seed(b"seed"); 281 let second = RadrootsSimplexSmpX25519Keypair::from_seed(b"seed"); 282 assert_eq!(first, second); 283 } 284 285 #[test] 286 fn encrypts_and_decrypts_padded_message() { 287 let alice = RadrootsSimplexSmpX25519Keypair::from_seed(b"alice"); 288 let bob = RadrootsSimplexSmpX25519Keypair::from_seed(b"bob"); 289 let alice_secret = derive_shared_secret(&alice.private_key, &bob.public_key).unwrap(); 290 let bob_secret = derive_shared_secret(&bob.private_key, &alice.public_key).unwrap(); 291 assert_eq!(alice_secret, bob_secret); 292 293 let nonce = [5_u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH]; 294 let ciphertext = encrypt_padded(&alice_secret, &nonce, b"hello", 32).unwrap(); 295 let plaintext = decrypt_padded(&bob_secret, &nonce, &ciphertext).unwrap(); 296 assert_eq!(plaintext, b"hello"); 297 } 298 299 #[test] 300 fn encrypt_padded_uses_simplex_transport_padding() { 301 let key = [7_u8; RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH]; 302 let nonce = [11_u8; RADROOTS_SIMPLEX_SMP_NONCE_LENGTH]; 303 let ciphertext = encrypt_padded(&key, &nonce, b"hello", 12).unwrap(); 304 let padded = decrypt_no_pad(&key, &nonce, &ciphertext).unwrap(); 305 306 assert_eq!(&padded[..7], &[0, 5, b'h', b'e', b'l', b'l', b'o']); 307 assert!(padded[7..].iter().all(|byte| *byte == b'#')); 308 } 309 310 #[test] 311 fn derives_repeatable_secretbox_chain_progression() { 312 let (rcv_first, snd_first) = init_secretbox_chain(b"session", b"shared-secret").unwrap(); 313 let (rcv_second, snd_second) = init_secretbox_chain(b"session", b"shared-secret").unwrap(); 314 assert_eq!(rcv_first, rcv_second); 315 assert_eq!(snd_first, snd_second); 316 317 let ((send_key, send_nonce), next_send) = advance_secretbox_chain(&snd_first).unwrap(); 318 let ((recv_key, recv_nonce), next_recv) = advance_secretbox_chain(&rcv_first).unwrap(); 319 320 assert_eq!(send_key.len(), RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH); 321 assert_eq!(recv_key.len(), RADROOTS_SIMPLEX_SMP_SHARED_SECRET_LENGTH); 322 assert_ne!(send_nonce, recv_nonce); 323 assert_ne!(next_send, snd_first); 324 assert_ne!(next_recv, rcv_first); 325 } 326 }