codec.rs (33643B)
1 use crate::error::RadrootsSimplexAgentProtoError; 2 use crate::model::{ 3 RADROOTS_SIMPLEX_AGENT_CURRENT_VERSION, RadrootsSimplexAgentConnectionLink, 4 RadrootsSimplexAgentDecryptedMessage, RadrootsSimplexAgentEncryptedPayload, 5 RadrootsSimplexAgentEnvelope, RadrootsSimplexAgentMessage, RadrootsSimplexAgentMessageFrame, 6 RadrootsSimplexAgentMessageHeader, RadrootsSimplexAgentMessageReceipt, 7 RadrootsSimplexAgentQueueAddress, RadrootsSimplexAgentQueueDescriptor, 8 RadrootsSimplexAgentQueueUseDecision, 9 }; 10 use alloc::string::{String, ToString}; 11 use alloc::vec::Vec; 12 use radroots_simplex_smp_crypto::prelude::{ 13 RadrootsSimplexOfficialX3dhParams, RadrootsSimplexSmpRatchetHeader, 14 }; 15 use radroots_simplex_smp_proto::prelude::{ 16 RadrootsSimplexSmpQueueUri, RadrootsSimplexSmpServerAddress, RadrootsSimplexSmpVersionRange, 17 }; 18 19 pub fn encode_connection_link( 20 link: &RadrootsSimplexAgentConnectionLink, 21 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 22 let mut buffer = Vec::new(); 23 push_short_bytes(&mut buffer, link.invitation_queue.to_string().as_bytes())?; 24 push_short_bytes(&mut buffer, &link.connection_id)?; 25 let e2e_ratchet_params = encode_x3dh_params_binary(&link.e2e_ratchet_params)?; 26 push_large_bytes(&mut buffer, &e2e_ratchet_params)?; 27 buffer.push(encode_bool(link.contact_address)); 28 Ok(buffer) 29 } 30 31 pub fn decode_connection_link( 32 bytes: &[u8], 33 ) -> Result<RadrootsSimplexAgentConnectionLink, RadrootsSimplexAgentProtoError> { 34 let mut cursor = Cursor::new(bytes); 35 let invitation_queue = String::from_utf8(cursor.read_short_bytes()?) 36 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidUtf8(error.to_string()))?; 37 let link = RadrootsSimplexAgentConnectionLink { 38 invitation_queue: RadrootsSimplexSmpQueueUri::parse(&invitation_queue)?, 39 connection_id: cursor.read_short_bytes()?, 40 e2e_ratchet_params: decode_x3dh_params_binary(&cursor.read_large_bytes()?)?, 41 contact_address: decode_bool(cursor.read_byte()?)?, 42 }; 43 cursor.finish()?; 44 Ok(link) 45 } 46 47 pub fn encode_agent_message_frame( 48 frame: &RadrootsSimplexAgentMessageFrame, 49 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 50 let mut buffer = Vec::new(); 51 buffer.push(b'M'); 52 buffer.extend_from_slice(&frame.header.message_id.to_be_bytes()); 53 push_short_bytes(&mut buffer, &frame.header.previous_message_hash)?; 54 buffer.extend_from_slice(&encode_agent_message(&frame.message)?); 55 buffer.extend_from_slice(&frame.padding); 56 Ok(buffer) 57 } 58 59 pub fn decode_agent_message_frame( 60 bytes: &[u8], 61 ) -> Result<RadrootsSimplexAgentMessageFrame, RadrootsSimplexAgentProtoError> { 62 let mut cursor = Cursor::new(bytes); 63 cursor.expect_tag(b"M")?; 64 let header = RadrootsSimplexAgentMessageHeader { 65 message_id: cursor.read_u64()?, 66 previous_message_hash: cursor.read_short_bytes()?, 67 }; 68 let message = decode_agent_message(&mut cursor)?; 69 let padding = cursor.read_remaining().to_vec(); 70 Ok(RadrootsSimplexAgentMessageFrame { 71 header, 72 message, 73 padding, 74 }) 75 } 76 77 pub fn encode_decrypted_message( 78 message: &RadrootsSimplexAgentDecryptedMessage, 79 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 80 let mut buffer = Vec::new(); 81 match message { 82 RadrootsSimplexAgentDecryptedMessage::ConnectionInfo(info) => { 83 buffer.push(b'I'); 84 buffer.extend_from_slice(info); 85 } 86 RadrootsSimplexAgentDecryptedMessage::ConnectionInfoReply { reply_queues, info } => { 87 buffer.push(b'D'); 88 push_list(&mut buffer, reply_queues, encode_queue_descriptor)?; 89 push_large_bytes(&mut buffer, info)?; 90 } 91 RadrootsSimplexAgentDecryptedMessage::RatchetInfo(info) => { 92 buffer.push(b'R'); 93 push_large_bytes(&mut buffer, info)?; 94 } 95 RadrootsSimplexAgentDecryptedMessage::Message(frame) => { 96 buffer.extend_from_slice(&encode_agent_message_frame(frame)?); 97 } 98 } 99 Ok(buffer) 100 } 101 102 pub fn decode_decrypted_message( 103 bytes: &[u8], 104 ) -> Result<RadrootsSimplexAgentDecryptedMessage, RadrootsSimplexAgentProtoError> { 105 let mut cursor = Cursor::new(bytes); 106 match cursor.read_byte()? { 107 b'I' => Ok(RadrootsSimplexAgentDecryptedMessage::ConnectionInfo( 108 cursor.read_remaining().to_vec(), 109 )), 110 b'D' => { 111 let reply_queues = cursor.read_list(decode_queue_descriptor)?; 112 let info = cursor.read_large_bytes()?; 113 cursor.finish()?; 114 Ok(RadrootsSimplexAgentDecryptedMessage::ConnectionInfoReply { reply_queues, info }) 115 } 116 b'R' => { 117 let info = cursor.read_large_bytes()?; 118 cursor.finish()?; 119 Ok(RadrootsSimplexAgentDecryptedMessage::RatchetInfo(info)) 120 } 121 b'M' => { 122 decode_agent_message_frame(bytes).map(RadrootsSimplexAgentDecryptedMessage::Message) 123 } 124 tag => Err(RadrootsSimplexAgentProtoError::InvalidTag( 125 String::from_utf8_lossy(&[tag]).into_owned(), 126 )), 127 } 128 } 129 130 pub fn encode_envelope( 131 envelope: &RadrootsSimplexAgentEnvelope, 132 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 133 let mut buffer = Vec::new(); 134 buffer.extend_from_slice(&RADROOTS_SIMPLEX_AGENT_CURRENT_VERSION.to_be_bytes()); 135 match envelope { 136 RadrootsSimplexAgentEnvelope::Confirmation { 137 reply_queue, 138 e2e_ratchet_params, 139 encrypted, 140 } => { 141 buffer.push(b'C'); 142 buffer.push(encode_bool(*reply_queue)); 143 encode_optional_x3dh_params(&mut buffer, e2e_ratchet_params)?; 144 encode_encrypted_payload(&mut buffer, encrypted)?; 145 } 146 RadrootsSimplexAgentEnvelope::Message(encrypted) => { 147 buffer.push(b'M'); 148 encode_encrypted_payload(&mut buffer, encrypted)?; 149 } 150 RadrootsSimplexAgentEnvelope::Invitation { 151 request, 152 connection_info, 153 } => { 154 buffer.push(b'I'); 155 push_large_bytes(&mut buffer, request)?; 156 push_large_bytes(&mut buffer, connection_info)?; 157 } 158 RadrootsSimplexAgentEnvelope::RatchetKey { info, encrypted } => { 159 buffer.push(b'R'); 160 push_large_bytes(&mut buffer, info)?; 161 encode_encrypted_payload(&mut buffer, encrypted)?; 162 } 163 } 164 Ok(buffer) 165 } 166 167 pub fn decode_envelope( 168 bytes: &[u8], 169 ) -> Result<RadrootsSimplexAgentEnvelope, RadrootsSimplexAgentProtoError> { 170 let mut cursor = Cursor::new(bytes); 171 let _version = cursor.read_u16()?; 172 match cursor.read_byte()? { 173 b'C' => { 174 let reply_queue = decode_bool(cursor.read_byte()?)?; 175 let e2e_ratchet_params = decode_optional_x3dh_params(&mut cursor)?; 176 let encrypted = decode_encrypted_payload(&mut cursor)?; 177 cursor.finish()?; 178 Ok(RadrootsSimplexAgentEnvelope::Confirmation { 179 reply_queue, 180 e2e_ratchet_params, 181 encrypted, 182 }) 183 } 184 b'M' => { 185 let encrypted = decode_encrypted_payload(&mut cursor)?; 186 cursor.finish()?; 187 Ok(RadrootsSimplexAgentEnvelope::Message(encrypted)) 188 } 189 b'I' => { 190 let request = cursor.read_large_bytes()?; 191 let connection_info = cursor.read_large_bytes()?; 192 cursor.finish()?; 193 Ok(RadrootsSimplexAgentEnvelope::Invitation { 194 request, 195 connection_info, 196 }) 197 } 198 b'R' => { 199 let info = cursor.read_large_bytes()?; 200 let encrypted = decode_encrypted_payload(&mut cursor)?; 201 cursor.finish()?; 202 Ok(RadrootsSimplexAgentEnvelope::RatchetKey { info, encrypted }) 203 } 204 tag => Err(RadrootsSimplexAgentProtoError::InvalidTag( 205 String::from_utf8_lossy(&[tag]).into_owned(), 206 )), 207 } 208 } 209 210 fn encode_agent_message( 211 message: &RadrootsSimplexAgentMessage, 212 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 213 let mut buffer = Vec::new(); 214 match message { 215 RadrootsSimplexAgentMessage::Hello => buffer.push(b'H'), 216 RadrootsSimplexAgentMessage::UserMessage(body) => { 217 buffer.push(b'M'); 218 buffer.extend_from_slice(body); 219 } 220 RadrootsSimplexAgentMessage::Receipt(receipt) => { 221 buffer.push(b'V'); 222 buffer.extend_from_slice(&receipt.message_id.to_be_bytes()); 223 push_short_bytes(&mut buffer, &receipt.message_hash)?; 224 push_large_bytes(&mut buffer, &receipt.receipt_info)?; 225 } 226 RadrootsSimplexAgentMessage::EncryptionReady { up_to_message_id } => { 227 buffer.push(b'E'); 228 buffer.extend_from_slice(&up_to_message_id.to_be_bytes()); 229 } 230 RadrootsSimplexAgentMessage::QueueContinue(queue) => { 231 buffer.extend_from_slice(b"QC"); 232 encode_queue_address(&mut buffer, queue)?; 233 } 234 RadrootsSimplexAgentMessage::QueueAdd(queues) => { 235 buffer.extend_from_slice(b"QA"); 236 push_list(&mut buffer, queues, encode_queue_descriptor)?; 237 } 238 RadrootsSimplexAgentMessage::QueueKey(queues) => { 239 buffer.extend_from_slice(b"QK"); 240 push_list(&mut buffer, queues, encode_queue_descriptor)?; 241 } 242 RadrootsSimplexAgentMessage::QueueUse(queues) => { 243 buffer.extend_from_slice(b"QU"); 244 push_list(&mut buffer, queues, encode_queue_use_decision)?; 245 } 246 RadrootsSimplexAgentMessage::QueueTest(queues) => { 247 buffer.extend_from_slice(b"QT"); 248 push_list(&mut buffer, queues, encode_queue_address)?; 249 } 250 } 251 Ok(buffer) 252 } 253 254 fn decode_agent_message( 255 cursor: &mut Cursor<'_>, 256 ) -> Result<RadrootsSimplexAgentMessage, RadrootsSimplexAgentProtoError> { 257 let first = cursor.read_byte()?; 258 match first { 259 b'H' => Ok(RadrootsSimplexAgentMessage::Hello), 260 b'M' => Ok(RadrootsSimplexAgentMessage::UserMessage( 261 cursor.read_remaining().to_vec(), 262 )), 263 b'V' => Ok(RadrootsSimplexAgentMessage::Receipt( 264 RadrootsSimplexAgentMessageReceipt { 265 message_id: cursor.read_u64()?, 266 message_hash: cursor.read_short_bytes()?, 267 receipt_info: cursor.read_large_bytes()?, 268 }, 269 )), 270 b'E' => Ok(RadrootsSimplexAgentMessage::EncryptionReady { 271 up_to_message_id: cursor.read_u64()?, 272 }), 273 b'Q' => match cursor.read_byte()? { 274 b'C' => Ok(RadrootsSimplexAgentMessage::QueueContinue( 275 decode_queue_address(cursor)?, 276 )), 277 b'A' => Ok(RadrootsSimplexAgentMessage::QueueAdd( 278 cursor.read_list(decode_queue_descriptor)?, 279 )), 280 b'K' => Ok(RadrootsSimplexAgentMessage::QueueKey( 281 cursor.read_list(decode_queue_descriptor)?, 282 )), 283 b'U' => Ok(RadrootsSimplexAgentMessage::QueueUse( 284 cursor.read_list(decode_queue_use_decision)?, 285 )), 286 b'T' => Ok(RadrootsSimplexAgentMessage::QueueTest( 287 cursor.read_list(decode_queue_address)?, 288 )), 289 tag => Err(RadrootsSimplexAgentProtoError::InvalidTag(alloc::format!( 290 "Q{}", 291 tag as char 292 ))), 293 }, 294 tag => Err(RadrootsSimplexAgentProtoError::InvalidTag( 295 String::from_utf8_lossy(&[tag]).into_owned(), 296 )), 297 } 298 } 299 300 fn encode_encrypted_payload( 301 buffer: &mut Vec<u8>, 302 encrypted: &RadrootsSimplexAgentEncryptedPayload, 303 ) -> Result<(), RadrootsSimplexAgentProtoError> { 304 if let Some(official_message) = encrypted.official_message.as_ref() { 305 if encrypted.ratchet_header.is_some() || !encrypted.ciphertext.is_empty() { 306 return Err(RadrootsSimplexAgentProtoError::InvalidRatchetHeader( 307 "official encrypted payload cannot include legacy ratchet fields".into(), 308 )); 309 } 310 buffer.push(2); 311 push_large_bytes(buffer, official_message)?; 312 return Ok(()); 313 } 314 match &encrypted.ratchet_header { 315 Some(header) => { 316 buffer.push(1); 317 let ratchet = encode_ratchet_header(header)?; 318 push_large_bytes(buffer, &ratchet)?; 319 } 320 None => buffer.push(0), 321 } 322 push_large_bytes(buffer, &encrypted.ciphertext) 323 } 324 325 fn decode_encrypted_payload( 326 cursor: &mut Cursor<'_>, 327 ) -> Result<RadrootsSimplexAgentEncryptedPayload, RadrootsSimplexAgentProtoError> { 328 match cursor.read_byte()? { 329 0 => Ok(RadrootsSimplexAgentEncryptedPayload { 330 ratchet_header: None, 331 official_message: None, 332 ciphertext: cursor.read_large_bytes()?, 333 }), 334 1 => Ok(RadrootsSimplexAgentEncryptedPayload { 335 ratchet_header: Some(decode_ratchet_header(&cursor.read_large_bytes()?)?), 336 official_message: None, 337 ciphertext: cursor.read_large_bytes()?, 338 }), 339 2 => Ok(RadrootsSimplexAgentEncryptedPayload { 340 ratchet_header: None, 341 official_message: Some(cursor.read_large_bytes()?), 342 ciphertext: Vec::new(), 343 }), 344 other => Err(RadrootsSimplexAgentProtoError::InvalidBoolEncoding(other)), 345 } 346 } 347 348 fn encode_optional_x3dh_params( 349 buffer: &mut Vec<u8>, 350 params: &Option<RadrootsSimplexOfficialX3dhParams>, 351 ) -> Result<(), RadrootsSimplexAgentProtoError> { 352 match params { 353 Some(params) => { 354 buffer.push(b'1'); 355 let encoded = encode_x3dh_params_binary(params)?; 356 push_large_bytes(buffer, &encoded) 357 } 358 None => { 359 buffer.push(b'0'); 360 Ok(()) 361 } 362 } 363 } 364 365 fn encode_x3dh_params_binary( 366 params: &RadrootsSimplexOfficialX3dhParams, 367 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 368 let mut buffer = Vec::new(); 369 buffer.extend_from_slice(¶ms.version_range.min.to_be_bytes()); 370 buffer.extend_from_slice(¶ms.version_range.max.to_be_bytes()); 371 push_short_bytes(&mut buffer, ¶ms.key_1)?; 372 push_short_bytes(&mut buffer, ¶ms.key_2)?; 373 push_maybe_large_bytes(&mut buffer, params.pq_public_key.as_deref())?; 374 push_maybe_large_bytes(&mut buffer, params.pq_ciphertext.as_deref())?; 375 Ok(buffer) 376 } 377 378 fn decode_x3dh_params_binary( 379 bytes: &[u8], 380 ) -> Result<RadrootsSimplexOfficialX3dhParams, RadrootsSimplexAgentProtoError> { 381 let mut cursor = Cursor::new(bytes); 382 let version_range = RadrootsSimplexSmpVersionRange::new(cursor.read_u16()?, cursor.read_u16()?) 383 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidE2eParameters(error.to_string()))?; 384 let params = RadrootsSimplexOfficialX3dhParams { 385 version_range, 386 key_1: cursor.read_short_bytes()?, 387 key_2: cursor.read_short_bytes()?, 388 pq_public_key: cursor.read_maybe(decode_large_bytes)?, 389 pq_ciphertext: cursor.read_maybe(decode_large_bytes)?, 390 }; 391 cursor.finish()?; 392 Ok(params) 393 } 394 395 fn decode_optional_x3dh_params( 396 cursor: &mut Cursor<'_>, 397 ) -> Result<Option<RadrootsSimplexOfficialX3dhParams>, RadrootsSimplexAgentProtoError> { 398 match cursor.read_byte()? { 399 b'0' => Ok(None), 400 b'1' => { 401 let encoded = cursor.read_large_bytes()?; 402 decode_x3dh_params_binary(&encoded).map(Some) 403 } 404 tag => Err(RadrootsSimplexAgentProtoError::InvalidTag( 405 String::from_utf8_lossy(&[tag]).into_owned(), 406 )), 407 } 408 } 409 410 fn encode_queue_descriptor( 411 buffer: &mut Vec<u8>, 412 descriptor: &RadrootsSimplexAgentQueueDescriptor, 413 ) -> Result<(), RadrootsSimplexAgentProtoError> { 414 push_short_bytes(buffer, descriptor.queue_uri.to_string().as_bytes())?; 415 push_maybe( 416 buffer, 417 descriptor.replaced_queue.as_ref(), 418 encode_queue_address, 419 )?; 420 buffer.push(encode_bool(descriptor.primary)); 421 push_maybe_short_bytes(buffer, descriptor.sender_key.as_deref())?; 422 Ok(()) 423 } 424 425 fn decode_queue_descriptor( 426 cursor: &mut Cursor<'_>, 427 ) -> Result<RadrootsSimplexAgentQueueDescriptor, RadrootsSimplexAgentProtoError> { 428 let queue_uri = String::from_utf8(cursor.read_short_bytes()?) 429 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidUtf8(error.to_string()))?; 430 Ok(RadrootsSimplexAgentQueueDescriptor { 431 queue_uri: RadrootsSimplexSmpQueueUri::parse(&queue_uri)?, 432 replaced_queue: cursor.read_maybe(decode_queue_address)?, 433 primary: decode_bool(cursor.read_byte()?)?, 434 sender_key: cursor.read_maybe(decode_short_bytes)?, 435 }) 436 } 437 438 fn encode_queue_use_decision( 439 buffer: &mut Vec<u8>, 440 decision: &RadrootsSimplexAgentQueueUseDecision, 441 ) -> Result<(), RadrootsSimplexAgentProtoError> { 442 encode_queue_address(buffer, &decision.queue_address)?; 443 buffer.push(encode_bool(decision.primary)); 444 Ok(()) 445 } 446 447 fn decode_queue_use_decision( 448 cursor: &mut Cursor<'_>, 449 ) -> Result<RadrootsSimplexAgentQueueUseDecision, RadrootsSimplexAgentProtoError> { 450 Ok(RadrootsSimplexAgentQueueUseDecision { 451 queue_address: decode_queue_address(cursor)?, 452 primary: decode_bool(cursor.read_byte()?)?, 453 }) 454 } 455 456 fn encode_queue_address( 457 buffer: &mut Vec<u8>, 458 queue: &RadrootsSimplexAgentQueueAddress, 459 ) -> Result<(), RadrootsSimplexAgentProtoError> { 460 push_short_bytes(buffer, queue.server.server_identity.as_bytes())?; 461 push_list(buffer, &queue.server.hosts, |buffer, host| { 462 push_short_bytes(buffer, host.as_bytes()) 463 })?; 464 let port = queue 465 .server 466 .port 467 .map(|value| value.to_string()) 468 .unwrap_or_default(); 469 push_short_bytes(buffer, port.as_bytes())?; 470 push_short_bytes(buffer, &queue.sender_id)?; 471 Ok(()) 472 } 473 474 fn decode_queue_address( 475 cursor: &mut Cursor<'_>, 476 ) -> Result<RadrootsSimplexAgentQueueAddress, RadrootsSimplexAgentProtoError> { 477 let server_identity = String::from_utf8(cursor.read_short_bytes()?) 478 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidUtf8(error.to_string()))?; 479 let hosts = cursor.read_list(|cursor| { 480 let host = String::from_utf8(cursor.read_short_bytes()?) 481 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidUtf8(error.to_string()))?; 482 Ok(host) 483 })?; 484 let port_raw = String::from_utf8(cursor.read_short_bytes()?) 485 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidUtf8(error.to_string()))?; 486 let port = if port_raw.is_empty() { 487 None 488 } else { 489 Some( 490 port_raw 491 .parse::<u16>() 492 .map_err(|_| RadrootsSimplexAgentProtoError::InvalidUtf8(port_raw.clone()))?, 493 ) 494 }; 495 Ok(RadrootsSimplexAgentQueueAddress { 496 server: RadrootsSimplexSmpServerAddress { 497 server_identity, 498 hosts, 499 port, 500 }, 501 sender_id: cursor.read_short_bytes()?, 502 }) 503 } 504 505 fn encode_ratchet_header( 506 header: &RadrootsSimplexSmpRatchetHeader, 507 ) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 508 header 509 .validate() 510 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidRatchetHeader(error.to_string()))?; 511 let mut buffer = Vec::new(); 512 buffer.extend_from_slice(&header.previous_sending_chain_length.to_be_bytes()); 513 buffer.extend_from_slice(&header.message_number.to_be_bytes()); 514 push_short_bytes(&mut buffer, &header.dh_public_key)?; 515 push_maybe_large_bytes(&mut buffer, header.pq_public_key.as_deref())?; 516 push_maybe_large_bytes(&mut buffer, header.pq_ciphertext.as_deref())?; 517 Ok(buffer) 518 } 519 520 fn decode_ratchet_header( 521 bytes: &[u8], 522 ) -> Result<RadrootsSimplexSmpRatchetHeader, RadrootsSimplexAgentProtoError> { 523 let mut cursor = Cursor::new(bytes); 524 let header = RadrootsSimplexSmpRatchetHeader { 525 previous_sending_chain_length: cursor.read_u32()?, 526 message_number: cursor.read_u32()?, 527 dh_public_key: cursor.read_short_bytes()?, 528 pq_public_key: cursor.read_maybe(decode_large_bytes)?, 529 pq_ciphertext: cursor.read_maybe(decode_large_bytes)?, 530 }; 531 cursor.finish()?; 532 header 533 .validate() 534 .map_err(|error| RadrootsSimplexAgentProtoError::InvalidRatchetHeader(error.to_string()))?; 535 Ok(header) 536 } 537 538 fn decode_short_bytes(cursor: &mut Cursor<'_>) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 539 cursor.read_short_bytes() 540 } 541 542 fn decode_large_bytes(cursor: &mut Cursor<'_>) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 543 cursor.read_large_bytes() 544 } 545 546 fn push_short_bytes( 547 buffer: &mut Vec<u8>, 548 value: &[u8], 549 ) -> Result<(), RadrootsSimplexAgentProtoError> { 550 if value.len() > u8::MAX as usize { 551 return Err(RadrootsSimplexAgentProtoError::InvalidShortFieldLength( 552 value.len(), 553 )); 554 } 555 buffer.push(value.len() as u8); 556 buffer.extend_from_slice(value); 557 Ok(()) 558 } 559 560 fn push_large_bytes( 561 buffer: &mut Vec<u8>, 562 value: &[u8], 563 ) -> Result<(), RadrootsSimplexAgentProtoError> { 564 if value.len() > u16::MAX as usize { 565 return Err(RadrootsSimplexAgentProtoError::InvalidLargeFieldLength( 566 value.len(), 567 )); 568 } 569 buffer.extend_from_slice(&(value.len() as u16).to_be_bytes()); 570 buffer.extend_from_slice(value); 571 Ok(()) 572 } 573 574 fn push_maybe_short_bytes( 575 buffer: &mut Vec<u8>, 576 value: Option<&[u8]>, 577 ) -> Result<(), RadrootsSimplexAgentProtoError> { 578 match value { 579 Some(value) => { 580 buffer.push(1); 581 push_short_bytes(buffer, value) 582 } 583 None => { 584 buffer.push(0); 585 Ok(()) 586 } 587 } 588 } 589 590 fn push_maybe_large_bytes( 591 buffer: &mut Vec<u8>, 592 value: Option<&[u8]>, 593 ) -> Result<(), RadrootsSimplexAgentProtoError> { 594 match value { 595 Some(value) => { 596 buffer.push(1); 597 push_large_bytes(buffer, value) 598 } 599 None => { 600 buffer.push(0); 601 Ok(()) 602 } 603 } 604 } 605 606 fn push_maybe<T>( 607 buffer: &mut Vec<u8>, 608 value: Option<&T>, 609 encode: fn(&mut Vec<u8>, &T) -> Result<(), RadrootsSimplexAgentProtoError>, 610 ) -> Result<(), RadrootsSimplexAgentProtoError> { 611 match value { 612 Some(value) => { 613 buffer.push(1); 614 encode(buffer, value) 615 } 616 None => { 617 buffer.push(0); 618 Ok(()) 619 } 620 } 621 } 622 623 fn push_list<T>( 624 buffer: &mut Vec<u8>, 625 values: &[T], 626 encode: fn(&mut Vec<u8>, &T) -> Result<(), RadrootsSimplexAgentProtoError>, 627 ) -> Result<(), RadrootsSimplexAgentProtoError> { 628 if values.len() > u8::MAX as usize { 629 return Err(RadrootsSimplexAgentProtoError::InvalidShortFieldLength( 630 values.len(), 631 )); 632 } 633 buffer.push(values.len() as u8); 634 for value in values { 635 encode(buffer, value)?; 636 } 637 Ok(()) 638 } 639 640 const fn encode_bool(value: bool) -> u8 { 641 if value { 1 } else { 0 } 642 } 643 644 fn decode_bool(value: u8) -> Result<bool, RadrootsSimplexAgentProtoError> { 645 match value { 646 0 => Ok(false), 647 1 => Ok(true), 648 other => Err(RadrootsSimplexAgentProtoError::InvalidBoolEncoding(other)), 649 } 650 } 651 652 struct Cursor<'a> { 653 bytes: &'a [u8], 654 position: usize, 655 } 656 657 impl<'a> Cursor<'a> { 658 const fn new(bytes: &'a [u8]) -> Self { 659 Self { bytes, position: 0 } 660 } 661 662 fn finish(&self) -> Result<(), RadrootsSimplexAgentProtoError> { 663 if self.position == self.bytes.len() { 664 Ok(()) 665 } else { 666 Err(RadrootsSimplexAgentProtoError::TrailingBytes) 667 } 668 } 669 670 fn read_byte(&mut self) -> Result<u8, RadrootsSimplexAgentProtoError> { 671 let Some(value) = self.bytes.get(self.position) else { 672 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 673 }; 674 self.position += 1; 675 Ok(*value) 676 } 677 678 fn expect_tag(&mut self, tag: &[u8]) -> Result<(), RadrootsSimplexAgentProtoError> { 679 let Some(value) = self.bytes.get(self.position..self.position + tag.len()) else { 680 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 681 }; 682 if value != tag { 683 return Err(RadrootsSimplexAgentProtoError::InvalidTag( 684 String::from_utf8_lossy(value).into_owned(), 685 )); 686 } 687 self.position += tag.len(); 688 Ok(()) 689 } 690 691 fn read_u16(&mut self) -> Result<u16, RadrootsSimplexAgentProtoError> { 692 let Some(value) = self.bytes.get(self.position..self.position + 2) else { 693 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 694 }; 695 self.position += 2; 696 Ok(u16::from_be_bytes([value[0], value[1]])) 697 } 698 699 fn read_u32(&mut self) -> Result<u32, RadrootsSimplexAgentProtoError> { 700 let Some(value) = self.bytes.get(self.position..self.position + 4) else { 701 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 702 }; 703 self.position += 4; 704 Ok(u32::from_be_bytes([value[0], value[1], value[2], value[3]])) 705 } 706 707 fn read_u64(&mut self) -> Result<u64, RadrootsSimplexAgentProtoError> { 708 let Some(value) = self.bytes.get(self.position..self.position + 8) else { 709 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 710 }; 711 self.position += 8; 712 Ok(u64::from_be_bytes([ 713 value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], 714 ])) 715 } 716 717 fn read_short_bytes(&mut self) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 718 let length = self.read_byte()? as usize; 719 let Some(value) = self.bytes.get(self.position..self.position + length) else { 720 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 721 }; 722 self.position += length; 723 Ok(value.to_vec()) 724 } 725 726 fn read_large_bytes(&mut self) -> Result<Vec<u8>, RadrootsSimplexAgentProtoError> { 727 let length = self.read_u16()? as usize; 728 let Some(value) = self.bytes.get(self.position..self.position + length) else { 729 return Err(RadrootsSimplexAgentProtoError::UnexpectedEof); 730 }; 731 self.position += length; 732 Ok(value.to_vec()) 733 } 734 735 fn read_maybe<T>( 736 &mut self, 737 decode: fn(&mut Cursor<'_>) -> Result<T, RadrootsSimplexAgentProtoError>, 738 ) -> Result<Option<T>, RadrootsSimplexAgentProtoError> { 739 match self.read_byte()? { 740 0 => Ok(None), 741 1 => decode(self).map(Some), 742 other => Err(RadrootsSimplexAgentProtoError::InvalidBoolEncoding(other)), 743 } 744 } 745 746 fn read_list<T>( 747 &mut self, 748 decode: fn(&mut Cursor<'_>) -> Result<T, RadrootsSimplexAgentProtoError>, 749 ) -> Result<Vec<T>, RadrootsSimplexAgentProtoError> { 750 let len = self.read_byte()? as usize; 751 let mut values = Vec::with_capacity(len); 752 for _ in 0..len { 753 values.push(decode(self)?); 754 } 755 Ok(values) 756 } 757 758 fn read_remaining(&mut self) -> &'a [u8] { 759 let remaining = &self.bytes[self.position..]; 760 self.position = self.bytes.len(); 761 remaining 762 } 763 } 764 765 #[cfg(test)] 766 mod tests { 767 use super::*; 768 use radroots_simplex_smp_crypto::prelude::{ 769 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, 770 RadrootsSimplexOfficialX3dhParams, official_x448_keypair_from_seed, 771 }; 772 use radroots_simplex_smp_proto::prelude::{ 773 RadrootsSimplexSmpQueueMode, RadrootsSimplexSmpQueueUri, RadrootsSimplexSmpVersionRange, 774 }; 775 776 fn sample_queue_uri() -> RadrootsSimplexSmpQueueUri { 777 RadrootsSimplexSmpQueueUri::parse( 778 "smp://aGVsbG8@relay.example/cXVldWU#/?v=4&dh=Zm9vYmFy&q=m", 779 ) 780 .unwrap() 781 } 782 783 fn sample_x3dh_params() -> RadrootsSimplexOfficialX3dhParams { 784 RadrootsSimplexOfficialX3dhParams { 785 version_range: RadrootsSimplexSmpVersionRange::new( 786 RADROOTS_SIMPLEX_OFFICIAL_E2E_KDF_VERSION, 787 RADROOTS_SIMPLEX_OFFICIAL_E2E_CURRENT_VERSION, 788 ) 789 .unwrap(), 790 key_1: official_x448_keypair_from_seed(b"rr-synth-agent-link-x3dh-1").public_key, 791 key_2: official_x448_keypair_from_seed(b"rr-synth-agent-link-x3dh-2").public_key, 792 pq_public_key: None, 793 pq_ciphertext: None, 794 } 795 } 796 797 #[test] 798 fn roundtrips_connection_link() { 799 let link = RadrootsSimplexAgentConnectionLink { 800 invitation_queue: sample_queue_uri(), 801 connection_id: b"conn-1".to_vec(), 802 e2e_ratchet_params: sample_x3dh_params(), 803 contact_address: true, 804 }; 805 let encoded = encode_connection_link(&link).unwrap(); 806 let decoded = decode_connection_link(&encoded).unwrap(); 807 assert_eq!(decoded, link); 808 } 809 810 #[test] 811 fn roundtrips_confirmation_x3dh_params() { 812 let envelope = RadrootsSimplexAgentEnvelope::Confirmation { 813 reply_queue: true, 814 e2e_ratchet_params: Some(sample_x3dh_params()), 815 encrypted: RadrootsSimplexAgentEncryptedPayload { 816 ratchet_header: None, 817 official_message: Some(b"official".to_vec()), 818 ciphertext: Vec::new(), 819 }, 820 }; 821 822 let encoded = encode_envelope(&envelope).unwrap(); 823 let decoded = decode_envelope(&encoded).unwrap(); 824 assert_eq!(decoded, envelope); 825 } 826 827 #[test] 828 fn roundtrips_message_frame_and_envelope() { 829 let descriptor = RadrootsSimplexAgentQueueDescriptor { 830 queue_uri: sample_queue_uri(), 831 replaced_queue: None, 832 primary: true, 833 sender_key: Some(b"sender-key".to_vec()), 834 }; 835 let frame = RadrootsSimplexAgentMessageFrame { 836 header: RadrootsSimplexAgentMessageHeader { 837 message_id: 7, 838 previous_message_hash: b"hash".to_vec(), 839 }, 840 message: RadrootsSimplexAgentMessage::QueueAdd(vec![descriptor.clone()]), 841 padding: b"pad".to_vec(), 842 }; 843 let decrypted = RadrootsSimplexAgentDecryptedMessage::Message(frame); 844 let encoded_decrypted = encode_decrypted_message(&decrypted).unwrap(); 845 let decoded_decrypted = decode_decrypted_message(&encoded_decrypted).unwrap(); 846 assert_eq!(decoded_decrypted, decrypted); 847 848 let envelope = 849 RadrootsSimplexAgentEnvelope::Message(RadrootsSimplexAgentEncryptedPayload { 850 ratchet_header: Some(RadrootsSimplexSmpRatchetHeader { 851 previous_sending_chain_length: 1, 852 message_number: 2, 853 dh_public_key: b"dh".to_vec(), 854 pq_public_key: Some(b"pq".to_vec()), 855 pq_ciphertext: Some(b"ct".to_vec()), 856 }), 857 official_message: None, 858 ciphertext: encoded_decrypted, 859 }); 860 let encoded_envelope = encode_envelope(&envelope).unwrap(); 861 let decoded_envelope = decode_envelope(&encoded_envelope).unwrap(); 862 assert_eq!(decoded_envelope, envelope); 863 864 assert_eq!( 865 descriptor.client_version_range(), 866 RadrootsSimplexSmpVersionRange::single(4) 867 ); 868 assert_eq!( 869 descriptor.queue_uri.queue_mode, 870 Some(RadrootsSimplexSmpQueueMode::Messaging) 871 ); 872 } 873 874 #[test] 875 fn roundtrips_official_sized_pq_ratchet_header() { 876 let envelope = 877 RadrootsSimplexAgentEnvelope::Message(RadrootsSimplexAgentEncryptedPayload { 878 ratchet_header: Some(RadrootsSimplexSmpRatchetHeader { 879 previous_sending_chain_length: 4, 880 message_number: 9, 881 dh_public_key: vec![7_u8; 56], 882 pq_public_key: Some(vec![8_u8; 1158]), 883 pq_ciphertext: Some(vec![9_u8; 1039]), 884 }), 885 official_message: None, 886 ciphertext: b"opaque".to_vec(), 887 }); 888 889 let encoded = encode_envelope(&envelope).unwrap(); 890 let decoded = decode_envelope(&encoded).unwrap(); 891 assert_eq!(decoded, envelope); 892 } 893 894 #[test] 895 fn rejects_incomplete_pq_ratchet_header() { 896 let envelope = 897 RadrootsSimplexAgentEnvelope::Message(RadrootsSimplexAgentEncryptedPayload { 898 ratchet_header: Some(RadrootsSimplexSmpRatchetHeader { 899 previous_sending_chain_length: 0, 900 message_number: 0, 901 dh_public_key: vec![1_u8; 56], 902 pq_public_key: None, 903 pq_ciphertext: Some(vec![3_u8; 1039]), 904 }), 905 official_message: None, 906 ciphertext: b"opaque".to_vec(), 907 }); 908 909 let error = encode_envelope(&envelope).unwrap_err(); 910 assert!(matches!( 911 error, 912 RadrootsSimplexAgentProtoError::InvalidRatchetHeader(_) 913 )); 914 } 915 916 #[test] 917 fn roundtrips_official_opaque_encrypted_payload() { 918 let envelope = 919 RadrootsSimplexAgentEnvelope::Message(RadrootsSimplexAgentEncryptedPayload { 920 ratchet_header: None, 921 official_message: Some(b"official-encrypted-ratchet-message".to_vec()), 922 ciphertext: Vec::new(), 923 }); 924 925 let encoded = encode_envelope(&envelope).unwrap(); 926 let decoded = decode_envelope(&encoded).unwrap(); 927 assert_eq!(decoded, envelope); 928 } 929 }