we (web engine): Experimental web browser project to understand the limits of Claude
at utf-codecs 1107 lines 37 kB view raw
1//! TLS 1.3 record layer (RFC 8446 §5). 2//! 3//! Handles framing, encryption, and decryption of TLS records over a TCP stream. 4 5use std::io::{self, Read, Write}; 6 7// --------------------------------------------------------------------------- 8// Constants 9// --------------------------------------------------------------------------- 10 11/// TLS 1.2 legacy version used in record headers (RFC 8446 §5.1). 12const LEGACY_VERSION: [u8; 2] = [0x03, 0x03]; 13 14/// Maximum plaintext fragment size: 2^14 = 16384 bytes. 15const MAX_PLAINTEXT_LENGTH: usize = 16384; 16 17/// Maximum ciphertext overhead: 256 bytes (tag + inner content type + padding). 18const MAX_CIPHERTEXT_OVERHEAD: usize = 256; 19 20/// Maximum ciphertext fragment size: 2^14 + 256. 21const MAX_CIPHERTEXT_LENGTH: usize = MAX_PLAINTEXT_LENGTH + MAX_CIPHERTEXT_OVERHEAD; 22 23/// AEAD tag size for all TLS 1.3 cipher suites. 24const TAG_SIZE: usize = 16; 25 26/// Record header size: content type (1) + version (2) + length (2). 27const RECORD_HEADER_SIZE: usize = 5; 28 29// --------------------------------------------------------------------------- 30// Content types (RFC 8446 §5.1) 31// --------------------------------------------------------------------------- 32 33/// TLS record content types. 34#[derive(Debug, Clone, Copy, PartialEq, Eq)] 35#[repr(u8)] 36pub enum ContentType { 37 ChangeCipherSpec = 20, 38 Alert = 21, 39 Handshake = 22, 40 ApplicationData = 23, 41} 42 43impl ContentType { 44 fn from_u8(v: u8) -> Result<Self> { 45 match v { 46 20 => Ok(ContentType::ChangeCipherSpec), 47 21 => Ok(ContentType::Alert), 48 22 => Ok(ContentType::Handshake), 49 23 => Ok(ContentType::ApplicationData), 50 _ => Err(TlsError::UnknownContentType(v)), 51 } 52 } 53} 54 55// --------------------------------------------------------------------------- 56// Alert protocol (RFC 8446 §6) 57// --------------------------------------------------------------------------- 58 59/// TLS alert severity level. 60#[derive(Debug, Clone, Copy, PartialEq, Eq)] 61#[repr(u8)] 62pub enum AlertLevel { 63 Warning = 1, 64 Fatal = 2, 65} 66 67impl AlertLevel { 68 fn from_u8(v: u8) -> Result<Self> { 69 match v { 70 1 => Ok(AlertLevel::Warning), 71 2 => Ok(AlertLevel::Fatal), 72 _ => Err(TlsError::MalformedAlert), 73 } 74 } 75} 76 77/// TLS alert descriptions (subset covering TLS 1.3). 78#[derive(Debug, Clone, Copy, PartialEq, Eq)] 79#[repr(u8)] 80pub enum AlertDescription { 81 CloseNotify = 0, 82 UnexpectedMessage = 10, 83 BadRecordMac = 20, 84 RecordOverflow = 22, 85 HandshakeFailure = 40, 86 BadCertificate = 42, 87 CertificateRevoked = 44, 88 CertificateExpired = 45, 89 CertificateUnknown = 46, 90 IllegalParameter = 47, 91 UnknownCa = 48, 92 AccessDenied = 49, 93 DecodeError = 50, 94 DecryptError = 51, 95 ProtocolVersion = 70, 96 InsufficientSecurity = 71, 97 InternalError = 80, 98 MissingExtension = 109, 99 UnsupportedExtension = 110, 100 UnrecognizedName = 112, 101 BadCertificateStatusResponse = 113, 102 NoApplicationProtocol = 120, 103} 104 105impl AlertDescription { 106 fn from_u8(v: u8) -> Result<Self> { 107 match v { 108 0 => Ok(AlertDescription::CloseNotify), 109 10 => Ok(AlertDescription::UnexpectedMessage), 110 20 => Ok(AlertDescription::BadRecordMac), 111 22 => Ok(AlertDescription::RecordOverflow), 112 40 => Ok(AlertDescription::HandshakeFailure), 113 42 => Ok(AlertDescription::BadCertificate), 114 44 => Ok(AlertDescription::CertificateRevoked), 115 45 => Ok(AlertDescription::CertificateExpired), 116 46 => Ok(AlertDescription::CertificateUnknown), 117 47 => Ok(AlertDescription::IllegalParameter), 118 48 => Ok(AlertDescription::UnknownCa), 119 49 => Ok(AlertDescription::AccessDenied), 120 50 => Ok(AlertDescription::DecodeError), 121 51 => Ok(AlertDescription::DecryptError), 122 70 => Ok(AlertDescription::ProtocolVersion), 123 71 => Ok(AlertDescription::InsufficientSecurity), 124 80 => Ok(AlertDescription::InternalError), 125 109 => Ok(AlertDescription::MissingExtension), 126 110 => Ok(AlertDescription::UnsupportedExtension), 127 112 => Ok(AlertDescription::UnrecognizedName), 128 113 => Ok(AlertDescription::BadCertificateStatusResponse), 129 120 => Ok(AlertDescription::NoApplicationProtocol), 130 _ => Err(TlsError::MalformedAlert), 131 } 132 } 133} 134 135/// A parsed TLS alert message. 136#[derive(Debug, Clone, Copy, PartialEq, Eq)] 137pub struct Alert { 138 pub level: AlertLevel, 139 pub description: AlertDescription, 140} 141 142impl Alert { 143 /// Create a new alert. 144 pub fn new(level: AlertLevel, description: AlertDescription) -> Self { 145 Self { level, description } 146 } 147 148 /// Create a close_notify alert. 149 pub fn close_notify() -> Self { 150 Self::new(AlertLevel::Warning, AlertDescription::CloseNotify) 151 } 152 153 /// Encode the alert to bytes. 154 pub fn encode(&self) -> [u8; 2] { 155 [self.level as u8, self.description as u8] 156 } 157 158 /// Parse an alert from bytes. 159 pub fn parse(data: &[u8]) -> Result<Self> { 160 if data.len() < 2 { 161 return Err(TlsError::MalformedAlert); 162 } 163 Ok(Self { 164 level: AlertLevel::from_u8(data[0])?, 165 description: AlertDescription::from_u8(data[1])?, 166 }) 167 } 168 169 /// Returns true if this is a fatal alert. 170 pub fn is_fatal(&self) -> bool { 171 self.level == AlertLevel::Fatal 172 } 173} 174 175// --------------------------------------------------------------------------- 176// Error types 177// --------------------------------------------------------------------------- 178 179/// TLS record layer errors. 180#[derive(Debug)] 181pub enum TlsError { 182 /// Unknown content type byte. 183 UnknownContentType(u8), 184 /// Record exceeds maximum allowed size. 185 RecordOverflow, 186 /// AEAD decryption failed (bad MAC). 187 DecryptionFailed, 188 /// Malformed alert message. 189 MalformedAlert, 190 /// Received a fatal alert from the peer. 191 AlertReceived(Alert), 192 /// An I/O error occurred. 193 Io(io::Error), 194} 195 196impl std::fmt::Display for TlsError { 197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 198 match self { 199 Self::UnknownContentType(v) => write!(f, "unknown TLS content type: {v}"), 200 Self::RecordOverflow => write!(f, "TLS record overflow"), 201 Self::DecryptionFailed => write!(f, "TLS AEAD decryption failed"), 202 Self::MalformedAlert => write!(f, "malformed TLS alert"), 203 Self::AlertReceived(alert) => { 204 write!(f, "TLS alert received: {:?}", alert.description) 205 } 206 Self::Io(e) => write!(f, "TLS I/O error: {e}"), 207 } 208 } 209} 210 211impl From<io::Error> for TlsError { 212 fn from(err: io::Error) -> Self { 213 TlsError::Io(err) 214 } 215} 216 217pub type Result<T> = std::result::Result<T, TlsError>; 218 219// --------------------------------------------------------------------------- 220// Cipher suite abstraction 221// --------------------------------------------------------------------------- 222 223/// Supported TLS 1.3 cipher suites. 224#[derive(Debug, Clone, Copy, PartialEq, Eq)] 225pub enum CipherSuite { 226 Aes128Gcm, 227 Aes256Gcm, 228 Chacha20Poly1305, 229} 230 231impl CipherSuite { 232 /// Key length in bytes. 233 pub fn key_len(&self) -> usize { 234 match self { 235 CipherSuite::Aes128Gcm => 16, 236 CipherSuite::Aes256Gcm => 32, 237 CipherSuite::Chacha20Poly1305 => 32, 238 } 239 } 240 241 /// IV (nonce base) length in bytes. 242 pub fn iv_len(&self) -> usize { 243 12 // All TLS 1.3 suites use 12-byte nonces 244 } 245} 246 247// --------------------------------------------------------------------------- 248// TLS record 249// --------------------------------------------------------------------------- 250 251/// A plaintext TLS record. 252#[derive(Debug, Clone)] 253pub struct TlsRecord { 254 pub content_type: ContentType, 255 pub data: Vec<u8>, 256} 257 258impl TlsRecord { 259 /// Create a new TLS record. 260 pub fn new(content_type: ContentType, data: Vec<u8>) -> Self { 261 Self { content_type, data } 262 } 263} 264 265// --------------------------------------------------------------------------- 266// Plaintext record I/O 267// --------------------------------------------------------------------------- 268 269/// Read a single plaintext TLS record from a stream. 270pub fn read_record<R: Read>(stream: &mut R) -> Result<TlsRecord> { 271 // Read 5-byte header 272 let mut header = [0u8; RECORD_HEADER_SIZE]; 273 stream.read_exact(&mut header)?; 274 275 let content_type = ContentType::from_u8(header[0])?; 276 // We accept any legacy version in the header (RFC 8446 §5.1) 277 let length = u16::from_be_bytes([header[3], header[4]]) as usize; 278 279 if length > MAX_CIPHERTEXT_LENGTH { 280 return Err(TlsError::RecordOverflow); 281 } 282 283 let mut data = vec![0u8; length]; 284 stream.read_exact(&mut data)?; 285 286 Ok(TlsRecord { content_type, data }) 287} 288 289/// Write a single plaintext TLS record to a stream. 290pub fn write_record<W: Write>(stream: &mut W, record: &TlsRecord) -> Result<()> { 291 if record.data.len() > MAX_PLAINTEXT_LENGTH { 292 return Err(TlsError::RecordOverflow); 293 } 294 295 let mut header = [0u8; RECORD_HEADER_SIZE]; 296 header[0] = record.content_type as u8; 297 header[1..3].copy_from_slice(&LEGACY_VERSION); 298 header[3..5].copy_from_slice(&(record.data.len() as u16).to_be_bytes()); 299 300 stream.write_all(&header)?; 301 stream.write_all(&record.data)?; 302 stream.flush()?; 303 304 Ok(()) 305} 306 307// --------------------------------------------------------------------------- 308// Nonce construction (RFC 8446 §5.3) 309// --------------------------------------------------------------------------- 310 311/// Construct a per-record nonce by XORing the base IV with the sequence number. 312/// 313/// The sequence number is left-padded with zeros to match the IV length, 314/// then XORed with the base IV. 315fn make_nonce(iv: &[u8; 12], seq: u64) -> [u8; 12] { 316 let mut nonce = *iv; 317 let seq_bytes = seq.to_be_bytes(); // 8 bytes 318 // XOR seq into the last 8 bytes of the 12-byte nonce 319 for i in 0..8 { 320 nonce[4 + i] ^= seq_bytes[i]; 321 } 322 nonce 323} 324 325// --------------------------------------------------------------------------- 326// AEAD encrypt/decrypt wrappers 327// --------------------------------------------------------------------------- 328 329fn aead_encrypt( 330 suite: CipherSuite, 331 key: &[u8], 332 nonce: &[u8; 12], 333 plaintext: &[u8], 334 aad: &[u8], 335) -> (Vec<u8>, [u8; 16]) { 336 match suite { 337 CipherSuite::Aes128Gcm => { 338 let key: [u8; 16] = key.try_into().expect("AES-128 key must be 16 bytes"); 339 we_crypto::aes_gcm::aes128_gcm_encrypt(&key, nonce, plaintext, aad) 340 } 341 CipherSuite::Aes256Gcm => { 342 let key: [u8; 32] = key.try_into().expect("AES-256 key must be 32 bytes"); 343 we_crypto::aes_gcm::aes256_gcm_encrypt(&key, nonce, plaintext, aad) 344 } 345 CipherSuite::Chacha20Poly1305 => { 346 let key: [u8; 32] = key.try_into().expect("ChaCha20 key must be 32 bytes"); 347 we_crypto::chacha20_poly1305::chacha20_poly1305_encrypt(&key, nonce, plaintext, aad) 348 } 349 } 350} 351 352fn aead_decrypt( 353 suite: CipherSuite, 354 key: &[u8], 355 nonce: &[u8; 12], 356 ciphertext: &[u8], 357 aad: &[u8], 358 tag: &[u8; 16], 359) -> Option<Vec<u8>> { 360 match suite { 361 CipherSuite::Aes128Gcm => { 362 let key: [u8; 16] = key.try_into().expect("AES-128 key must be 16 bytes"); 363 we_crypto::aes_gcm::aes128_gcm_decrypt(&key, nonce, ciphertext, aad, tag) 364 } 365 CipherSuite::Aes256Gcm => { 366 let key: [u8; 32] = key.try_into().expect("AES-256 key must be 32 bytes"); 367 we_crypto::aes_gcm::aes256_gcm_decrypt(&key, nonce, ciphertext, aad, tag) 368 } 369 CipherSuite::Chacha20Poly1305 => { 370 let key: [u8; 32] = key.try_into().expect("ChaCha20 key must be 32 bytes"); 371 we_crypto::chacha20_poly1305::chacha20_poly1305_decrypt( 372 &key, nonce, ciphertext, aad, tag, 373 ) 374 } 375 } 376} 377 378// --------------------------------------------------------------------------- 379// Encrypted record layer (RFC 8446 §5.2) 380// --------------------------------------------------------------------------- 381 382/// State for encrypting/decrypting TLS 1.3 records. 383/// 384/// Each direction (read/write) needs its own `RecordCryptoState` with 385/// independent keys, IVs, and sequence numbers. 386pub struct RecordCryptoState { 387 suite: CipherSuite, 388 key: Vec<u8>, 389 iv: [u8; 12], 390 seq: u64, 391} 392 393impl RecordCryptoState { 394 /// Create a new record crypto state. 395 pub fn new(suite: CipherSuite, key: Vec<u8>, iv: [u8; 12]) -> Self { 396 assert_eq!(key.len(), suite.key_len()); 397 Self { 398 suite, 399 key, 400 iv, 401 seq: 0, 402 } 403 } 404 405 /// Encrypt a TLS record (RFC 8446 §5.2). 406 /// 407 /// Builds TLSInnerPlaintext (content ∥ content_type ∥ zeros), 408 /// then encrypts with AEAD using the record header as AAD. 409 /// Returns the encrypted TLS record with outer type ApplicationData. 410 pub fn encrypt(&mut self, record: &TlsRecord) -> Result<TlsRecord> { 411 if record.data.len() > MAX_PLAINTEXT_LENGTH { 412 return Err(TlsError::RecordOverflow); 413 } 414 415 // Build TLSInnerPlaintext: content ∥ content_type (no padding) 416 let mut inner = Vec::with_capacity(record.data.len() + 1); 417 inner.extend_from_slice(&record.data); 418 inner.push(record.content_type as u8); 419 420 // Construct nonce 421 let nonce = make_nonce(&self.iv, self.seq); 422 423 // The AAD is the record header of the *outer* record: 424 // type(23) ∥ legacy_version(0x0303) ∥ length(encrypted_len) 425 // encrypted_len = inner.len() + tag_size 426 let encrypted_len = inner.len() + TAG_SIZE; 427 if encrypted_len > MAX_CIPHERTEXT_LENGTH { 428 return Err(TlsError::RecordOverflow); 429 } 430 431 let mut aad = [0u8; RECORD_HEADER_SIZE]; 432 aad[0] = ContentType::ApplicationData as u8; 433 aad[1..3].copy_from_slice(&LEGACY_VERSION); 434 aad[3..5].copy_from_slice(&(encrypted_len as u16).to_be_bytes()); 435 436 let (ciphertext, tag) = aead_encrypt(self.suite, &self.key, &nonce, &inner, &aad); 437 438 // Combine ciphertext + tag 439 let mut encrypted = ciphertext; 440 encrypted.extend_from_slice(&tag); 441 442 self.seq = self.seq.wrapping_add(1); 443 444 Ok(TlsRecord { 445 content_type: ContentType::ApplicationData, 446 data: encrypted, 447 }) 448 } 449 450 /// Decrypt a TLS record (RFC 8446 §5.2). 451 /// 452 /// Expects the outer content type to be ApplicationData. 453 /// Decrypts, strips padding and inner content type. 454 /// Returns the decrypted record with the real content type. 455 pub fn decrypt(&mut self, record: &TlsRecord) -> Result<TlsRecord> { 456 if record.content_type != ContentType::ApplicationData { 457 return Err(TlsError::UnknownContentType(record.content_type as u8)); 458 } 459 460 if record.data.len() < TAG_SIZE + 1 { 461 // Need at least tag + 1 byte for inner content type 462 return Err(TlsError::DecryptionFailed); 463 } 464 465 if record.data.len() > MAX_CIPHERTEXT_LENGTH { 466 return Err(TlsError::RecordOverflow); 467 } 468 469 // Split ciphertext and tag 470 let ct_len = record.data.len() - TAG_SIZE; 471 let ciphertext = &record.data[..ct_len]; 472 let tag: [u8; 16] = record.data[ct_len..].try_into().expect("tag is 16 bytes"); 473 474 // Construct nonce 475 let nonce = make_nonce(&self.iv, self.seq); 476 477 // Build AAD: the record header as received 478 let mut aad = [0u8; RECORD_HEADER_SIZE]; 479 aad[0] = ContentType::ApplicationData as u8; 480 aad[1..3].copy_from_slice(&LEGACY_VERSION); 481 aad[3..5].copy_from_slice(&(record.data.len() as u16).to_be_bytes()); 482 483 let inner = aead_decrypt(self.suite, &self.key, &nonce, ciphertext, &aad, &tag) 484 .ok_or(TlsError::DecryptionFailed)?; 485 486 self.seq = self.seq.wrapping_add(1); 487 488 // Strip padding zeros and extract inner content type 489 // TLSInnerPlaintext = content ∥ ContentType ∥ zeros 490 // Find the last non-zero byte (the content type) 491 let ct_pos = inner 492 .iter() 493 .rposition(|&b| b != 0) 494 .ok_or(TlsError::DecryptionFailed)?; 495 496 let content_type = ContentType::from_u8(inner[ct_pos])?; 497 let data = inner[..ct_pos].to_vec(); 498 499 if data.len() > MAX_PLAINTEXT_LENGTH { 500 return Err(TlsError::RecordOverflow); 501 } 502 503 Ok(TlsRecord { content_type, data }) 504 } 505} 506 507// --------------------------------------------------------------------------- 508// RecordLayer: combines plaintext and encrypted record I/O 509// --------------------------------------------------------------------------- 510 511/// The TLS record layer, handling both plaintext and encrypted records. 512pub struct RecordLayer<S> { 513 stream: S, 514 write_state: Option<RecordCryptoState>, 515 read_state: Option<RecordCryptoState>, 516} 517 518impl<S: Read + Write> RecordLayer<S> { 519 /// Create a new record layer in plaintext mode. 520 pub fn new(stream: S) -> Self { 521 Self { 522 stream, 523 write_state: None, 524 read_state: None, 525 } 526 } 527 528 /// Enable encryption for writing. 529 pub fn set_write_crypto(&mut self, state: RecordCryptoState) { 530 self.write_state = Some(state); 531 } 532 533 /// Enable encryption for reading. 534 pub fn set_read_crypto(&mut self, state: RecordCryptoState) { 535 self.read_state = Some(state); 536 } 537 538 /// Get a reference to the underlying stream. 539 pub fn stream(&self) -> &S { 540 &self.stream 541 } 542 543 /// Get a mutable reference to the underlying stream. 544 pub fn stream_mut(&mut self) -> &mut S { 545 &mut self.stream 546 } 547 548 /// Write a TLS record, encrypting if crypto state is set. 549 pub fn write_record(&mut self, record: &TlsRecord) -> Result<()> { 550 let record_to_write = match &mut self.write_state { 551 Some(state) => state.encrypt(record)?, 552 None => record.clone(), 553 }; 554 write_record(&mut self.stream, &record_to_write) 555 } 556 557 /// Read a TLS record, decrypting if crypto state is set. 558 pub fn read_record(&mut self) -> Result<TlsRecord> { 559 let raw = read_record(&mut self.stream)?; 560 match &mut self.read_state { 561 Some(state) if raw.content_type == ContentType::ApplicationData => state.decrypt(&raw), 562 _ => Ok(raw), 563 } 564 } 565 566 /// Send an alert. 567 pub fn send_alert(&mut self, alert: Alert) -> Result<()> { 568 let record = TlsRecord::new(ContentType::Alert, alert.encode().to_vec()); 569 self.write_record(&record) 570 } 571 572 /// Send a close_notify alert. 573 pub fn send_close_notify(&mut self) -> Result<()> { 574 self.send_alert(Alert::close_notify()) 575 } 576 577 /// Consume the record layer and return the underlying stream. 578 pub fn into_inner(self) -> S { 579 self.stream 580 } 581} 582 583// --------------------------------------------------------------------------- 584// Tests 585// --------------------------------------------------------------------------- 586 587#[cfg(test)] 588mod tests { 589 use super::*; 590 use std::io::Cursor; 591 592 // -- ContentType tests -- 593 594 #[test] 595 fn content_type_from_u8_valid() { 596 assert_eq!( 597 ContentType::from_u8(20).unwrap(), 598 ContentType::ChangeCipherSpec 599 ); 600 assert_eq!(ContentType::from_u8(21).unwrap(), ContentType::Alert); 601 assert_eq!(ContentType::from_u8(22).unwrap(), ContentType::Handshake); 602 assert_eq!( 603 ContentType::from_u8(23).unwrap(), 604 ContentType::ApplicationData 605 ); 606 } 607 608 #[test] 609 fn content_type_from_u8_invalid() { 610 assert!(ContentType::from_u8(0).is_err()); 611 assert!(ContentType::from_u8(19).is_err()); 612 assert!(ContentType::from_u8(24).is_err()); 613 assert!(ContentType::from_u8(255).is_err()); 614 } 615 616 // -- Alert tests -- 617 618 #[test] 619 fn alert_encode_decode() { 620 let alert = Alert::new(AlertLevel::Fatal, AlertDescription::HandshakeFailure); 621 let bytes = alert.encode(); 622 assert_eq!(bytes, [2, 40]); 623 let parsed = Alert::parse(&bytes).unwrap(); 624 assert_eq!(parsed, alert); 625 } 626 627 #[test] 628 fn alert_close_notify() { 629 let alert = Alert::close_notify(); 630 assert_eq!(alert.level, AlertLevel::Warning); 631 assert_eq!(alert.description, AlertDescription::CloseNotify); 632 assert!(!alert.is_fatal()); 633 assert_eq!(alert.encode(), [1, 0]); 634 } 635 636 #[test] 637 fn alert_fatal() { 638 let alert = Alert::new(AlertLevel::Fatal, AlertDescription::InternalError); 639 assert!(alert.is_fatal()); 640 } 641 642 #[test] 643 fn alert_parse_too_short() { 644 assert!(Alert::parse(&[1]).is_err()); 645 assert!(Alert::parse(&[]).is_err()); 646 } 647 648 #[test] 649 fn alert_parse_invalid_level() { 650 assert!(Alert::parse(&[0, 0]).is_err()); 651 assert!(Alert::parse(&[3, 0]).is_err()); 652 } 653 654 // -- Nonce construction tests -- 655 656 #[test] 657 fn nonce_seq_zero() { 658 let iv = [0x01; 12]; 659 let nonce = make_nonce(&iv, 0); 660 assert_eq!(nonce, iv); 661 } 662 663 #[test] 664 fn nonce_seq_one() { 665 let iv = [0u8; 12]; 666 let nonce = make_nonce(&iv, 1); 667 let mut expected = [0u8; 12]; 668 expected[11] = 1; 669 assert_eq!(nonce, expected); 670 } 671 672 #[test] 673 fn nonce_xor_correctness() { 674 let iv = [0xff; 12]; 675 let nonce = make_nonce(&iv, 0x0102030405060708); 676 // Last 8 bytes: 0xff ^ seq_bytes 677 assert_eq!(nonce[0..4], [0xff, 0xff, 0xff, 0xff]); // untouched first 4 678 assert_eq!(nonce[4], 0xff ^ 0x01); 679 assert_eq!(nonce[5], 0xff ^ 0x02); 680 assert_eq!(nonce[6], 0xff ^ 0x03); 681 assert_eq!(nonce[7], 0xff ^ 0x04); 682 assert_eq!(nonce[8], 0xff ^ 0x05); 683 assert_eq!(nonce[9], 0xff ^ 0x06); 684 assert_eq!(nonce[10], 0xff ^ 0x07); 685 assert_eq!(nonce[11], 0xff ^ 0x08); 686 } 687 688 // -- Plaintext record I/O tests -- 689 690 #[test] 691 fn write_read_plaintext_record() { 692 let record = TlsRecord::new(ContentType::Handshake, vec![0x01, 0x02, 0x03]); 693 let mut buf = Vec::new(); 694 write_record(&mut buf, &record).unwrap(); 695 696 // Verify wire format 697 assert_eq!(buf[0], 22); // Handshake 698 assert_eq!(buf[1..3], LEGACY_VERSION); 699 assert_eq!(buf[3..5], [0x00, 0x03]); // length = 3 700 assert_eq!(buf[5..], [0x01, 0x02, 0x03]); 701 702 // Read it back 703 let mut cursor = Cursor::new(&buf); 704 let read_back = read_record(&mut cursor).unwrap(); 705 assert_eq!(read_back.content_type, ContentType::Handshake); 706 assert_eq!(read_back.data, vec![0x01, 0x02, 0x03]); 707 } 708 709 #[test] 710 fn write_read_empty_record() { 711 let record = TlsRecord::new(ContentType::ApplicationData, vec![]); 712 let mut buf = Vec::new(); 713 write_record(&mut buf, &record).unwrap(); 714 715 let mut cursor = Cursor::new(&buf); 716 let read_back = read_record(&mut cursor).unwrap(); 717 assert_eq!(read_back.content_type, ContentType::ApplicationData); 718 assert!(read_back.data.is_empty()); 719 } 720 721 #[test] 722 fn write_record_overflow() { 723 let record = TlsRecord::new( 724 ContentType::ApplicationData, 725 vec![0u8; MAX_PLAINTEXT_LENGTH + 1], 726 ); 727 let mut buf = Vec::new(); 728 let result = write_record(&mut buf, &record); 729 assert!(result.is_err()); 730 } 731 732 #[test] 733 fn read_record_overflow() { 734 // Craft a header claiming more than MAX_CIPHERTEXT_LENGTH 735 let mut buf = vec![23u8]; // ApplicationData 736 buf.extend_from_slice(&LEGACY_VERSION); 737 let bad_len = (MAX_CIPHERTEXT_LENGTH + 1) as u16; 738 buf.extend_from_slice(&bad_len.to_be_bytes()); 739 let mut cursor = Cursor::new(&buf); 740 let result = read_record(&mut cursor); 741 assert!(result.is_err()); 742 } 743 744 #[test] 745 fn read_record_truncated_header() { 746 let buf = vec![22u8, 0x03]; // Only 2 bytes of header 747 let mut cursor = Cursor::new(&buf); 748 let result = read_record(&mut cursor); 749 assert!(result.is_err()); 750 } 751 752 #[test] 753 fn read_record_truncated_body() { 754 // Header says 10 bytes but only 5 are available 755 let mut buf = vec![22u8]; // Handshake 756 buf.extend_from_slice(&LEGACY_VERSION); 757 buf.extend_from_slice(&10u16.to_be_bytes()); 758 buf.extend_from_slice(&[0u8; 5]); // Only 5 of 10 bytes 759 let mut cursor = Cursor::new(&buf); 760 let result = read_record(&mut cursor); 761 assert!(result.is_err()); 762 } 763 764 #[test] 765 fn write_read_multiple_records() { 766 let records = vec![ 767 TlsRecord::new(ContentType::Handshake, vec![1, 2, 3]), 768 TlsRecord::new(ContentType::ApplicationData, vec![4, 5]), 769 TlsRecord::new(ContentType::Alert, vec![1, 0]), 770 ]; 771 772 let mut buf = Vec::new(); 773 for r in &records { 774 write_record(&mut buf, r).unwrap(); 775 } 776 777 let mut cursor = Cursor::new(&buf); 778 for expected in &records { 779 let read_back = read_record(&mut cursor).unwrap(); 780 assert_eq!(read_back.content_type, expected.content_type); 781 assert_eq!(read_back.data, expected.data); 782 } 783 } 784 785 // -- Encrypted record tests -- 786 787 fn test_keys(suite: CipherSuite) -> (Vec<u8>, [u8; 12]) { 788 let key = vec![0x42u8; suite.key_len()]; 789 let iv = [0x01u8; 12]; 790 (key, iv) 791 } 792 793 #[test] 794 fn encrypt_decrypt_roundtrip_aes128() { 795 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 796 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key.clone(), iv); 797 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 798 799 let original = TlsRecord::new(ContentType::Handshake, b"hello TLS".to_vec()); 800 let encrypted = enc.encrypt(&original).unwrap(); 801 assert_eq!(encrypted.content_type, ContentType::ApplicationData); 802 assert_ne!(encrypted.data, original.data); 803 804 let decrypted = dec.decrypt(&encrypted).unwrap(); 805 assert_eq!(decrypted.content_type, ContentType::Handshake); 806 assert_eq!(decrypted.data, b"hello TLS"); 807 } 808 809 #[test] 810 fn encrypt_decrypt_roundtrip_aes256() { 811 let (key, iv) = test_keys(CipherSuite::Aes256Gcm); 812 let mut enc = RecordCryptoState::new(CipherSuite::Aes256Gcm, key.clone(), iv); 813 let mut dec = RecordCryptoState::new(CipherSuite::Aes256Gcm, key, iv); 814 815 let original = TlsRecord::new(ContentType::ApplicationData, b"data".to_vec()); 816 let encrypted = enc.encrypt(&original).unwrap(); 817 let decrypted = dec.decrypt(&encrypted).unwrap(); 818 assert_eq!(decrypted.content_type, ContentType::ApplicationData); 819 assert_eq!(decrypted.data, b"data"); 820 } 821 822 #[test] 823 fn encrypt_decrypt_roundtrip_chacha20() { 824 let (key, iv) = test_keys(CipherSuite::Chacha20Poly1305); 825 let mut enc = RecordCryptoState::new(CipherSuite::Chacha20Poly1305, key.clone(), iv); 826 let mut dec = RecordCryptoState::new(CipherSuite::Chacha20Poly1305, key, iv); 827 828 let original = TlsRecord::new(ContentType::Handshake, b"chacha test".to_vec()); 829 let encrypted = enc.encrypt(&original).unwrap(); 830 let decrypted = dec.decrypt(&encrypted).unwrap(); 831 assert_eq!(decrypted.content_type, ContentType::Handshake); 832 assert_eq!(decrypted.data, b"chacha test"); 833 } 834 835 #[test] 836 fn sequence_number_increments() { 837 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 838 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key.clone(), iv); 839 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 840 841 for i in 0..5u8 { 842 let original = TlsRecord::new(ContentType::ApplicationData, vec![i]); 843 let encrypted = enc.encrypt(&original).unwrap(); 844 let decrypted = dec.decrypt(&encrypted).unwrap(); 845 assert_eq!(decrypted.data, vec![i]); 846 } 847 } 848 849 #[test] 850 fn out_of_order_decryption_fails() { 851 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 852 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key.clone(), iv); 853 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 854 855 let r1 = enc 856 .encrypt(&TlsRecord::new(ContentType::ApplicationData, vec![1])) 857 .unwrap(); 858 let r2 = enc 859 .encrypt(&TlsRecord::new(ContentType::ApplicationData, vec![2])) 860 .unwrap(); 861 862 // Decrypt r2 first (out of order) should fail 863 assert!(dec.decrypt(&r2).is_err()); 864 // r1 should still work 865 let d1 = dec.decrypt(&r1).unwrap(); 866 assert_eq!(d1.data, vec![1]); 867 } 868 869 #[test] 870 fn tampered_ciphertext_rejected() { 871 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 872 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key.clone(), iv); 873 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 874 875 let original = TlsRecord::new(ContentType::ApplicationData, b"sensitive".to_vec()); 876 let mut encrypted = enc.encrypt(&original).unwrap(); 877 encrypted.data[0] ^= 0xff; // Tamper with ciphertext 878 879 assert!(dec.decrypt(&encrypted).is_err()); 880 } 881 882 #[test] 883 fn content_type_hidden_in_encrypted_record() { 884 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 885 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 886 887 // Encrypt a Handshake record 888 let original = TlsRecord::new(ContentType::Handshake, vec![0x01]); 889 let encrypted = enc.encrypt(&original).unwrap(); 890 891 // Outer type must be ApplicationData (content type hiding) 892 assert_eq!(encrypted.content_type, ContentType::ApplicationData); 893 } 894 895 #[test] 896 fn encrypt_empty_record() { 897 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 898 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key.clone(), iv); 899 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 900 901 let original = TlsRecord::new(ContentType::ApplicationData, vec![]); 902 let encrypted = enc.encrypt(&original).unwrap(); 903 let decrypted = dec.decrypt(&encrypted).unwrap(); 904 assert_eq!(decrypted.content_type, ContentType::ApplicationData); 905 assert!(decrypted.data.is_empty()); 906 } 907 908 #[test] 909 fn encrypt_record_overflow() { 910 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 911 let mut enc = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 912 913 let big = TlsRecord::new( 914 ContentType::ApplicationData, 915 vec![0u8; MAX_PLAINTEXT_LENGTH + 1], 916 ); 917 assert!(enc.encrypt(&big).is_err()); 918 } 919 920 #[test] 921 fn decrypt_non_appdata_rejected() { 922 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 923 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 924 925 let bad = TlsRecord::new(ContentType::Handshake, vec![0, 1, 2]); 926 assert!(dec.decrypt(&bad).is_err()); 927 } 928 929 #[test] 930 fn decrypt_too_short_rejected() { 931 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 932 let mut dec = RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv); 933 934 // Less than TAG_SIZE + 1 935 let bad = TlsRecord::new(ContentType::ApplicationData, vec![0u8; TAG_SIZE]); 936 assert!(dec.decrypt(&bad).is_err()); 937 } 938 939 // -- RecordLayer integration tests -- 940 941 #[test] 942 fn record_layer_plaintext_roundtrip() { 943 let mut buf = Vec::new(); 944 945 // Write 946 { 947 let cursor = Cursor::new(&mut buf); 948 let mut layer = RecordLayer::new(cursor); 949 layer 950 .write_record(&TlsRecord::new( 951 ContentType::Handshake, 952 b"client hello".to_vec(), 953 )) 954 .unwrap(); 955 } 956 957 // Read 958 { 959 let cursor = Cursor::new(buf.clone()); 960 let mut layer = RecordLayer::new(cursor); 961 let record = layer.read_record().unwrap(); 962 assert_eq!(record.content_type, ContentType::Handshake); 963 assert_eq!(record.data, b"client hello"); 964 } 965 } 966 967 #[test] 968 fn record_layer_encrypted_roundtrip() { 969 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 970 let mut buf = Vec::new(); 971 972 // Write encrypted 973 { 974 let cursor = Cursor::new(&mut buf); 975 let mut layer = RecordLayer::new(cursor); 976 layer.set_write_crypto(RecordCryptoState::new( 977 CipherSuite::Aes128Gcm, 978 key.clone(), 979 iv, 980 )); 981 layer 982 .write_record(&TlsRecord::new( 983 ContentType::ApplicationData, 984 b"encrypted data".to_vec(), 985 )) 986 .unwrap(); 987 } 988 989 // Read encrypted 990 { 991 let cursor = Cursor::new(buf.clone()); 992 let mut layer = RecordLayer::new(cursor); 993 layer.set_read_crypto(RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv)); 994 let record = layer.read_record().unwrap(); 995 assert_eq!(record.content_type, ContentType::ApplicationData); 996 assert_eq!(record.data, b"encrypted data"); 997 } 998 } 999 1000 #[test] 1001 fn record_layer_send_close_notify() { 1002 let mut buf = Vec::new(); 1003 { 1004 let cursor = Cursor::new(&mut buf); 1005 let mut layer = RecordLayer::new(cursor); 1006 layer.send_close_notify().unwrap(); 1007 } 1008 1009 let cursor = Cursor::new(buf.clone()); 1010 let mut layer = RecordLayer::new(cursor); 1011 let record = layer.read_record().unwrap(); 1012 assert_eq!(record.content_type, ContentType::Alert); 1013 let alert = Alert::parse(&record.data).unwrap(); 1014 assert_eq!(alert.description, AlertDescription::CloseNotify); 1015 assert!(!alert.is_fatal()); 1016 } 1017 1018 #[test] 1019 fn record_layer_plaintext_then_encrypted() { 1020 let (key, iv) = test_keys(CipherSuite::Aes128Gcm); 1021 let mut buf = Vec::new(); 1022 1023 // Write: one plaintext, then switch to encrypted 1024 { 1025 let cursor = Cursor::new(&mut buf); 1026 let mut layer = RecordLayer::new(cursor); 1027 1028 // Plaintext handshake 1029 layer 1030 .write_record(&TlsRecord::new(ContentType::Handshake, b"hello".to_vec())) 1031 .unwrap(); 1032 1033 // Switch to encrypted 1034 layer.set_write_crypto(RecordCryptoState::new( 1035 CipherSuite::Aes128Gcm, 1036 key.clone(), 1037 iv, 1038 )); 1039 1040 // Encrypted application data 1041 layer 1042 .write_record(&TlsRecord::new( 1043 ContentType::ApplicationData, 1044 b"secret".to_vec(), 1045 )) 1046 .unwrap(); 1047 } 1048 1049 // Read: one plaintext, then switch to encrypted 1050 { 1051 let cursor = Cursor::new(buf.clone()); 1052 let mut layer = RecordLayer::new(cursor); 1053 1054 // Read plaintext 1055 let r1 = layer.read_record().unwrap(); 1056 assert_eq!(r1.content_type, ContentType::Handshake); 1057 assert_eq!(r1.data, b"hello"); 1058 1059 // Switch to encrypted 1060 layer.set_read_crypto(RecordCryptoState::new(CipherSuite::Aes128Gcm, key, iv)); 1061 1062 // Read encrypted 1063 let r2 = layer.read_record().unwrap(); 1064 assert_eq!(r2.content_type, ContentType::ApplicationData); 1065 assert_eq!(r2.data, b"secret"); 1066 } 1067 } 1068 1069 // -- CipherSuite tests -- 1070 1071 #[test] 1072 fn cipher_suite_key_lengths() { 1073 assert_eq!(CipherSuite::Aes128Gcm.key_len(), 16); 1074 assert_eq!(CipherSuite::Aes256Gcm.key_len(), 32); 1075 assert_eq!(CipherSuite::Chacha20Poly1305.key_len(), 32); 1076 } 1077 1078 #[test] 1079 fn cipher_suite_iv_lengths() { 1080 assert_eq!(CipherSuite::Aes128Gcm.iv_len(), 12); 1081 assert_eq!(CipherSuite::Aes256Gcm.iv_len(), 12); 1082 assert_eq!(CipherSuite::Chacha20Poly1305.iv_len(), 12); 1083 } 1084 1085 // -- Error Display tests -- 1086 1087 #[test] 1088 fn tls_error_display() { 1089 assert_eq!( 1090 TlsError::UnknownContentType(99).to_string(), 1091 "unknown TLS content type: 99" 1092 ); 1093 assert_eq!(TlsError::RecordOverflow.to_string(), "TLS record overflow"); 1094 assert_eq!( 1095 TlsError::DecryptionFailed.to_string(), 1096 "TLS AEAD decryption failed" 1097 ); 1098 assert_eq!(TlsError::MalformedAlert.to_string(), "malformed TLS alert"); 1099 } 1100 1101 #[test] 1102 fn tls_error_from_io() { 1103 let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "broken"); 1104 let tls_err = TlsError::from(io_err); 1105 assert!(matches!(tls_err, TlsError::Io(_))); 1106 } 1107}