client.rs (48371B)
1 use crate::error::RadrootsSimplexSmpTransportError; 2 use crate::executor::{ 3 RadrootsSimplexSmpCommandTransport, RadrootsSimplexSmpSubscriptionReceiveRequest, 4 RadrootsSimplexSmpSubscriptionTransport, RadrootsSimplexSmpTransportRequest, 5 RadrootsSimplexSmpTransportResponse, 6 }; 7 use crate::frame::{RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE, RadrootsSimplexSmpTransportBlock}; 8 use crate::handshake::{ 9 RADROOTS_SIMPLEX_SMP_TLS_ALPN_V1, RadrootsSimplexSmpClientHello, RadrootsSimplexSmpServerHello, 10 RadrootsSimplexSmpTlsHandshakeEvidence, RadrootsSimplexSmpTlsPolicy, 11 RadrootsSimplexSmpTransportServerProof, validate_tls_handshake, 12 }; 13 use base64::Engine as _; 14 use base64::engine::general_purpose::{URL_SAFE, URL_SAFE_NO_PAD}; 15 use radroots_simplex_smp_crypto::prelude::{ 16 RadrootsSimplexSmpQueueAuthorizationMaterial, RadrootsSimplexSmpQueueAuthorizationScope, 17 RadrootsSimplexSmpSecretBoxChainKey, RadrootsSimplexSmpX25519Keypair, advance_secretbox_chain, 18 decode_x25519_public_key_x509, derive_shared_secret, encode_x25519_public_key_x509, 19 encrypt_padded, init_secretbox_chain, verify_signature, 20 }; 21 use radroots_simplex_smp_proto::prelude::{ 22 RADROOTS_SIMPLEX_SMP_AUTH_COMMANDS_TRANSPORT_VERSION, 23 RADROOTS_SIMPLEX_SMP_ENCRYPTED_BLOCK_TRANSPORT_VERSION, RadrootsSimplexSmpBrokerMessage, 24 RadrootsSimplexSmpCommandTransmission, RadrootsSimplexSmpCorrelationId, 25 RadrootsSimplexSmpServerAddress, 26 }; 27 use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; 28 use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; 29 use rustls::{ 30 ClientConfig, ClientConnection, DigitallySignedStruct, Error as RustlsError, SignatureScheme, 31 StreamOwned, 32 }; 33 use sha2::{Digest, Sha256}; 34 use std::collections::{BTreeMap, VecDeque}; 35 use std::io::{ErrorKind, Read, Write}; 36 use std::net::{IpAddr, TcpStream, ToSocketAddrs}; 37 use std::sync::Arc; 38 use std::time::Duration; 39 use x509_parser::prelude::FromDer; 40 41 #[derive(Default)] 42 pub struct RadrootsSimplexSmpTlsCommandTransport { 43 sessions: BTreeMap<String, RadrootsSimplexSmpLiveSession>, 44 } 45 46 const LIVE_SESSION_TIMEOUT: Duration = Duration::from_secs(5); 47 const LIVE_EMPTY_SUBSCRIPTION_TIMEOUT: Duration = Duration::from_millis(150); 48 49 struct RadrootsSimplexSmpLiveSession { 50 stream: StreamOwned<ClientConnection, TcpStream>, 51 transport_version: u16, 52 session_identifier: Vec<u8>, 53 send_chain_key: Option<RadrootsSimplexSmpSecretBoxChainKey>, 54 receive_chain_key: Option<RadrootsSimplexSmpSecretBoxChainKey>, 55 debug_shared_secret: Option<Vec<u8>>, 56 pending_broker_responses: VecDeque<RadrootsSimplexSmpTransportResponse>, 57 } 58 59 impl RadrootsSimplexSmpTlsCommandTransport { 60 pub fn new() -> Self { 61 Self::default() 62 } 63 64 fn session_key(server: &RadrootsSimplexSmpServerAddress, kind: &str) -> String { 65 let mut key = server.server_identity.clone(); 66 key.push('@'); 67 key.push_str(&server.hosts.join(",")); 68 key.push(':'); 69 key.push_str(&server.port.unwrap_or(5223).to_string()); 70 key.push('#'); 71 key.push_str(kind); 72 key 73 } 74 75 fn session_for( 76 &mut self, 77 server: &RadrootsSimplexSmpServerAddress, 78 kind: &str, 79 ) -> Result<&mut RadrootsSimplexSmpLiveSession, RadrootsSimplexSmpTransportError> { 80 let key = Self::session_key(server, kind); 81 if !self.sessions.contains_key(&key) { 82 let session = connect_live_session(server)?; 83 self.sessions.insert(key.clone(), session); 84 } 85 self.sessions.get_mut(&key).ok_or_else(|| { 86 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 87 "missing live SMP session for `{}`", 88 server.server_identity 89 )) 90 }) 91 } 92 } 93 94 impl RadrootsSimplexSmpCommandTransport for RadrootsSimplexSmpTlsCommandTransport { 95 type Error = RadrootsSimplexSmpTransportError; 96 97 fn execute( 98 &mut self, 99 request: RadrootsSimplexSmpTransportRequest, 100 ) -> Result<RadrootsSimplexSmpTransportResponse, Self::Error> { 101 let session_kind = session_kind_for_command(&request.command); 102 let key = Self::session_key(&request.server, session_kind); 103 let accepts_uncorrelated_subscription_response = 104 accepts_uncorrelated_subscription_response(&request.command); 105 match execute_live_request( 106 self.session_for(&request.server, session_kind)?, 107 &request, 108 accepts_uncorrelated_subscription_response, 109 ) { 110 Ok(response) => Ok(response), 111 Err(RadrootsSimplexSmpTransportError::LiveTransportIo(error)) => { 112 self.sessions.remove(&key); 113 let response = execute_live_request( 114 self.session_for(&request.server, session_kind)?, 115 &request, 116 accepts_uncorrelated_subscription_response, 117 ); 118 match response { 119 Ok(response) => Ok(response), 120 Err(RadrootsSimplexSmpTransportError::LiveTransportIo(_)) => { 121 Err(RadrootsSimplexSmpTransportError::LiveTransportIo(error)) 122 } 123 Err(error) => Err(error), 124 } 125 } 126 Err(error) => Err(error), 127 } 128 } 129 } 130 131 impl RadrootsSimplexSmpSubscriptionTransport for RadrootsSimplexSmpTlsCommandTransport { 132 fn receive_subscription( 133 &mut self, 134 request: RadrootsSimplexSmpSubscriptionReceiveRequest, 135 ) -> Result<Option<RadrootsSimplexSmpTransportResponse>, Self::Error> { 136 let key = Self::session_key(&request.server, "subscription"); 137 match read_live_response( 138 self.session_for(&request.server, "subscription")?, 139 &request.server, 140 None, 141 true, 142 None, 143 ) { 144 Ok(response) => Ok(response), 145 Err(RadrootsSimplexSmpTransportError::LiveTransportIo(error)) => { 146 self.sessions.remove(&key); 147 Err(RadrootsSimplexSmpTransportError::LiveTransportIo(error)) 148 } 149 Err(error) => Err(error), 150 } 151 } 152 } 153 154 fn session_kind_for_command( 155 command: &radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand, 156 ) -> &'static str { 157 match command { 158 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Sub 159 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Subs 160 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::NSub 161 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::NSubs 162 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Ack(_) => "subscription", 163 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Get 164 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::LGet => "poll", 165 _ => "command", 166 } 167 } 168 169 fn accepts_uncorrelated_subscription_response( 170 command: &radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand, 171 ) -> bool { 172 matches!( 173 command, 174 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Sub 175 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Subs 176 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::NSub 177 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::NSubs 178 | radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpCommand::Ack(_) 179 ) 180 } 181 182 fn execute_live_request( 183 session: &mut RadrootsSimplexSmpLiveSession, 184 request: &RadrootsSimplexSmpTransportRequest, 185 accept_uncorrelated_subscription_response: bool, 186 ) -> Result<RadrootsSimplexSmpTransportResponse, RadrootsSimplexSmpTransportError> { 187 let correlation_id = request 188 .correlation_id 189 .ok_or(RadrootsSimplexSmpTransportError::MissingCorrelationId)?; 190 let scope = RadrootsSimplexSmpQueueAuthorizationScope::new( 191 session.session_identifier.clone(), 192 correlation_id, 193 request.entity_id.clone(), 194 )?; 195 let material = RadrootsSimplexSmpQueueAuthorizationMaterial::for_command( 196 &scope, 197 &request.command, 198 session.transport_version, 199 &request.authorization, 200 )?; 201 let transmission = RadrootsSimplexSmpCommandTransmission { 202 authorization: material.authorization, 203 correlation_id: Some(correlation_id), 204 entity_id: request.entity_id.clone(), 205 command: request.command.clone(), 206 }; 207 let block = RadrootsSimplexSmpTransportBlock::from_command_transmissions( 208 &[transmission], 209 session.transport_version, 210 )?; 211 let encoded = encode_live_transport_block(session, &block)?; 212 session 213 .stream 214 .write_all(&encoded) 215 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 216 session 217 .stream 218 .flush() 219 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 220 221 let accepted_entity_id = 222 accept_uncorrelated_subscription_response.then_some(request.entity_id.as_slice()); 223 read_live_response( 224 session, 225 &request.server, 226 Some(correlation_id), 227 false, 228 accepted_entity_id, 229 )? 230 .ok_or_else(|| { 231 RadrootsSimplexSmpTransportError::LiveTransportIo( 232 "SMP command response was not available before the read timeout".into(), 233 ) 234 }) 235 } 236 237 fn read_live_response( 238 session: &mut RadrootsSimplexSmpLiveSession, 239 server: &RadrootsSimplexSmpServerAddress, 240 expected_correlation_id: Option<RadrootsSimplexSmpCorrelationId>, 241 timeout_is_empty: bool, 242 accepted_subscription_entity_id: Option<&[u8]>, 243 ) -> Result<Option<RadrootsSimplexSmpTransportResponse>, RadrootsSimplexSmpTransportError> { 244 if expected_correlation_id.is_none() 245 && let Some(response) = session.pending_broker_responses.pop_front() 246 { 247 return Ok(Some(response)); 248 } 249 if let Some(entity_id) = accepted_subscription_entity_id 250 && let Some(position) = session 251 .pending_broker_responses 252 .iter() 253 .position(|response| is_subscription_response_for_entity(response, entity_id)) 254 { 255 return Ok(session.pending_broker_responses.remove(position)); 256 } 257 let mut response_block = vec![0_u8; RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE]; 258 if timeout_is_empty { 259 set_live_read_timeout(session, LIVE_EMPTY_SUBSCRIPTION_TIMEOUT)?; 260 } 261 let read_result = session.stream.read_exact(&mut response_block); 262 if timeout_is_empty { 263 set_live_read_timeout(session, LIVE_SESSION_TIMEOUT)?; 264 } 265 if let Err(error) = read_result { 266 if timeout_is_empty && matches!(error.kind(), ErrorKind::WouldBlock | ErrorKind::TimedOut) { 267 return Ok(None); 268 } 269 return Err(RadrootsSimplexSmpTransportError::LiveTransportIo( 270 error.to_string(), 271 )); 272 } 273 let response_hash = Sha256::digest(&response_block).to_vec(); 274 let decoded = decode_live_transport_block(session, &response_block)?; 275 let transmissions = decoded.decode_broker_transmissions(session.transport_version)?; 276 let responses = transmissions 277 .into_iter() 278 .map(|transmission| RadrootsSimplexSmpTransportResponse { 279 server: server.clone(), 280 transport_version: session.transport_version, 281 transmission, 282 transport_hash: response_hash.clone(), 283 }) 284 .collect::<Vec<_>>(); 285 select_live_response( 286 &mut session.pending_broker_responses, 287 responses, 288 expected_correlation_id, 289 accepted_subscription_entity_id, 290 ) 291 } 292 293 fn select_live_response( 294 pending_broker_responses: &mut VecDeque<RadrootsSimplexSmpTransportResponse>, 295 mut responses: Vec<RadrootsSimplexSmpTransportResponse>, 296 expected_correlation_id: Option<RadrootsSimplexSmpCorrelationId>, 297 accepted_subscription_entity_id: Option<&[u8]>, 298 ) -> Result<Option<RadrootsSimplexSmpTransportResponse>, RadrootsSimplexSmpTransportError> { 299 if let Some(expected_correlation_id) = expected_correlation_id { 300 if let Some(position) = responses.iter().position(|response| { 301 response.transmission.correlation_id == Some(expected_correlation_id) 302 }) { 303 let matched_response = responses.remove(position); 304 pending_broker_responses.extend(responses); 305 return Ok(Some(matched_response)); 306 } 307 if let Some(entity_id) = accepted_subscription_entity_id 308 && let Some(position) = responses 309 .iter() 310 .position(|response| is_subscription_response_for_entity(response, entity_id)) 311 { 312 let matched_response = responses.remove(position); 313 pending_broker_responses.extend(responses); 314 return Ok(Some(matched_response)); 315 } 316 pending_broker_responses.extend(responses); 317 return Err(RadrootsSimplexSmpTransportError::CorrelationIdMismatch); 318 } 319 pending_broker_responses.extend(responses); 320 Ok(pending_broker_responses.pop_front()) 321 } 322 323 fn is_subscription_response_for_entity( 324 response: &RadrootsSimplexSmpTransportResponse, 325 entity_id: &[u8], 326 ) -> bool { 327 response.transmission.entity_id == entity_id 328 && matches!( 329 response.transmission.message, 330 RadrootsSimplexSmpBrokerMessage::Msg(_) 331 | RadrootsSimplexSmpBrokerMessage::NMsg { .. } 332 | RadrootsSimplexSmpBrokerMessage::Sok(_) 333 | RadrootsSimplexSmpBrokerMessage::Soks(_) 334 | RadrootsSimplexSmpBrokerMessage::Ok 335 | RadrootsSimplexSmpBrokerMessage::Err(_) 336 ) 337 } 338 339 fn set_live_read_timeout( 340 session: &mut RadrootsSimplexSmpLiveSession, 341 timeout: Duration, 342 ) -> Result<(), RadrootsSimplexSmpTransportError> { 343 session 344 .stream 345 .sock 346 .set_read_timeout(Some(timeout)) 347 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string())) 348 } 349 350 fn transport_debug_enabled() -> bool { 351 std::env::var_os("RADROOTS_SIMPLEX_DEBUG_TRANSPORT").is_some() 352 } 353 354 fn debug_sha256_label(label: &str, value: &[u8]) { 355 if transport_debug_enabled() { 356 eprintln!( 357 "[simplex-smp-transport] {label}: len={} sha256={}", 358 value.len(), 359 URL_SAFE_NO_PAD.encode(Sha256::digest(value)), 360 ); 361 } 362 } 363 364 fn encode_live_transport_block( 365 session: &mut RadrootsSimplexSmpLiveSession, 366 block: &RadrootsSimplexSmpTransportBlock, 367 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 368 if session.transport_version >= RADROOTS_SIMPLEX_SMP_ENCRYPTED_BLOCK_TRANSPORT_VERSION 369 && let Some(chain_key) = session.send_chain_key.as_mut() 370 { 371 return encode_encrypted_transport_payload(chain_key, &block.encode_payload()?); 372 } 373 block.encode() 374 } 375 376 fn encode_encrypted_transport_payload( 377 chain_key: &mut RadrootsSimplexSmpSecretBoxChainKey, 378 payload: &[u8], 379 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 380 let ((secretbox_key, nonce), next_chain_key) = advance_secretbox_chain(chain_key)?; 381 *chain_key = next_chain_key; 382 encrypt_padded( 383 &secretbox_key, 384 &nonce, 385 payload, 386 RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE - 16, 387 ) 388 .map_err(Into::into) 389 } 390 391 fn decode_live_transport_block( 392 session: &mut RadrootsSimplexSmpLiveSession, 393 bytes: &[u8], 394 ) -> Result<RadrootsSimplexSmpTransportBlock, RadrootsSimplexSmpTransportError> { 395 if session.transport_version >= RADROOTS_SIMPLEX_SMP_ENCRYPTED_BLOCK_TRANSPORT_VERSION 396 && let Some(chain_key) = session.receive_chain_key.as_mut() 397 { 398 match decode_encrypted_transport_block(chain_key, bytes) { 399 Ok(block) => { 400 let payload = block.encode_payload()?; 401 debug_sha256_label("live-response-payload", &payload); 402 return Ok(block); 403 } 404 Err(error) => { 405 if transport_debug_enabled() { 406 eprintln!("[simplex-smp-transport] live response decrypt failed: {error}"); 407 debug_sha256_label("live-response-ciphertext", bytes); 408 } 409 if let Some(send_chain_key) = session.send_chain_key.as_ref() { 410 let mut alternate_chain_key = send_chain_key.clone(); 411 if decode_encrypted_transport_block(&mut alternate_chain_key, bytes).is_ok() { 412 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 413 "server response decrypted with the outbound chain key; live SMP block direction is assigned incorrectly".into(), 414 )); 415 } 416 } 417 debug_probe_transport_candidates(session, bytes); 418 if let Ok(block) = RadrootsSimplexSmpTransportBlock::decode(bytes) { 419 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 420 format!( 421 "server returned plaintext SMP block while encrypted transport was expected: {:?}", 422 block.transmissions.first().map(|t| &t[..t.len().min(8)]) 423 ), 424 )); 425 } 426 return Err(error); 427 } 428 } 429 } 430 RadrootsSimplexSmpTransportBlock::decode(bytes) 431 } 432 433 fn decode_encrypted_transport_block( 434 chain_key: &mut RadrootsSimplexSmpSecretBoxChainKey, 435 bytes: &[u8], 436 ) -> Result<RadrootsSimplexSmpTransportBlock, RadrootsSimplexSmpTransportError> { 437 let ((secretbox_key, nonce), next_chain_key) = advance_secretbox_chain(chain_key)?; 438 let payload = 439 radroots_simplex_smp_crypto::prelude::decrypt_padded(&secretbox_key, &nonce, bytes)?; 440 let block = RadrootsSimplexSmpTransportBlock::from_payload(&payload)?; 441 *chain_key = next_chain_key; 442 Ok(block) 443 } 444 445 fn debug_probe_transport_candidates(session: &mut RadrootsSimplexSmpLiveSession, bytes: &[u8]) { 446 if !transport_debug_enabled() { 447 return; 448 } 449 let Some(shared_secret) = session.debug_shared_secret.as_ref() else { 450 return; 451 }; 452 let Ok((first_chain_key, second_chain_key)) = 453 init_secretbox_chain(&session.session_identifier, shared_secret) 454 else { 455 return; 456 }; 457 for (label, chain_key) in [ 458 ("initial-first", first_chain_key), 459 ("initial-second", second_chain_key), 460 ] { 461 let Ok(((secretbox_key, nonce), _)) = advance_secretbox_chain(&chain_key) else { 462 continue; 463 }; 464 let result = 465 radroots_simplex_smp_crypto::prelude::decrypt_padded(&secretbox_key, &nonce, bytes); 466 match result { 467 Ok(payload) => { 468 eprintln!("[simplex-smp-transport] debug candidate {label} decrypted live block"); 469 debug_sha256_label("debug-candidate-payload", &payload); 470 } 471 Err(error) => { 472 eprintln!("[simplex-smp-transport] debug candidate {label} failed: {error}"); 473 } 474 } 475 } 476 } 477 478 fn connect_live_session( 479 server: &RadrootsSimplexSmpServerAddress, 480 ) -> Result<RadrootsSimplexSmpLiveSession, RadrootsSimplexSmpTransportError> { 481 let mut last_error = None; 482 for host in &server.hosts { 483 match connect_live_session_host(server, host) { 484 Ok(session) => return Ok(session), 485 Err(error) => last_error = Some(error), 486 } 487 } 488 489 Err(last_error.unwrap_or_else(|| { 490 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 491 "SMP server `{}` has no usable hosts", 492 server.server_identity 493 )) 494 })) 495 } 496 497 fn connect_live_session_host( 498 server: &RadrootsSimplexSmpServerAddress, 499 host: &str, 500 ) -> Result<RadrootsSimplexSmpLiveSession, RadrootsSimplexSmpTransportError> { 501 let port = server.port.unwrap_or(5223); 502 let mut addresses = (host, port).to_socket_addrs().map_err(|error| { 503 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 504 "failed to resolve SMP server host `{host}:{port}`: {error}" 505 )) 506 })?; 507 let socket_addr = addresses.next().ok_or_else(|| { 508 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 509 "failed to resolve SMP server host `{host}:{port}`" 510 )) 511 })?; 512 let tcp = TcpStream::connect_timeout(&socket_addr, LIVE_SESSION_TIMEOUT).map_err(|error| { 513 RadrootsSimplexSmpTransportError::LiveTransportIo(format!( 514 "failed to connect to SMP server `{host}:{port}`: {error}" 515 )) 516 })?; 517 tcp.set_nodelay(true) 518 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 519 tcp.set_read_timeout(Some(LIVE_SESSION_TIMEOUT)) 520 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 521 tcp.set_write_timeout(Some(LIVE_SESSION_TIMEOUT)) 522 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 523 524 let server_name = match host.parse::<IpAddr>() { 525 Ok(address) => ServerName::IpAddress(address.into()), 526 Err(_) => ServerName::try_from(host.to_owned()).map_err(|_| { 527 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 528 "invalid SMP server name `{host}`" 529 )) 530 })?, 531 }; 532 let verifier = Arc::new(PermissiveSimplexServerVerifier); 533 let mut config = ClientConfig::builder() 534 .dangerous() 535 .with_custom_certificate_verifier(verifier) 536 .with_no_client_auth(); 537 config.alpn_protocols = vec![RADROOTS_SIMPLEX_SMP_TLS_ALPN_V1.as_bytes().to_vec()]; 538 539 let mut stream = StreamOwned::new( 540 ClientConnection::new(Arc::new(config), server_name).map_err(|error| { 541 RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()) 542 })?, 543 tcp, 544 ); 545 while stream.conn.is_handshaking() { 546 stream.conn.complete_io(&mut stream.sock).map_err(|error| { 547 RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()) 548 })?; 549 } 550 551 let peer_certs = stream 552 .conn 553 .peer_certificates() 554 .ok_or(RadrootsSimplexSmpTransportError::MissingPeerCertificates)? 555 .to_vec(); 556 let server_hello = read_server_hello(&mut stream)?; 557 let actual_identity = matching_server_identity(&peer_certs, &server.server_identity)?; 558 let expected_identity = canonical_server_identity(&server.server_identity)?; 559 let mut policy = RadrootsSimplexSmpTlsPolicy::modern(expected_identity.clone()); 560 policy.require_tls_unique_binding = false; 561 let transport_version = validate_tls_handshake( 562 &policy, 563 &server_hello, 564 &RadrootsSimplexSmpTlsHandshakeEvidence { 565 confirmed_alpn: stream 566 .conn 567 .alpn_protocol() 568 .map(|value| String::from_utf8_lossy(value).into_owned()), 569 session_resumed: false, 570 certificate_chain_length: peer_certs.len(), 571 online_certificate_fingerprint: actual_identity, 572 tls_unique_channel_binding: None, 573 }, 574 )?; 575 let transport_keypair = 576 if transport_version >= RADROOTS_SIMPLEX_SMP_AUTH_COMMANDS_TRANSPORT_VERSION { 577 Some(RadrootsSimplexSmpX25519Keypair::generate()?) 578 } else { 579 None 580 }; 581 let client_hello = RadrootsSimplexSmpClientHello { 582 chosen_version: transport_version, 583 server_key_hash: decode_server_identity(&expected_identity)?, 584 client_key: transport_keypair 585 .as_ref() 586 .map(|keypair| encode_x25519_public_key_x509(&keypair.public_key)) 587 .transpose()?, 588 proxy_server: false, 589 ignored_part: Vec::new(), 590 }; 591 let encoded_client_hello = client_hello.encode()?; 592 if transport_debug_enabled() { 593 debug_sha256_label("client-hello", &encoded_client_hello); 594 debug_sha256_label("server-session-id", &server_hello.session_identifier); 595 } 596 stream 597 .write_all(&encoded_client_hello) 598 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 599 stream 600 .flush() 601 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 602 603 let mut debug_shared_secret = None; 604 let (receive_chain_key, send_chain_key) = 605 if transport_version >= RADROOTS_SIMPLEX_SMP_ENCRYPTED_BLOCK_TRANSPORT_VERSION { 606 let server_key = decode_server_transport_public_key( 607 server_hello 608 .server_proof 609 .as_ref() 610 .ok_or(RadrootsSimplexSmpTransportError::MissingServerProof)?, 611 )?; 612 let shared_secret = derive_shared_secret( 613 &transport_keypair 614 .as_ref() 615 .ok_or(RadrootsSimplexSmpTransportError::MissingServerProof)? 616 .private_key, 617 &server_key, 618 )?; 619 if transport_debug_enabled() { 620 if let Some(keypair) = transport_keypair.as_ref() { 621 debug_sha256_label("client-transport-public-key", &keypair.public_key); 622 } 623 debug_sha256_label("server-transport-public-key", &server_key); 624 } 625 debug_shared_secret = transport_debug_enabled().then_some(shared_secret.clone()); 626 let (receive_chain_key, send_chain_key) = 627 init_secretbox_chain(&server_hello.session_identifier, &shared_secret)?; 628 (Some(receive_chain_key), Some(send_chain_key)) 629 } else { 630 (None, None) 631 }; 632 633 Ok(RadrootsSimplexSmpLiveSession { 634 stream, 635 transport_version, 636 session_identifier: server_hello.session_identifier, 637 send_chain_key, 638 receive_chain_key, 639 debug_shared_secret, 640 pending_broker_responses: VecDeque::new(), 641 }) 642 } 643 644 fn decode_server_transport_public_key( 645 proof: &RadrootsSimplexSmpTransportServerProof, 646 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 647 let (signed_object, signature) = decode_signed_server_key_parts(&proof.signed_server_key)?; 648 if transport_debug_enabled() { 649 eprintln!( 650 "[simplex-smp-transport] signed-server-key: proof_len={} signed_object_len={} signature_len={}", 651 proof.signed_server_key.len(), 652 signed_object.len(), 653 signature.len() 654 ); 655 } 656 if !proof.certificate_payload.is_empty() { 657 let verify_key = decode_server_certificate_verify_key(&proof.certificate_payload)?; 658 verify_signature(signed_object, &verify_key, signature).map_err(|error| { 659 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 660 "failed to verify SMP server transport key signature: {error}" 661 )) 662 })?; 663 } 664 665 decode_x25519_public_key_x509(signed_object) 666 .or_else(|_| { 667 first_der_sequence_element(signed_object) 668 .and_then(|candidate| decode_x25519_public_key_x509(candidate).map_err(Into::into)) 669 }) 670 .map_err(|error: RadrootsSimplexSmpTransportError| { 671 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 672 "failed to decode verified SMP server transport key: {error}" 673 )) 674 }) 675 } 676 677 fn first_der_sequence_element(bytes: &[u8]) -> Result<&[u8], RadrootsSimplexSmpTransportError> { 678 let (sequence_tag, _, sequence_header_end, sequence_content_end) = parse_der_element(bytes, 0)?; 679 if sequence_tag != 0x30 { 680 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 681 "invalid SMP server proof: expected DER sequence".into(), 682 )); 683 } 684 let (_, element_start, _, element_end) = parse_der_element(bytes, sequence_header_end)?; 685 if element_end > sequence_content_end { 686 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 687 "invalid SMP server proof: first element exceeds sequence bounds".into(), 688 )); 689 } 690 Ok(&bytes[element_start..element_end]) 691 } 692 693 fn decode_signed_server_key_parts( 694 bytes: &[u8], 695 ) -> Result<(&[u8], &[u8]), RadrootsSimplexSmpTransportError> { 696 let (sequence_tag, _, sequence_header_end, sequence_content_end) = parse_der_element(bytes, 0)?; 697 if sequence_tag != 0x30 { 698 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 699 "invalid SMP server proof: signed key is not a DER sequence".into(), 700 )); 701 } 702 703 let (_, signed_object_start, _, signed_object_end) = 704 parse_der_element(bytes, sequence_header_end)?; 705 let (_, _, _, algorithm_end) = parse_der_element(bytes, signed_object_end)?; 706 if algorithm_end > sequence_content_end { 707 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 708 "invalid SMP server proof: signature algorithm exceeds sequence bounds".into(), 709 )); 710 } 711 let (signature_tag, _, signature_value_start, signature_end) = 712 parse_der_element(bytes, algorithm_end)?; 713 if signature_tag != 0x03 { 714 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 715 "invalid SMP server proof: expected DER bit string signature".into(), 716 )); 717 } 718 if signature_end > sequence_content_end { 719 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 720 "invalid SMP server proof: signature exceeds sequence bounds".into(), 721 )); 722 } 723 let signature_value = bytes 724 .get(signature_value_start..signature_end) 725 .ok_or_else(|| { 726 RadrootsSimplexSmpTransportError::InvalidServerAddress( 727 "invalid SMP server proof: truncated signature".into(), 728 ) 729 })?; 730 let (unused_bits, signature) = signature_value.split_first().ok_or_else(|| { 731 RadrootsSimplexSmpTransportError::InvalidServerAddress( 732 "invalid SMP server proof: missing signature payload".into(), 733 ) 734 })?; 735 if *unused_bits != 0 { 736 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 737 "invalid SMP server proof: unsupported signature bit padding".into(), 738 )); 739 } 740 Ok((&bytes[signed_object_start..signed_object_end], signature)) 741 } 742 743 fn decode_server_certificate_verify_key( 744 certificate_payload: &[u8], 745 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 746 let Some(&cert_count) = certificate_payload.first() else { 747 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 748 "invalid SMP server proof: missing certificate chain".into(), 749 )); 750 }; 751 if cert_count == 0 { 752 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 753 "invalid SMP server proof: empty certificate chain".into(), 754 )); 755 } 756 let (certificate_der, _) = read_large_handshake_field(certificate_payload, 1)?; 757 let (_, certificate) = x509_parser::certificate::X509Certificate::from_der(&certificate_der) 758 .map_err(|error| { 759 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 760 "failed to parse SMP proof certificate: {error}" 761 )) 762 })?; 763 Ok(certificate 764 .tbs_certificate 765 .subject_pki 766 .subject_public_key 767 .data 768 .to_vec()) 769 } 770 771 fn read_large_handshake_field( 772 bytes: &[u8], 773 offset: usize, 774 ) -> Result<(Vec<u8>, usize), RadrootsSimplexSmpTransportError> { 775 let Some(length_bytes) = bytes.get(offset..offset + 2) else { 776 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 777 "invalid SMP server proof: truncated certificate length".into(), 778 )); 779 }; 780 let length = u16::from_be_bytes([length_bytes[0], length_bytes[1]]) as usize; 781 let start = offset + 2; 782 let end = start + length; 783 let value = bytes.get(start..end).ok_or_else(|| { 784 RadrootsSimplexSmpTransportError::InvalidServerAddress( 785 "invalid SMP server proof: certificate exceeds payload".into(), 786 ) 787 })?; 788 Ok((value.to_vec(), end)) 789 } 790 791 fn parse_der_element( 792 bytes: &[u8], 793 offset: usize, 794 ) -> Result<(u8, usize, usize, usize), RadrootsSimplexSmpTransportError> { 795 let tag = *bytes.get(offset).ok_or_else(|| { 796 RadrootsSimplexSmpTransportError::InvalidServerAddress( 797 "invalid SMP server proof: truncated DER element".into(), 798 ) 799 })?; 800 let length_offset = offset + 1; 801 let length_tag = *bytes.get(length_offset).ok_or_else(|| { 802 RadrootsSimplexSmpTransportError::InvalidServerAddress( 803 "invalid SMP server proof: missing DER length".into(), 804 ) 805 })?; 806 let (value_len, header_len) = if length_tag & 0x80 == 0 { 807 (length_tag as usize, 2) 808 } else { 809 let length_bytes = (length_tag & 0x7f) as usize; 810 if length_bytes == 0 || length_bytes > 4 { 811 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 812 "invalid SMP server proof: unsupported DER length encoding".into(), 813 )); 814 } 815 let length_start = length_offset + 1; 816 let length_end = length_start + length_bytes; 817 let encoded_length = bytes.get(length_start..length_end).ok_or_else(|| { 818 RadrootsSimplexSmpTransportError::InvalidServerAddress( 819 "invalid SMP server proof: truncated DER length".into(), 820 ) 821 })?; 822 let value_len = encoded_length 823 .iter() 824 .fold(0_usize, |acc, byte| (acc << 8) | (*byte as usize)); 825 (value_len, 2 + length_bytes) 826 }; 827 let value_start = offset + header_len; 828 let value_end = value_start + value_len; 829 if value_end > bytes.len() { 830 return Err(RadrootsSimplexSmpTransportError::InvalidServerAddress( 831 "invalid SMP server proof: DER element exceeds input".into(), 832 )); 833 } 834 Ok((tag, offset, value_start, value_end)) 835 } 836 837 fn read_server_hello( 838 stream: &mut StreamOwned<ClientConnection, TcpStream>, 839 ) -> Result<RadrootsSimplexSmpServerHello, RadrootsSimplexSmpTransportError> { 840 let mut block = vec![0_u8; RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE]; 841 stream 842 .read_exact(&mut block) 843 .map_err(|error| RadrootsSimplexSmpTransportError::LiveTransportIo(error.to_string()))?; 844 RadrootsSimplexSmpServerHello::decode(&block) 845 } 846 847 fn matching_server_identity( 848 chain: &[CertificateDer<'static>], 849 expected_identity: &str, 850 ) -> Result<String, RadrootsSimplexSmpTransportError> { 851 let expected_identity = canonical_server_identity(expected_identity)?; 852 for certificate in chain { 853 let identity = server_identity_from_certificate(certificate.as_ref())?; 854 if identity == expected_identity { 855 return Ok(identity); 856 } 857 } 858 Err(RadrootsSimplexSmpTransportError::ServerIdentityMismatch { 859 expected: expected_identity, 860 actual: chain 861 .first() 862 .map(|certificate| server_identity_from_certificate(certificate.as_ref())) 863 .transpose()? 864 .unwrap_or_default(), 865 }) 866 } 867 868 fn server_identity_from_certificate( 869 der: &[u8], 870 ) -> Result<String, RadrootsSimplexSmpTransportError> { 871 x509_parser::certificate::X509Certificate::from_der(der).map_err(|error| { 872 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 873 "failed to parse SMP certificate: {error}" 874 )) 875 })?; 876 let digest = Sha256::digest(der); 877 Ok(URL_SAFE_NO_PAD.encode(digest)) 878 } 879 880 fn canonical_server_identity(value: &str) -> Result<String, RadrootsSimplexSmpTransportError> { 881 URL_SAFE_NO_PAD 882 .decode(value) 883 .or_else(|_| URL_SAFE.decode(value)) 884 .map(|decoded| URL_SAFE_NO_PAD.encode(decoded)) 885 .map_err(|_| { 886 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 887 "invalid base64url server identity `{value}`" 888 )) 889 }) 890 } 891 892 fn decode_server_identity(value: &str) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 893 URL_SAFE_NO_PAD 894 .decode(value) 895 .or_else(|_| URL_SAFE.decode(value)) 896 .map_err(|_| { 897 RadrootsSimplexSmpTransportError::InvalidServerAddress(format!( 898 "invalid base64url server identity `{value}`" 899 )) 900 }) 901 } 902 903 #[derive(Debug)] 904 struct PermissiveSimplexServerVerifier; 905 906 impl ServerCertVerifier for PermissiveSimplexServerVerifier { 907 fn verify_server_cert( 908 &self, 909 _end_entity: &CertificateDer<'_>, 910 _intermediates: &[CertificateDer<'_>], 911 _server_name: &ServerName<'_>, 912 _ocsp_response: &[u8], 913 _now: UnixTime, 914 ) -> Result<ServerCertVerified, RustlsError> { 915 Ok(ServerCertVerified::assertion()) 916 } 917 918 fn verify_tls12_signature( 919 &self, 920 _message: &[u8], 921 _cert: &CertificateDer<'_>, 922 _dss: &DigitallySignedStruct, 923 ) -> Result<HandshakeSignatureValid, RustlsError> { 924 Ok(HandshakeSignatureValid::assertion()) 925 } 926 927 fn verify_tls13_signature( 928 &self, 929 _message: &[u8], 930 _cert: &CertificateDer<'_>, 931 _dss: &DigitallySignedStruct, 932 ) -> Result<HandshakeSignatureValid, RustlsError> { 933 Ok(HandshakeSignatureValid::assertion()) 934 } 935 936 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { 937 vec![ 938 SignatureScheme::ED25519, 939 SignatureScheme::ECDSA_NISTP256_SHA256, 940 SignatureScheme::ECDSA_NISTP384_SHA384, 941 SignatureScheme::RSA_PSS_SHA256, 942 SignatureScheme::RSA_PSS_SHA384, 943 SignatureScheme::RSA_PKCS1_SHA256, 944 SignatureScheme::RSA_PKCS1_SHA384, 945 ] 946 } 947 } 948 949 #[cfg(test)] 950 mod tests { 951 use super::{ 952 canonical_server_identity, decode_encrypted_transport_block, 953 decode_server_transport_public_key, encode_encrypted_transport_payload, 954 select_live_response, 955 }; 956 use crate::handshake::RadrootsSimplexSmpTransportServerProof; 957 use crate::prelude::{RadrootsSimplexSmpTransportBlock, RadrootsSimplexSmpTransportResponse}; 958 use radroots_simplex_smp_crypto::prelude::{ 959 RadrootsSimplexSmpX25519Keypair, encode_x25519_public_key_x509, init_secretbox_chain, 960 }; 961 use radroots_simplex_smp_proto::prelude::{ 962 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, RadrootsSimplexSmpBrokerMessage, 963 RadrootsSimplexSmpBrokerTransmission, RadrootsSimplexSmpCommand, 964 RadrootsSimplexSmpCommandTransmission, RadrootsSimplexSmpCorrelationId, 965 RadrootsSimplexSmpReceivedMessage, RadrootsSimplexSmpServerAddress, 966 }; 967 use std::collections::VecDeque; 968 969 #[test] 970 fn canonicalizes_padded_and_unpadded_server_identity() { 971 assert_eq!(canonical_server_identity("YWJjZA").unwrap(), "YWJjZA"); 972 assert_eq!(canonical_server_identity("YWJjZA==").unwrap(), "YWJjZA"); 973 } 974 975 #[test] 976 fn extracts_spki_from_signed_server_key_sequence() { 977 let keypair = RadrootsSimplexSmpX25519Keypair::from_seed(b"transport-proof"); 978 let spki = encode_x25519_public_key_x509(&keypair.public_key).unwrap(); 979 let empty_sequence = der_sequence(core::iter::once(&[][..])); 980 let signature = [0x03, 0x01, 0x00]; 981 let signed_object = der_sequence([ 982 spki.as_slice(), 983 empty_sequence.as_slice(), 984 signature.as_slice(), 985 ]); 986 let proof = RadrootsSimplexSmpTransportServerProof { 987 certificate_payload: Vec::new(), 988 signed_server_key: signed_object, 989 }; 990 assert_eq!( 991 decode_server_transport_public_key(&proof).unwrap(), 992 keypair.public_key 993 ); 994 } 995 996 #[test] 997 fn encrypted_transport_blocks_use_upstream_client_chain_direction() { 998 let session_identifier = b"rr-synth-session-id"; 999 let shared_secret = b"rr-synth-shared-secret"; 1000 let (mut server_send_chain, mut server_receive_chain) = 1001 init_secretbox_chain(session_identifier, shared_secret).unwrap(); 1002 let (client_receive_chain, client_send_chain) = 1003 init_secretbox_chain(session_identifier, shared_secret).unwrap(); 1004 let mut client_receive_chain_for_response = client_receive_chain.clone(); 1005 let mut client_send_chain_for_request = client_send_chain.clone(); 1006 1007 let command_transmission = RadrootsSimplexSmpCommandTransmission { 1008 authorization: Vec::new(), 1009 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([3_u8; 24])), 1010 entity_id: b"rr-synth-queue".to_vec(), 1011 command: RadrootsSimplexSmpCommand::Ping, 1012 }; 1013 let command_block = RadrootsSimplexSmpTransportBlock::from_command_transmissions( 1014 std::slice::from_ref(&command_transmission), 1015 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 1016 ) 1017 .unwrap(); 1018 let encrypted_command = encode_encrypted_transport_payload( 1019 &mut client_send_chain_for_request, 1020 &command_block.encode_payload().unwrap(), 1021 ) 1022 .unwrap(); 1023 assert_eq!( 1024 decode_encrypted_transport_block(&mut server_receive_chain, &encrypted_command) 1025 .unwrap() 1026 .decode_command_transmissions(RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION) 1027 .unwrap(), 1028 vec![command_transmission] 1029 ); 1030 1031 let broker_transmission = RadrootsSimplexSmpBrokerTransmission { 1032 authorization: Vec::new(), 1033 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([3_u8; 24])), 1034 entity_id: b"rr-synth-queue".to_vec(), 1035 message: RadrootsSimplexSmpBrokerMessage::Ok, 1036 }; 1037 let broker_block = RadrootsSimplexSmpTransportBlock::from_broker_transmissions( 1038 std::slice::from_ref(&broker_transmission), 1039 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 1040 ) 1041 .unwrap(); 1042 let encrypted_broker = encode_encrypted_transport_payload( 1043 &mut server_send_chain, 1044 &broker_block.encode_payload().unwrap(), 1045 ) 1046 .unwrap(); 1047 assert_eq!( 1048 decode_encrypted_transport_block( 1049 &mut client_receive_chain_for_response, 1050 &encrypted_broker, 1051 ) 1052 .unwrap() 1053 .decode_broker_transmissions(RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION) 1054 .unwrap(), 1055 vec![broker_transmission] 1056 ); 1057 1058 let mut wrong_response_chain = client_send_chain; 1059 let wrong_direction_broker = encode_encrypted_transport_payload( 1060 &mut wrong_response_chain, 1061 &broker_block.encode_payload().unwrap(), 1062 ) 1063 .unwrap(); 1064 let mut fresh_client_receive_chain = client_receive_chain; 1065 assert!( 1066 decode_encrypted_transport_block( 1067 &mut fresh_client_receive_chain, 1068 &wrong_direction_broker 1069 ) 1070 .is_err() 1071 ); 1072 } 1073 1074 #[test] 1075 fn ack_uses_subscription_session_state() { 1076 assert_eq!( 1077 super::session_kind_for_command(&RadrootsSimplexSmpCommand::Ack(b"message".to_vec())), 1078 "subscription" 1079 ); 1080 assert!(super::accepts_uncorrelated_subscription_response( 1081 &RadrootsSimplexSmpCommand::Ack(b"message".to_vec()) 1082 )); 1083 assert!(super::accepts_uncorrelated_subscription_response( 1084 &RadrootsSimplexSmpCommand::Sub 1085 )); 1086 } 1087 1088 #[test] 1089 fn strict_command_selection_buffers_unmatched_response_and_errors() { 1090 let mut pending = VecDeque::new(); 1091 let expected = RadrootsSimplexSmpCorrelationId::new([1_u8; 24]); 1092 let unmatched = response( 1093 Some(RadrootsSimplexSmpCorrelationId::new([2_u8; 24])), 1094 b"rr-synth-entity", 1095 RadrootsSimplexSmpBrokerMessage::Ok, 1096 ); 1097 1098 assert_eq!( 1099 select_live_response(&mut pending, vec![unmatched.clone()], Some(expected), None) 1100 .unwrap_err(), 1101 crate::prelude::RadrootsSimplexSmpTransportError::CorrelationIdMismatch 1102 ); 1103 assert_eq!(pending.into_iter().collect::<Vec<_>>(), vec![unmatched]); 1104 } 1105 1106 #[test] 1107 fn matched_response_wins_and_buffers_subscription_message() { 1108 let mut pending = VecDeque::new(); 1109 let expected = RadrootsSimplexSmpCorrelationId::new([1_u8; 24]); 1110 let message = response( 1111 None, 1112 b"rr-synth-entity", 1113 RadrootsSimplexSmpBrokerMessage::Msg(RadrootsSimplexSmpReceivedMessage { 1114 message_id: b"message-1".to_vec(), 1115 encrypted_body: b"body".to_vec(), 1116 }), 1117 ); 1118 let matched = response( 1119 Some(expected), 1120 b"rr-synth-entity", 1121 RadrootsSimplexSmpBrokerMessage::Sok(None), 1122 ); 1123 1124 let selected = select_live_response( 1125 &mut pending, 1126 vec![message.clone(), matched.clone()], 1127 Some(expected), 1128 Some(b"rr-synth-entity"), 1129 ) 1130 .unwrap(); 1131 1132 assert_eq!(selected, Some(matched)); 1133 assert_eq!(pending.into_iter().collect::<Vec<_>>(), vec![message]); 1134 } 1135 1136 #[test] 1137 fn subscription_selection_accepts_uncorrelated_message_for_entity() { 1138 let mut pending = VecDeque::new(); 1139 let expected = RadrootsSimplexSmpCorrelationId::new([1_u8; 24]); 1140 let message = response( 1141 None, 1142 b"rr-synth-entity", 1143 RadrootsSimplexSmpBrokerMessage::Msg(RadrootsSimplexSmpReceivedMessage { 1144 message_id: b"message-1".to_vec(), 1145 encrypted_body: b"body".to_vec(), 1146 }), 1147 ); 1148 let other = response( 1149 None, 1150 b"rr-other-entity", 1151 RadrootsSimplexSmpBrokerMessage::Msg(RadrootsSimplexSmpReceivedMessage { 1152 message_id: b"message-2".to_vec(), 1153 encrypted_body: b"other".to_vec(), 1154 }), 1155 ); 1156 1157 let selected = select_live_response( 1158 &mut pending, 1159 vec![other.clone(), message.clone()], 1160 Some(expected), 1161 Some(b"rr-synth-entity"), 1162 ) 1163 .unwrap(); 1164 1165 assert_eq!(selected, Some(message)); 1166 assert_eq!(pending.into_iter().collect::<Vec<_>>(), vec![other]); 1167 } 1168 1169 fn der_sequence<'a, I>(elements: I) -> Vec<u8> 1170 where 1171 I: IntoIterator<Item = &'a [u8]>, 1172 { 1173 let mut body = Vec::new(); 1174 for element in elements { 1175 if element.is_empty() { 1176 body.extend_from_slice(&[0x30, 0x00]); 1177 } else { 1178 body.extend_from_slice(element); 1179 } 1180 } 1181 let mut sequence = vec![0x30]; 1182 push_der_length(&mut sequence, body.len()); 1183 sequence.extend_from_slice(&body); 1184 sequence 1185 } 1186 1187 fn push_der_length(buffer: &mut Vec<u8>, len: usize) { 1188 if len < 0x80 { 1189 buffer.push(len as u8); 1190 return; 1191 } 1192 let mut bytes = Vec::new(); 1193 let mut remaining = len; 1194 while remaining > 0 { 1195 bytes.push((remaining & 0xff) as u8); 1196 remaining >>= 8; 1197 } 1198 bytes.reverse(); 1199 buffer.push(0x80 | (bytes.len() as u8)); 1200 buffer.extend_from_slice(&bytes); 1201 } 1202 1203 fn response( 1204 correlation_id: Option<RadrootsSimplexSmpCorrelationId>, 1205 entity_id: &[u8], 1206 message: RadrootsSimplexSmpBrokerMessage, 1207 ) -> RadrootsSimplexSmpTransportResponse { 1208 RadrootsSimplexSmpTransportResponse { 1209 server: RadrootsSimplexSmpServerAddress { 1210 server_identity: "cnItc3ludGgtc2VydmVy".to_owned(), 1211 hosts: vec!["127.0.0.1".to_owned()], 1212 port: Some(5223), 1213 }, 1214 transport_version: RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 1215 transmission: RadrootsSimplexSmpBrokerTransmission { 1216 authorization: Vec::new(), 1217 correlation_id, 1218 entity_id: entity_id.to_vec(), 1219 message, 1220 }, 1221 transport_hash: vec![9_u8; 32], 1222 } 1223 } 1224 }