⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/net/tls/
mod.rs

1//! TLS 1.3 Protocol Implementation (RFC 8446)
2//!
3//! Provides a complete TLS 1.3 client implementation for VeridianOS, including:
4//! - Record layer with fragmentation and encrypted record wrapping
5//! - Full handshake state machine (ClientHello through Finished)
6//! - Crypto primitives: ChaCha20-Poly1305, AES-128-GCM, X25519, HKDF-SHA256
7//! - Simplified X.509 certificate parsing and chain validation
8//! - Session ticket resumption and 0-RTT stubs
9//! - Connection API: connect(), send(), recv(), close()
10//!
11//! Cipher suites supported:
12//! - TLS_AES_128_GCM_SHA256 (0x1301)
13//! - TLS_CHACHA20_POLY1305_SHA256 (0x1303)
14
15#![allow(dead_code)]
16
17#[cfg(feature = "alloc")]
18extern crate alloc;
19
20#[cfg(feature = "alloc")]
21use alloc::vec::Vec;
22
23pub mod certificate;
24pub mod cipher;
25pub mod handshake;
26pub mod record;
27
28// Re-export public API
29pub use certificate::{TrustStore, X509Certificate};
30pub use cipher::{
31    hkdf_expand, hkdf_expand_label, hkdf_extract, hmac_sha256, x25519_keypair, x25519_shared_secret,
32};
33pub use handshake::{
34    ClientHello, HandshakeEngine, HandshakeState, HandshakeType, NamedGroup, ServerHello,
35    SignatureScheme,
36};
37pub use record::{
38    decrypt_record, encrypt_record, ContentType, FragmentBuffer, RecordHeader, TlsRecord,
39};
40
41// ============================================================================
42// Constants
43// ============================================================================
44
45/// Maximum TLS record payload size (2^14 = 16384 bytes)
46pub(crate) const MAX_RECORD_SIZE: usize = 16384;
47
48/// TLS 1.3 protocol version
49pub(crate) const TLS_13_VERSION: u16 = 0x0304;
50
51/// Legacy TLS 1.2 version used in record headers
52pub(crate) const TLS_LEGACY_VERSION: u16 = 0x0303;
53
54/// AEAD tag length for both AES-128-GCM and ChaCha20-Poly1305
55pub(crate) const AEAD_TAG_LEN: usize = 16;
56
57/// HKDF-SHA256 hash output length
58pub(crate) const HASH_LEN: usize = 32;
59
60/// X25519 key size (scalar and point)
61pub(crate) const X25519_KEY_LEN: usize = 32;
62
63/// IV/nonce length for TLS 1.3 AEAD ciphers
64pub(crate) const NONCE_LEN: usize = 12;
65
66/// AES-128 key length
67pub(crate) const AES_128_KEY_LEN: usize = 16;
68
69/// ChaCha20 key length
70pub(crate) const CHACHA20_KEY_LEN: usize = 32;
71
72// ============================================================================
73// Cipher Suite
74// ============================================================================
75
76/// Cipher suites supported by this implementation
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum CipherSuite {
79    /// TLS_AES_128_GCM_SHA256 (0x1301)
80    Aes128GcmSha256,
81    /// TLS_CHACHA20_POLY1305_SHA256 (0x1303)
82    ChaCha20Poly1305Sha256,
83}
84
85impl CipherSuite {
86    /// Wire format code point
87    pub fn code(&self) -> u16 {
88        match self {
89            Self::Aes128GcmSha256 => 0x1301,
90            Self::ChaCha20Poly1305Sha256 => 0x1303,
91        }
92    }
93
94    /// Parse from wire format
95    pub fn from_code(code: u16) -> Option<Self> {
96        match code {
97            0x1301 => Some(Self::Aes128GcmSha256),
98            0x1303 => Some(Self::ChaCha20Poly1305Sha256),
99            _ => None,
100        }
101    }
102
103    /// Key length for this cipher suite
104    pub fn key_len(&self) -> usize {
105        match self {
106            Self::Aes128GcmSha256 => AES_128_KEY_LEN,
107            Self::ChaCha20Poly1305Sha256 => CHACHA20_KEY_LEN,
108        }
109    }
110}
111
112// ============================================================================
113// Alert Protocol
114// ============================================================================
115
116/// TLS 1.3 alert levels
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118#[repr(u8)]
119pub enum AlertLevel {
120    Warning = 1,
121    Fatal = 2,
122}
123
124impl AlertLevel {
125    fn from_u8(v: u8) -> Option<Self> {
126        match v {
127            1 => Some(Self::Warning),
128            2 => Some(Self::Fatal),
129            _ => None,
130        }
131    }
132}
133
134/// TLS 1.3 alert descriptions
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136#[repr(u8)]
137pub enum AlertDescription {
138    CloseNotify = 0,
139    UnexpectedMessage = 10,
140    BadRecordMac = 20,
141    RecordOverflow = 22,
142    HandshakeFailure = 40,
143    BadCertificate = 42,
144    CertificateRevoked = 44,
145    CertificateExpired = 45,
146    CertificateUnknown = 46,
147    IllegalParameter = 47,
148    UnknownCa = 48,
149    DecodeError = 50,
150    DecryptError = 51,
151    ProtocolVersion = 70,
152    InsufficientSecurity = 71,
153    InternalError = 80,
154    MissingExtension = 109,
155    UnsupportedExtension = 110,
156    UnrecognizedName = 112,
157    NoApplicationProtocol = 120,
158}
159
160impl AlertDescription {
161    fn from_u8(v: u8) -> Option<Self> {
162        match v {
163            0 => Some(Self::CloseNotify),
164            10 => Some(Self::UnexpectedMessage),
165            20 => Some(Self::BadRecordMac),
166            22 => Some(Self::RecordOverflow),
167            40 => Some(Self::HandshakeFailure),
168            42 => Some(Self::BadCertificate),
169            44 => Some(Self::CertificateRevoked),
170            45 => Some(Self::CertificateExpired),
171            46 => Some(Self::CertificateUnknown),
172            47 => Some(Self::IllegalParameter),
173            48 => Some(Self::UnknownCa),
174            50 => Some(Self::DecodeError),
175            51 => Some(Self::DecryptError),
176            70 => Some(Self::ProtocolVersion),
177            71 => Some(Self::InsufficientSecurity),
178            80 => Some(Self::InternalError),
179            109 => Some(Self::MissingExtension),
180            110 => Some(Self::UnsupportedExtension),
181            112 => Some(Self::UnrecognizedName),
182            120 => Some(Self::NoApplicationProtocol),
183            _ => None,
184        }
185    }
186}
187
188/// TLS alert message
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub struct TlsAlert {
191    pub level: AlertLevel,
192    pub description: AlertDescription,
193}
194
195impl TlsAlert {
196    pub fn new(level: AlertLevel, description: AlertDescription) -> Self {
197        Self { level, description }
198    }
199
200    pub fn encode(&self) -> [u8; 2] {
201        [self.level as u8, self.description as u8]
202    }
203
204    pub fn decode(data: &[u8]) -> Option<Self> {
205        if data.len() < 2 {
206            return None;
207        }
208        Some(Self {
209            level: AlertLevel::from_u8(data[0])?,
210            description: AlertDescription::from_u8(data[1])?,
211        })
212    }
213
214    /// Is this a fatal alert?
215    pub fn is_fatal(&self) -> bool {
216        self.level == AlertLevel::Fatal
217    }
218}
219
220// ============================================================================
221// Session Ticket Resumption
222// ============================================================================
223
224/// Session ticket for TLS 1.3 resumption (PSK-based)
225#[derive(Debug, Clone)]
226pub struct SessionTicket {
227    /// Ticket lifetime in seconds
228    pub lifetime: u32,
229    /// Ticket age add (for obfuscation)
230    pub age_add: u32,
231    /// Ticket nonce
232    pub nonce: Vec<u8>,
233    /// Opaque ticket data
234    pub ticket: Vec<u8>,
235    /// Cipher suite used in original session
236    pub cipher_suite: CipherSuite,
237    /// Resumption master secret
238    pub resumption_secret: [u8; HASH_LEN],
239    /// Creation timestamp (kernel ticks or epoch seconds)
240    pub created_at: u64,
241}
242
243impl SessionTicket {
244    /// Parse a NewSessionTicket handshake message
245    pub fn from_message(
246        data: &[u8],
247        cipher_suite: CipherSuite,
248        resumption_secret: [u8; HASH_LEN],
249        now: u64,
250    ) -> Option<Self> {
251        if data.len() < 4 {
252            return None;
253        }
254        if data[0] != HandshakeType::NewSessionTicket as u8 {
255            return None;
256        }
257        let payload = &data[4..];
258        if payload.len() < 12 {
259            return None;
260        }
261
262        let lifetime = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
263        let age_add = u32::from_be_bytes([payload[4], payload[5], payload[6], payload[7]]);
264
265        let nonce_len = payload[8] as usize;
266        if 9 + nonce_len + 2 > payload.len() {
267            return None;
268        }
269        let nonce = payload[9..9 + nonce_len].to_vec();
270
271        let ticket_pos = 9 + nonce_len;
272        let ticket_len =
273            u16::from_be_bytes([payload[ticket_pos], payload[ticket_pos + 1]]) as usize;
274        let ticket_start = ticket_pos + 2;
275        if ticket_start + ticket_len > payload.len() {
276            return None;
277        }
278        let ticket = payload[ticket_start..ticket_start + ticket_len].to_vec();
279
280        Some(Self {
281            lifetime,
282            age_add,
283            nonce,
284            ticket,
285            cipher_suite,
286            resumption_secret,
287            created_at: now,
288        })
289    }
290
291    /// Derive the PSK for resumption
292    pub fn derive_psk(&self) -> [u8; HASH_LEN] {
293        let expanded = hkdf_expand_label(
294            &self.resumption_secret,
295            b"resumption",
296            &self.nonce,
297            HASH_LEN,
298        );
299        let mut psk = [0u8; HASH_LEN];
300        psk.copy_from_slice(&expanded);
301        psk
302    }
303
304    /// Check if the ticket has expired
305    pub fn is_expired(&self, now: u64) -> bool {
306        now.saturating_sub(self.created_at) > self.lifetime as u64
307    }
308}
309
310/// Session ticket store (limited size, FIFO eviction)
311pub struct SessionStore {
312    tickets: Vec<SessionTicket>,
313    max_entries: usize,
314}
315
316impl SessionStore {
317    pub fn new(max_entries: usize) -> Self {
318        Self {
319            tickets: Vec::new(),
320            max_entries,
321        }
322    }
323
324    /// Store a session ticket
325    pub fn store(&mut self, ticket: SessionTicket) {
326        if self.tickets.len() >= self.max_entries {
327            self.tickets.remove(0);
328        }
329        self.tickets.push(ticket);
330    }
331
332    /// Find a valid (non-expired) ticket for resumption
333    pub fn find_valid(&self, now: u64) -> Option<&SessionTicket> {
334        self.tickets.iter().rev().find(|t| !t.is_expired(now))
335    }
336
337    /// Remove expired tickets
338    pub fn prune(&mut self, now: u64) {
339        self.tickets.retain(|t| !t.is_expired(now));
340    }
341
342    /// Number of stored tickets
343    pub fn count(&self) -> usize {
344        self.tickets.len()
345    }
346}
347
348// 0-RTT early data support stubs
349
350/// Early data configuration
351#[derive(Debug, Clone, Copy, PartialEq, Eq)]
352pub enum EarlyDataState {
353    /// Not attempting 0-RTT
354    NotAttempted,
355    /// 0-RTT offered in ClientHello
356    Offered,
357    /// Server accepted 0-RTT
358    Accepted,
359    /// Server rejected 0-RTT
360    Rejected,
361}
362
363// ============================================================================
364// TLS Connection Errors
365// ============================================================================
366
367/// TLS connection errors
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369pub enum TlsError {
370    /// Handshake not completed
371    NotConnected,
372    /// Connection already closed
373    Closed,
374    /// Handshake failure
375    HandshakeError,
376    /// Record decryption failed
377    DecryptionFailed,
378    /// Alert received from peer
379    AlertReceived(AlertDescription),
380    /// Invalid state transition
381    InvalidState,
382    /// Buffer too small
383    BufferTooSmall,
384    /// Data too large for single record
385    DataTooLarge,
386}
387
388// ============================================================================
389// TLS Connection API
390// ============================================================================
391
392/// TLS 1.3 connection state
393pub struct TlsConnection {
394    /// Handshake engine
395    engine: HandshakeEngine,
396    /// Send sequence number
397    send_seq: u64,
398    /// Receive sequence number
399    recv_seq: u64,
400    /// Client write key (application data)
401    client_write_key: Option<Vec<u8>>,
402    /// Server write key (application data)
403    server_write_key: Option<Vec<u8>>,
404    /// Client write IV
405    client_write_iv: Option<[u8; NONCE_LEN]>,
406    /// Server write IV
407    server_write_iv: Option<[u8; NONCE_LEN]>,
408    /// Fragment reassembly buffer
409    fragment_buf: FragmentBuffer,
410    /// Received application data buffer
411    app_data_buf: Vec<u8>,
412    /// Session ticket store
413    session_store: SessionStore,
414    /// Early data state
415    pub early_data: EarlyDataState,
416    /// Whether the connection has been closed
417    closed: bool,
418    /// Last alert received
419    pub last_alert: Option<TlsAlert>,
420}
421
422impl Default for TlsConnection {
423    fn default() -> Self {
424        Self::new()
425    }
426}
427
428impl TlsConnection {
429    /// Create a new TLS connection (client mode)
430    pub fn new() -> Self {
431        Self {
432            engine: HandshakeEngine::new(),
433            send_seq: 0,
434            recv_seq: 0,
435            client_write_key: None,
436            server_write_key: None,
437            client_write_iv: None,
438            server_write_iv: None,
439            fragment_buf: FragmentBuffer::new(),
440            app_data_buf: Vec::new(),
441            session_store: SessionStore::new(4),
442            early_data: EarlyDataState::NotAttempted,
443            closed: false,
444            last_alert: None,
445        }
446    }
447
448    /// Initiate the TLS handshake. Returns the ClientHello record to send.
449    pub fn connect(&mut self, random: [u8; 32]) -> Result<Vec<u8>, TlsError> {
450        let ch_msg = self
451            .engine
452            .build_client_hello(random)
453            .ok_or(TlsError::InvalidState)?;
454
455        let record = TlsRecord::new(ContentType::Handshake, ch_msg);
456        Ok(record.encode())
457    }
458
459    /// Process incoming data from the peer. Returns any response records to
460    /// send.
461    pub fn process_incoming(&mut self, data: &[u8]) -> Result<Vec<u8>, TlsError> {
462        let mut response = Vec::new();
463        let mut pos = 0;
464
465        while pos < data.len() {
466            let (record, consumed) =
467                TlsRecord::decode(&data[pos..]).ok_or(TlsError::DecryptionFailed)?;
468            pos += consumed;
469
470            match record.content_type {
471                ContentType::Alert => {
472                    if let Some(alert) = TlsAlert::decode(&record.fragment) {
473                        self.last_alert = Some(alert);
474                        if alert.is_fatal() {
475                            self.closed = true;
476                            return Err(TlsError::AlertReceived(alert.description));
477                        }
478                        if alert.description == AlertDescription::CloseNotify {
479                            self.closed = true;
480                        }
481                    }
482                }
483                ContentType::Handshake => {
484                    if let Some(msg) = self.fragment_buf.append(&record) {
485                        self.process_handshake_message(&msg, &mut response)?;
486                    }
487                }
488                ContentType::ApplicationData => {
489                    if self.engine.state == HandshakeState::Connected {
490                        // Decrypt application data
491                        let cipher = self.engine.cipher_suite.ok_or(TlsError::NotConnected)?;
492                        let iv = self
493                            .server_write_iv
494                            .as_ref()
495                            .ok_or(TlsError::NotConnected)?;
496                        let key = self
497                            .server_write_key
498                            .as_ref()
499                            .ok_or(TlsError::NotConnected)?;
500                        if let Some(plain) = decrypt_record(&record, key, iv, self.recv_seq, cipher)
501                        {
502                            self.recv_seq += 1;
503                            self.app_data_buf.extend_from_slice(&plain.fragment);
504                        } else {
505                            return Err(TlsError::DecryptionFailed);
506                        }
507                    }
508                }
509                ContentType::ChangeCipherSpec => {
510                    // TLS 1.3 ignores CCS for compatibility
511                }
512            }
513        }
514
515        Ok(response)
516    }
517
518    /// Process a complete handshake message
519    fn process_handshake_message(
520        &mut self,
521        msg: &[u8],
522        response: &mut Vec<u8>,
523    ) -> Result<(), TlsError> {
524        if msg.is_empty() {
525            return Err(TlsError::HandshakeError);
526        }
527
528        let hs_type = HandshakeType::from_u8(msg[0]).ok_or(TlsError::HandshakeError)?;
529
530        match hs_type {
531            HandshakeType::ServerHello => {
532                self.engine
533                    .process_server_hello(msg)
534                    .ok_or(TlsError::HandshakeError)?;
535            }
536            HandshakeType::EncryptedExtensions => {
537                self.engine
538                    .process_encrypted_extensions(msg)
539                    .ok_or(TlsError::HandshakeError)?;
540            }
541            HandshakeType::Certificate => {
542                self.engine
543                    .process_certificate(msg)
544                    .ok_or(TlsError::HandshakeError)?;
545            }
546            HandshakeType::CertificateVerify => {
547                self.engine
548                    .process_certificate_verify(msg)
549                    .ok_or(TlsError::HandshakeError)?;
550            }
551            HandshakeType::Finished => {
552                self.engine
553                    .process_finished(msg)
554                    .ok_or(TlsError::HandshakeError)?;
555
556                // Derive application traffic keys
557                self.derive_traffic_keys()?;
558
559                // Send client Finished
560                if let Some(client_finished) = self.engine.build_client_finished() {
561                    let record = TlsRecord::new(ContentType::Handshake, client_finished);
562                    response.extend_from_slice(&record.encode());
563                }
564            }
565            HandshakeType::NewSessionTicket => {
566                // Store session ticket for future resumption
567                if let Some(cipher) = self.engine.cipher_suite {
568                    let resumption_secret = self
569                        .engine
570                        .client_app_traffic_secret
571                        .unwrap_or([0u8; HASH_LEN]);
572                    if let Some(ticket) =
573                        SessionTicket::from_message(msg, cipher, resumption_secret, 0)
574                    {
575                        self.session_store.store(ticket);
576                    }
577                }
578            }
579            _ => {
580                return Err(TlsError::HandshakeError);
581            }
582        }
583
584        Ok(())
585    }
586
587    /// Derive traffic keys from handshake secrets
588    fn derive_traffic_keys(&mut self) -> Result<(), TlsError> {
589        let cipher = self.engine.cipher_suite.ok_or(TlsError::NotConnected)?;
590        let key_len = cipher.key_len();
591
592        // Client write key and IV
593        let c_secret = self
594            .engine
595            .client_app_traffic_secret
596            .as_ref()
597            .ok_or(TlsError::NotConnected)?;
598        self.client_write_key = Some(hkdf_expand_label(c_secret, b"key", &[], key_len));
599        let c_iv = hkdf_expand_label(c_secret, b"iv", &[], NONCE_LEN);
600        let mut civ = [0u8; NONCE_LEN];
601        civ.copy_from_slice(&c_iv);
602        self.client_write_iv = Some(civ);
603
604        // Server write key and IV
605        let s_secret = self
606            .engine
607            .server_app_traffic_secret
608            .as_ref()
609            .ok_or(TlsError::NotConnected)?;
610        self.server_write_key = Some(hkdf_expand_label(s_secret, b"key", &[], key_len));
611        let s_iv = hkdf_expand_label(s_secret, b"iv", &[], NONCE_LEN);
612        let mut siv = [0u8; NONCE_LEN];
613        siv.copy_from_slice(&s_iv);
614        self.server_write_iv = Some(siv);
615
616        Ok(())
617    }
618
619    /// Send application data. Returns the encrypted record to transmit.
620    pub fn send(&mut self, data: &[u8]) -> Result<Vec<u8>, TlsError> {
621        if self.closed {
622            return Err(TlsError::Closed);
623        }
624        if self.engine.state != HandshakeState::Connected {
625            return Err(TlsError::NotConnected);
626        }
627
628        let cipher = self.engine.cipher_suite.ok_or(TlsError::NotConnected)?;
629        let key = self
630            .client_write_key
631            .as_ref()
632            .ok_or(TlsError::NotConnected)?;
633        let iv = self
634            .client_write_iv
635            .as_ref()
636            .ok_or(TlsError::NotConnected)?;
637
638        let mut output = Vec::new();
639
640        // Fragment data into MAX_RECORD_SIZE chunks
641        for chunk in data.chunks(MAX_RECORD_SIZE) {
642            let record = TlsRecord::new(ContentType::ApplicationData, chunk.to_vec());
643            let encrypted = encrypt_record(&record, key, iv, self.send_seq, cipher)
644                .ok_or(TlsError::DecryptionFailed)?;
645            self.send_seq += 1;
646            output.extend_from_slice(&encrypted.encode());
647        }
648
649        Ok(output)
650    }
651
652    /// Read received application data from the internal buffer
653    pub fn recv(&mut self, buf: &mut [u8]) -> Result<usize, TlsError> {
654        if self.app_data_buf.is_empty() {
655            if self.closed {
656                return Err(TlsError::Closed);
657            }
658            return Ok(0);
659        }
660
661        let copy_len = core::cmp::min(buf.len(), self.app_data_buf.len());
662        buf[..copy_len].copy_from_slice(&self.app_data_buf[..copy_len]);
663        self.app_data_buf = self.app_data_buf[copy_len..].to_vec();
664        Ok(copy_len)
665    }
666
667    /// Close the TLS connection. Returns a close_notify alert record to send.
668    pub fn close(&mut self) -> Result<Vec<u8>, TlsError> {
669        if self.closed {
670            return Err(TlsError::Closed);
671        }
672        self.closed = true;
673
674        let alert = TlsAlert::new(AlertLevel::Warning, AlertDescription::CloseNotify);
675        let alert_bytes = alert.encode().to_vec();
676
677        if self.engine.state == HandshakeState::Connected {
678            let cipher = self.engine.cipher_suite.ok_or(TlsError::NotConnected)?;
679            let key = self
680                .client_write_key
681                .as_ref()
682                .ok_or(TlsError::NotConnected)?;
683            let iv = self
684                .client_write_iv
685                .as_ref()
686                .ok_or(TlsError::NotConnected)?;
687
688            let record = TlsRecord::new(ContentType::Alert, alert_bytes);
689            let encrypted = encrypt_record(&record, key, iv, self.send_seq, cipher)
690                .ok_or(TlsError::DecryptionFailed)?;
691            self.send_seq += 1;
692            Ok(encrypted.encode())
693        } else {
694            let record = TlsRecord::new(ContentType::Alert, alert_bytes);
695            Ok(record.encode())
696        }
697    }
698
699    /// Get the current handshake state
700    pub fn state(&self) -> HandshakeState {
701        self.engine.state
702    }
703
704    /// Check if the connection is established
705    pub fn is_connected(&self) -> bool {
706        self.engine.state == HandshakeState::Connected && !self.closed
707    }
708
709    /// Get the negotiated cipher suite
710    pub fn cipher_suite(&self) -> Option<CipherSuite> {
711        self.engine.cipher_suite
712    }
713
714    /// Get the number of stored session tickets
715    pub fn session_ticket_count(&self) -> usize {
716        self.session_store.count()
717    }
718}
719
720// ============================================================================
721// Tests
722// ============================================================================
723
724#[cfg(test)]
725mod tests {
726    #[allow(unused_imports)]
727    use alloc::vec;
728
729    use super::{
730        certificate::asn1_parse_tlv,
731        cipher::{
732            aes128_gcm_decrypt, aes128_gcm_encrypt, chacha20_crypt, chacha20_poly1305_decrypt,
733            chacha20_poly1305_encrypt, x25519_scalar_mult,
734        },
735        record::compute_nonce,
736        *,
737    };
738
739    // --- Record Layer Tests ---
740
741    #[test]
742    fn test_record_header_encode_decode() {
743        let header = RecordHeader {
744            content_type: ContentType::Handshake,
745            legacy_version: TLS_LEGACY_VERSION,
746            length: 256,
747        };
748        let mut buf = [0u8; 5];
749        let n = header.encode(&mut buf);
750        assert_eq!(n, 5);
751
752        let decoded = RecordHeader::decode(&buf).unwrap();
753        assert_eq!(decoded.content_type, ContentType::Handshake);
754        assert_eq!(decoded.legacy_version, TLS_LEGACY_VERSION);
755        assert_eq!(decoded.length, 256);
756    }
757
758    #[test]
759    fn test_record_encode_decode() {
760        let data = vec![1, 2, 3, 4, 5];
761        let record = TlsRecord::new(ContentType::ApplicationData, data.clone());
762        let encoded = record.encode();
763
764        let (decoded, consumed) = TlsRecord::decode(&encoded).unwrap();
765        assert_eq!(consumed, encoded.len());
766        assert_eq!(decoded.content_type, ContentType::ApplicationData);
767        assert_eq!(decoded.fragment, data);
768    }
769
770    #[test]
771    fn test_record_header_rejects_oversized() {
772        let mut buf = [0u8; 5];
773        buf[0] = ContentType::ApplicationData as u8;
774        buf[1..3].copy_from_slice(&TLS_LEGACY_VERSION.to_be_bytes());
775        // Length = MAX_RECORD_SIZE + AEAD_TAG_LEN + 2 (too large)
776        let too_large = (MAX_RECORD_SIZE + AEAD_TAG_LEN + 2) as u16;
777        buf[3..5].copy_from_slice(&too_large.to_be_bytes());
778        assert!(RecordHeader::decode(&buf).is_none());
779    }
780
781    #[test]
782    fn test_content_type_roundtrip() {
783        assert_eq!(
784            ContentType::from_u8(20),
785            Some(ContentType::ChangeCipherSpec)
786        );
787        assert_eq!(ContentType::from_u8(21), Some(ContentType::Alert));
788        assert_eq!(ContentType::from_u8(22), Some(ContentType::Handshake));
789        assert_eq!(ContentType::from_u8(23), Some(ContentType::ApplicationData));
790        assert_eq!(ContentType::from_u8(99), None);
791    }
792
793    // --- Handshake State Machine Tests ---
794
795    #[test]
796    fn test_handshake_initial_state() {
797        let engine = HandshakeEngine::new();
798        assert_eq!(engine.state, HandshakeState::Start);
799    }
800
801    #[test]
802    fn test_handshake_client_hello_advances_state() {
803        let mut engine = HandshakeEngine::new();
804        let random = [0x42u8; 32];
805        let msg = engine.build_client_hello(random);
806        assert!(msg.is_some());
807        assert_eq!(engine.state, HandshakeState::WaitServerHello);
808    }
809
810    #[test]
811    fn test_handshake_rejects_wrong_state() {
812        let mut engine = HandshakeEngine::new();
813        // Cannot process ServerHello without first sending ClientHello
814        assert!(engine.process_server_hello(&[]).is_none());
815    }
816
817    #[test]
818    fn test_cipher_suite_code_roundtrip() {
819        assert_eq!(
820            CipherSuite::from_code(0x1301),
821            Some(CipherSuite::Aes128GcmSha256)
822        );
823        assert_eq!(
824            CipherSuite::from_code(0x1303),
825            Some(CipherSuite::ChaCha20Poly1305Sha256)
826        );
827        assert_eq!(CipherSuite::from_code(0x9999), None);
828        assert_eq!(CipherSuite::Aes128GcmSha256.code(), 0x1301);
829        assert_eq!(CipherSuite::ChaCha20Poly1305Sha256.code(), 0x1303);
830    }
831
832    #[test]
833    fn test_client_hello_encoding() {
834        let ch = ClientHello {
835            random: [0xAA; 32],
836            session_id: [0; 32],
837            cipher_suites: vec![CipherSuite::Aes128GcmSha256],
838            key_share_public: [0xBB; 32],
839        };
840        let encoded = ch.encode();
841        // First byte should be ClientHello type
842        assert_eq!(encoded[0], HandshakeType::ClientHello as u8);
843        // Should contain our random bytes
844        assert_eq!(&encoded[6..38], &[0xAA; 32]);
845    }
846
847    // --- HKDF Tests ---
848
849    #[test]
850    fn test_hkdf_extract_rfc5869_vector1() {
851        // RFC 5869 Test Case 1 (SHA-256)
852        let ikm = [0x0bu8; 22];
853        let salt = [
854            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
855        ];
856        let prk = hkdf_extract(&salt, &ikm);
857
858        let expected: [u8; 32] = [
859            0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b,
860            0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a,
861            0xd7, 0xc2, 0xb3, 0xe5,
862        ];
863        assert_eq!(prk, expected);
864    }
865
866    #[test]
867    fn test_hkdf_expand_rfc5869_vector1() {
868        let prk: [u8; 32] = [
869            0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b,
870            0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a,
871            0xd7, 0xc2, 0xb3, 0xe5,
872        ];
873        let info = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
874        let okm = hkdf_expand(&prk, &info, 42);
875
876        let expected: [u8; 42] = [
877            0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
878            0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
879            0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
880        ];
881        assert_eq!(&okm[..], &expected[..]);
882    }
883
884    // --- X25519 Tests ---
885
886    #[test]
887    fn test_x25519_basepoint_multiplication() {
888        // RFC 7748 Section 6.1: scalar * basepoint
889        let scalar: [u8; 32] = [
890            0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46,
891            0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44,
892            0xba, 0x44, 0x9a, 0xc4,
893        ];
894        // Verified against RFC 7748 reference and Python cryptography library
895        let expected: [u8; 32] = [
896            0x1c, 0x9f, 0xd8, 0x8f, 0x45, 0x60, 0x6d, 0x93, 0x2a, 0x80, 0xc7, 0x18, 0x24, 0xae,
897            0x15, 0x1d, 0x15, 0xd7, 0x3e, 0x77, 0xde, 0x38, 0xe8, 0xe0, 0x00, 0x85, 0x2e, 0x61,
898            0x4f, 0xae, 0x70, 0x19,
899        ];
900
901        let x25519_basepoint: [u8; 32] = {
902            let mut b = [0u8; 32];
903            b[0] = 9;
904            b
905        };
906        let result = x25519_scalar_mult(&scalar, &x25519_basepoint);
907        assert_eq!(result, expected);
908    }
909
910    #[test]
911    fn test_x25519_shared_secret_symmetry() {
912        // Two parties should derive the same shared secret
913        let alice_sk: [u8; 32] = {
914            let mut k = [0u8; 32];
915            for (i, b) in k.iter_mut().enumerate() {
916                *b = (i as u8).wrapping_mul(7).wrapping_add(3);
917            }
918            k
919        };
920        let bob_sk: [u8; 32] = {
921            let mut k = [0u8; 32];
922            for (i, b) in k.iter_mut().enumerate() {
923                *b = (i as u8).wrapping_mul(13).wrapping_add(17);
924            }
925            k
926        };
927
928        let x25519_basepoint: [u8; 32] = {
929            let mut b = [0u8; 32];
930            b[0] = 9;
931            b
932        };
933        let alice_pk = x25519_scalar_mult(&alice_sk, &x25519_basepoint);
934        let bob_pk = x25519_scalar_mult(&bob_sk, &x25519_basepoint);
935
936        let alice_shared = x25519_shared_secret(&alice_sk, &bob_pk);
937        let bob_shared = x25519_shared_secret(&bob_sk, &alice_pk);
938
939        assert_eq!(alice_shared, bob_shared);
940    }
941
942    // --- ChaCha20 Tests ---
943
944    #[test]
945    fn test_chacha20_encrypt_decrypt_roundtrip() {
946        let key = [0x42u8; 32];
947        let nonce = [0x01u8; 12];
948        let plaintext = b"Hello, TLS 1.3 from VeridianOS!";
949
950        let ciphertext = chacha20_crypt(&key, &nonce, 1, plaintext);
951        assert_ne!(&ciphertext[..], &plaintext[..]);
952
953        let decrypted = chacha20_crypt(&key, &nonce, 1, &ciphertext);
954        assert_eq!(&decrypted[..], &plaintext[..]);
955    }
956
957    #[test]
958    fn test_chacha20_poly1305_roundtrip() {
959        let key = [0x42u8; 32];
960        let nonce = [0x01u8; 12];
961        let aad = b"additional authenticated data";
962        let plaintext = b"secret message for TLS";
963
964        let ct = chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext);
965        assert_eq!(ct.len(), plaintext.len() + AEAD_TAG_LEN);
966
967        let pt = chacha20_poly1305_decrypt(&key, &nonce, aad, &ct).unwrap();
968        assert_eq!(&pt[..], &plaintext[..]);
969    }
970
971    #[test]
972    fn test_chacha20_poly1305_tamper_detection() {
973        let key = [0x42u8; 32];
974        let nonce = [0x01u8; 12];
975        let aad = b"aad";
976        let plaintext = b"secret";
977
978        let mut ct = chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext);
979        // Tamper with ciphertext
980        ct[0] ^= 0xFF;
981        assert!(chacha20_poly1305_decrypt(&key, &nonce, aad, &ct).is_none());
982    }
983
984    // --- AES-128-GCM Tests ---
985
986    #[test]
987    fn test_aes128_gcm_roundtrip() {
988        let key = [0x42u8; 16];
989        let nonce = [0x01u8; 12];
990        let aad = b"additional data";
991        let plaintext = b"AES-GCM test message for TLS";
992
993        let ct = aes128_gcm_encrypt(&key, &nonce, aad, plaintext);
994        assert_eq!(ct.len(), plaintext.len() + AEAD_TAG_LEN);
995
996        let pt = aes128_gcm_decrypt(&key, &nonce, aad, &ct).unwrap();
997        assert_eq!(&pt[..], &plaintext[..]);
998    }
999
1000    #[test]
1001    fn test_aes128_gcm_tamper_detection() {
1002        let key = [0x42u8; 16];
1003        let nonce = [0x01u8; 12];
1004        let aad = b"aad";
1005        let plaintext = b"secret";
1006
1007        let mut ct = aes128_gcm_encrypt(&key, &nonce, aad, plaintext);
1008        ct[0] ^= 0xFF;
1009        assert!(aes128_gcm_decrypt(&key, &nonce, aad, &ct).is_none());
1010    }
1011
1012    // --- Alert Tests ---
1013
1014    #[test]
1015    fn test_alert_encode_decode() {
1016        let alert = TlsAlert::new(AlertLevel::Fatal, AlertDescription::HandshakeFailure);
1017        let encoded = alert.encode();
1018        assert_eq!(encoded, [2, 40]);
1019
1020        let decoded = TlsAlert::decode(&encoded).unwrap();
1021        assert_eq!(decoded.level, AlertLevel::Fatal);
1022        assert_eq!(decoded.description, AlertDescription::HandshakeFailure);
1023        assert!(decoded.is_fatal());
1024    }
1025
1026    #[test]
1027    fn test_alert_close_notify() {
1028        let alert = TlsAlert::new(AlertLevel::Warning, AlertDescription::CloseNotify);
1029        assert!(!alert.is_fatal());
1030        let encoded = alert.encode();
1031        let decoded = TlsAlert::decode(&encoded).unwrap();
1032        assert_eq!(decoded.description, AlertDescription::CloseNotify);
1033    }
1034
1035    // --- Session Ticket Tests ---
1036
1037    #[test]
1038    fn test_session_store_lifecycle() {
1039        let mut store = SessionStore::new(2);
1040        assert_eq!(store.count(), 0);
1041
1042        let ticket1 = SessionTicket {
1043            lifetime: 3600,
1044            age_add: 0,
1045            nonce: vec![1],
1046            ticket: vec![0xAA; 16],
1047            cipher_suite: CipherSuite::Aes128GcmSha256,
1048            resumption_secret: [0x11; 32],
1049            created_at: 100,
1050        };
1051        let ticket2 = SessionTicket {
1052            lifetime: 3600,
1053            age_add: 0,
1054            nonce: vec![2],
1055            ticket: vec![0xBB; 16],
1056            cipher_suite: CipherSuite::ChaCha20Poly1305Sha256,
1057            resumption_secret: [0x22; 32],
1058            created_at: 200,
1059        };
1060
1061        store.store(ticket1);
1062        store.store(ticket2);
1063        assert_eq!(store.count(), 2);
1064
1065        // Should find the most recent valid ticket
1066        let found = store.find_valid(300).unwrap();
1067        assert_eq!(found.nonce, vec![2]);
1068    }
1069
1070    #[test]
1071    fn test_session_ticket_expiry() {
1072        let mut store = SessionStore::new(4);
1073        let ticket = SessionTicket {
1074            lifetime: 100,
1075            age_add: 0,
1076            nonce: vec![1],
1077            ticket: vec![0xAA; 8],
1078            cipher_suite: CipherSuite::Aes128GcmSha256,
1079            resumption_secret: [0; 32],
1080            created_at: 1000,
1081        };
1082
1083        store.store(ticket);
1084        assert!(store.find_valid(1050).is_some()); // Not expired
1085        assert!(store.find_valid(1200).is_none()); // Expired
1086
1087        store.prune(1200);
1088        assert_eq!(store.count(), 0);
1089    }
1090
1091    #[test]
1092    fn test_session_store_eviction() {
1093        let mut store = SessionStore::new(2);
1094
1095        for i in 0..3 {
1096            store.store(SessionTicket {
1097                lifetime: 3600,
1098                age_add: 0,
1099                nonce: vec![i as u8],
1100                ticket: vec![i as u8; 8],
1101                cipher_suite: CipherSuite::Aes128GcmSha256,
1102                resumption_secret: [0; 32],
1103                created_at: i as u64,
1104            });
1105        }
1106
1107        // Only 2 should remain (FIFO eviction)
1108        assert_eq!(store.count(), 2);
1109    }
1110
1111    // --- Certificate Tests ---
1112
1113    #[test]
1114    fn test_asn1_parse_tlv_short_form() {
1115        // SEQUENCE with 3 bytes of content
1116        let data = [0x30, 0x03, 0x01, 0x02, 0x03];
1117        let (tag, start, len) = asn1_parse_tlv(&data).unwrap();
1118        assert_eq!(tag, 0x30);
1119        assert_eq!(start, 2);
1120        assert_eq!(len, 3);
1121    }
1122
1123    #[test]
1124    fn test_asn1_parse_tlv_long_form() {
1125        // SEQUENCE with 128 bytes of content (long form: 0x81, 0x80)
1126        let mut data = vec![0x30, 0x81, 0x80];
1127        data.extend_from_slice(&[0u8; 128]);
1128        let (tag, start, len) = asn1_parse_tlv(&data).unwrap();
1129        assert_eq!(tag, 0x30);
1130        assert_eq!(start, 3);
1131        assert_eq!(len, 128);
1132    }
1133
1134    #[test]
1135    fn test_trust_store_empty_chain_rejected() {
1136        let store = TrustStore::new();
1137        assert!(!store.validate_chain(&[]));
1138    }
1139
1140    // --- Encrypted Record Tests ---
1141
1142    #[test]
1143    fn test_encrypted_record_roundtrip_chacha() {
1144        let key = [0x42u8; 32];
1145        let iv = [0x01u8; 12];
1146        let plaintext = b"application data payload";
1147
1148        let record = TlsRecord::new(ContentType::ApplicationData, plaintext.to_vec());
1149        let encrypted =
1150            encrypt_record(&record, &key, &iv, 0, CipherSuite::ChaCha20Poly1305Sha256).unwrap();
1151        let decrypted = decrypt_record(
1152            &encrypted,
1153            &key,
1154            &iv,
1155            0,
1156            CipherSuite::ChaCha20Poly1305Sha256,
1157        )
1158        .unwrap();
1159
1160        assert_eq!(decrypted.content_type, ContentType::ApplicationData);
1161        assert_eq!(&decrypted.fragment[..], &plaintext[..]);
1162    }
1163
1164    #[test]
1165    fn test_encrypted_record_roundtrip_aes() {
1166        let key = [0x42u8; 16];
1167        let iv = [0x01u8; 12];
1168        let plaintext = b"AES encrypted record data";
1169
1170        let record = TlsRecord::new(ContentType::Handshake, plaintext.to_vec());
1171        let encrypted =
1172            encrypt_record(&record, &key, &iv, 5, CipherSuite::Aes128GcmSha256).unwrap();
1173
1174        // Encrypted record should have ApplicationData content type on wire
1175        assert_eq!(encrypted.content_type, ContentType::ApplicationData);
1176
1177        let decrypted =
1178            decrypt_record(&encrypted, &key, &iv, 5, CipherSuite::Aes128GcmSha256).unwrap();
1179        assert_eq!(decrypted.content_type, ContentType::Handshake);
1180        assert_eq!(&decrypted.fragment[..], &plaintext[..]);
1181    }
1182
1183    // --- TLS Connection API Tests ---
1184
1185    #[test]
1186    fn test_tls_connection_initial_state() {
1187        let conn = TlsConnection::new();
1188        assert_eq!(conn.state(), HandshakeState::Start);
1189        assert!(!conn.is_connected());
1190        assert!(conn.cipher_suite().is_none());
1191    }
1192
1193    #[test]
1194    fn test_tls_connection_connect_produces_record() {
1195        let mut conn = TlsConnection::new();
1196        let result = conn.connect([0x42; 32]);
1197        assert!(result.is_ok());
1198        let record_bytes = result.unwrap();
1199        // Should start with a valid TLS record header
1200        assert!(record_bytes.len() > 5);
1201        assert_eq!(record_bytes[0], ContentType::Handshake as u8);
1202        assert_eq!(conn.state(), HandshakeState::WaitServerHello);
1203    }
1204
1205    #[test]
1206    fn test_tls_connection_send_before_connect() {
1207        let mut conn = TlsConnection::new();
1208        let result = conn.send(b"hello");
1209        assert_eq!(result, Err(TlsError::NotConnected));
1210    }
1211
1212    #[test]
1213    fn test_tls_connection_close_before_connect() {
1214        let mut conn = TlsConnection::new();
1215        let result = conn.close();
1216        // Close sends an unencrypted alert when not connected
1217        assert!(result.is_ok());
1218        assert!(conn.closed);
1219    }
1220
1221    // --- Nonce computation test ---
1222
1223    #[test]
1224    fn test_compute_nonce() {
1225        let iv = [
1226            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
1227        ];
1228        let nonce = compute_nonce(&iv, 1);
1229        // Last 8 bytes XOR'd with seq=1
1230        assert_eq!(nonce[0], 0x00);
1231        assert_eq!(nonce[11], 0x0b ^ 0x01);
1232    }
1233
1234    // --- HMAC-SHA256 test ---
1235
1236    #[test]
1237    fn test_hmac_sha256_rfc4231_vector1() {
1238        // RFC 4231 Test Case 1
1239        let key = [0x0bu8; 20];
1240        let data = b"Hi There";
1241        let mac = hmac_sha256(&key, data);
1242        let expected: [u8; 32] = [
1243            0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b,
1244            0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
1245            0x2e, 0x32, 0xcf, 0xf7,
1246        ];
1247        assert_eq!(mac, expected);
1248    }
1249}