official_ratchet.rs (63527B)
1 use crate::error::RadrootsSimplexSmpCryptoError; 2 use aes_gcm::aead::consts::U16; 3 use aes_gcm::aead::{Aead, KeyInit, Payload}; 4 use aes_gcm::{AesGcm, Nonce, aes::Aes256}; 5 use alloc::borrow::ToOwned; 6 use alloc::format; 7 use alloc::string::String; 8 use alloc::vec::Vec; 9 use base64::Engine as _; 10 use base64::engine::general_purpose::{URL_SAFE, URL_SAFE_NO_PAD}; 11 use hkdf::Hkdf; 12 use radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpVersionRange; 13 use sha2::{Digest, Sha256, Sha512}; 14 15 pub const RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION: u16 = 2; 16 pub const RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION: u16 = 3; 17 pub const RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION: u16 = 3; 18 pub const RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH: usize = 56; 19 pub const RADROOTS_SIMPLEX_OFFICIAL_X448_SHARED_SECRET_LENGTH: usize = 56; 20 pub const RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH: usize = 32; 21 pub const RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH: usize = 16; 22 pub const RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH: usize = 16; 23 pub const RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PUBLIC_KEY_LENGTH: usize = sntrup761::PUBLIC_KEY_SIZE; 24 pub const RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PRIVATE_KEY_LENGTH: usize = 25 sntrup761::SECRET_KEY_SIZE; 26 pub const RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_CIPHERTEXT_LENGTH: usize = sntrup761::CIPHERTEXT_SIZE; 27 pub const RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_SHARED_SECRET_LENGTH: usize = 28 sntrup761::SHARED_SECRET_SIZE; 29 pub const RADROOTS_SIMPLEX_OFFICIAL_RATCHET_HEADER_LENGTH: usize = 88; 30 pub const RADROOTS_SIMPLEX_OFFICIAL_PQ_RATCHET_HEADER_LENGTH: usize = 2_310; 31 pub const RADROOTS_SIMPLEX_OFFICIAL_ROOT_RATCHET_INFO: &[u8] = b"SimpleXRootRatchet"; 32 pub const RADROOTS_SIMPLEX_OFFICIAL_CHAIN_RATCHET_INFO: &[u8] = b"SimpleXChainRatchet"; 33 pub const RADROOTS_SIMPLEX_OFFICIAL_X3DH_INFO: &[u8] = b"SimpleXX3DH"; 34 35 const RADROOTS_SIMPLEX_OFFICIAL_HKDF3_OUTPUT_LENGTH: usize = 36 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH * 3; 37 const RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES: usize = 2; 38 const RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX: [u8; 12] = [ 39 0x30, 0x42, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6f, 0x03, 0x39, 0x00, 40 ]; 41 type RadrootsSimplexOfficialAes256Gcm = AesGcm<Aes256, U16>; 42 type RadrootsSimplexOfficialHkdf3Output = (Vec<u8>, Vec<u8>, Vec<u8>); 43 type RadrootsSimplexOfficialPqHeaderParts = (Option<Vec<u8>>, Option<Vec<u8>>); 44 45 #[derive(Debug, Clone, PartialEq, Eq)] 46 pub struct RadrootsSimplexOfficialX448Keypair { 47 pub public_key: Vec<u8>, 48 pub private_key: Vec<u8>, 49 } 50 51 #[derive(Debug, Clone, PartialEq, Eq)] 52 pub struct RadrootsSimplexOfficialSntrup761Keypair { 53 pub public_key: Vec<u8>, 54 pub private_key: Vec<u8>, 55 } 56 57 #[derive(Debug, Clone, PartialEq, Eq)] 58 pub struct RadrootsSimplexOfficialAesGcmPayload { 59 pub auth_tag: Vec<u8>, 60 pub ciphertext: Vec<u8>, 61 } 62 63 #[derive(Debug, Clone, PartialEq, Eq)] 64 pub struct RadrootsSimplexOfficialEncryptedHeader { 65 pub version: u16, 66 pub iv: [u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH], 67 pub auth_tag: Vec<u8>, 68 pub body: Vec<u8>, 69 } 70 71 #[derive(Debug, Clone, PartialEq, Eq)] 72 pub struct RadrootsSimplexOfficialEncryptedMessage { 73 pub encrypted_header: Vec<u8>, 74 pub auth_tag: Vec<u8>, 75 pub body: Vec<u8>, 76 } 77 78 #[derive(Debug, Clone, PartialEq, Eq)] 79 pub struct RadrootsSimplexOfficialX3dhParams { 80 pub version_range: RadrootsSimplexSmpVersionRange, 81 pub key_1: Vec<u8>, 82 pub key_2: Vec<u8>, 83 pub pq_public_key: Option<Vec<u8>>, 84 pub pq_ciphertext: Option<Vec<u8>>, 85 } 86 87 #[derive(Debug, Clone, PartialEq, Eq)] 88 pub struct RadrootsSimplexOfficialX3dhInit { 89 pub associated_data: Vec<u8>, 90 pub ratchet_key: Vec<u8>, 91 pub sending_header_key: Vec<u8>, 92 pub receiving_next_header_key: Vec<u8>, 93 pub accepted_pq_shared_secret: Option<Vec<u8>>, 94 } 95 96 #[derive(Debug, Clone, PartialEq, Eq)] 97 pub struct RadrootsSimplexOfficialMsgHeader { 98 pub max_version: u16, 99 pub dh_public_key: Vec<u8>, 100 pub pq_public_key: Option<Vec<u8>>, 101 pub pq_ciphertext: Option<Vec<u8>>, 102 pub previous_sending_chain_length: u32, 103 pub message_number: u32, 104 } 105 106 #[derive(Debug, Clone, PartialEq, Eq)] 107 pub struct RadrootsSimplexOfficialRootKdfOutput { 108 pub root_key: Vec<u8>, 109 pub chain_key: Vec<u8>, 110 pub next_header_key: Vec<u8>, 111 } 112 113 #[derive(Debug, Clone, PartialEq, Eq)] 114 pub struct RadrootsSimplexOfficialChainKdfOutput { 115 pub chain_key: Vec<u8>, 116 pub message_key: Vec<u8>, 117 pub message_iv: [u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH], 118 pub header_iv: [u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH], 119 } 120 121 #[derive(Debug, Clone, PartialEq, Eq)] 122 pub struct RadrootsSimplexOfficialX3dhSenderPqInit { 123 pub init: RadrootsSimplexOfficialX3dhInit, 124 pub sender_params: RadrootsSimplexOfficialX3dhParams, 125 pub local_pq_keypair: RadrootsSimplexOfficialSntrup761Keypair, 126 pub pq_shared_secret: Vec<u8>, 127 } 128 129 #[derive(Debug, Clone, PartialEq, Eq)] 130 pub struct RadrootsSimplexOfficialX3dhReceiverPqInit { 131 pub init: RadrootsSimplexOfficialX3dhInit, 132 pub pq_shared_secret: Vec<u8>, 133 } 134 135 pub fn official_ratchet_header_len( 136 version: u16, 137 pq_enabled: bool, 138 ) -> Result<usize, RadrootsSimplexSmpCryptoError> { 139 if !(RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION..=RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION) 140 .contains(&version) 141 { 142 return Err(RadrootsSimplexSmpCryptoError::InvalidOfficialRatchetVersion(version)); 143 } 144 Ok( 145 if pq_enabled && version >= RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 146 RADROOTS_SIMPLEX_OFFICIAL_PQ_RATCHET_HEADER_LENGTH 147 } else { 148 RADROOTS_SIMPLEX_OFFICIAL_RATCHET_HEADER_LENGTH 149 }, 150 ) 151 } 152 153 pub fn official_full_header_len( 154 version: u16, 155 pq_enabled: bool, 156 ) -> Result<usize, RadrootsSimplexSmpCryptoError> { 157 Ok(2 + 1 158 + official_ratchet_header_len(version, pq_enabled)? 159 + RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 160 + RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH) 161 } 162 163 pub fn official_encoded_encrypted_header_len( 164 version: u16, 165 pq_enabled: bool, 166 ) -> Result<usize, RadrootsSimplexSmpCryptoError> { 167 Ok(2 + RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH 168 + RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 169 + official_large_prefix_len(version)? 170 + official_ratchet_header_len(version, pq_enabled)?) 171 } 172 173 pub fn official_encoded_encrypted_message_len( 174 version: u16, 175 pq_enabled: bool, 176 padded_body_len: usize, 177 ) -> Result<usize, RadrootsSimplexSmpCryptoError> { 178 Ok(official_large_prefix_len(version)? 179 + official_encoded_encrypted_header_len(version, pq_enabled)? 180 + RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 181 + padded_body_len) 182 } 183 184 pub fn official_x448_keypair_from_seed(seed: &[u8]) -> RadrootsSimplexOfficialX448Keypair { 185 let digest = Sha512::digest(seed); 186 let mut private_key = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH]; 187 private_key.copy_from_slice(&digest[..RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH]); 188 official_x448_keypair_from_private(private_key) 189 } 190 191 pub fn generate_official_x448_keypair() 192 -> Result<RadrootsSimplexOfficialX448Keypair, RadrootsSimplexSmpCryptoError> { 193 let mut private_key = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH]; 194 getrandom::getrandom(&mut private_key) 195 .map_err(|_| RadrootsSimplexSmpCryptoError::EntropyUnavailable)?; 196 Ok(official_x448_keypair_from_private(private_key)) 197 } 198 199 pub fn derive_official_x448_shared_secret( 200 private_key: &[u8], 201 public_key: &[u8], 202 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 203 let private_key: [u8; RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH] = private_key 204 .try_into() 205 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength(private_key.len()))?; 206 let public_key = x448::PublicKey::from_bytes(public_key).ok_or( 207 RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len()), 208 )?; 209 let private = x448::StaticSecret::from(private_key); 210 Ok(private.diffie_hellman(&public_key).as_bytes().to_vec()) 211 } 212 213 pub fn encode_official_x448_public_key_der( 214 public_key: &[u8], 215 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 216 if public_key.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH { 217 return Err(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 218 public_key.len(), 219 )); 220 } 221 let mut encoded = Vec::with_capacity( 222 RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX.len() + public_key.len(), 223 ); 224 encoded.extend_from_slice(&RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX); 225 encoded.extend_from_slice(public_key); 226 Ok(encoded) 227 } 228 229 pub fn decode_official_x448_public_key_der( 230 encoded: &[u8], 231 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 232 let expected_len = RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX.len() 233 + RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH; 234 if encoded.len() != expected_len 235 || !encoded.starts_with(&RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX) 236 { 237 return Err(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 238 encoded.len(), 239 )); 240 } 241 Ok(encoded[RADROOTS_SIMPLEX_OFFICIAL_X448_DER_PUBLIC_KEY_PREFIX.len()..].to_vec()) 242 } 243 244 pub fn encode_official_x3dh_params_uri( 245 params: &RadrootsSimplexOfficialX3dhParams, 246 ) -> Result<String, RadrootsSimplexSmpCryptoError> { 247 validate_official_x3dh_params(params)?; 248 let key_1 = encode_official_urlsafe_bytes(&encode_official_x448_public_key_der(¶ms.key_1)?); 249 let key_2 = encode_official_urlsafe_bytes(&encode_official_x448_public_key_der(¶ms.key_2)?); 250 let mut encoded = format!("v={}&x3dh={key_1},{key_2}", params.version_range); 251 if let Some(pq_public_key) = params.pq_public_key.as_deref() { 252 encoded.push_str("&kem_key="); 253 encoded.push_str(&encode_official_urlsafe_bytes(pq_public_key)); 254 } 255 if let Some(pq_ciphertext) = params.pq_ciphertext.as_deref() { 256 encoded.push_str("&kem_ct="); 257 encoded.push_str(&encode_official_urlsafe_bytes(pq_ciphertext)); 258 } 259 Ok(encoded) 260 } 261 262 pub fn decode_official_x3dh_params_uri( 263 encoded: &str, 264 ) -> Result<RadrootsSimplexOfficialX3dhParams, RadrootsSimplexSmpCryptoError> { 265 let mut version_range = None; 266 let mut x3dh = None; 267 let mut pq_public_key = None; 268 let mut pq_ciphertext = None; 269 for pair in encoded.split('&') { 270 if pair.is_empty() { 271 continue; 272 } 273 let (key, value) = pair.split_once('=').ok_or_else(|| { 274 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 275 "field is missing `=`".to_owned(), 276 ) 277 })?; 278 match key { 279 "v" => { 280 if version_range.replace(value.parse()?).is_some() { 281 return Err( 282 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 283 "duplicate `v` field".to_owned(), 284 ), 285 ); 286 } 287 } 288 "x3dh" => { 289 if x3dh.replace(value.to_owned()).is_some() { 290 return Err( 291 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 292 "duplicate `x3dh` field".to_owned(), 293 ), 294 ); 295 } 296 } 297 "kem_key" => { 298 if pq_public_key 299 .replace(decode_official_urlsafe_bytes(value)?) 300 .is_some() 301 { 302 return Err( 303 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 304 "duplicate `kem_key` field".to_owned(), 305 ), 306 ); 307 } 308 } 309 "kem_ct" => { 310 if pq_ciphertext 311 .replace(decode_official_urlsafe_bytes(value)?) 312 .is_some() 313 { 314 return Err( 315 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 316 "duplicate `kem_ct` field".to_owned(), 317 ), 318 ); 319 } 320 } 321 _ => { 322 return Err( 323 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 324 "unknown field".to_owned(), 325 ), 326 ); 327 } 328 } 329 } 330 let x3dh = x3dh.ok_or_else(|| { 331 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 332 "missing `x3dh` field".to_owned(), 333 ) 334 })?; 335 let keys = split_official_x3dh_keys(&x3dh)?; 336 let params = RadrootsSimplexOfficialX3dhParams { 337 version_range: version_range.ok_or_else(|| { 338 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 339 "missing `v` field".to_owned(), 340 ) 341 })?, 342 key_1: decode_official_x448_public_key_der(&decode_official_urlsafe_bytes(keys.0)?)?, 343 key_2: decode_official_x448_public_key_der(&decode_official_urlsafe_bytes(keys.1)?)?, 344 pq_public_key, 345 pq_ciphertext, 346 }; 347 validate_official_x3dh_params(¶ms)?; 348 Ok(params) 349 } 350 351 pub fn official_x3dh_sender_init( 352 local_key_1: &RadrootsSimplexOfficialX448Keypair, 353 local_key_2: &RadrootsSimplexOfficialX448Keypair, 354 remote_params: &RadrootsSimplexOfficialX3dhParams, 355 ) -> Result<RadrootsSimplexOfficialX3dhInit, RadrootsSimplexSmpCryptoError> { 356 validate_official_x3dh_keypair(local_key_1)?; 357 validate_official_x3dh_keypair(local_key_2)?; 358 validate_official_x3dh_params(remote_params)?; 359 if remote_params.pq_public_key.is_some() || remote_params.pq_ciphertext.is_some() { 360 return Err(RadrootsSimplexSmpCryptoError::IncompletePqHeader); 361 } 362 official_x3dh_init( 363 &local_key_1.public_key, 364 &remote_params.key_1, 365 &[ 366 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_1)?, 367 derive_official_x448_shared_secret(&local_key_1.private_key, &remote_params.key_2)?, 368 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_2)?, 369 ], 370 None, 371 ) 372 } 373 374 pub fn official_x3dh_receiver_init( 375 local_key_1: &RadrootsSimplexOfficialX448Keypair, 376 local_key_2: &RadrootsSimplexOfficialX448Keypair, 377 remote_params: &RadrootsSimplexOfficialX3dhParams, 378 ) -> Result<RadrootsSimplexOfficialX3dhInit, RadrootsSimplexSmpCryptoError> { 379 validate_official_x3dh_keypair(local_key_1)?; 380 validate_official_x3dh_keypair(local_key_2)?; 381 validate_official_x3dh_params(remote_params)?; 382 if remote_params.pq_public_key.is_some() || remote_params.pq_ciphertext.is_some() { 383 return Err(RadrootsSimplexSmpCryptoError::IncompletePqHeader); 384 } 385 official_x3dh_init( 386 &remote_params.key_1, 387 &local_key_1.public_key, 388 &[ 389 derive_official_x448_shared_secret(&local_key_1.private_key, &remote_params.key_2)?, 390 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_1)?, 391 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_2)?, 392 ], 393 None, 394 ) 395 } 396 397 pub fn official_x3dh_sender_init_accepting_pq( 398 local_key_1: &RadrootsSimplexOfficialX448Keypair, 399 local_key_2: &RadrootsSimplexOfficialX448Keypair, 400 local_pq_keypair: RadrootsSimplexOfficialSntrup761Keypair, 401 remote_params: &RadrootsSimplexOfficialX3dhParams, 402 encapsulation_seed: &[u8], 403 ) -> Result<RadrootsSimplexOfficialX3dhSenderPqInit, RadrootsSimplexSmpCryptoError> { 404 validate_official_x3dh_keypair(local_key_1)?; 405 validate_official_x3dh_keypair(local_key_2)?; 406 validate_official_sntrup761_keypair(&local_pq_keypair)?; 407 validate_official_x3dh_params(remote_params)?; 408 let remote_pq_public_key = remote_params.pq_public_key.as_deref().ok_or( 409 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 410 "PQ sender init requires remote proposed KEM key".to_owned(), 411 ), 412 )?; 413 if remote_params.pq_ciphertext.is_some() { 414 return Err( 415 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 416 "PQ sender init requires proposed KEM key without ciphertext".to_owned(), 417 ), 418 ); 419 } 420 let (pq_ciphertext, pq_shared_secret) = 421 encapsulate_official_sntrup761(remote_pq_public_key, encapsulation_seed)?; 422 let init = official_x3dh_init( 423 &local_key_1.public_key, 424 &remote_params.key_1, 425 &[ 426 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_1)?, 427 derive_official_x448_shared_secret(&local_key_1.private_key, &remote_params.key_2)?, 428 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_2)?, 429 ], 430 Some(&pq_shared_secret), 431 )?; 432 let sender_params = RadrootsSimplexOfficialX3dhParams { 433 version_range: remote_params.version_range, 434 key_1: local_key_1.public_key.clone(), 435 key_2: local_key_2.public_key.clone(), 436 pq_public_key: Some(local_pq_keypair.public_key.clone()), 437 pq_ciphertext: Some(pq_ciphertext), 438 }; 439 validate_official_x3dh_params(&sender_params)?; 440 Ok(RadrootsSimplexOfficialX3dhSenderPqInit { 441 init, 442 sender_params, 443 local_pq_keypair, 444 pq_shared_secret, 445 }) 446 } 447 448 pub fn official_x3dh_receiver_init_accepting_pq( 449 local_key_1: &RadrootsSimplexOfficialX448Keypair, 450 local_key_2: &RadrootsSimplexOfficialX448Keypair, 451 local_pq_keypair: &RadrootsSimplexOfficialSntrup761Keypair, 452 remote_params: &RadrootsSimplexOfficialX3dhParams, 453 ) -> Result<RadrootsSimplexOfficialX3dhReceiverPqInit, RadrootsSimplexSmpCryptoError> { 454 validate_official_x3dh_keypair(local_key_1)?; 455 validate_official_x3dh_keypair(local_key_2)?; 456 validate_official_sntrup761_keypair(local_pq_keypair)?; 457 validate_official_x3dh_params(remote_params)?; 458 let pq_ciphertext = remote_params.pq_ciphertext.as_deref().ok_or( 459 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 460 "PQ receiver init requires accepted KEM ciphertext".to_owned(), 461 ), 462 )?; 463 if remote_params.pq_public_key.is_none() { 464 return Err(RadrootsSimplexSmpCryptoError::IncompletePqHeader); 465 } 466 let pq_shared_secret = 467 decapsulate_official_sntrup761(&local_pq_keypair.private_key, pq_ciphertext)?; 468 let init = official_x3dh_init( 469 &remote_params.key_1, 470 &local_key_1.public_key, 471 &[ 472 derive_official_x448_shared_secret(&local_key_1.private_key, &remote_params.key_2)?, 473 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_1)?, 474 derive_official_x448_shared_secret(&local_key_2.private_key, &remote_params.key_2)?, 475 ], 476 Some(&pq_shared_secret), 477 )?; 478 Ok(RadrootsSimplexOfficialX3dhReceiverPqInit { 479 init, 480 pq_shared_secret, 481 }) 482 } 483 484 pub fn official_sntrup761_keypair_from_seed( 485 seed: &[u8], 486 ) -> RadrootsSimplexOfficialSntrup761Keypair { 487 let seed = pq_seed(seed); 488 let (public_key, private_key) = sntrup761::generate_key_from_seed(seed); 489 RadrootsSimplexOfficialSntrup761Keypair { 490 public_key: public_key.as_ref().to_vec(), 491 private_key: private_key.as_ref().to_vec(), 492 } 493 } 494 495 pub fn generate_official_sntrup761_keypair() 496 -> Result<RadrootsSimplexOfficialSntrup761Keypair, RadrootsSimplexSmpCryptoError> { 497 let mut seed = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH]; 498 getrandom::getrandom(&mut seed) 499 .map_err(|_| RadrootsSimplexSmpCryptoError::EntropyUnavailable)?; 500 Ok(official_sntrup761_keypair_from_seed(&seed)) 501 } 502 503 pub fn encapsulate_official_sntrup761( 504 public_key: &[u8], 505 seed: &[u8], 506 ) -> Result<(Vec<u8>, Vec<u8>), RadrootsSimplexSmpCryptoError> { 507 let public_key = sntrup761::EncapsulationKey::try_from(public_key) 508 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPqKeyLength(public_key.len()))?; 509 let (ciphertext, shared_secret) = public_key.encapsulate_deterministic(pq_seed(seed)); 510 Ok(( 511 ciphertext.as_ref().to_vec(), 512 shared_secret.as_ref().to_vec(), 513 )) 514 } 515 516 pub fn decapsulate_official_sntrup761( 517 private_key: &[u8], 518 ciphertext: &[u8], 519 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 520 let private_key = sntrup761::DecapsulationKey::try_from(private_key) 521 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength(private_key.len()))?; 522 let ciphertext = sntrup761::Ciphertext::try_from(ciphertext) 523 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPqCiphertextLength(ciphertext.len()))?; 524 Ok(private_key.decapsulate(&ciphertext).as_ref().to_vec()) 525 } 526 527 pub fn official_root_kdf( 528 root_key: &[u8], 529 dh_shared_secret: &[u8], 530 pq_shared_secret: Option<&[u8]>, 531 ) -> Result<RadrootsSimplexOfficialRootKdfOutput, RadrootsSimplexSmpCryptoError> { 532 if root_key.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH { 533 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 534 root_key.len(), 535 )); 536 } 537 let mut input = 538 Vec::with_capacity(dh_shared_secret.len() + pq_shared_secret.map_or(0, <[u8]>::len)); 539 input.extend_from_slice(dh_shared_secret); 540 if let Some(shared_secret) = pq_shared_secret { 541 input.extend_from_slice(shared_secret); 542 } 543 let (root_key, chain_key, next_header_key) = official_hkdf3( 544 root_key, 545 &input, 546 RADROOTS_SIMPLEX_OFFICIAL_ROOT_RATCHET_INFO, 547 )?; 548 Ok(RadrootsSimplexOfficialRootKdfOutput { 549 root_key, 550 chain_key, 551 next_header_key, 552 }) 553 } 554 555 pub fn official_chain_kdf( 556 chain_key: &[u8], 557 ) -> Result<RadrootsSimplexOfficialChainKdfOutput, RadrootsSimplexSmpCryptoError> { 558 if chain_key.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH { 559 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 560 chain_key.len(), 561 )); 562 } 563 let (chain_key, message_key, iv_material) = 564 official_hkdf3(b"", chain_key, RADROOTS_SIMPLEX_OFFICIAL_CHAIN_RATCHET_INFO)?; 565 let mut message_iv = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH]; 566 let mut header_iv = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH]; 567 message_iv.copy_from_slice(&iv_material[..RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH]); 568 header_iv.copy_from_slice( 569 &iv_material 570 [RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH..RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH * 2], 571 ); 572 Ok(RadrootsSimplexOfficialChainKdfOutput { 573 chain_key, 574 message_key, 575 message_iv, 576 header_iv, 577 }) 578 } 579 580 pub fn official_aes_gcm_encrypt_padded( 581 key: &[u8], 582 iv: &[u8], 583 plaintext: &[u8], 584 padded_len: usize, 585 associated_data: &[u8], 586 ) -> Result<RadrootsSimplexOfficialAesGcmPayload, RadrootsSimplexSmpCryptoError> { 587 let padded = official_pad(plaintext, padded_len)?; 588 let encrypted = official_aes_gcm_cipher(key)? 589 .encrypt( 590 official_aes_gcm_nonce(iv)?, 591 Payload { 592 msg: &padded, 593 aad: associated_data, 594 }, 595 ) 596 .map_err(|_| RadrootsSimplexSmpCryptoError::AesGcmAuthenticationFailed)?; 597 split_official_aes_gcm_payload(&encrypted) 598 } 599 600 pub fn encode_official_msg_header( 601 version: u16, 602 header: &RadrootsSimplexOfficialMsgHeader, 603 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 604 validate_official_version(version)?; 605 validate_official_version(header.max_version)?; 606 let public_key = encode_official_x448_public_key_der(&header.dh_public_key)?; 607 let mut buffer = Vec::with_capacity(2 + 1 + public_key.len() + 1 + 4 + 4); 608 buffer.extend_from_slice(&header.max_version.to_be_bytes()); 609 push_official_short_bytes(&mut buffer, &public_key)?; 610 if version >= RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 611 push_official_msg_header_pq(&mut buffer, header)?; 612 } else if header.pq_public_key.is_some() || header.pq_ciphertext.is_some() { 613 return Err( 614 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 615 "PQ header params require E2E version 3".to_owned(), 616 ), 617 ); 618 } 619 buffer.extend_from_slice(&header.previous_sending_chain_length.to_be_bytes()); 620 buffer.extend_from_slice(&header.message_number.to_be_bytes()); 621 Ok(buffer) 622 } 623 624 pub fn decode_official_msg_header( 625 version: u16, 626 bytes: &[u8], 627 ) -> Result<RadrootsSimplexOfficialMsgHeader, RadrootsSimplexSmpCryptoError> { 628 validate_official_version(version)?; 629 let mut cursor = OfficialCursor::new(bytes); 630 let max_version = cursor.read_u16()?; 631 validate_official_version(max_version)?; 632 let dh_public_key = decode_official_x448_public_key_der(cursor.read_short_bytes()?)?; 633 let (pq_public_key, pq_ciphertext) = if version >= RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 634 read_official_msg_header_pq(&mut cursor)? 635 } else { 636 (None, None) 637 }; 638 let previous_sending_chain_length = cursor.read_u32()?; 639 let message_number = cursor.read_u32()?; 640 cursor.finish()?; 641 Ok(RadrootsSimplexOfficialMsgHeader { 642 max_version, 643 dh_public_key, 644 pq_public_key, 645 pq_ciphertext, 646 previous_sending_chain_length, 647 message_number, 648 }) 649 } 650 651 pub fn encode_official_encrypted_header( 652 header: &RadrootsSimplexOfficialEncryptedHeader, 653 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 654 validate_official_version(header.version)?; 655 if header.auth_tag.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH { 656 return Err(RadrootsSimplexSmpCryptoError::InvalidSignatureLength( 657 header.auth_tag.len(), 658 )); 659 } 660 let mut buffer = Vec::with_capacity( 661 2 + RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH 662 + RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 663 + official_large_prefix_len(header.version)? 664 + header.body.len(), 665 ); 666 buffer.extend_from_slice(&header.version.to_be_bytes()); 667 buffer.extend_from_slice(&header.iv); 668 buffer.extend_from_slice(&header.auth_tag); 669 push_official_large_by_version(&mut buffer, header.version, &header.body)?; 670 Ok(buffer) 671 } 672 673 pub fn decode_official_encrypted_header( 674 bytes: &[u8], 675 ) -> Result<RadrootsSimplexOfficialEncryptedHeader, RadrootsSimplexSmpCryptoError> { 676 let mut cursor = OfficialCursor::new(bytes); 677 let version = cursor.read_u16()?; 678 validate_official_version(version)?; 679 let iv = cursor.read_array::<RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH>()?; 680 let auth_tag = cursor 681 .read_slice(RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH)? 682 .to_vec(); 683 let body = cursor.read_official_large()?.to_vec(); 684 cursor.finish()?; 685 Ok(RadrootsSimplexOfficialEncryptedHeader { 686 version, 687 iv, 688 auth_tag, 689 body, 690 }) 691 } 692 693 pub fn encode_official_encrypted_message( 694 version: u16, 695 message: &RadrootsSimplexOfficialEncryptedMessage, 696 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 697 validate_official_version(version)?; 698 if message.auth_tag.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH { 699 return Err(RadrootsSimplexSmpCryptoError::InvalidSignatureLength( 700 message.auth_tag.len(), 701 )); 702 } 703 let mut buffer = Vec::with_capacity( 704 official_large_prefix_len(version)? 705 + message.encrypted_header.len() 706 + RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 707 + message.body.len(), 708 ); 709 push_official_large_by_version(&mut buffer, version, &message.encrypted_header)?; 710 buffer.extend_from_slice(&message.auth_tag); 711 buffer.extend_from_slice(&message.body); 712 Ok(buffer) 713 } 714 715 pub fn decode_official_encrypted_message( 716 bytes: &[u8], 717 ) -> Result<RadrootsSimplexOfficialEncryptedMessage, RadrootsSimplexSmpCryptoError> { 718 let mut cursor = OfficialCursor::new(bytes); 719 let encrypted_header = cursor.read_official_large()?.to_vec(); 720 let auth_tag = cursor 721 .read_slice(RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH)? 722 .to_vec(); 723 let body = cursor.read_remaining().to_vec(); 724 Ok(RadrootsSimplexOfficialEncryptedMessage { 725 encrypted_header, 726 auth_tag, 727 body, 728 }) 729 } 730 731 pub fn official_aes_gcm_decrypt_padded( 732 key: &[u8], 733 iv: &[u8], 734 payload: &RadrootsSimplexOfficialAesGcmPayload, 735 associated_data: &[u8], 736 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 737 if payload.auth_tag.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH { 738 return Err(RadrootsSimplexSmpCryptoError::InvalidSignatureLength( 739 payload.auth_tag.len(), 740 )); 741 } 742 let mut encrypted = Vec::with_capacity(payload.ciphertext.len() + payload.auth_tag.len()); 743 encrypted.extend_from_slice(&payload.ciphertext); 744 encrypted.extend_from_slice(&payload.auth_tag); 745 let padded = official_aes_gcm_cipher(key)? 746 .decrypt( 747 official_aes_gcm_nonce(iv)?, 748 Payload { 749 msg: &encrypted, 750 aad: associated_data, 751 }, 752 ) 753 .map_err(|_| RadrootsSimplexSmpCryptoError::AesGcmAuthenticationFailed)?; 754 official_unpad(&padded) 755 } 756 757 fn official_x448_keypair_from_private( 758 private_key: [u8; RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH], 759 ) -> RadrootsSimplexOfficialX448Keypair { 760 let private = x448::StaticSecret::from(private_key); 761 let public = x448::PublicKey::from(&private); 762 RadrootsSimplexOfficialX448Keypair { 763 public_key: public.as_bytes().to_vec(), 764 private_key: private.as_bytes().to_vec(), 765 } 766 } 767 768 fn official_aes_gcm_cipher( 769 key: &[u8], 770 ) -> Result<RadrootsSimplexOfficialAes256Gcm, RadrootsSimplexSmpCryptoError> { 771 if key.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH { 772 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 773 key.len(), 774 )); 775 } 776 RadrootsSimplexOfficialAes256Gcm::new_from_slice(key) 777 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength(key.len())) 778 } 779 780 fn official_aes_gcm_nonce(iv: &[u8]) -> Result<&Nonce<U16>, RadrootsSimplexSmpCryptoError> { 781 if iv.len() != RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH { 782 return Err(RadrootsSimplexSmpCryptoError::InvalidNonceLength(iv.len())); 783 } 784 Ok(Nonce::<U16>::from_slice(iv)) 785 } 786 787 fn split_official_aes_gcm_payload( 788 encrypted: &[u8], 789 ) -> Result<RadrootsSimplexOfficialAesGcmPayload, RadrootsSimplexSmpCryptoError> { 790 if encrypted.len() < RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH { 791 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 792 encrypted.len(), 793 )); 794 } 795 let tag_offset = encrypted.len() - RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH; 796 let (ciphertext, auth_tag) = encrypted.split_at(tag_offset); 797 Ok(RadrootsSimplexOfficialAesGcmPayload { 798 auth_tag: auth_tag.to_vec(), 799 ciphertext: ciphertext.to_vec(), 800 }) 801 } 802 803 fn official_pad( 804 plaintext: &[u8], 805 padded_len: usize, 806 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 807 if plaintext.len() > u16::MAX as usize 808 || plaintext 809 .len() 810 .saturating_add(RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES) 811 > padded_len 812 { 813 return Err(RadrootsSimplexSmpCryptoError::InvalidMessageLength { 814 actual: plaintext.len(), 815 padded: padded_len, 816 }); 817 } 818 let mut padded = Vec::with_capacity(padded_len); 819 padded.extend_from_slice(&(plaintext.len() as u16).to_be_bytes()); 820 padded.extend_from_slice(plaintext); 821 padded.resize(padded_len, b'#'); 822 Ok(padded) 823 } 824 825 fn official_unpad(padded: &[u8]) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 826 if padded.len() < RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES { 827 return Err(RadrootsSimplexSmpCryptoError::InvalidOfficialRatchetPadding); 828 } 829 let length = u16::from_be_bytes([padded[0], padded[1]]) as usize; 830 if length 831 > padded 832 .len() 833 .saturating_sub(RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES) 834 { 835 return Err(RadrootsSimplexSmpCryptoError::InvalidOfficialRatchetPadding); 836 } 837 Ok(padded[RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES 838 ..RADROOTS_SIMPLEX_OFFICIAL_PADDING_LENGTH_BYTES + length] 839 .to_vec()) 840 } 841 842 fn official_hkdf3( 843 salt: &[u8], 844 ikm: &[u8], 845 info: &[u8], 846 ) -> Result<RadrootsSimplexOfficialHkdf3Output, RadrootsSimplexSmpCryptoError> { 847 let hkdf = Hkdf::<Sha512>::new(Some(salt), ikm); 848 let mut output = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_HKDF3_OUTPUT_LENGTH]; 849 hkdf.expand(info, &mut output).map_err(|_| { 850 RadrootsSimplexSmpCryptoError::InvalidKeyDerivationLength( 851 RADROOTS_SIMPLEX_OFFICIAL_HKDF3_OUTPUT_LENGTH, 852 ) 853 })?; 854 Ok(( 855 output[..RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH].to_vec(), 856 output[RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 857 ..RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH * 2] 858 .to_vec(), 859 output[RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH * 2..].to_vec(), 860 )) 861 } 862 863 fn validate_official_version(version: u16) -> Result<(), RadrootsSimplexSmpCryptoError> { 864 if !(RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION..=RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION) 865 .contains(&version) 866 { 867 return Err(RadrootsSimplexSmpCryptoError::InvalidOfficialRatchetVersion(version)); 868 } 869 Ok(()) 870 } 871 872 fn validate_official_version_range( 873 range: RadrootsSimplexSmpVersionRange, 874 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 875 validate_official_version(range.min)?; 876 validate_official_version(range.max) 877 } 878 879 fn validate_official_x3dh_params( 880 params: &RadrootsSimplexOfficialX3dhParams, 881 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 882 validate_official_version_range(params.version_range)?; 883 if params.key_1.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH { 884 return Err(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 885 params.key_1.len(), 886 )); 887 } 888 if params.key_2.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH { 889 return Err(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 890 params.key_2.len(), 891 )); 892 } 893 if params.pq_ciphertext.is_some() && params.pq_public_key.is_none() { 894 return Err(RadrootsSimplexSmpCryptoError::IncompletePqHeader); 895 } 896 if let Some(pq_public_key) = params.pq_public_key.as_deref() { 897 if params.version_range.max < RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 898 return Err( 899 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 900 "PQ key requires E2E version 3".to_owned(), 901 ), 902 ); 903 } 904 if pq_public_key.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PUBLIC_KEY_LENGTH { 905 return Err(RadrootsSimplexSmpCryptoError::InvalidPqKeyLength( 906 pq_public_key.len(), 907 )); 908 } 909 } 910 if let Some(pq_ciphertext) = params.pq_ciphertext.as_deref() 911 && pq_ciphertext.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_CIPHERTEXT_LENGTH 912 { 913 return Err(RadrootsSimplexSmpCryptoError::InvalidPqCiphertextLength( 914 pq_ciphertext.len(), 915 )); 916 } 917 Ok(()) 918 } 919 920 fn validate_official_x3dh_keypair( 921 keypair: &RadrootsSimplexOfficialX448Keypair, 922 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 923 if keypair.public_key.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH { 924 return Err(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 925 keypair.public_key.len(), 926 )); 927 } 928 if keypair.private_key.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH { 929 return Err(RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength( 930 keypair.private_key.len(), 931 )); 932 } 933 Ok(()) 934 } 935 936 fn validate_official_sntrup761_keypair( 937 keypair: &RadrootsSimplexOfficialSntrup761Keypair, 938 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 939 if keypair.public_key.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PUBLIC_KEY_LENGTH { 940 return Err(RadrootsSimplexSmpCryptoError::InvalidPqKeyLength( 941 keypair.public_key.len(), 942 )); 943 } 944 if keypair.private_key.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PRIVATE_KEY_LENGTH { 945 return Err(RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength( 946 keypair.private_key.len(), 947 )); 948 } 949 Ok(()) 950 } 951 952 fn official_x3dh_init( 953 sender_key_1: &[u8], 954 receiver_key_1: &[u8], 955 shared_secrets: &[Vec<u8>; 3], 956 pq_shared_secret: Option<&[u8]>, 957 ) -> Result<RadrootsSimplexOfficialX3dhInit, RadrootsSimplexSmpCryptoError> { 958 let mut associated_data = Vec::with_capacity(sender_key_1.len() + receiver_key_1.len()); 959 associated_data.extend_from_slice(sender_key_1); 960 associated_data.extend_from_slice(receiver_key_1); 961 let mut input = Vec::with_capacity( 962 RADROOTS_SIMPLEX_OFFICIAL_X448_SHARED_SECRET_LENGTH * shared_secrets.len(), 963 ); 964 for shared_secret in shared_secrets { 965 if shared_secret.len() != RADROOTS_SIMPLEX_OFFICIAL_X448_SHARED_SECRET_LENGTH { 966 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 967 shared_secret.len(), 968 )); 969 } 970 input.extend_from_slice(shared_secret); 971 } 972 if let Some(pq_shared_secret) = pq_shared_secret { 973 if pq_shared_secret.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_SHARED_SECRET_LENGTH { 974 return Err(RadrootsSimplexSmpCryptoError::InvalidSharedSecretLength( 975 pq_shared_secret.len(), 976 )); 977 } 978 input.extend_from_slice(pq_shared_secret); 979 } 980 let zero_salt = [0_u8; 64]; 981 let (sending_header_key, receiving_next_header_key, ratchet_key) = 982 official_hkdf3(&zero_salt, &input, RADROOTS_SIMPLEX_OFFICIAL_X3DH_INFO)?; 983 Ok(RadrootsSimplexOfficialX3dhInit { 984 associated_data, 985 ratchet_key, 986 sending_header_key, 987 receiving_next_header_key, 988 accepted_pq_shared_secret: pq_shared_secret.map(<[u8]>::to_vec), 989 }) 990 } 991 992 fn push_official_msg_header_pq( 993 buffer: &mut Vec<u8>, 994 header: &RadrootsSimplexOfficialMsgHeader, 995 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 996 match ( 997 header.pq_public_key.as_deref(), 998 header.pq_ciphertext.as_deref(), 999 ) { 1000 (None, None) => buffer.push(b'0'), 1001 (Some(pq_public_key), None) => { 1002 validate_official_pq_public_key(pq_public_key)?; 1003 buffer.push(b'1'); 1004 buffer.push(b'P'); 1005 push_official_large_by_version( 1006 buffer, 1007 RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION, 1008 pq_public_key, 1009 )?; 1010 } 1011 (Some(pq_public_key), Some(pq_ciphertext)) => { 1012 validate_official_pq_public_key(pq_public_key)?; 1013 validate_official_pq_ciphertext(pq_ciphertext)?; 1014 buffer.push(b'1'); 1015 buffer.push(b'A'); 1016 push_official_large_by_version( 1017 buffer, 1018 RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION, 1019 pq_ciphertext, 1020 )?; 1021 push_official_large_by_version( 1022 buffer, 1023 RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION, 1024 pq_public_key, 1025 )?; 1026 } 1027 (None, Some(_)) => return Err(RadrootsSimplexSmpCryptoError::IncompletePqHeader), 1028 } 1029 Ok(()) 1030 } 1031 1032 fn read_official_msg_header_pq( 1033 cursor: &mut OfficialCursor<'_>, 1034 ) -> Result<RadrootsSimplexOfficialPqHeaderParts, RadrootsSimplexSmpCryptoError> { 1035 match cursor.read_byte()? { 1036 b'0' => Ok((None, None)), 1037 b'1' => match cursor.read_byte()? { 1038 b'P' => { 1039 let pq_public_key = cursor.read_official_large()?.to_vec(); 1040 validate_official_pq_public_key(&pq_public_key)?; 1041 Ok((Some(pq_public_key), None)) 1042 } 1043 b'A' => { 1044 let pq_ciphertext = cursor.read_official_large()?.to_vec(); 1045 let pq_public_key = cursor.read_official_large()?.to_vec(); 1046 validate_official_pq_ciphertext(&pq_ciphertext)?; 1047 validate_official_pq_public_key(&pq_public_key)?; 1048 Ok((Some(pq_public_key), Some(pq_ciphertext))) 1049 } 1050 value => Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 1051 value as usize, 1052 )), 1053 }, 1054 value => Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 1055 value as usize, 1056 )), 1057 } 1058 } 1059 1060 fn validate_official_pq_public_key(value: &[u8]) -> Result<(), RadrootsSimplexSmpCryptoError> { 1061 if value.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PUBLIC_KEY_LENGTH { 1062 return Err(RadrootsSimplexSmpCryptoError::InvalidPqKeyLength( 1063 value.len(), 1064 )); 1065 } 1066 Ok(()) 1067 } 1068 1069 fn validate_official_pq_ciphertext(value: &[u8]) -> Result<(), RadrootsSimplexSmpCryptoError> { 1070 if value.len() != RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_CIPHERTEXT_LENGTH { 1071 return Err(RadrootsSimplexSmpCryptoError::InvalidPqCiphertextLength( 1072 value.len(), 1073 )); 1074 } 1075 Ok(()) 1076 } 1077 1078 fn encode_official_urlsafe_bytes(bytes: &[u8]) -> String { 1079 URL_SAFE.encode(bytes) 1080 } 1081 1082 fn decode_official_urlsafe_bytes(value: &str) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 1083 URL_SAFE 1084 .decode(value.as_bytes()) 1085 .or_else(|_| URL_SAFE_NO_PAD.decode(value.as_bytes())) 1086 .map_err(|_| { 1087 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 1088 "invalid base64url field".to_owned(), 1089 ) 1090 }) 1091 } 1092 1093 fn split_official_x3dh_keys(value: &str) -> Result<(&str, &str), RadrootsSimplexSmpCryptoError> { 1094 let (key_1, rest) = value.split_once(',').ok_or_else(|| { 1095 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 1096 "`x3dh` field must contain two keys".to_owned(), 1097 ) 1098 })?; 1099 if rest.contains(',') { 1100 return Err( 1101 RadrootsSimplexSmpCryptoError::InvalidOfficialX3dhParameters( 1102 "`x3dh` field must contain two keys".to_owned(), 1103 ), 1104 ); 1105 } 1106 Ok((key_1, rest)) 1107 } 1108 1109 fn official_large_prefix_len(version: u16) -> Result<usize, RadrootsSimplexSmpCryptoError> { 1110 validate_official_version(version)?; 1111 Ok(if version >= RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 1112 2 1113 } else { 1114 1 1115 }) 1116 } 1117 1118 fn push_official_large_by_version( 1119 buffer: &mut Vec<u8>, 1120 version: u16, 1121 value: &[u8], 1122 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 1123 if version >= RADROOTS_SIMPLEX_OFFICIAL_E2E_PQ_VERSION { 1124 if value.len() > u16::MAX as usize { 1125 return Err(RadrootsSimplexSmpCryptoError::InvalidMessageLength { 1126 actual: value.len(), 1127 padded: u16::MAX as usize, 1128 }); 1129 } 1130 buffer.extend_from_slice(&(value.len() as u16).to_be_bytes()); 1131 } else { 1132 if value.len() > u8::MAX as usize { 1133 return Err(RadrootsSimplexSmpCryptoError::InvalidMessageLength { 1134 actual: value.len(), 1135 padded: u8::MAX as usize, 1136 }); 1137 } 1138 buffer.push(value.len() as u8); 1139 } 1140 buffer.extend_from_slice(value); 1141 Ok(()) 1142 } 1143 1144 fn push_official_short_bytes( 1145 buffer: &mut Vec<u8>, 1146 value: &[u8], 1147 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 1148 if value.len() > u8::MAX as usize { 1149 return Err(RadrootsSimplexSmpCryptoError::InvalidShortFieldLength( 1150 value.len(), 1151 )); 1152 } 1153 buffer.push(value.len() as u8); 1154 buffer.extend_from_slice(value); 1155 Ok(()) 1156 } 1157 1158 struct OfficialCursor<'a> { 1159 bytes: &'a [u8], 1160 position: usize, 1161 } 1162 1163 impl<'a> OfficialCursor<'a> { 1164 const fn new(bytes: &'a [u8]) -> Self { 1165 Self { bytes, position: 0 } 1166 } 1167 1168 fn finish(&self) -> Result<(), RadrootsSimplexSmpCryptoError> { 1169 if self.position == self.bytes.len() { 1170 Ok(()) 1171 } else { 1172 Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 1173 self.bytes.len() - self.position, 1174 )) 1175 } 1176 } 1177 1178 fn read_u16(&mut self) -> Result<u16, RadrootsSimplexSmpCryptoError> { 1179 let bytes = self.read_slice(2)?; 1180 Ok(u16::from_be_bytes([bytes[0], bytes[1]])) 1181 } 1182 1183 fn read_u32(&mut self) -> Result<u32, RadrootsSimplexSmpCryptoError> { 1184 let bytes = self.read_slice(4)?; 1185 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])) 1186 } 1187 1188 fn read_byte(&mut self) -> Result<u8, RadrootsSimplexSmpCryptoError> { 1189 let Some(value) = self.bytes.get(self.position) else { 1190 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength(0)); 1191 }; 1192 self.position += 1; 1193 Ok(*value) 1194 } 1195 1196 fn read_short_bytes(&mut self) -> Result<&'a [u8], RadrootsSimplexSmpCryptoError> { 1197 let length = self.read_byte()? as usize; 1198 self.read_slice(length) 1199 } 1200 1201 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], RadrootsSimplexSmpCryptoError> { 1202 let bytes = self.read_slice(N)?; 1203 let mut value = [0_u8; N]; 1204 value.copy_from_slice(bytes); 1205 Ok(value) 1206 } 1207 1208 fn read_slice(&mut self, len: usize) -> Result<&'a [u8], RadrootsSimplexSmpCryptoError> { 1209 let Some(bytes) = self.bytes.get(self.position..self.position + len) else { 1210 return Err(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength( 1211 self.bytes.len().saturating_sub(self.position), 1212 )); 1213 }; 1214 self.position += len; 1215 Ok(bytes) 1216 } 1217 1218 fn read_official_large(&mut self) -> Result<&'a [u8], RadrootsSimplexSmpCryptoError> { 1219 let first = *self 1220 .bytes 1221 .get(self.position) 1222 .ok_or(RadrootsSimplexSmpCryptoError::InvalidCiphertextLength(0))?; 1223 let len = if first < 32 { 1224 self.read_u16()? as usize 1225 } else { 1226 self.position += 1; 1227 first as usize 1228 }; 1229 self.read_slice(len) 1230 } 1231 1232 fn read_remaining(&mut self) -> &'a [u8] { 1233 let bytes = &self.bytes[self.position..]; 1234 self.position = self.bytes.len(); 1235 bytes 1236 } 1237 } 1238 1239 fn pq_seed(seed: &[u8]) -> [u8; RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH] { 1240 let digest = Sha256::digest(seed); 1241 let mut value = [0_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH]; 1242 value.copy_from_slice(&digest); 1243 value 1244 } 1245 1246 #[cfg(test)] 1247 mod tests { 1248 use super::*; 1249 1250 #[test] 1251 fn official_header_lengths_match_upstream_constants() { 1252 assert_eq!(official_ratchet_header_len(2, false).unwrap(), 88); 1253 assert_eq!(official_ratchet_header_len(3, false).unwrap(), 88); 1254 assert_eq!(official_ratchet_header_len(3, true).unwrap(), 2_310); 1255 assert_eq!(official_full_header_len(3, false).unwrap(), 123); 1256 assert_eq!(official_full_header_len(3, true).unwrap(), 2_345); 1257 assert_eq!( 1258 official_encoded_encrypted_header_len(2, false).unwrap(), 1259 123 1260 ); 1261 assert_eq!( 1262 official_encoded_encrypted_header_len(3, false).unwrap(), 1263 124 1264 ); 1265 assert_eq!( 1266 official_encoded_encrypted_header_len(3, true).unwrap(), 1267 2_346 1268 ); 1269 assert_eq!( 1270 official_encoded_encrypted_message_len(3, false, 15_840).unwrap(), 1271 15_982 1272 ); 1273 } 1274 1275 #[test] 1276 fn x448_key_agreement_roundtrips() { 1277 let alice = official_x448_keypair_from_seed(b"rr-synth-official-alice-x448"); 1278 let bob = official_x448_keypair_from_seed(b"rr-synth-official-bob-x448"); 1279 1280 let alice_secret = 1281 derive_official_x448_shared_secret(&alice.private_key, &bob.public_key).unwrap(); 1282 let bob_secret = 1283 derive_official_x448_shared_secret(&bob.private_key, &alice.public_key).unwrap(); 1284 1285 assert_eq!( 1286 alice.public_key.len(), 1287 RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH 1288 ); 1289 assert_eq!( 1290 alice.private_key.len(), 1291 RADROOTS_SIMPLEX_OFFICIAL_X448_KEY_LENGTH 1292 ); 1293 assert_eq!( 1294 alice_secret.len(), 1295 RADROOTS_SIMPLEX_OFFICIAL_X448_SHARED_SECRET_LENGTH 1296 ); 1297 assert_eq!(alice_secret, bob_secret); 1298 } 1299 1300 #[test] 1301 fn official_x448_der_public_key_roundtrips() { 1302 let keypair = official_x448_keypair_from_seed(b"rr-synth-official-der-x448"); 1303 let encoded = encode_official_x448_public_key_der(&keypair.public_key).unwrap(); 1304 assert_eq!(encoded.len(), 68); 1305 assert_eq!( 1306 decode_official_x448_public_key_der(&encoded).unwrap(), 1307 keypair.public_key 1308 ); 1309 } 1310 1311 #[test] 1312 fn official_no_pq_msg_header_roundtrips() { 1313 let keypair = official_x448_keypair_from_seed(b"rr-synth-official-header-x448"); 1314 let header = RadrootsSimplexOfficialMsgHeader { 1315 max_version: RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1316 dh_public_key: keypair.public_key, 1317 pq_public_key: None, 1318 pq_ciphertext: None, 1319 previous_sending_chain_length: 5, 1320 message_number: 8, 1321 }; 1322 let encoded = 1323 encode_official_msg_header(RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, &header) 1324 .unwrap(); 1325 assert_eq!(encoded.len(), 80); 1326 assert_eq!( 1327 decode_official_msg_header(RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, &encoded) 1328 .unwrap(), 1329 header 1330 ); 1331 } 1332 1333 #[test] 1334 fn official_pq_msg_headers_roundtrip_proposed_and_accepted_kem() { 1335 let keypair = official_x448_keypair_from_seed(b"rr-synth-official-header-pq-x448"); 1336 let pq_keypair = official_sntrup761_keypair_from_seed(b"rr-synth-official-header-pq-kem"); 1337 let (pq_ciphertext, _) = 1338 encapsulate_official_sntrup761(&pq_keypair.public_key, b"rr-synth-header-pq-ct") 1339 .unwrap(); 1340 let proposed = RadrootsSimplexOfficialMsgHeader { 1341 max_version: RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1342 dh_public_key: keypair.public_key.clone(), 1343 pq_public_key: Some(pq_keypair.public_key.clone()), 1344 pq_ciphertext: None, 1345 previous_sending_chain_length: 5, 1346 message_number: 8, 1347 }; 1348 let encoded_proposed = 1349 encode_official_msg_header(RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, &proposed) 1350 .unwrap(); 1351 assert_eq!(encoded_proposed.len(), 1_241); 1352 assert_eq!( 1353 decode_official_msg_header( 1354 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1355 &encoded_proposed 1356 ) 1357 .unwrap(), 1358 proposed 1359 ); 1360 1361 let accepted = RadrootsSimplexOfficialMsgHeader { 1362 max_version: RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1363 dh_public_key: keypair.public_key, 1364 pq_public_key: Some(pq_keypair.public_key), 1365 pq_ciphertext: Some(pq_ciphertext), 1366 previous_sending_chain_length: 9, 1367 message_number: 10, 1368 }; 1369 let encoded_accepted = 1370 encode_official_msg_header(RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, &accepted) 1371 .unwrap(); 1372 assert_eq!(encoded_accepted.len(), 2_282); 1373 assert_eq!( 1374 decode_official_msg_header( 1375 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1376 &encoded_accepted 1377 ) 1378 .unwrap(), 1379 accepted 1380 ); 1381 } 1382 1383 #[test] 1384 fn official_x3dh_params_uri_roundtrips() { 1385 let keypair_1 = official_x448_keypair_from_seed(b"rr-synth-official-x3dh-1"); 1386 let keypair_2 = official_x448_keypair_from_seed(b"rr-synth-official-x3dh-2"); 1387 let params = RadrootsSimplexOfficialX3dhParams { 1388 version_range: RadrootsSimplexSmpVersionRange::new( 1389 RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, 1390 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1391 ) 1392 .unwrap(), 1393 key_1: keypair_1.public_key, 1394 key_2: keypair_2.public_key, 1395 pq_public_key: None, 1396 pq_ciphertext: None, 1397 }; 1398 let encoded = encode_official_x3dh_params_uri(¶ms).unwrap(); 1399 assert!(encoded.starts_with("v=2-3&x3dh=MEIwBQYDK2VvAzkA")); 1400 assert!(encoded.contains(',')); 1401 assert_eq!(decode_official_x3dh_params_uri(&encoded).unwrap(), params); 1402 } 1403 1404 #[test] 1405 fn official_x3dh_params_rejects_incomplete_pq_fields() { 1406 let keypair_1 = official_x448_keypair_from_seed(b"rr-synth-official-x3dh-pq-1"); 1407 let keypair_2 = official_x448_keypair_from_seed(b"rr-synth-official-x3dh-pq-2"); 1408 let params = RadrootsSimplexOfficialX3dhParams { 1409 version_range: RadrootsSimplexSmpVersionRange::single( 1410 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1411 ), 1412 key_1: keypair_1.public_key, 1413 key_2: keypair_2.public_key, 1414 pq_public_key: None, 1415 pq_ciphertext: Some(vec![ 1416 0_u8; 1417 RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_CIPHERTEXT_LENGTH 1418 ]), 1419 }; 1420 assert_eq!( 1421 encode_official_x3dh_params_uri(¶ms).unwrap_err(), 1422 RadrootsSimplexSmpCryptoError::IncompletePqHeader 1423 ); 1424 } 1425 1426 #[test] 1427 fn official_x3dh_no_pq_init_matches_on_both_sides() { 1428 let receiver_key_1 = official_x448_keypair_from_seed(b"rr-synth-x3dh-rcv-1"); 1429 let receiver_key_2 = official_x448_keypair_from_seed(b"rr-synth-x3dh-rcv-2"); 1430 let sender_key_1 = official_x448_keypair_from_seed(b"rr-synth-x3dh-snd-1"); 1431 let sender_key_2 = official_x448_keypair_from_seed(b"rr-synth-x3dh-snd-2"); 1432 let receiver_params = RadrootsSimplexOfficialX3dhParams { 1433 version_range: RadrootsSimplexSmpVersionRange::new( 1434 RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, 1435 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1436 ) 1437 .unwrap(), 1438 key_1: receiver_key_1.public_key.clone(), 1439 key_2: receiver_key_2.public_key.clone(), 1440 pq_public_key: None, 1441 pq_ciphertext: None, 1442 }; 1443 let sender_params = RadrootsSimplexOfficialX3dhParams { 1444 version_range: receiver_params.version_range, 1445 key_1: sender_key_1.public_key.clone(), 1446 key_2: sender_key_2.public_key.clone(), 1447 pq_public_key: None, 1448 pq_ciphertext: None, 1449 }; 1450 1451 let sender_init = 1452 official_x3dh_sender_init(&sender_key_1, &sender_key_2, &receiver_params).unwrap(); 1453 let receiver_init = 1454 official_x3dh_receiver_init(&receiver_key_1, &receiver_key_2, &sender_params).unwrap(); 1455 1456 assert_eq!(sender_init, receiver_init); 1457 assert_eq!( 1458 sender_init.associated_data, 1459 [sender_key_1.public_key, receiver_key_1.public_key].concat() 1460 ); 1461 assert_eq!( 1462 sender_init.ratchet_key.len(), 1463 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1464 ); 1465 assert_eq!( 1466 sender_init.sending_header_key.len(), 1467 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1468 ); 1469 assert_eq!( 1470 sender_init.receiving_next_header_key.len(), 1471 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1472 ); 1473 assert!(sender_init.accepted_pq_shared_secret.is_none()); 1474 } 1475 1476 #[test] 1477 fn official_x3dh_pq_init_matches_on_both_sides() { 1478 let receiver_key_1 = official_x448_keypair_from_seed(b"rr-synth-x3dh-pq-rcv-1"); 1479 let receiver_key_2 = official_x448_keypair_from_seed(b"rr-synth-x3dh-pq-rcv-2"); 1480 let sender_key_1 = official_x448_keypair_from_seed(b"rr-synth-x3dh-pq-snd-1"); 1481 let sender_key_2 = official_x448_keypair_from_seed(b"rr-synth-x3dh-pq-snd-2"); 1482 let receiver_pq = official_sntrup761_keypair_from_seed(b"rr-synth-x3dh-pq-rcv-kem"); 1483 let sender_pq = official_sntrup761_keypair_from_seed(b"rr-synth-x3dh-pq-snd-kem"); 1484 let receiver_params = RadrootsSimplexOfficialX3dhParams { 1485 version_range: RadrootsSimplexSmpVersionRange::new( 1486 RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, 1487 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1488 ) 1489 .unwrap(), 1490 key_1: receiver_key_1.public_key.clone(), 1491 key_2: receiver_key_2.public_key.clone(), 1492 pq_public_key: Some(receiver_pq.public_key.clone()), 1493 pq_ciphertext: None, 1494 }; 1495 1496 let sender_init = official_x3dh_sender_init_accepting_pq( 1497 &sender_key_1, 1498 &sender_key_2, 1499 sender_pq.clone(), 1500 &receiver_params, 1501 b"rr-synth-x3dh-pq-encap", 1502 ) 1503 .unwrap(); 1504 let receiver_init = official_x3dh_receiver_init_accepting_pq( 1505 &receiver_key_1, 1506 &receiver_key_2, 1507 &receiver_pq, 1508 &sender_init.sender_params, 1509 ) 1510 .unwrap(); 1511 1512 assert_eq!(sender_init.init, receiver_init.init); 1513 assert_eq!(sender_init.pq_shared_secret, receiver_init.pq_shared_secret); 1514 assert_eq!( 1515 sender_init.init.accepted_pq_shared_secret.as_deref(), 1516 Some(sender_init.pq_shared_secret.as_slice()) 1517 ); 1518 assert_eq!( 1519 sender_init.sender_params.pq_public_key, 1520 Some(sender_pq.public_key) 1521 ); 1522 assert!(sender_init.sender_params.pq_ciphertext.is_some()); 1523 } 1524 1525 #[test] 1526 fn sntrup761_encapsulation_roundtrips() { 1527 let recipient = official_sntrup761_keypair_from_seed(b"rr-synth-official-pq-recipient"); 1528 let (ciphertext, sender_secret) = 1529 encapsulate_official_sntrup761(&recipient.public_key, b"rr-synth-official-pq-send") 1530 .unwrap(); 1531 let receiver_secret = 1532 decapsulate_official_sntrup761(&recipient.private_key, &ciphertext).unwrap(); 1533 1534 assert_eq!( 1535 recipient.public_key.len(), 1536 RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PUBLIC_KEY_LENGTH 1537 ); 1538 assert_eq!( 1539 recipient.private_key.len(), 1540 RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_PRIVATE_KEY_LENGTH 1541 ); 1542 assert_eq!( 1543 ciphertext.len(), 1544 RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_CIPHERTEXT_LENGTH 1545 ); 1546 assert_eq!( 1547 sender_secret.len(), 1548 RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_SHARED_SECRET_LENGTH 1549 ); 1550 assert_eq!(sender_secret, receiver_secret); 1551 } 1552 1553 #[test] 1554 fn official_aes_gcm_padding_authenticates_associated_data() { 1555 let key = [11_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH]; 1556 let iv = [12_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH]; 1557 let associated_data = b"rr-synth-official-associated-data"; 1558 let payload = official_aes_gcm_encrypt_padded( 1559 &key, 1560 &iv, 1561 b"hello official simplex", 1562 96, 1563 associated_data, 1564 ) 1565 .unwrap(); 1566 1567 assert_eq!( 1568 payload.auth_tag.len(), 1569 RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH 1570 ); 1571 assert_eq!(payload.ciphertext.len(), 96); 1572 assert_ne!(payload.ciphertext, b"hello official simplex"); 1573 assert_eq!( 1574 official_aes_gcm_decrypt_padded(&key, &iv, &payload, associated_data).unwrap(), 1575 b"hello official simplex" 1576 ); 1577 assert!(matches!( 1578 official_aes_gcm_decrypt_padded(&key, &iv, &payload, b"wrong-ad").unwrap_err(), 1579 RadrootsSimplexSmpCryptoError::AesGcmAuthenticationFailed 1580 )); 1581 } 1582 1583 #[test] 1584 fn official_encrypted_header_and_message_wire_roundtrip() { 1585 let header = RadrootsSimplexOfficialEncryptedHeader { 1586 version: RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1587 iv: [21_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH], 1588 auth_tag: vec![22_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH], 1589 body: vec![23_u8; RADROOTS_SIMPLEX_OFFICIAL_RATCHET_HEADER_LENGTH], 1590 }; 1591 let encoded_header = encode_official_encrypted_header(&header).unwrap(); 1592 assert_eq!(encoded_header.len(), 124); 1593 assert_eq!( 1594 decode_official_encrypted_header(&encoded_header).unwrap(), 1595 header 1596 ); 1597 1598 let message = RadrootsSimplexOfficialEncryptedMessage { 1599 encrypted_header: encoded_header, 1600 auth_tag: vec![24_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH], 1601 body: vec![25_u8; 96], 1602 }; 1603 let encoded = encode_official_encrypted_message( 1604 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 1605 &message, 1606 ) 1607 .unwrap(); 1608 assert_eq!(encoded.len(), 2 + 124 + 16 + 96); 1609 assert_eq!( 1610 decode_official_encrypted_message(&encoded).unwrap(), 1611 message 1612 ); 1613 } 1614 1615 #[test] 1616 fn official_encrypted_message_rejects_malformed_wire_lengths() { 1617 let header = RadrootsSimplexOfficialEncryptedHeader { 1618 version: 3, 1619 iv: [31_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_IV_LENGTH], 1620 auth_tag: vec![32_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH], 1621 body: vec![33_u8; RADROOTS_SIMPLEX_OFFICIAL_RATCHET_HEADER_LENGTH], 1622 }; 1623 let mut encoded_header = encode_official_encrypted_header(&header).unwrap(); 1624 encoded_header.truncate(encoded_header.len() - 1); 1625 assert!(matches!( 1626 decode_official_encrypted_header(&encoded_header).unwrap_err(), 1627 RadrootsSimplexSmpCryptoError::InvalidCiphertextLength(_) 1628 )); 1629 1630 let message = RadrootsSimplexOfficialEncryptedMessage { 1631 encrypted_header: encode_official_encrypted_header(&header).unwrap(), 1632 auth_tag: vec![34_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_AUTH_TAG_LENGTH - 1], 1633 body: vec![35_u8; 32], 1634 }; 1635 assert!(matches!( 1636 encode_official_encrypted_message(3, &message).unwrap_err(), 1637 RadrootsSimplexSmpCryptoError::InvalidSignatureLength(_) 1638 )); 1639 } 1640 1641 #[test] 1642 fn official_kdfs_split_root_and_chain_material() { 1643 let root = official_root_kdf( 1644 &[1_u8; RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH], 1645 &[2_u8; RADROOTS_SIMPLEX_OFFICIAL_X448_SHARED_SECRET_LENGTH], 1646 Some(&[3_u8; RADROOTS_SIMPLEX_OFFICIAL_SNTRUP761_SHARED_SECRET_LENGTH]), 1647 ) 1648 .unwrap(); 1649 let chain = official_chain_kdf(&root.chain_key).unwrap(); 1650 1651 assert_eq!( 1652 root.root_key.len(), 1653 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1654 ); 1655 assert_eq!( 1656 root.chain_key.len(), 1657 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1658 ); 1659 assert_eq!( 1660 root.next_header_key.len(), 1661 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1662 ); 1663 assert_eq!( 1664 chain.chain_key.len(), 1665 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1666 ); 1667 assert_eq!( 1668 chain.message_key.len(), 1669 RADROOTS_SIMPLEX_OFFICIAL_AES_KEY_LENGTH 1670 ); 1671 assert_ne!(chain.message_iv, chain.header_iv); 1672 } 1673 }