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