Skip to main content

noxtls/protocol/
connection.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18use super::dtls::{
19    noxtls_apply_dtls13_ack_ranges, noxtls_encode_dtls12_handshake_fragments,
20    noxtls_encode_dtls13_ack, noxtls_encode_dtls_record_packet,
21    noxtls_open_dtls13_aes128gcm_record, noxtls_open_dtls13_unified_aes128gcm_record_with_cid,
22    noxtls_parse_dtls13_ack, noxtls_parse_dtls13_record_packet_with_cid_len,
23    noxtls_parse_dtls_record_packet, noxtls_reassemble_dtls12_handshake_fragments,
24    noxtls_seal_dtls13_aes128gcm_record, noxtls_seal_dtls13_unified_aes128gcm_record_with_cid,
25    Dtls13AckRange, Dtls13RecordHeader, Dtls13TransportEvent, DtlsEpochReplayTracker,
26    DtlsFlightRetransmitTracker, DtlsRecordHeader, DtlsReplayWindow, DtlsReplayWindowSnapshot,
27    DTLS13_HANDSHAKE_ACK,
28};
29use super::handshake::{noxtls_encode_handshake_message, noxtls_parse_handshake_message};
30use super::kdf::{
31    noxtls_finished_hmac_for_hash, noxtls_hash_bytes_for_algorithm, noxtls_hkdf_expand_for_hash,
32    noxtls_hkdf_extract_for_hash, noxtls_hkdf_extract_with_salt_for_hash,
33    noxtls_tls13_expand_label_for_hash, HashAlgorithm,
34};
35use super::keyshare::{
36    noxtls_derive_deterministic_brainpoolp256r1_private,
37    noxtls_derive_deterministic_brainpoolp384r1_private,
38    noxtls_derive_deterministic_brainpoolp512r1_private,
39    noxtls_derive_deterministic_mlkem768_keypair, noxtls_derive_deterministic_p256_private,
40    noxtls_derive_deterministic_p384_private, noxtls_derive_deterministic_p521_private,
41    noxtls_derive_deterministic_x25519_private, noxtls_derive_tls13_mlkem768_shared_secret,
42    noxtls_derive_tls13_named_ec_shared_secret, noxtls_derive_tls13_p256_shared_secret,
43    noxtls_derive_tls13_p384_shared_secret, noxtls_derive_tls13_p521_shared_secret,
44    noxtls_derive_tls13_x25519_shared_secret,
45    noxtls_tls13_client_hello_offers_supported_key_exchange,
46};
47#[cfg(feature = "hazardous-legacy-crypto")]
48use super::keyshare::{
49    noxtls_derive_deterministic_x448_private, noxtls_derive_tls13_x448_shared_secret,
50};
51use super::psk::{
52    noxtls_ticket_age_matches_policy, ResumptionTicket, TicketStore, TicketUsagePolicy,
53};
54use super::record::{
55    noxtls_build_record_nonce, noxtls_decode_tls12_ciphertext_record,
56    noxtls_decode_tls13_ciphertext_record, noxtls_decode_tls13_inner_plaintext,
57    noxtls_encode_tls12_ciphertext_record, noxtls_encode_tls13_ciphertext_record,
58    noxtls_encode_tls13_inner_plaintext,
59};
60use super::state::{
61    AlertDescription, AlertLevel, CipherSuite, HandshakeState, RecordContentType, TlsRole,
62    TlsVersion,
63};
64use super::tls_wire::split_tls13_handshake_payload;
65#[cfg(not(feature = "std"))]
66use crate::internal_alloc::ToOwned;
67use crate::internal_alloc::{String, Vec};
68use noxtls_core::{Error, Result};
69use noxtls_crypto::{
70    noxtls_aes_cbc_decrypt, noxtls_aes_cbc_encrypt, noxtls_aes_ccm_decrypt_with_tag_len,
71    noxtls_aes_ccm_encrypt_with_tag_len, noxtls_aes_gcm_decrypt, noxtls_aes_gcm_encrypt,
72    noxtls_chacha20_poly1305_decrypt, noxtls_chacha20_poly1305_encrypt,
73    noxtls_ed25519_public_key_from_subject_public_key_info, noxtls_ed25519_verify,
74    noxtls_ffdhe_shared_secret, noxtls_hkdf_extract_sha256, noxtls_hmac_sha1, noxtls_mldsa_verify,
75    noxtls_mlkem_encapsulate_auto, noxtls_p256_ecdsa_sign_sha256, noxtls_p256_ecdsa_verify_sha256,
76    noxtls_p384_ecdsa_sign_sha384, noxtls_p384_ecdsa_verify_sha384, noxtls_rsaes_pkcs1_v15_decrypt,
77    noxtls_rsassa_pss_sha256_sign_auto, noxtls_rsassa_pss_sha256_verify,
78    noxtls_rsassa_pss_sha384_sign_auto, noxtls_rsassa_pss_sha384_verify, noxtls_rsassa_pss_sign,
79    noxtls_rsassa_pss_verify, noxtls_secp521r1_ecdsa_sign_sha512, noxtls_tls12_prf_sha256,
80    noxtls_tls12_prf_sha384, AesCipher, HmacDrbgSha256, MlDsaPublicKey, MlKemParameterSet,
81    MlKemPrivateKey, MlKemPublicKey, NamedCurve, NamedEcPrivateKey, NamedEcPublicKey,
82    P256PrivateKey, P256PublicKey, P384PrivateKey, P384PublicKey, RsaPrivateKey,
83    RsaPssHashAlgorithm, RsaPublicKey, TlsTranscriptSha256, TlsTranscriptSha384, X25519PrivateKey,
84    MLKEM1024_CIPHERTEXT_LEN, MLKEM1024_PUBLIC_KEY_LEN, MLKEM_CIPHERTEXT_LEN, MLKEM_PUBLIC_KEY_LEN,
85};
86use noxtls_x509::{
87    noxtls_certificate_matches_hostname, noxtls_p256_private_key_from_pkcs8_der,
88    noxtls_p384_private_key_from_pkcs8_der, noxtls_p521_private_key_from_pkcs8_der,
89    noxtls_parse_certificate, noxtls_parse_der_node, noxtls_parse_ecdsa_p384_signature_der,
90    noxtls_parse_ecdsa_signature_der, noxtls_parse_spki_public_key_info_der,
91    noxtls_rsa_private_key_from_pkcs8_der, noxtls_rsa_pss_private_key_from_pkcs8_der,
92    noxtls_validate_certificate_chain, noxtls_write_der_integer, noxtls_write_der_sequence,
93    ValidationError,
94};
95
96/// Holds configured TLS 1.3 server identity signing material for CertificateVerify.
97#[derive(Debug, Clone)]
98pub enum Tls13ServerIdentityKey {
99    /// P-256 ECDSA private key used with `ecdsa_secp256r1_sha256`.
100    P256(P256PrivateKey),
101    /// P-384 ECDSA private key used with `ecdsa_secp384r1_sha384`.
102    P384(P384PrivateKey),
103    /// P-521 ECDSA private key used with `ecdsa_secp521r1_sha512`.
104    P521(NamedEcPrivateKey),
105    /// RSA private key used with RSASSA-PSS and SHA-256.
106    Rsa(RsaPrivateKey),
107    /// RSASSA-PSS RSA private key used with rsa_pss_pss signature schemes.
108    RsaPss(RsaPrivateKey),
109}
110
111/// Bundles a TLS 1.3 server certificate chain with the signing key used for `CertificateVerify`.
112#[derive(Debug, Clone)]
113pub struct Tls13ServerIdentity {
114    /// DER-encoded certificate chain with the leaf certificate first.
115    pub certificate_chain_der: Vec<Vec<u8>>,
116    /// Private key matched to the leaf certificate.
117    pub signing_key: Tls13ServerIdentityKey,
118}
119
120impl Tls13ServerIdentity {
121    /// Builds a server identity from a DER certificate chain and a PKCS#8 private key.
122    pub fn noxtls_from_pkcs8_der(
123        certificate_chain_der: &[Vec<u8>],
124        private_key_pkcs8_der: &[u8],
125    ) -> Result<Self> {
126        if certificate_chain_der.is_empty() {
127            return Err(Error::InvalidLength(
128                "server certificate chain must contain at least one certificate",
129            ));
130        }
131        let signing_key =
132            Connection::noxtls_tls13_server_identity_key_from_pkcs8_der(private_key_pkcs8_der)?;
133        Ok(Self {
134            certificate_chain_der: certificate_chain_der.to_vec(),
135            signing_key,
136        })
137    }
138}
139
140/// Holds connection version, handshake state, and transcript bytes.
141#[derive(Debug, Clone)]
142pub struct Connection {
143    pub version: TlsVersion,
144    pub tls_role: TlsRole,
145    pub state: HandshakeState,
146    noxtls_selected_cipher_suite: Option<CipherSuite>,
147    client_offered_cipher_suites: Vec<CipherSuite>,
148    client_offered_tls_fallback_scsv: bool,
149    tls13_client_cipher_suites: Option<Vec<CipherSuite>>,
150    transcript: Vec<u8>,
151    noxtls_transcript_hash: TranscriptHashState,
152    handshake_secret: Option<Vec<u8>>,
153    tls13_master_secret: Option<Vec<u8>>,
154    tls13_client_handshake_traffic_secret: Option<Vec<u8>>,
155    tls13_server_handshake_traffic_secret: Option<Vec<u8>>,
156    tls13_client_application_traffic_secret: Option<Vec<u8>>,
157    tls13_server_application_traffic_secret: Option<Vec<u8>>,
158    tls13_exporter_master_secret: Option<Vec<u8>>,
159    noxtls_tls13_resumption_master_secret: Option<Vec<u8>>,
160    tls13_client_x25519_private: Option<X25519PrivateKey>,
161    tls13_client_p256_private: Option<P256PrivateKey>,
162    tls13_client_mlkem768_private: Option<MlKemPrivateKey>,
163    tls13_shared_secret: Option<Vec<u8>>,
164    tls13_hrr_requested_group: Option<u16>,
165    tls13_hrr_client_hello_extension_order: Option<Vec<u16>>,
166    tls13_hrr_seen: bool,
167    tls13_negotiated_key_exchange_group: Option<u16>,
168    tls13_negotiated_certificate_verify_signature_scheme: Option<u16>,
169    /// Holds up to 32 bytes of AEAD key material (AES-128 uses the first 16; AES-256/ChaCha use 32).
170    client_write_key: Option<[u8; 32]>,
171    server_write_key: Option<[u8; 32]>,
172    tls12_client_write_mac_key: Option<[u8; 20]>,
173    tls12_server_write_mac_key: Option<[u8; 20]>,
174    client_write_iv: Option<[u8; 12]>,
175    server_write_iv: Option<[u8; 12]>,
176    client_sequence: u64,
177    server_sequence: u64,
178    noxtls_tls13_peer_close_notify_received: bool,
179    noxtls_tls13_local_close_notify_sent: bool,
180    tls13_require_certificate_auth: bool,
181    tls13_server_trust_anchors_der: Vec<Vec<u8>>,
182    tls13_server_intermediates_der: Vec<Vec<u8>>,
183    tls13_server_validation_time: Option<String>,
184    tls13_server_expected_hostname: Option<String>,
185    tls13_client_server_name: Option<String>,
186    tls13_request_ocsp_stapling: bool,
187    tls13_require_ocsp_staple: bool,
188    tls13_ocsp_staple_verifier: Option<Tls13OcspStapleVerifier>,
189    noxtls_tls13_server_ocsp_staple: Option<Vec<u8>>,
190    noxtls_tls13_server_ocsp_staple_verified: bool,
191    tls13_require_server_name_ack: bool,
192    noxtls_tls13_server_name_acknowledged: bool,
193    tls13_client_alpn_protocols: Vec<Vec<u8>>,
194    noxtls_tls13_selected_alpn_protocol: Option<Vec<u8>>,
195    tls13_client_signature_algorithms: Vec<u16>,
196    tls13_client_certificate_compression_algorithms: Vec<u16>,
197    tls13_application_traffic_transcript_hash: Option<Vec<u8>>,
198    tls13_client_offer_pq_key_shares: bool,
199    tls13_client_offer_mldsa_signature: bool,
200    tls13_server_leaf_public_key_der: Option<Vec<u8>>,
201    tls13_client_identity_certificate_chain_der: Vec<Vec<u8>>,
202    tls13_client_signing_key: Option<Tls13ServerIdentityKey>,
203    tls13_client_trust_anchors_der: Vec<Vec<u8>>,
204    tls13_client_intermediates_der: Vec<Vec<u8>>,
205    tls13_client_validation_time: Option<String>,
206    tls13_client_leaf_public_key_der: Option<Vec<u8>>,
207    tls13_client_certificate_chain_validated: bool,
208    tls13_require_client_certificate_auth: bool,
209    tls13_server_requested_client_certificate: bool,
210    tls13_server_certificate_chain_validated: bool,
211    tls13_server_certificate_chain_der: Vec<Vec<u8>>,
212    tls13_client_raw_public_keys_enabled: bool,
213    tls13_expected_server_raw_public_key_der: Option<Vec<u8>>,
214    tls13_server_identity_certificate_type: u8,
215    tls13_negotiated_server_certificate_type: u8,
216    tls13_client_offered_server_certificate_types: Vec<u8>,
217    tls13_server_signing_key: Option<Tls13ServerIdentityKey>,
218    tls13_server_preferred_cipher_suites: Vec<CipherSuite>,
219    tls13_server_preferred_key_exchange_groups: Vec<u16>,
220    tls13_server_alpn_protocols: Vec<Vec<u8>>,
221    tls13_server_x25519_private: Option<X25519PrivateKey>,
222    tls13_server_p256_private: Option<P256PrivateKey>,
223    tls13_early_data_require_acceptance: bool,
224    tls13_early_data_accepted_psk: Option<Vec<u8>>,
225    tls13_early_data_max_bytes: Option<u32>,
226    tls13_early_data_opened_bytes: u64,
227    tls13_early_data_offered_in_client_hello: bool,
228    tls13_early_data_accepted_in_encrypted_extensions: bool,
229    tls13_end_of_early_data_seen: bool,
230    tls13_early_data_anti_replay_enabled: bool,
231    tls13_early_data_replay_window: DtlsReplayWindow,
232    noxtls_tls13_early_data_telemetry: Tls13EarlyDataTelemetry,
233    tls12_change_cipher_spec_seen: bool,
234    tls12_secure_renegotiation_enabled: bool,
235    tls12_secure_renegotiation_offered: bool,
236    tls12_secure_renegotiation_negotiated: bool,
237    tls12_secure_renegotiation_renegotiating: bool,
238    tls12_secure_renegotiation_client_verify_data: Vec<u8>,
239    tls12_secure_renegotiation_server_verify_data: Vec<u8>,
240    noxtls_tls12_session_id: Option<Vec<u8>>,
241    tls12_allow_legacy_record_versions: bool,
242    tls12_client_random: Option<[u8; 32]>,
243    tls12_server_random: Option<[u8; 32]>,
244    tls12_pre_master_secret: Option<Vec<u8>>,
245    tls12_rsa_encrypted_pre_master_secret: Option<Vec<u8>>,
246    tls12_dhe_prime: Option<Vec<u8>>,
247    tls12_dhe_server_public_key: Option<Vec<u8>>,
248    tls12_dhe_client_public_key: Option<Vec<u8>>,
249    tls12_master_secret: Option<[u8; 48]>,
250    tls12_extended_master_secret_session_hash: Option<Vec<u8>>,
251    dtls13_client_write_key: Option<[u8; 16]>,
252    dtls13_client_write_iv: Option<[u8; 12]>,
253    dtls13_server_write_key: Option<[u8; 16]>,
254    dtls13_server_write_iv: Option<[u8; 12]>,
255    dtls13_outbound_epoch: u16,
256    dtls13_outbound_sequence: u64,
257    dtls13_outbound_connection_id: Vec<u8>,
258    dtls13_inbound_connection_id: Vec<u8>,
259    dtls13_inbound_replay_tracker: DtlsEpochReplayTracker,
260    dtls13_client_inbound_replay_tracker: DtlsEpochReplayTracker,
261    dtls13_active_flight: Vec<(u16, u64)>,
262    dtls13_pending_ack_ranges: Vec<Dtls13AckRange>,
263    dtls13_active_flight_started_at_ms: Option<u64>,
264    dtls13_active_flight_timeout_ms: u64,
265    noxtls_dtls13_active_flight_failed: bool,
266    dtls_retransmit_tracker: DtlsFlightRetransmitTracker,
267    dtls_retransmit_initial_timeout_ms: u64,
268    dtls_max_retransmit_attempts: u8,
269    noxtls_dtls12_handshake_phase: Dtls12HandshakePhase,
270    dtls12_expected_cookie: Option<Vec<u8>>,
271    dtls12_anti_amplification_enforced: bool,
272    dtls12_inbound_bytes: u64,
273    dtls12_outbound_bytes: u64,
274    max_record_plaintext_len: usize,
275}
276
277/// Represents one protected TLS record carrying ciphertext and authentication tag.
278#[derive(Debug, Clone)]
279pub struct ProtectedRecord {
280    pub sequence: u64,
281    pub ciphertext: Vec<u8>,
282    pub tag: [u8; 16],
283}
284
285/// Captures transport-facing DTLS retry and timeout knobs.
286#[derive(Debug, Copy, Clone, Eq, PartialEq)]
287pub struct DtlsOperationalPolicy {
288    pub retransmit_initial_timeout_ms: u64,
289    pub max_retransmit_attempts: u8,
290    pub active_flight_timeout_ms: u64,
291}
292
293/// Names pre-tuned DTLS operational profiles for common deployment environments.
294#[derive(Debug, Copy, Clone, Eq, PartialEq)]
295pub enum DtlsOperationalProfile {
296    Conservative,
297    LanLowLatency,
298    LossyNetwork,
299}
300
301/// Captures tunable policy controls for TLS 1.3 modeled early-data handling.
302#[derive(Debug, Copy, Clone, Eq, PartialEq)]
303pub struct Tls13EarlyDataOperationalPolicy {
304    pub require_acceptance: bool,
305    pub anti_replay_enabled: bool,
306}
307
308/// Names pre-tuned operational profiles for TLS 1.3 modeled early-data policy.
309#[derive(Debug, Copy, Clone, Eq, PartialEq)]
310pub enum Tls13EarlyDataOperationalProfile {
311    Compatibility,
312    Strict,
313}
314
315/// Tracks counters for modeled TLS 1.3 early-data accept/reject outcomes.
316#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
317pub struct Tls13EarlyDataTelemetry {
318    pub accepted_records: u64,
319    pub rejected_missing_acceptance: u64,
320    pub rejected_psk_mismatch: u64,
321    pub rejected_replay_or_too_old: u64,
322    pub rejected_invalid_input: u64,
323    pub rejected_decrypt_or_policy: u64,
324}
325
326/// Serializable replay-window state for carrying TLS 1.3 early-data anti-replay continuity.
327#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
328pub struct Tls13EarlyDataReplayState {
329    pub latest_sequence: u64,
330    pub bitmap: u64,
331    pub initialized: bool,
332}
333
334/// Captures QUIC Initial secrets derived from destination connection ID.
335#[derive(Debug, Clone, Eq, PartialEq)]
336pub struct Tls13QuicInitialSecrets {
337    pub initial_secret: Vec<u8>,
338    pub client_initial_secret: Vec<u8>,
339    pub server_initial_secret: Vec<u8>,
340}
341
342/// Captures one QUIC packet-protection keyset derived from one traffic secret.
343#[derive(Debug, Clone, Eq, PartialEq)]
344pub struct Tls13QuicPacketProtectionKeys {
345    pub key: Vec<u8>,
346    pub iv: Vec<u8>,
347    pub header_protection_key: Vec<u8>,
348}
349
350/// Captures current QUIC handshake and 1-RTT traffic secret snapshots.
351#[derive(Debug, Clone, Eq, PartialEq)]
352pub struct Tls13QuicTrafficSecretSnapshot {
353    pub client_handshake_secret: Vec<u8>,
354    pub server_handshake_secret: Vec<u8>,
355    pub client_application_secret: Vec<u8>,
356    pub server_application_secret: Vec<u8>,
357}
358
359/// Captures next-generation QUIC 1-RTT traffic secrets derived via `quic ku`.
360#[derive(Debug, Clone, Eq, PartialEq)]
361pub struct Tls13QuicNextTrafficSecrets {
362    pub client_next_application_secret: Vec<u8>,
363    pub server_next_application_secret: Vec<u8>,
364}
365
366/// QUIC exporter label for client 1-RTT secret derivations.
367pub const TLS13_QUIC_EXPORTER_LABEL_CLIENT_1RTT: &[u8] = b"EXPORTER-QUIC client 1rtt";
368/// QUIC exporter label for server 1-RTT secret derivations.
369pub const TLS13_QUIC_EXPORTER_LABEL_SERVER_1RTT: &[u8] = b"EXPORTER-QUIC server 1rtt";
370
371const TLS13_QUIC_V1_INITIAL_SALT: [u8; 20] = [
372    0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
373    0xcc, 0xbb, 0x7f, 0x0a,
374];
375
376const HANDSHAKE_CLIENT_HELLO: u8 = 0x01;
377const HANDSHAKE_SERVER_HELLO: u8 = 0x02;
378const HANDSHAKE_HELLO_VERIFY_REQUEST: u8 = 0x03;
379const HANDSHAKE_NEW_SESSION_TICKET: u8 = 0x04;
380const HANDSHAKE_END_OF_EARLY_DATA: u8 = 0x05;
381const HANDSHAKE_ENCRYPTED_EXTENSIONS: u8 = 0x08;
382const HANDSHAKE_CERTIFICATE: u8 = 0x0B;
383const HANDSHAKE_SERVER_KEY_EXCHANGE: u8 = 0x0C;
384const HANDSHAKE_CERTIFICATE_REQUEST: u8 = 0x0D;
385const HANDSHAKE_SERVER_HELLO_DONE: u8 = 0x0E;
386const HANDSHAKE_CLIENT_KEY_EXCHANGE: u8 = 0x10;
387const HANDSHAKE_CERTIFICATE_VERIFY: u8 = 0x0F;
388const HANDSHAKE_FINISHED: u8 = 0x14;
389#[allow(dead_code)]
390const HANDSHAKE_COMPRESSED_CERTIFICATE: u8 = 0x19;
391const HANDSHAKE_KEY_UPDATE: u8 = 0x18;
392const EXT_SERVER_NAME: u16 = 0x0000;
393const EXT_STATUS_REQUEST: u16 = 0x0005;
394const EXT_SUPPORTED_GROUPS: u16 = 0x000A;
395const EXT_ALPN: u16 = 0x0010;
396const EXT_CLIENT_CERTIFICATE_TYPE: u16 = 0x0013;
397const EXT_SERVER_CERTIFICATE_TYPE: u16 = 0x0014;
398const EXT_COMPRESS_CERTIFICATE: u16 = 0x001B;
399const EXT_RENEGOTIATION_INFO: u16 = 0xFF01;
400const TLS_EMPTY_RENEGOTIATION_INFO_SCSV: u16 = 0x00FF;
401const TLS_FALLBACK_SCSV: u16 = 0x5600;
402const EXT_SUPPORTED_VERSIONS: u16 = 0x002B;
403const EXT_SIGNATURE_ALGORITHMS: u16 = 0x000D;
404const EXT_KEY_SHARE: u16 = 0x0033;
405const EXT_PSK_KEY_EXCHANGE_MODES: u16 = 0x002D;
406const EXT_PRE_SHARED_KEY: u16 = 0x0029;
407const EXT_EARLY_DATA: u16 = 0x002A;
408const TLS_CERTIFICATE_TYPE_X509: u8 = 0x00;
409const TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY: u8 = 0x02;
410const TLS13_KEY_SHARE_GROUP_SECP256R1: u16 = 0x0017;
411const TLS13_KEY_SHARE_GROUP_SECP384R1: u16 = 0x0018;
412const TLS13_KEY_SHARE_GROUP_SECP521R1: u16 = 0x0019;
413const TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13: u16 = 0x001F;
414const TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13: u16 = 0x0020;
415const TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13: u16 = 0x0021;
416const TLS13_KEY_SHARE_GROUP_X25519: u16 = 0x001D;
417const TLS13_KEY_SHARE_GROUP_X448: u16 = 0x001E;
418const TLS13_KEY_SHARE_GROUP_MLKEM768: u16 = 0x0201;
419const TLS13_KEY_SHARE_GROUP_SECP256R1_MLKEM768_HYBRID: u16 = 0x11EB;
420const TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID: u16 = 0x11EC;
421const TLS13_KEY_SHARE_GROUP_SECP384R1_MLKEM1024_HYBRID: u16 = 0x11ED;
422const TLS13_PSK_KEY_EXCHANGE_MODE_PSK_DHE_KE: u8 = 0x01;
423const TLS13_CERT_COMPRESSION_ZLIB: u16 = 0x0001;
424const TLS13_SIGALG_ECDSA_SECP256R1_SHA256: u16 = 0x0403;
425const TLS13_SIGALG_ECDSA_SECP384R1_SHA384: u16 = 0x0503;
426const TLS13_SIGALG_ECDSA_SECP521R1_SHA512: u16 = 0x0603;
427const TLS13_SIGALG_RSA_PSS_RSAE_SHA256: u16 = 0x0804;
428const TLS13_SIGALG_RSA_PSS_RSAE_SHA384: u16 = 0x0805;
429const TLS13_SIGALG_RSA_PSS_RSAE_SHA512: u16 = 0x0806;
430const TLS13_SIGALG_ED25519: u16 = 0x0807;
431const TLS13_SIGALG_RSA_PSS_PSS_SHA256: u16 = 0x0809;
432const TLS13_SIGALG_RSA_PSS_PSS_SHA384: u16 = 0x080A;
433const TLS13_SIGALG_RSA_PSS_PSS_SHA512: u16 = 0x080B;
434const TLS13_SIGALG_MLDSA65: u16 = 0x0905;
435const TLS13_MAX_EXTENSION_VALUE_BYTES: usize = 16_384;
436const TLS_MAX_RECORD_PLAINTEXT_LEN: usize = 16_384;
437const DTLS_RETRANSMIT_TRACKER_MAX_RECORDS: usize = 256;
438const DTLS_RETRANSMIT_INITIAL_TIMEOUT_MS: u64 = 1_000;
439const DTLS_MAX_RETRANSMIT_ATTEMPTS: u8 = 4;
440const DTLS13_ACTIVE_FLIGHT_TIMEOUT_MS: u64 = 10_000;
441const DTLS13_MAX_SEQUENCE: u64 = (1_u64 << 48) - 1;
442const DTLS12_MAX_COOKIE_LEN: usize = 255;
443const DTLS12_ANTI_AMPLIFICATION_FACTOR: u64 = 3;
444const TLS13_HRR_RANDOM: [u8; 32] = [
445    0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
446    0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
447];
448
449/// Captures parsed extension data from a minimally-modeled ClientHello.
450#[derive(Debug, Clone, Eq, PartialEq, Default)]
451pub struct ClientHelloExtensions {
452    pub supported_versions: Vec<u16>,
453    pub supported_groups: Vec<u16>,
454    pub signature_algorithms: Vec<u16>,
455    pub certificate_compression_algorithms: Vec<u16>,
456    pub key_share_groups: Vec<u16>,
457    pub key_share_offered: bool,
458    pub sni_server_name: Option<String>,
459    pub alpn_protocols: Vec<Vec<u8>>,
460    pub status_request_ocsp: bool,
461    pub psk_key_exchange_modes: Vec<u8>,
462    pub psk_identity_count: usize,
463    pub psk_identities: Vec<Vec<u8>>,
464    pub psk_obfuscated_ticket_ages: Vec<u32>,
465    pub psk_binders: Vec<Vec<u8>>,
466    pub early_data_offered: bool,
467    pub secure_renegotiation_info: Option<Vec<u8>>,
468    pub secure_renegotiation_scsv: bool,
469    pub client_certificate_types: Vec<u8>,
470    pub server_certificate_types: Vec<u8>,
471}
472
473/// Summarizes parsed suite and extension data from ClientHello.
474#[derive(Debug, Clone, Eq, PartialEq, Default)]
475pub struct ClientHelloInfo {
476    pub legacy_version: u16,
477    pub offered_cipher_suites: Vec<CipherSuite>,
478    pub offered_tls_fallback_scsv: bool,
479    pub extensions: ClientHelloExtensions,
480}
481
482/// Captures one PSK identity entry used in TLS 1.3 pre_shared_key offers.
483struct PskIdentityOffer<'a> {
484    identity: &'a [u8],
485    obfuscated_ticket_age: u32,
486}
487
488/// Carries one or more TLS 1.3 PSK identity+binder offers for ClientHello encoding.
489struct PskClientOffer<'a> {
490    identities: Vec<PskIdentityOffer<'a>>,
491    binders: Vec<&'a [u8]>,
492}
493
494/// Holds TLS 1.3 client `key_share` public material emitted in ClientHello.
495#[derive(Debug, Clone, Eq, PartialEq, Default)]
496struct Tls13ClientPublicKeyShares {
497    x25519: Option<[u8; 32]>,
498    secp256r1_uncompressed: Option<[u8; 65]>,
499    mlkem768: Option<Vec<u8>>,
500    x25519_mlkem768_hybrid: Option<Vec<u8>>,
501}
502
503/// Parsed server `key_share` payload for TLS 1.3 ServerHello (non-HRR).
504#[derive(Debug, Clone, Eq, PartialEq)]
505enum Tls13ServerKeyShareParsed {
506    X25519([u8; 32]),
507    Secp256r1([u8; 65]),
508    MlKem768(Vec<u8>),
509    X25519MlKem768Hybrid { x25519: [u8; 32], mlkem768: Vec<u8> },
510}
511
512/// Summarizes parsed ServerHello/HelloRetryRequest details needed by connection flow.
513struct ParsedServerHello {
514    suite: CipherSuite,
515    random: Option<[u8; 32]>,
516    key_share: Option<Tls13ServerKeyShareParsed>,
517    hello_retry_request: bool,
518    requested_group: Option<u16>,
519    secure_renegotiation_info: Option<Vec<u8>>,
520}
521
522/// Captures parsed EncryptedExtensions values required by modeled handshake policy.
523struct ParsedEncryptedExtensions {
524    selected_alpn_protocol: Option<Vec<u8>>,
525    server_name_acknowledged: bool,
526    early_data_accepted: bool,
527    server_certificate_type: Option<u8>,
528}
529
530/// Captures parsed TLS 1.3 Certificate contents and optional leaf stapled OCSP bytes.
531struct ParsedTls13CertificateBody {
532    certificate_type: u8,
533    certificates: Vec<Vec<u8>>,
534    leaf_ocsp_staple: Option<Vec<u8>>,
535}
536
537/// Describes validation outcome for one stapled OCSP response.
538#[derive(Debug, Copy, Clone, Eq, PartialEq)]
539pub enum Tls13OcspStapleVerification {
540    Good,
541    Expired,
542    Revoked,
543}
544
545/// Function-pointer hook used to validate one stapled OCSP response payload.
546pub type Tls13OcspStapleVerifier = fn(&[u8]) -> Result<Tls13OcspStapleVerification>;
547
548/// Parsed freshness and revocation summary for a stapled OCSP response.
549#[derive(Debug, Clone, Eq, PartialEq)]
550pub struct Tls13OcspStapleInfo {
551    pub produced_at: String,
552    pub this_update: String,
553    pub next_update: Option<String>,
554    pub status: Tls13OcspStapleVerification,
555}
556
557/// Models deterministic DTLS1.2 handshake progression for cookie and flight sequencing.
558#[derive(Debug, Copy, Clone, Eq, PartialEq)]
559enum Dtls12HandshakePhase {
560    AwaitingClientHello,
561    AwaitingClientHelloWithCookie,
562    AwaitingClientKeyExchange,
563    AwaitingFinished,
564    Connected,
565}
566
567/// Selects transcript hashing noxtls_algorithm based on protocol version profile.
568#[derive(Debug, Clone)]
569enum TranscriptHashState {
570    Sha256(TlsTranscriptSha256),
571    Sha384(TlsTranscriptSha384),
572}
573
574impl TranscriptHashState {
575    /// Builds transcript hashing state aligned with connection version defaults.
576    ///
577    /// # Arguments
578    ///
579    /// * `version` — `version: TlsVersion`.
580    ///
581    /// # Returns
582    ///
583    /// A noxtls_new or updated `Self` value as constructed in the function body.
584    ///
585    /// # Panics
586    ///
587    /// This function does not panic.
588    ///
589    fn noxtls_for_version(version: TlsVersion) -> Self {
590        match version {
591            TlsVersion::Tls13 | TlsVersion::Dtls13 => {
592                Self::Sha384(TlsTranscriptSha384::noxtls_new())
593            }
594            TlsVersion::Tls10 | TlsVersion::Tls11 | TlsVersion::Tls12 | TlsVersion::Dtls12 => {
595                Self::Sha256(TlsTranscriptSha256::noxtls_new())
596            }
597        }
598    }
599
600    /// Feeds handshake bytes into the selected transcript hash implementation.
601    ///
602    /// # Arguments
603    ///
604    /// * `self` — `&mut self`.
605    /// * `message` — `message: &[u8]`.
606    ///
607    /// # Panics
608    ///
609    /// This function does not panic.
610    ///
611    fn noxtls_update(&mut self, message: &[u8]) {
612        match self {
613            Self::Sha256(hasher) => hasher.noxtls_update(message),
614            Self::Sha384(hasher) => hasher.noxtls_update(message),
615        }
616    }
617
618    /// Returns current transcript hash bytes without consuming internal state.
619    ///
620    /// # Arguments
621    ///
622    /// * `&self` — `&self`.
623    ///
624    /// # Returns
625    ///
626    /// The value described by the return type in the function signature.
627    ///
628    /// # Panics
629    ///
630    /// This function does not panic.
631    ///
632    fn noxtls_snapshot_hash(&self) -> Vec<u8> {
633        match self {
634            Self::Sha256(hasher) => hasher.noxtls_snapshot_hash().to_vec(),
635            Self::Sha384(hasher) => hasher.noxtls_snapshot_hash().to_vec(),
636        }
637    }
638
639    /// Returns hash noxtls_algorithm represented by this transcript state.
640    ///
641    /// # Arguments
642    ///
643    /// * `&self` — `&self`.
644    ///
645    /// # Returns
646    ///
647    /// The value described by the return type in the function signature.
648    ///
649    /// # Panics
650    ///
651    /// This function does not panic.
652    ///
653    fn noxtls_algorithm(&self) -> HashAlgorithm {
654        match self {
655            Self::Sha256(_) => HashAlgorithm::Sha256,
656            Self::Sha384(_) => HashAlgorithm::Sha384,
657        }
658    }
659}
660
661impl CipherSuite {
662    fn noxtls_from_u16(codepoint: u16) -> Option<Self> {
663        match codepoint {
664            0x1301 => Some(Self::TlsAes128GcmSha256),
665            0x1302 => Some(Self::TlsAes256GcmSha384),
666            0x1303 => Some(Self::TlsChacha20Poly1305Sha256),
667            0xC02B => Some(Self::TlsEcdheEcdsaWithAes128GcmSha256),
668            0xC02C => Some(Self::TlsEcdheEcdsaWithAes256GcmSha384),
669            0xCCA9 => Some(Self::TlsEcdheEcdsaWithChacha20Poly1305Sha256),
670            0xC02F => Some(Self::TlsEcdheRsaWithAes128GcmSha256),
671            0xC030 => Some(Self::TlsEcdheRsaWithAes256GcmSha384),
672            0xCCA8 => Some(Self::TlsEcdheRsaWithChacha20Poly1305Sha256),
673            0xCCAA => Some(Self::TlsDheRsaWithChacha20Poly1305Sha256),
674            0x009E => Some(Self::TlsDheRsaWithAes128GcmSha256),
675            0x009F => Some(Self::TlsDheRsaWithAes256GcmSha384),
676            0x009C => Some(Self::TlsRsaWithAes128GcmSha256),
677            0x009D => Some(Self::TlsRsaWithAes256GcmSha384),
678            0xC013 => Some(Self::TlsEcdheRsaWithAes128CbcSha),
679            0x002F => Some(Self::TlsRsaWithAes128CbcSha),
680            0xC0AC => Some(Self::TlsEcdheEcdsaWithAes128CcmSha256),
681            0xC0AD => Some(Self::TlsEcdheEcdsaWithAes256CcmSha256),
682            0xC0AE => Some(Self::TlsEcdheEcdsaWithAes128Ccm8Sha256),
683            0xC0AF => Some(Self::TlsEcdheEcdsaWithAes256Ccm8Sha256),
684            0xC09C => Some(Self::TlsRsaWithAes128CcmSha256),
685            0xC09D => Some(Self::TlsRsaWithAes256CcmSha256),
686            0xC0A0 => Some(Self::TlsRsaWithAes128Ccm8Sha256),
687            0xC0A1 => Some(Self::TlsRsaWithAes256Ccm8Sha256),
688            0xC09E => Some(Self::TlsDheRsaWithAes128CcmSha256),
689            0xC09F => Some(Self::TlsDheRsaWithAes256CcmSha256),
690            0xC0A2 => Some(Self::TlsDheRsaWithAes128Ccm8Sha256),
691            0xC0A3 => Some(Self::TlsDheRsaWithAes256Ccm8Sha256),
692            0xC0A4 => Some(Self::TlsPskWithAes128Ccm8Sha256),
693            0xC0FF => Some(Self::TlsEcjpakeWithAes128Ccm8Sha256),
694            _ => None,
695        }
696    }
697
698    fn noxtls_transcript_hash_state(self) -> TranscriptHashState {
699        match self.noxtls_hash_algorithm() {
700            HashAlgorithm::Sha256 => TranscriptHashState::Sha256(TlsTranscriptSha256::noxtls_new()),
701            HashAlgorithm::Sha384 => TranscriptHashState::Sha384(TlsTranscriptSha384::noxtls_new()),
702        }
703    }
704
705    fn noxtls_hash_algorithm(self) -> HashAlgorithm {
706        match self {
707            Self::TlsAes256GcmSha384
708            | Self::TlsEcdheRsaWithAes256GcmSha384
709            | Self::TlsEcdheEcdsaWithAes256GcmSha384
710            | Self::TlsDheRsaWithAes256GcmSha384
711            | Self::TlsRsaWithAes256GcmSha384 => HashAlgorithm::Sha384,
712            _ => HashAlgorithm::Sha256,
713        }
714    }
715
716    fn noxtls_tls13_traffic_key_len(self) -> Option<usize> {
717        match self {
718            Self::TlsAes128GcmSha256 => Some(16),
719            Self::TlsAes256GcmSha384 | Self::TlsChacha20Poly1305Sha256 => Some(32),
720            _ => None,
721        }
722    }
723
724    fn noxtls_tls12_aead_key_len(self) -> Option<usize> {
725        match self {
726            Self::TlsEcdheRsaWithAes128GcmSha256
727            | Self::TlsEcdheEcdsaWithAes128GcmSha256
728            | Self::TlsDheRsaWithAes128GcmSha256
729            | Self::TlsRsaWithAes128GcmSha256
730            | Self::TlsEcdheEcdsaWithAes128CcmSha256
731            | Self::TlsEcdheEcdsaWithAes128Ccm8Sha256
732            | Self::TlsRsaWithAes128CcmSha256
733            | Self::TlsRsaWithAes128Ccm8Sha256
734            | Self::TlsDheRsaWithAes128CcmSha256
735            | Self::TlsDheRsaWithAes128Ccm8Sha256
736            | Self::TlsPskWithAes128Ccm8Sha256
737            | Self::TlsEcjpakeWithAes128Ccm8Sha256 => Some(16),
738            Self::TlsEcdheRsaWithAes256GcmSha384
739            | Self::TlsEcdheEcdsaWithAes256GcmSha384
740            | Self::TlsDheRsaWithAes256GcmSha384
741            | Self::TlsRsaWithAes256GcmSha384
742            | Self::TlsEcdheEcdsaWithAes256CcmSha256
743            | Self::TlsEcdheEcdsaWithAes256Ccm8Sha256
744            | Self::TlsRsaWithAes256CcmSha256
745            | Self::TlsRsaWithAes256Ccm8Sha256
746            | Self::TlsDheRsaWithAes256CcmSha256
747            | Self::TlsDheRsaWithAes256Ccm8Sha256 => Some(32),
748            _ => None,
749        }
750    }
751
752    fn noxtls_tls12_cbc_key_len(self) -> Option<usize> {
753        match self {
754            Self::TlsEcdheRsaWithAes128CbcSha | Self::TlsRsaWithAes128CbcSha => Some(16),
755            _ => None,
756        }
757    }
758
759    fn noxtls_tls12_chacha20_poly1305_key_len(self) -> Option<usize> {
760        match self {
761            Self::TlsEcdheRsaWithChacha20Poly1305Sha256
762            | Self::TlsEcdheEcdsaWithChacha20Poly1305Sha256
763            | Self::TlsDheRsaWithChacha20Poly1305Sha256 => Some(32),
764            _ => None,
765        }
766    }
767
768    fn noxtls_tls12_aead_tag_len(self) -> Option<usize> {
769        match self {
770            Self::TlsEcdheEcdsaWithAes128Ccm8Sha256
771            | Self::TlsEcdheEcdsaWithAes256Ccm8Sha256
772            | Self::TlsRsaWithAes128Ccm8Sha256
773            | Self::TlsRsaWithAes256Ccm8Sha256
774            | Self::TlsDheRsaWithAes128Ccm8Sha256
775            | Self::TlsDheRsaWithAes256Ccm8Sha256
776            | Self::TlsPskWithAes128Ccm8Sha256
777            | Self::TlsEcjpakeWithAes128Ccm8Sha256 => Some(8),
778            Self::TlsEcdheRsaWithAes128GcmSha256
779            | Self::TlsEcdheRsaWithAes256GcmSha384
780            | Self::TlsEcdheEcdsaWithAes128GcmSha256
781            | Self::TlsEcdheEcdsaWithAes256GcmSha384
782            | Self::TlsDheRsaWithAes128GcmSha256
783            | Self::TlsDheRsaWithAes256GcmSha384
784            | Self::TlsRsaWithAes128GcmSha256
785            | Self::TlsRsaWithAes256GcmSha384
786            | Self::TlsEcdheEcdsaWithAes128CcmSha256
787            | Self::TlsEcdheEcdsaWithAes256CcmSha256
788            | Self::TlsRsaWithAes128CcmSha256
789            | Self::TlsRsaWithAes256CcmSha256
790            | Self::TlsDheRsaWithAes128CcmSha256
791            | Self::TlsDheRsaWithAes256CcmSha256 => Some(16),
792            _ => None,
793        }
794    }
795
796    fn noxtls_to_u16(self) -> u16 {
797        match self {
798            Self::TlsAes128GcmSha256 => 0x1301,
799            Self::TlsAes256GcmSha384 => 0x1302,
800            Self::TlsChacha20Poly1305Sha256 => 0x1303,
801            Self::TlsEcdheEcdsaWithAes128GcmSha256 => 0xC02B,
802            Self::TlsEcdheEcdsaWithAes256GcmSha384 => 0xC02C,
803            Self::TlsEcdheEcdsaWithChacha20Poly1305Sha256 => 0xCCA9,
804            Self::TlsEcdheRsaWithAes128GcmSha256 => 0xC02F,
805            Self::TlsEcdheRsaWithAes256GcmSha384 => 0xC030,
806            Self::TlsEcdheRsaWithChacha20Poly1305Sha256 => 0xCCA8,
807            Self::TlsDheRsaWithChacha20Poly1305Sha256 => 0xCCAA,
808            Self::TlsDheRsaWithAes128GcmSha256 => 0x009E,
809            Self::TlsDheRsaWithAes256GcmSha384 => 0x009F,
810            Self::TlsRsaWithAes128GcmSha256 => 0x009C,
811            Self::TlsRsaWithAes256GcmSha384 => 0x009D,
812            Self::TlsEcdheRsaWithAes128CbcSha => 0xC013,
813            Self::TlsRsaWithAes128CbcSha => 0x002F,
814            Self::TlsEcdheEcdsaWithAes128CcmSha256 => 0xC0AC,
815            Self::TlsEcdheEcdsaWithAes256CcmSha256 => 0xC0AD,
816            Self::TlsEcdheEcdsaWithAes128Ccm8Sha256 => 0xC0AE,
817            Self::TlsEcdheEcdsaWithAes256Ccm8Sha256 => 0xC0AF,
818            Self::TlsRsaWithAes128CcmSha256 => 0xC09C,
819            Self::TlsRsaWithAes256CcmSha256 => 0xC09D,
820            Self::TlsRsaWithAes128Ccm8Sha256 => 0xC0A0,
821            Self::TlsRsaWithAes256Ccm8Sha256 => 0xC0A1,
822            Self::TlsDheRsaWithAes128CcmSha256 => 0xC09E,
823            Self::TlsDheRsaWithAes256CcmSha256 => 0xC09F,
824            Self::TlsDheRsaWithAes128Ccm8Sha256 => 0xC0A2,
825            Self::TlsDheRsaWithAes256Ccm8Sha256 => 0xC0A3,
826            Self::TlsPskWithAes128Ccm8Sha256 => 0xC0A4,
827            Self::TlsEcjpakeWithAes128Ccm8Sha256 => 0xC0FF,
828        }
829    }
830}
831
832mod client_hello;
833mod common;
834mod dtls12;
835mod dtls13;
836mod quic;
837mod record_common;
838mod record_server;
839mod tls12_handshake;
840mod tls12_security;
841mod tls13_client;
842mod tls13_handshake;
843mod tls13_server;
844mod tls13_server_role;
845mod tls_kdf;
846mod tls_key_exchange;
847
848use self::common::noxtls_constant_time_eq;
849use self::tls12_security::noxtls_parse_tls12_signature_fields;
850use self::tls_kdf::{noxtls_derive_tls13_handshake_secret, noxtls_tls12_prf_for_hash};
851use self::tls_key_exchange::noxtls_combine_tls13_hybrid_shared_secret;
852
853impl Connection {
854    /// Creates a noxtls_new connection initialized in the `Idle` handshake state.
855    ///
856    /// # Arguments
857    /// * `version`: Protocol version profile for this connection.
858    ///
859    /// # Returns
860    /// Fresh `Connection` in `Idle` state.
861    /// # Panics
862    ///
863    /// This function does not panic.
864    ///
865    pub fn noxtls_new(version: TlsVersion) -> Self {
866        Self {
867            version,
868            tls_role: TlsRole::Client,
869            state: HandshakeState::Idle,
870            noxtls_selected_cipher_suite: None,
871            client_offered_cipher_suites: Vec::new(),
872            client_offered_tls_fallback_scsv: false,
873            tls13_client_cipher_suites: None,
874            transcript: Vec::new(),
875            noxtls_transcript_hash: TranscriptHashState::noxtls_for_version(version),
876            handshake_secret: None,
877            tls13_master_secret: None,
878            tls13_client_handshake_traffic_secret: None,
879            tls13_server_handshake_traffic_secret: None,
880            tls13_client_application_traffic_secret: None,
881            tls13_server_application_traffic_secret: None,
882            tls13_exporter_master_secret: None,
883            noxtls_tls13_resumption_master_secret: None,
884            tls13_client_x25519_private: None,
885            tls13_client_p256_private: None,
886            tls13_client_mlkem768_private: None,
887            tls13_shared_secret: None,
888            tls13_hrr_requested_group: None,
889            tls13_hrr_client_hello_extension_order: None,
890            tls13_hrr_seen: false,
891            tls13_negotiated_key_exchange_group: None,
892            tls13_negotiated_certificate_verify_signature_scheme: None,
893            client_write_key: None,
894            server_write_key: None,
895            tls12_client_write_mac_key: None,
896            tls12_server_write_mac_key: None,
897            client_write_iv: None,
898            server_write_iv: None,
899            client_sequence: 0,
900            server_sequence: 0,
901            noxtls_tls13_peer_close_notify_received: false,
902            noxtls_tls13_local_close_notify_sent: false,
903            tls13_require_certificate_auth: false,
904            tls13_server_trust_anchors_der: Vec::new(),
905            tls13_server_intermediates_der: Vec::new(),
906            tls13_server_validation_time: None,
907            tls13_server_expected_hostname: None,
908            tls13_client_server_name: None,
909            tls13_request_ocsp_stapling: false,
910            tls13_require_ocsp_staple: false,
911            tls13_ocsp_staple_verifier: None,
912            noxtls_tls13_server_ocsp_staple: None,
913            noxtls_tls13_server_ocsp_staple_verified: false,
914            tls13_require_server_name_ack: false,
915            noxtls_tls13_server_name_acknowledged: false,
916            tls13_client_alpn_protocols: Vec::new(),
917            noxtls_tls13_selected_alpn_protocol: None,
918            tls13_client_signature_algorithms: Vec::new(),
919            tls13_client_certificate_compression_algorithms: Vec::new(),
920            tls13_application_traffic_transcript_hash: None,
921            tls13_client_offer_pq_key_shares: true,
922            tls13_client_offer_mldsa_signature: true,
923            tls13_server_leaf_public_key_der: None,
924            tls13_client_identity_certificate_chain_der: Vec::new(),
925            tls13_client_signing_key: None,
926            tls13_client_trust_anchors_der: Vec::new(),
927            tls13_client_intermediates_der: Vec::new(),
928            tls13_client_validation_time: None,
929            tls13_client_leaf_public_key_der: None,
930            tls13_client_certificate_chain_validated: false,
931            tls13_require_client_certificate_auth: false,
932            tls13_server_requested_client_certificate: false,
933            tls13_server_certificate_chain_validated: false,
934            tls13_server_certificate_chain_der: Vec::new(),
935            tls13_client_raw_public_keys_enabled: false,
936            tls13_expected_server_raw_public_key_der: None,
937            tls13_server_identity_certificate_type: TLS_CERTIFICATE_TYPE_X509,
938            tls13_negotiated_server_certificate_type: TLS_CERTIFICATE_TYPE_X509,
939            tls13_client_offered_server_certificate_types: Vec::new(),
940            tls13_server_signing_key: None,
941            tls13_server_preferred_cipher_suites: Vec::new(),
942            tls13_server_preferred_key_exchange_groups: Vec::new(),
943            tls13_server_alpn_protocols: Vec::new(),
944            tls13_server_x25519_private: None,
945            tls13_server_p256_private: None,
946            tls13_early_data_require_acceptance: false,
947            tls13_early_data_accepted_psk: None,
948            tls13_early_data_max_bytes: None,
949            tls13_early_data_opened_bytes: 0,
950            tls13_early_data_offered_in_client_hello: false,
951            tls13_early_data_accepted_in_encrypted_extensions: false,
952            tls13_end_of_early_data_seen: false,
953            tls13_early_data_anti_replay_enabled: true,
954            tls13_early_data_replay_window: DtlsReplayWindow::noxtls_new(),
955            noxtls_tls13_early_data_telemetry: Tls13EarlyDataTelemetry::default(),
956            tls12_change_cipher_spec_seen: false,
957            tls12_secure_renegotiation_enabled: true,
958            tls12_secure_renegotiation_offered: false,
959            tls12_secure_renegotiation_negotiated: false,
960            tls12_secure_renegotiation_renegotiating: false,
961            tls12_secure_renegotiation_client_verify_data: Vec::new(),
962            tls12_secure_renegotiation_server_verify_data: Vec::new(),
963            noxtls_tls12_session_id: None,
964            tls12_allow_legacy_record_versions: false,
965            tls12_client_random: None,
966            tls12_server_random: None,
967            tls12_pre_master_secret: None,
968            tls12_rsa_encrypted_pre_master_secret: None,
969            tls12_dhe_prime: None,
970            tls12_dhe_server_public_key: None,
971            tls12_dhe_client_public_key: None,
972            tls12_master_secret: None,
973            tls12_extended_master_secret_session_hash: None,
974            dtls13_client_write_key: None,
975            dtls13_client_write_iv: None,
976            dtls13_server_write_key: None,
977            dtls13_server_write_iv: None,
978            dtls13_outbound_epoch: 0,
979            dtls13_outbound_sequence: 0,
980            dtls13_outbound_connection_id: Vec::new(),
981            dtls13_inbound_connection_id: Vec::new(),
982            dtls13_inbound_replay_tracker: DtlsEpochReplayTracker::noxtls_new(),
983            dtls13_client_inbound_replay_tracker: DtlsEpochReplayTracker::noxtls_new(),
984            dtls13_active_flight: Vec::new(),
985            dtls13_pending_ack_ranges: Vec::new(),
986            dtls13_active_flight_started_at_ms: None,
987            dtls13_active_flight_timeout_ms: DTLS13_ACTIVE_FLIGHT_TIMEOUT_MS,
988            noxtls_dtls13_active_flight_failed: false,
989            dtls_retransmit_tracker: DtlsFlightRetransmitTracker::noxtls_new(
990                DTLS_RETRANSMIT_TRACKER_MAX_RECORDS,
991            ),
992            dtls_retransmit_initial_timeout_ms: DTLS_RETRANSMIT_INITIAL_TIMEOUT_MS,
993            dtls_max_retransmit_attempts: DTLS_MAX_RETRANSMIT_ATTEMPTS,
994            noxtls_dtls12_handshake_phase: Dtls12HandshakePhase::AwaitingClientHello,
995            dtls12_expected_cookie: None,
996            dtls12_anti_amplification_enforced: true,
997            dtls12_inbound_bytes: 0,
998            dtls12_outbound_bytes: 0,
999            max_record_plaintext_len: TLS_MAX_RECORD_PLAINTEXT_LEN,
1000        }
1001    }
1002
1003    /// Returns the currently configured DTLS retry/timeout policy for this connection.
1004    ///
1005    /// # Returns
1006    /// `Some(policy)` for DTLS profiles; `None` for TLS stream profiles.
1007    #[must_use]
1008    /// # Arguments
1009    ///
1010    /// * `&self` — `&self`.
1011    ///
1012    /// # Returns
1013    ///
1014    /// On success, `Some` as described by the return type; see the function body for when `None` is returned.
1015    ///
1016    /// # Panics
1017    ///
1018    /// This function does not panic.
1019    ///
1020    pub fn noxtls_dtls_operational_policy(&self) -> Option<DtlsOperationalPolicy> {
1021        if !self.version.is_dtls() {
1022            return None;
1023        }
1024        Some(DtlsOperationalPolicy {
1025            retransmit_initial_timeout_ms: self.dtls_retransmit_initial_timeout_ms,
1026            max_retransmit_attempts: self.dtls_max_retransmit_attempts,
1027            active_flight_timeout_ms: self.dtls13_active_flight_timeout_ms,
1028        })
1029    }
1030
1031    /// Applies a full DTLS retry/timeout policy in one call.
1032    ///
1033    /// # Arguments
1034    /// * `policy`: DTLS timer/retry settings. Zero values are clamped to 1.
1035    ///
1036    /// # Returns
1037    /// Effective policy after clamping and application.
1038    /// # Errors
1039    ///
1040    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1041    ///
1042    /// # Panics
1043    ///
1044    /// This function does not panic.
1045    ///
1046    pub fn noxtls_set_dtls_operational_policy(
1047        &mut self,
1048        policy: DtlsOperationalPolicy,
1049    ) -> Result<DtlsOperationalPolicy> {
1050        self.noxtls_ensure_dtls12_mode()?;
1051        let effective = DtlsOperationalPolicy {
1052            retransmit_initial_timeout_ms: policy.retransmit_initial_timeout_ms.max(1),
1053            max_retransmit_attempts: policy.max_retransmit_attempts.max(1),
1054            active_flight_timeout_ms: policy.active_flight_timeout_ms.max(1),
1055        };
1056        self.dtls_retransmit_initial_timeout_ms = effective.retransmit_initial_timeout_ms;
1057        self.dtls_max_retransmit_attempts = effective.max_retransmit_attempts;
1058        self.dtls13_active_flight_timeout_ms = effective.active_flight_timeout_ms;
1059        Ok(effective)
1060    }
1061
1062    /// Applies one built-in DTLS operational profile and returns the resulting policy.
1063    ///
1064    /// # Returns
1065    ///
1066    /// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
1067    ///
1068    /// # Arguments
1069    /// * `profile`: Built-in profile tuned for a deployment environment.
1070    /// # Errors
1071    ///
1072    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1073    ///
1074    /// # Panics
1075    ///
1076    /// This function does not panic.
1077    ///
1078    pub fn noxtls_apply_dtls_operational_profile(
1079        &mut self,
1080        profile: DtlsOperationalProfile,
1081    ) -> Result<DtlsOperationalPolicy> {
1082        let policy = match profile {
1083            DtlsOperationalProfile::Conservative => DtlsOperationalPolicy {
1084                retransmit_initial_timeout_ms: DTLS_RETRANSMIT_INITIAL_TIMEOUT_MS,
1085                max_retransmit_attempts: DTLS_MAX_RETRANSMIT_ATTEMPTS,
1086                active_flight_timeout_ms: DTLS13_ACTIVE_FLIGHT_TIMEOUT_MS,
1087            },
1088            DtlsOperationalProfile::LanLowLatency => DtlsOperationalPolicy {
1089                retransmit_initial_timeout_ms: 250,
1090                max_retransmit_attempts: 3,
1091                active_flight_timeout_ms: 3_000,
1092            },
1093            DtlsOperationalProfile::LossyNetwork => DtlsOperationalPolicy {
1094                retransmit_initial_timeout_ms: 1_500,
1095                max_retransmit_attempts: 6,
1096                active_flight_timeout_ms: 20_000,
1097            },
1098        };
1099        self.noxtls_set_dtls_operational_policy(policy)
1100    }
1101
1102    /// Enables or disables strict TLS 1.3 certificate-authentication enforcement.
1103    ///
1104    /// # Arguments
1105    /// * `required`: Whether Certificate and CertificateVerify must be validated.
1106    /// # Panics
1107    ///
1108    /// This function does not panic.
1109    ///
1110    pub fn noxtls_set_tls13_require_certificate_auth(&mut self, required: bool) {
1111        self.tls13_require_certificate_auth = required;
1112    }
1113
1114    /// Enables or disables strict TLS 1.3 client-certificate authentication on server-role connections.
1115    pub fn noxtls_set_tls13_require_client_auth(&mut self, required: bool) {
1116        self.tls13_require_client_certificate_auth = required;
1117    }
1118
1119    /// Configures trust material used by a TLS 1.3 server to validate client certificates.
1120    pub fn noxtls_configure_tls13_client_auth(
1121        &mut self,
1122        trust_anchors_der: &[Vec<u8>],
1123        intermediates_der: &[Vec<u8>],
1124        validation_time: &str,
1125    ) -> Result<()> {
1126        if trust_anchors_der.is_empty() {
1127            return Err(Error::InvalidLength(
1128                "tls13 client trust anchor list must not be empty",
1129            ));
1130        }
1131        if validation_time.is_empty() {
1132            return Err(Error::InvalidLength(
1133                "tls13 client validation time must not be empty",
1134            ));
1135        }
1136        self.tls13_client_trust_anchors_der = trust_anchors_der.to_vec();
1137        self.tls13_client_intermediates_der = intermediates_der.to_vec();
1138        self.tls13_client_validation_time = Some(validation_time.to_owned());
1139        Ok(())
1140    }
1141
1142    /// Configures certificate-chain material used for TLS 1.3 server auth validation.
1143    ///
1144    /// # Arguments
1145    /// * `trust_anchors_der`: Trusted root certificates in DER form.
1146    /// * `intermediates_der`: Optional intermediate certificates in DER form.
1147    /// * `validation_time`: Validation timestamp in canonical ASN.1 text form.
1148    ///
1149    /// # Returns
1150    /// `Ok(())` when configuration is stored.
1151    /// # Errors
1152    ///
1153    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1154    ///
1155    /// # Panics
1156    ///
1157    /// This function does not panic.
1158    ///
1159    pub fn noxtls_configure_tls13_server_auth(
1160        &mut self,
1161        trust_anchors_der: &[Vec<u8>],
1162        intermediates_der: &[Vec<u8>],
1163        validation_time: &str,
1164    ) -> Result<()> {
1165        if trust_anchors_der.is_empty() {
1166            return Err(Error::InvalidLength(
1167                "tls13 trust anchor list must not be empty",
1168            ));
1169        }
1170        if validation_time.is_empty() {
1171            return Err(Error::InvalidLength(
1172                "tls13 validation time must not be empty",
1173            ));
1174        }
1175        self.tls13_server_trust_anchors_der = trust_anchors_der.to_vec();
1176        self.tls13_server_intermediates_der = intermediates_der.to_vec();
1177        self.tls13_server_validation_time = Some(validation_time.to_owned());
1178        Ok(())
1179    }
1180
1181    /// Sets or clears expected server hostname for TLS 1.3 certificate authentication.
1182    ///
1183    /// # Arguments
1184    /// * `hostname`: `Some(name)` to enforce hostname matching, or `None` to disable.
1185    ///
1186    /// # Returns
1187    /// `Ok(())` when hostname policy is stored.
1188    /// # Errors
1189    ///
1190    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1191    ///
1192    /// # Panics
1193    ///
1194    /// This function does not panic.
1195    ///
1196    pub fn noxtls_set_tls13_server_expected_hostname(
1197        &mut self,
1198        hostname: Option<&str>,
1199    ) -> Result<()> {
1200        match hostname {
1201            Some(value) if value.is_empty() => Err(Error::InvalidLength(
1202                "tls13 expected hostname must not be empty",
1203            )),
1204            Some(value) => {
1205                self.tls13_server_expected_hostname = Some(value.to_owned());
1206                Ok(())
1207            }
1208            None => {
1209                self.tls13_server_expected_hostname = None;
1210                Ok(())
1211            }
1212        }
1213    }
1214
1215    /// Sets or clears TLS 1.2 session-id bytes used in outbound ClientHello.
1216    ///
1217    /// # Arguments
1218    /// * `session_id`: `Some(id)` to advertise a session-id (1..=32 bytes), or `None` to clear.
1219    ///
1220    /// # Returns
1221    /// `Ok(())` when the lifecycle value is stored.
1222    /// # Errors
1223    ///
1224    /// Returns [`noxtls_core::Error`] when size constraints are violated.
1225    ///
1226    /// # Panics
1227    ///
1228    /// This function does not panic.
1229    pub fn noxtls_set_tls12_session_id(&mut self, session_id: Option<&[u8]>) -> Result<()> {
1230        match session_id {
1231            Some(value) if value.is_empty() => Err(Error::InvalidLength(
1232                "tls12 session id must not be empty when present",
1233            )),
1234            Some(value) if value.len() > 32 => Err(Error::InvalidLength(
1235                "tls12 session id must not exceed 32 bytes",
1236            )),
1237            Some(value) => {
1238                self.noxtls_tls12_session_id = Some(value.to_vec());
1239                Ok(())
1240            }
1241            None => {
1242                self.noxtls_tls12_session_id = None;
1243                Ok(())
1244            }
1245        }
1246    }
1247
1248    /// Returns currently configured TLS 1.2 ClientHello session-id bytes.
1249    ///
1250    /// # Arguments
1251    ///
1252    /// * `self` — Connection carrying TLS 1.2 session lifecycle state.
1253    ///
1254    /// # Returns
1255    ///
1256    /// Configured session-id bytes when present.
1257    ///
1258    /// # Panics
1259    ///
1260    /// This function does not panic.
1261    #[must_use]
1262    pub fn noxtls_tls12_session_id(&self) -> Option<&[u8]> {
1263        self.noxtls_tls12_session_id.as_deref()
1264    }
1265
1266    /// Enables or disables TLS 1.0/1.1 compatibility record-version acceptance in TLS 1.2 packet APIs.
1267    ///
1268    /// # Arguments
1269    ///
1270    /// * `allow` — `true` to accept legacy record versions (`0x0301`, `0x0302`) in addition to `0x0303`.
1271    ///
1272    /// # Panics
1273    ///
1274    /// This function does not panic.
1275    pub fn noxtls_set_tls12_allow_legacy_record_versions(&mut self, allow: bool) {
1276        self.tls12_allow_legacy_record_versions = allow;
1277    }
1278
1279    pub fn noxtls_set_tls12_secure_renegotiation_enabled(&mut self, enabled: bool) {
1280        self.tls12_secure_renegotiation_enabled = enabled;
1281        if !enabled {
1282            self.tls12_secure_renegotiation_offered = false;
1283            self.tls12_secure_renegotiation_negotiated = false;
1284            self.tls12_secure_renegotiation_renegotiating = false;
1285        }
1286    }
1287
1288    #[must_use]
1289    pub fn noxtls_tls12_secure_renegotiation_negotiated(&self) -> bool {
1290        self.tls12_secure_renegotiation_negotiated
1291    }
1292
1293    #[must_use]
1294    pub fn noxtls_tls12_secure_renegotiation_verify_data(&self) -> (&[u8], &[u8]) {
1295        (
1296            self.tls12_secure_renegotiation_client_verify_data
1297                .as_slice(),
1298            self.tls12_secure_renegotiation_server_verify_data
1299                .as_slice(),
1300        )
1301    }
1302
1303    pub fn noxtls_set_tls12_secure_renegotiation_verify_data_for_test(
1304        &mut self,
1305        client_verify_data: &[u8],
1306        server_verify_data: &[u8],
1307    ) -> Result<()> {
1308        if client_verify_data.len() > u8::MAX as usize
1309            || server_verify_data.len() > u8::MAX as usize
1310            || client_verify_data
1311                .len()
1312                .saturating_add(server_verify_data.len())
1313                > u8::MAX as usize
1314        {
1315            return Err(Error::InvalidLength(
1316                "tls12 secure renegotiation verify data is too large",
1317            ));
1318        }
1319        self.tls12_secure_renegotiation_client_verify_data = client_verify_data.to_vec();
1320        self.tls12_secure_renegotiation_server_verify_data = server_verify_data.to_vec();
1321        self.tls12_secure_renegotiation_negotiated = true;
1322        Ok(())
1323    }
1324
1325    fn noxtls_validate_tls12_secure_renegotiation_server_hello(
1326        &mut self,
1327        renegotiation_info: Option<&[u8]>,
1328    ) -> Result<()> {
1329        if self.version != TlsVersion::Tls12 || !self.tls12_secure_renegotiation_enabled {
1330            return Ok(());
1331        }
1332        if !self.tls12_secure_renegotiation_offered {
1333            return Err(Error::StateError(
1334                "tls12 secure renegotiation was not offered in ClientHello",
1335            ));
1336        }
1337        let actual = renegotiation_info.ok_or(Error::ParseFailure(
1338            "tls12 server hello missing renegotiation_info extension",
1339        ))?;
1340        let mut expected = Vec::new();
1341        if self.tls12_secure_renegotiation_renegotiating {
1342            expected.extend_from_slice(&self.tls12_secure_renegotiation_client_verify_data);
1343            expected.extend_from_slice(&self.tls12_secure_renegotiation_server_verify_data);
1344        }
1345        if actual != expected.as_slice() {
1346            return Err(Error::ParseFailure(
1347                "tls12 server hello renegotiation_info verify_data mismatch",
1348            ));
1349        }
1350        self.tls12_secure_renegotiation_negotiated = true;
1351        Ok(())
1352    }
1353
1354    /// Builds a minimally-encoded TLS ServerHello handshake message.
1355    ///
1356    /// # Arguments
1357    /// * `version`: Protocol version to encode.
1358    /// * `suite`: Selected cipher suite to advertise.
1359    /// * `random`: 32-byte ServerHello random value.
1360    ///
1361    /// # Returns
1362    /// Encoded ServerHello handshake message bytes.
1363    /// # Errors
1364    ///
1365    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1366    ///
1367    /// # Panics
1368    ///
1369    /// This function does not panic.
1370    ///
1371    pub fn noxtls_build_server_hello(
1372        version: TlsVersion,
1373        suite: CipherSuite,
1374        random: &[u8],
1375    ) -> Result<Vec<u8>> {
1376        if random.len() != 32 {
1377            return Err(Error::InvalidLength("server hello random must be 32 bytes"));
1378        }
1379        let body = noxtls_encode_server_hello_body(version, suite, random)?;
1380        Ok(noxtls_encode_handshake_message(
1381            HANDSHAKE_SERVER_HELLO,
1382            &body,
1383        ))
1384    }
1385
1386    /// Builds a TLS ServerHello with randomness sourced from HMAC-DRBG.
1387    ///
1388    /// # Arguments
1389    /// * `version`: Protocol version to encode.
1390    /// * `suite`: Selected cipher suite to advertise.
1391    /// * `drbg`: DRBG instance used to generate ServerHello random bytes.
1392    ///
1393    /// # Returns
1394    /// Encoded ServerHello handshake message bytes.
1395    /// # Errors
1396    ///
1397    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1398    ///
1399    /// # Panics
1400    ///
1401    /// This function does not panic.
1402    ///
1403    pub fn noxtls_build_server_hello_auto(
1404        version: TlsVersion,
1405        suite: CipherSuite,
1406        drbg: &mut HmacDrbgSha256,
1407    ) -> Result<Vec<u8>> {
1408        let random = drbg.generate(32, b"server_hello_random")?;
1409        Self::noxtls_build_server_hello(version, suite, &random)
1410    }
1411
1412    pub fn noxtls_build_tls12_server_hello_with_secure_renegotiation(
1413        suite: CipherSuite,
1414        random: &[u8],
1415        client_verify_data: &[u8],
1416        server_verify_data: &[u8],
1417    ) -> Result<Vec<u8>> {
1418        let total_len = client_verify_data
1419            .len()
1420            .saturating_add(server_verify_data.len());
1421        if total_len > u8::MAX as usize {
1422            return Err(Error::InvalidLength(
1423                "tls12 secure renegotiation info is too large",
1424            ));
1425        }
1426        let mut renegotiation_info = Vec::with_capacity(total_len);
1427        renegotiation_info.extend_from_slice(client_verify_data);
1428        renegotiation_info.extend_from_slice(server_verify_data);
1429        let body = noxtls_encode_server_hello_body_with_key_share(
1430            TlsVersion::Tls12,
1431            suite,
1432            random,
1433            None,
1434            None,
1435            Some(&renegotiation_info),
1436        )?;
1437        Ok(noxtls_encode_handshake_message(
1438            HANDSHAKE_SERVER_HELLO,
1439            &body,
1440        ))
1441    }
1442
1443    /// Parses a ClientHello and returns advertised cipher suites in wire order.
1444    ///
1445    /// # Arguments
1446    /// * `msg`: Encoded ClientHello handshake message.
1447    ///
1448    /// # Returns
1449    /// Supported cipher suites offered by the client.
1450    /// # Errors
1451    ///
1452    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1453    ///
1454    /// # Panics
1455    ///
1456    /// This function does not panic.
1457    ///
1458    pub fn noxtls_parse_client_hello_cipher_suites(msg: &[u8]) -> Result<Vec<CipherSuite>> {
1459        noxtls_parse_client_hello_info(msg).map(|hello| hello.offered_cipher_suites)
1460    }
1461
1462    /// Parses a ClientHello into suites and selected extension metadata.
1463    ///
1464    /// # Arguments
1465    /// * `msg`: Encoded ClientHello handshake message.
1466    ///
1467    /// # Returns
1468    /// Parsed `ClientHelloInfo` with suites and extension summary.
1469    /// # Errors
1470    ///
1471    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1472    ///
1473    /// # Panics
1474    ///
1475    /// This function does not panic.
1476    ///
1477    pub fn noxtls_parse_client_hello_info(msg: &[u8]) -> Result<ClientHelloInfo> {
1478        noxtls_parse_client_hello_info(msg)
1479    }
1480
1481    /// Returns the negotiated ALPN protocol, when one has been selected.
1482    #[must_use]
1483    pub fn noxtls_selected_alpn_protocol(&self) -> Option<&[u8]> {
1484        self.noxtls_tls13_selected_alpn_protocol.as_deref()
1485    }
1486
1487    /// Backward-compatible alias for [`Self::noxtls_selected_alpn_protocol`].
1488    #[must_use]
1489    pub fn noxtls_tls13_selected_alpn_protocol(&self) -> Option<&[u8]> {
1490        self.noxtls_selected_alpn_protocol()
1491    }
1492
1493    /// Returns the negotiated TLS 1.3 key-exchange group, when one has been selected.
1494    #[must_use]
1495    pub fn noxtls_negotiated_key_exchange_group(&self) -> Option<u16> {
1496        self.tls13_negotiated_key_exchange_group
1497    }
1498
1499    /// Backward-compatible alias for [`Self::noxtls_negotiated_key_exchange_group`].
1500    #[must_use]
1501    pub fn noxtls_tls13_negotiated_key_exchange_group(&self) -> Option<u16> {
1502        self.noxtls_negotiated_key_exchange_group()
1503    }
1504
1505    /// Returns the negotiated TLS 1.3 CertificateVerify signature scheme, when one is known.
1506    #[must_use]
1507    pub fn noxtls_negotiated_certificate_verify_signature_scheme(&self) -> Option<u16> {
1508        self.tls13_negotiated_certificate_verify_signature_scheme
1509    }
1510
1511    /// Backward-compatible alias for [`Self::noxtls_negotiated_certificate_verify_signature_scheme`].
1512    #[must_use]
1513    pub fn noxtls_tls13_negotiated_certificate_verify_signature_scheme(&self) -> Option<u16> {
1514        self.noxtls_negotiated_certificate_verify_signature_scheme()
1515    }
1516
1517    /// Selects the first server-preferred ALPN protocol present in the offered ClientHello.
1518    pub fn noxtls_select_tls13_server_alpn_protocol(
1519        client_hello: &[u8],
1520        server_preferred: &[&[u8]],
1521    ) -> Result<Option<Vec<u8>>> {
1522        let hello = Self::noxtls_parse_client_hello_info(client_hello)?;
1523        Self::noxtls_select_server_alpn_protocol_from_offers(
1524            &hello.extensions.alpn_protocols,
1525            server_preferred,
1526        )
1527    }
1528
1529    /// Parses a PKCS#8 private key into a TLS 1.3 server signing-key enum.
1530    pub fn noxtls_tls13_server_identity_key_from_pkcs8_der(
1531        private_key_pkcs8_der: &[u8],
1532    ) -> Result<Tls13ServerIdentityKey> {
1533        if let Ok(private_key) = noxtls_p256_private_key_from_pkcs8_der(private_key_pkcs8_der) {
1534            return Ok(Tls13ServerIdentityKey::P256(private_key));
1535        }
1536        if let Ok(private_key) = noxtls_p384_private_key_from_pkcs8_der(private_key_pkcs8_der) {
1537            return Ok(Tls13ServerIdentityKey::P384(private_key));
1538        }
1539        if let Ok(private_key) = noxtls_p521_private_key_from_pkcs8_der(private_key_pkcs8_der) {
1540            return Ok(Tls13ServerIdentityKey::P521(private_key));
1541        }
1542        if let Ok(private_key) = noxtls_rsa_pss_private_key_from_pkcs8_der(private_key_pkcs8_der) {
1543            return Ok(Tls13ServerIdentityKey::RsaPss(private_key));
1544        }
1545        if let Ok(private_key) = noxtls_rsa_private_key_from_pkcs8_der(private_key_pkcs8_der) {
1546            return Ok(Tls13ServerIdentityKey::Rsa(private_key));
1547        }
1548        Err(Error::UnsupportedFeature(
1549            "unsupported pkcs8 private key algorithm for tls13 server identity",
1550        ))
1551    }
1552
1553    fn noxtls_select_server_alpn_protocol_from_offers(
1554        client_offered: &[Vec<u8>],
1555        server_preferred: &[&[u8]],
1556    ) -> Result<Option<Vec<u8>>> {
1557        for protocol in server_preferred {
1558            if protocol.is_empty() {
1559                return Err(Error::InvalidLength("alpn protocol must not be empty"));
1560            }
1561            if protocol.len() > u8::MAX as usize {
1562                return Err(Error::InvalidLength(
1563                    "alpn protocol length must not exceed 255 bytes",
1564                ));
1565            }
1566            if client_offered
1567                .iter()
1568                .any(|offered| offered.as_slice() == *protocol)
1569            {
1570                return Ok(Some(protocol.to_vec()));
1571            }
1572        }
1573        Ok(None)
1574    }
1575
1576    /// Builds TLS 1.3 server CertificateVerify signed content from transcript hash.
1577    ///
1578    /// # Arguments
1579    /// * `noxtls_transcript_hash`: Transcript hash bytes for the signing context.
1580    ///
1581    /// # Returns
1582    /// Byte vector to be signed/verified for server CertificateVerify.
1583    #[must_use]
1584    /// # Arguments
1585    ///
1586    /// * `noxtls_transcript_hash` — `noxtls_transcript_hash: &[u8]`.
1587    ///
1588    /// # Returns
1589    ///
1590    /// The value described by the return type in the function signature.
1591    ///
1592    /// # Panics
1593    ///
1594    /// This function does not panic.
1595    ///
1596    pub fn noxtls_tls13_server_certificate_verify_content(
1597        noxtls_transcript_hash: &[u8],
1598    ) -> Vec<u8> {
1599        noxtls_build_tls13_server_certificate_verify_message(noxtls_transcript_hash)
1600    }
1601
1602    /// Selects one server-preferred suite that is also offered by the client.
1603    ///
1604    /// # Arguments
1605    /// * `client_hello`: Encoded ClientHello bytes.
1606    /// * `server_preferred`: Server preference-ordered suite list.
1607    /// * `version`: Protocol version context for filtering.
1608    ///
1609    /// # Returns
1610    /// Selected mutually-supported cipher suite.
1611    /// # Errors
1612    ///
1613    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1614    ///
1615    /// # Panics
1616    ///
1617    /// This function does not panic.
1618    ///
1619    pub fn noxtls_select_cipher_suite_from_client_hello(
1620        client_hello: &[u8],
1621        server_preferred: &[CipherSuite],
1622        version: TlsVersion,
1623    ) -> Result<CipherSuite> {
1624        let hello = noxtls_parse_client_hello_info(client_hello)?;
1625        Self::noxtls_select_cipher_suite_from_client_hello_info(&hello, server_preferred, version)
1626    }
1627
1628    pub(crate) fn noxtls_select_cipher_suite_from_client_hello_info(
1629        hello: &ClientHelloInfo,
1630        server_preferred: &[CipherSuite],
1631        version: TlsVersion,
1632    ) -> Result<CipherSuite> {
1633        noxtls_pick_intersection_suite(&hello, server_preferred, version)
1634    }
1635
1636    /// Builds a ServerHello by negotiating against offered client cipher suites.
1637    ///
1638    /// # Arguments
1639    /// * `version`: Protocol version to encode.
1640    /// * `client_hello`: Encoded ClientHello bytes.
1641    /// * `server_random`: 32-byte ServerHello random value.
1642    /// * `server_preferred`: Server preference-ordered suite list.
1643    ///
1644    /// # Returns
1645    /// Encoded ServerHello handshake message bytes.
1646    /// # Errors
1647    ///
1648    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1649    ///
1650    /// # Panics
1651    ///
1652    /// This function does not panic.
1653    ///
1654    pub fn noxtls_build_server_hello_for_client(
1655        version: TlsVersion,
1656        client_hello: &[u8],
1657        server_random: &[u8],
1658        server_preferred: &[CipherSuite],
1659    ) -> Result<Vec<u8>> {
1660        let selected = Self::noxtls_select_cipher_suite_from_client_hello(
1661            client_hello,
1662            server_preferred,
1663            version,
1664        )?;
1665        Self::noxtls_build_server_hello(version, selected, server_random)
1666    }
1667
1668    /// Builds a ServerHello for a parsed ClientHello with DRBG-generated random.
1669    ///
1670    /// # Arguments
1671    /// * `version`: Protocol version to encode.
1672    /// * `client_hello`: Encoded ClientHello bytes.
1673    /// * `server_preferred`: Server preference-ordered suite list.
1674    /// * `drbg`: DRBG instance used to generate ServerHello random bytes.
1675    ///
1676    /// # Returns
1677    /// Encoded ServerHello handshake message bytes.
1678    /// # Errors
1679    ///
1680    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1681    ///
1682    /// # Panics
1683    ///
1684    /// This function does not panic.
1685    ///
1686    pub fn noxtls_build_server_hello_for_client_auto(
1687        version: TlsVersion,
1688        client_hello: &[u8],
1689        server_preferred: &[CipherSuite],
1690        drbg: &mut HmacDrbgSha256,
1691    ) -> Result<Vec<u8>> {
1692        let random = drbg.generate(32, b"server_hello_random")?;
1693        Self::noxtls_build_server_hello_for_client(version, client_hello, &random, server_preferred)
1694    }
1695
1696    /// Exports keying material from TLS 1.3 exporter secret for application protocols.
1697    ///
1698    /// # Arguments
1699    /// * `label`: Exporter label namespace chosen by the caller.
1700    /// * `context`: Application-specific exporter context bytes.
1701    /// * `len`: Requested output keying material length.
1702    ///
1703    /// # Returns
1704    /// Exported keying material bytes bound to transcript and context.
1705    /// # Errors
1706    ///
1707    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1708    ///
1709    /// # Panics
1710    ///
1711    /// This function does not panic.
1712    ///
1713    pub fn noxtls_export_keying_material(
1714        &self,
1715        label: &[u8],
1716        context: &[u8],
1717        len: usize,
1718    ) -> Result<Vec<u8>> {
1719        if !self.version.uses_tls13_handshake_semantics() {
1720            return Err(Error::StateError(
1721                "key exporter is currently only modeled for TLS 1.3",
1722            ));
1723        }
1724        if self.state != HandshakeState::Finished {
1725            return Err(Error::StateError(
1726                "key exporter requires finished handshake state",
1727            ));
1728        }
1729        let noxtls_hash_algorithm = self.noxtls_negotiated_hash_algorithm();
1730        let hash_len = noxtls_hash_algorithm.output_len();
1731        let exporter_master =
1732            self.tls13_exporter_master_secret
1733                .as_ref()
1734                .ok_or(Error::StateError(
1735                    "tls13 exporter master secret is not installed",
1736                ))?;
1737        let context_hash = noxtls_hash_bytes_for_algorithm(noxtls_hash_algorithm, context);
1738        let exporter_secret = noxtls_tls13_expand_label_for_hash(
1739            noxtls_hash_algorithm,
1740            exporter_master,
1741            b"exporter",
1742            &context_hash,
1743            hash_len,
1744        )?;
1745        noxtls_tls13_expand_label_for_hash(
1746            noxtls_hash_algorithm,
1747            &exporter_secret,
1748            label,
1749            &context_hash,
1750            len,
1751        )
1752    }
1753
1754    /// Issues one local TLS 1.3 resumption ticket from current resumption master secret.
1755    ///
1756    /// # Arguments
1757    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1758    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1759    ///
1760    /// # Returns
1761    /// `ResumptionTicket` containing identity, nonce, and age fields.
1762    /// # Errors
1763    ///
1764    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1765    ///
1766    /// # Panics
1767    ///
1768    /// This function does not panic.
1769    ///
1770    pub fn noxtls_issue_tls13_resumption_ticket(
1771        &self,
1772        drbg: &mut HmacDrbgSha256,
1773        age_add: u32,
1774    ) -> Result<ResumptionTicket> {
1775        self.noxtls_issue_tls13_resumption_ticket_with_time(drbg, age_add, 0, u64::MAX)
1776    }
1777
1778    /// Issues one TLS 1.3 ticket and inserts it into a mutable ticket store.
1779    ///
1780    /// # Arguments
1781    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1782    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1783    /// * `ticket_store`: Mutable ticket cache receiving the issued ticket.
1784    ///
1785    /// # Returns
1786    /// Issued `ResumptionTicket` after insertion into `ticket_store`.
1787    /// # Errors
1788    ///
1789    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1790    ///
1791    /// # Panics
1792    ///
1793    /// This function does not panic.
1794    ///
1795    pub fn noxtls_issue_tls13_resumption_ticket_into_store(
1796        &self,
1797        drbg: &mut HmacDrbgSha256,
1798        age_add: u32,
1799        ticket_store: &mut TicketStore,
1800    ) -> Result<ResumptionTicket> {
1801        let ticket = self.noxtls_issue_tls13_resumption_ticket(drbg, age_add)?;
1802        ticket_store.insert(ticket.clone());
1803        Ok(ticket)
1804    }
1805
1806    /// Issues one local TLS 1.3 resumption ticket with explicit issuance time and lifetime.
1807    ///
1808    /// # Arguments
1809    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1810    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1811    /// * `issued_at_ms`: Server-local issue timestamp in milliseconds.
1812    /// * `lifetime_ms`: Ticket lifetime window in milliseconds.
1813    ///
1814    /// # Returns
1815    /// `ResumptionTicket` containing identity, nonce, and age policy fields.
1816    /// # Errors
1817    ///
1818    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1819    ///
1820    /// # Panics
1821    ///
1822    /// This function does not panic.
1823    ///
1824    pub fn noxtls_issue_tls13_resumption_ticket_with_time(
1825        &self,
1826        drbg: &mut HmacDrbgSha256,
1827        age_add: u32,
1828        issued_at_ms: u64,
1829        lifetime_ms: u64,
1830    ) -> Result<ResumptionTicket> {
1831        if !self.version.uses_tls13_handshake_semantics() {
1832            return Err(Error::StateError(
1833                "resumption ticket issuance is only defined for TLS 1.3",
1834            ));
1835        }
1836        if self.state != HandshakeState::Finished {
1837            return Err(Error::StateError(
1838                "resumption ticket issuance requires finished handshake state",
1839            ));
1840        }
1841        let nonce = drbg.generate(16, b"tls13_ticket_nonce")?;
1842        let noxtls_hash_algorithm = self.noxtls_negotiated_hash_algorithm();
1843        let identity = noxtls_tls13_expand_label_for_hash(
1844            noxtls_hash_algorithm,
1845            &self.noxtls_tls13_resumption_master_secret()?,
1846            b"ticket",
1847            &nonce,
1848            16,
1849        )?;
1850        Ok(ResumptionTicket {
1851            identity,
1852            ticket_nonce: nonce,
1853            obfuscated_ticket_age: age_add,
1854            age_add,
1855            issued_at_ms,
1856            lifetime_ms,
1857            max_early_data_size: TLS_MAX_RECORD_PLAINTEXT_LEN as u32,
1858            consumed: false,
1859        })
1860    }
1861
1862    /// Issues one local TLS 1.3 resumption ticket with explicit early-data size allowance.
1863    ///
1864    /// # Arguments
1865    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1866    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1867    /// * `issued_at_ms`: Server-local issue timestamp in milliseconds.
1868    /// * `lifetime_ms`: Ticket lifetime window in milliseconds.
1869    /// * `max_early_data_size`: Maximum accepted 0-RTT plaintext bytes for this ticket.
1870    ///
1871    /// # Returns
1872    /// `ResumptionTicket` containing identity, nonce, age policy fields, and early-data limit.
1873    /// # Errors
1874    ///
1875    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1876    ///
1877    /// # Panics
1878    ///
1879    /// This function does not panic.
1880    pub fn noxtls_issue_tls13_resumption_ticket_with_time_and_early_data(
1881        &self,
1882        drbg: &mut HmacDrbgSha256,
1883        age_add: u32,
1884        issued_at_ms: u64,
1885        lifetime_ms: u64,
1886        max_early_data_size: u32,
1887    ) -> Result<ResumptionTicket> {
1888        let mut ticket = self.noxtls_issue_tls13_resumption_ticket_with_time(
1889            drbg,
1890            age_add,
1891            issued_at_ms,
1892            lifetime_ms,
1893        )?;
1894        ticket.max_early_data_size = max_early_data_size;
1895        Ok(ticket)
1896    }
1897
1898    /// Issues one timed TLS 1.3 ticket and inserts it into a mutable ticket store.
1899    ///
1900    /// # Arguments
1901    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1902    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1903    /// * `issued_at_ms`: Server-local issue timestamp in milliseconds.
1904    /// * `lifetime_ms`: Ticket lifetime window in milliseconds.
1905    /// * `ticket_store`: Mutable ticket cache receiving the issued ticket.
1906    ///
1907    /// # Returns
1908    /// Issued `ResumptionTicket` after insertion into `ticket_store`.
1909    /// # Errors
1910    ///
1911    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1912    ///
1913    /// # Panics
1914    ///
1915    /// This function does not panic.
1916    ///
1917    pub fn noxtls_issue_tls13_resumption_ticket_with_time_into_store(
1918        &self,
1919        drbg: &mut HmacDrbgSha256,
1920        age_add: u32,
1921        issued_at_ms: u64,
1922        lifetime_ms: u64,
1923        ticket_store: &mut TicketStore,
1924    ) -> Result<ResumptionTicket> {
1925        let ticket = self.noxtls_issue_tls13_resumption_ticket_with_time(
1926            drbg,
1927            age_add,
1928            issued_at_ms,
1929            lifetime_ms,
1930        )?;
1931        ticket_store.insert(ticket.clone());
1932        Ok(ticket)
1933    }
1934
1935    /// Issues one timed TLS 1.3 ticket with early-data allowance and inserts it into a store.
1936    ///
1937    /// # Arguments
1938    /// * `drbg`: DRBG used to generate per-ticket nonce material.
1939    /// * `age_add`: Ticket age_add value used for obfuscated ticket age encoding.
1940    /// * `issued_at_ms`: Server-local issue timestamp in milliseconds.
1941    /// * `lifetime_ms`: Ticket lifetime window in milliseconds.
1942    /// * `max_early_data_size`: Maximum accepted 0-RTT plaintext bytes for this ticket.
1943    /// * `ticket_store`: Mutable ticket cache receiving the issued ticket.
1944    ///
1945    /// # Returns
1946    /// Issued `ResumptionTicket` after insertion into `ticket_store`.
1947    /// # Errors
1948    ///
1949    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1950    ///
1951    /// # Panics
1952    ///
1953    /// This function does not panic.
1954    pub fn noxtls_issue_tls13_resumption_ticket_with_time_and_early_data_into_store(
1955        &self,
1956        drbg: &mut HmacDrbgSha256,
1957        age_add: u32,
1958        issued_at_ms: u64,
1959        lifetime_ms: u64,
1960        max_early_data_size: u32,
1961        ticket_store: &mut TicketStore,
1962    ) -> Result<ResumptionTicket> {
1963        let ticket = self.noxtls_issue_tls13_resumption_ticket_with_time_and_early_data(
1964            drbg,
1965            age_add,
1966            issued_at_ms,
1967            lifetime_ms,
1968            max_early_data_size,
1969        )?;
1970        ticket_store.insert(ticket.clone());
1971        Ok(ticket)
1972    }
1973
1974    /// Seals outbound application data using installed client traffic keys.
1975    ///
1976    /// # Arguments
1977    /// * `plaintext`: Application plaintext bytes to protect.
1978    /// * `aad`: Additional authenticated data for record protection.
1979    ///
1980    /// # Returns
1981    /// `ProtectedRecord` containing sequence, ciphertext, and tag.
1982    /// # Errors
1983    ///
1984    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
1985    ///
1986    /// # Panics
1987    ///
1988    /// This function does not panic.
1989    ///
1990    pub fn noxtls_seal_record(&mut self, plaintext: &[u8], aad: &[u8]) -> Result<ProtectedRecord> {
1991        if self.state != HandshakeState::Finished {
1992            return Err(Error::StateError(
1993                "cannot seal record before handshake noxtls_finish",
1994            ));
1995        }
1996        let plaintext_limit = if self.version.uses_tls13_handshake_semantics() {
1997            self.max_record_plaintext_len.saturating_add(1)
1998        } else {
1999            self.max_record_plaintext_len
2000        };
2001        if plaintext.len() > plaintext_limit {
2002            return Err(Error::InvalidLength(
2003                "record plaintext exceeds configured limit",
2004            ));
2005        }
2006        if self.client_sequence == u64::MAX {
2007            return Err(Error::StateError("client record sequence exhausted"));
2008        }
2009        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2010            "cipher suite must be selected before sealing records",
2011        ))?;
2012        let key = self
2013            .client_write_key
2014            .ok_or(Error::StateError("client write key is not installed"))?;
2015        let iv = self
2016            .client_write_iv
2017            .ok_or(Error::StateError("client write iv is not installed"))?;
2018        let nonce = noxtls_build_record_nonce(&iv, self.client_sequence);
2019        let (ciphertext, tag) = match suite {
2020            CipherSuite::TlsChacha20Poly1305Sha256 => {
2021                noxtls_chacha20_poly1305_encrypt(&key, &nonce, aad, plaintext)?
2022            }
2023            CipherSuite::TlsAes128GcmSha256 | CipherSuite::TlsAes256GcmSha384 => {
2024                let key_len = suite
2025                    .noxtls_tls13_traffic_key_len()
2026                    .ok_or(Error::StateError(
2027                        "tls 1.3 aes suites must define traffic key length",
2028                    ))?;
2029                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2030                noxtls_aes_gcm_encrypt(&cipher, &nonce, aad, plaintext)?
2031            }
2032            CipherSuite::TlsEcdheRsaWithAes128GcmSha256
2033            | CipherSuite::TlsEcdheRsaWithAes256GcmSha384
2034            | CipherSuite::TlsEcdheEcdsaWithAes128GcmSha256
2035            | CipherSuite::TlsEcdheEcdsaWithAes256GcmSha384
2036            | CipherSuite::TlsDheRsaWithAes128GcmSha256
2037            | CipherSuite::TlsDheRsaWithAes256GcmSha384
2038            | CipherSuite::TlsRsaWithAes128GcmSha256
2039            | CipherSuite::TlsRsaWithAes256GcmSha384 => {
2040                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2041                    "tls 1.2 aes-gcm suites must define traffic key length",
2042                ))?;
2043                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2044                noxtls_aes_gcm_encrypt(&cipher, &nonce, aad, plaintext)?
2045            }
2046            CipherSuite::TlsEcdheEcdsaWithAes128CcmSha256
2047            | CipherSuite::TlsEcdheEcdsaWithAes256CcmSha256
2048            | CipherSuite::TlsEcdheEcdsaWithAes128Ccm8Sha256
2049            | CipherSuite::TlsEcdheEcdsaWithAes256Ccm8Sha256
2050            | CipherSuite::TlsRsaWithAes128CcmSha256
2051            | CipherSuite::TlsRsaWithAes256CcmSha256
2052            | CipherSuite::TlsRsaWithAes128Ccm8Sha256
2053            | CipherSuite::TlsRsaWithAes256Ccm8Sha256
2054            | CipherSuite::TlsDheRsaWithAes128CcmSha256
2055            | CipherSuite::TlsDheRsaWithAes256CcmSha256
2056            | CipherSuite::TlsDheRsaWithAes128Ccm8Sha256
2057            | CipherSuite::TlsDheRsaWithAes256Ccm8Sha256
2058            | CipherSuite::TlsPskWithAes128Ccm8Sha256
2059            | CipherSuite::TlsEcjpakeWithAes128Ccm8Sha256 => {
2060                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2061                    "tls 1.2 aes-ccm suites must define traffic key length",
2062                ))?;
2063                let tag_len = suite.noxtls_tls12_aead_tag_len().ok_or(Error::StateError(
2064                    "tls 1.2 aes-ccm suites must define tag length",
2065                ))?;
2066                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2067                noxtls_aes_ccm_encrypt_with_tag_len(&cipher, &nonce, aad, plaintext, tag_len)?
2068            }
2069            CipherSuite::TlsEcdheRsaWithAes128CbcSha | CipherSuite::TlsRsaWithAes128CbcSha => {
2070                return Err(Error::UnsupportedFeature(
2071                    "tls 1.2 cbc uses block record protection",
2072                ));
2073            }
2074            CipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256
2075            | CipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256
2076            | CipherSuite::TlsDheRsaWithChacha20Poly1305Sha256 => {
2077                return Err(Error::UnsupportedFeature(
2078                    "tls 1.2 chacha20-poly1305 uses tls12 nonce record protection",
2079                ));
2080            }
2081        };
2082        let record = ProtectedRecord {
2083            sequence: self.client_sequence,
2084            ciphertext,
2085            tag,
2086        };
2087        self.client_sequence = self.client_sequence.wrapping_add(1);
2088        Ok(record)
2089    }
2090
2091    /// Opens inbound application data using installed server traffic keys.
2092    ///
2093    /// # Arguments
2094    /// * `record`: Protected record to decrypt and authenticate.
2095    /// * `aad`: Additional authenticated data used when sealing.
2096    ///
2097    /// # Returns
2098    /// Decrypted plaintext bytes on successful authentication.
2099    /// # Errors
2100    ///
2101    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2102    ///
2103    /// # Panics
2104    ///
2105    /// This function does not panic.
2106    ///
2107    pub fn noxtls_open_record(&mut self, record: &ProtectedRecord, aad: &[u8]) -> Result<Vec<u8>> {
2108        let tls13_handshake_open_allowed = self.version.uses_tls13_handshake_semantics()
2109            && matches!(
2110                self.state,
2111                HandshakeState::KeysDerived
2112                    | HandshakeState::ServerEncryptedExtensionsReceived
2113                    | HandshakeState::ServerCertificateRequestReceived
2114                    | HandshakeState::ServerCertificateReceived
2115                    | HandshakeState::ServerCertificateVerified
2116            );
2117        if self.state != HandshakeState::Finished && !tls13_handshake_open_allowed {
2118            return Err(Error::StateError(
2119                "cannot open record before handshake noxtls_finish",
2120            ));
2121        }
2122        if self.server_sequence == u64::MAX {
2123            return Err(Error::StateError("server record sequence exhausted"));
2124        }
2125        if record.sequence != self.server_sequence {
2126            return Err(Error::StateError(
2127                "unexpected server record sequence number",
2128            ));
2129        }
2130        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2131            "cipher suite must be selected before opening records",
2132        ))?;
2133        let key = self
2134            .server_write_key
2135            .ok_or(Error::StateError("server write key is not installed"))?;
2136        let iv = self
2137            .server_write_iv
2138            .ok_or(Error::StateError("server write iv is not installed"))?;
2139        let nonce = noxtls_build_record_nonce(&iv, record.sequence);
2140        let plaintext = match suite {
2141            CipherSuite::TlsChacha20Poly1305Sha256 => noxtls_chacha20_poly1305_decrypt(
2142                &key,
2143                &nonce,
2144                aad,
2145                &record.ciphertext,
2146                &record.tag,
2147            )?,
2148            CipherSuite::TlsAes128GcmSha256 | CipherSuite::TlsAes256GcmSha384 => {
2149                let key_len = suite
2150                    .noxtls_tls13_traffic_key_len()
2151                    .ok_or(Error::StateError(
2152                        "tls 1.3 aes suites must define traffic key length",
2153                    ))?;
2154                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2155                noxtls_aes_gcm_decrypt(&cipher, &nonce, aad, &record.ciphertext, &record.tag)?
2156            }
2157            CipherSuite::TlsEcdheRsaWithAes128GcmSha256
2158            | CipherSuite::TlsEcdheRsaWithAes256GcmSha384
2159            | CipherSuite::TlsEcdheEcdsaWithAes128GcmSha256
2160            | CipherSuite::TlsEcdheEcdsaWithAes256GcmSha384
2161            | CipherSuite::TlsDheRsaWithAes128GcmSha256
2162            | CipherSuite::TlsDheRsaWithAes256GcmSha384
2163            | CipherSuite::TlsRsaWithAes128GcmSha256
2164            | CipherSuite::TlsRsaWithAes256GcmSha384 => {
2165                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2166                    "tls 1.2 aes-gcm suites must define traffic key length",
2167                ))?;
2168                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2169                noxtls_aes_gcm_decrypt(&cipher, &nonce, aad, &record.ciphertext, &record.tag)?
2170            }
2171            CipherSuite::TlsEcdheEcdsaWithAes128CcmSha256
2172            | CipherSuite::TlsEcdheEcdsaWithAes256CcmSha256
2173            | CipherSuite::TlsEcdheEcdsaWithAes128Ccm8Sha256
2174            | CipherSuite::TlsEcdheEcdsaWithAes256Ccm8Sha256
2175            | CipherSuite::TlsRsaWithAes128CcmSha256
2176            | CipherSuite::TlsRsaWithAes256CcmSha256
2177            | CipherSuite::TlsRsaWithAes128Ccm8Sha256
2178            | CipherSuite::TlsRsaWithAes256Ccm8Sha256
2179            | CipherSuite::TlsDheRsaWithAes128CcmSha256
2180            | CipherSuite::TlsDheRsaWithAes256CcmSha256
2181            | CipherSuite::TlsDheRsaWithAes128Ccm8Sha256
2182            | CipherSuite::TlsDheRsaWithAes256Ccm8Sha256
2183            | CipherSuite::TlsPskWithAes128Ccm8Sha256
2184            | CipherSuite::TlsEcjpakeWithAes128Ccm8Sha256 => {
2185                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2186                    "tls 1.2 aes-ccm suites must define traffic key length",
2187                ))?;
2188                let tag_len = suite.noxtls_tls12_aead_tag_len().ok_or(Error::StateError(
2189                    "tls 1.2 aes-ccm suites must define tag length",
2190                ))?;
2191                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2192                noxtls_aes_ccm_decrypt_with_tag_len(
2193                    &cipher,
2194                    &nonce,
2195                    aad,
2196                    &record.ciphertext,
2197                    &record.tag,
2198                    tag_len,
2199                )?
2200            }
2201            CipherSuite::TlsEcdheRsaWithAes128CbcSha | CipherSuite::TlsRsaWithAes128CbcSha => {
2202                return Err(Error::UnsupportedFeature(
2203                    "tls 1.2 cbc uses block record protection",
2204                ));
2205            }
2206            CipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256
2207            | CipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256
2208            | CipherSuite::TlsDheRsaWithChacha20Poly1305Sha256 => {
2209                return Err(Error::UnsupportedFeature(
2210                    "tls 1.2 chacha20-poly1305 uses tls12 nonce record protection",
2211                ));
2212            }
2213        };
2214        let plaintext_limit = if self.version.uses_tls13_handshake_semantics() {
2215            self.max_record_plaintext_len.saturating_add(1)
2216        } else {
2217            self.max_record_plaintext_len
2218        };
2219        if plaintext.len() > plaintext_limit {
2220            return Err(Error::InvalidLength(
2221                "record plaintext exceeds configured limit",
2222            ));
2223        }
2224        self.server_sequence = self.server_sequence.wrapping_add(1);
2225        Ok(plaintext)
2226    }
2227
2228    /// Opens a locally-sealed record using client traffic keys for loopback testing.
2229    ///
2230    /// # Arguments
2231    /// * `record`: Protected record sealed with local client keys.
2232    /// * `aad`: Additional authenticated data used when sealing.
2233    ///
2234    /// # Returns
2235    /// Decrypted plaintext bytes on successful authentication.
2236    /// # Errors
2237    ///
2238    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2239    ///
2240    /// # Panics
2241    ///
2242    /// This function does not panic.
2243    ///
2244    pub fn noxtls_open_own_record(&self, record: &ProtectedRecord, aad: &[u8]) -> Result<Vec<u8>> {
2245        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2246            "cipher suite must be selected before opening own records",
2247        ))?;
2248        let key = self
2249            .client_write_key
2250            .ok_or(Error::StateError("client write key is not installed"))?;
2251        let iv = self
2252            .client_write_iv
2253            .ok_or(Error::StateError("client write iv is not installed"))?;
2254        let nonce = noxtls_build_record_nonce(&iv, record.sequence);
2255        let plaintext = match suite {
2256            CipherSuite::TlsChacha20Poly1305Sha256 => noxtls_chacha20_poly1305_decrypt(
2257                &key,
2258                &nonce,
2259                aad,
2260                &record.ciphertext,
2261                &record.tag,
2262            )?,
2263            CipherSuite::TlsAes128GcmSha256 | CipherSuite::TlsAes256GcmSha384 => {
2264                let key_len = suite
2265                    .noxtls_tls13_traffic_key_len()
2266                    .ok_or(Error::StateError(
2267                        "tls 1.3 aes suites must define traffic key length",
2268                    ))?;
2269                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2270                noxtls_aes_gcm_decrypt(&cipher, &nonce, aad, &record.ciphertext, &record.tag)?
2271            }
2272            CipherSuite::TlsEcdheRsaWithAes128GcmSha256
2273            | CipherSuite::TlsEcdheRsaWithAes256GcmSha384
2274            | CipherSuite::TlsEcdheEcdsaWithAes128GcmSha256
2275            | CipherSuite::TlsEcdheEcdsaWithAes256GcmSha384
2276            | CipherSuite::TlsDheRsaWithAes128GcmSha256
2277            | CipherSuite::TlsDheRsaWithAes256GcmSha384
2278            | CipherSuite::TlsRsaWithAes128GcmSha256
2279            | CipherSuite::TlsRsaWithAes256GcmSha384 => {
2280                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2281                    "tls 1.2 aes-gcm suites must define traffic key length",
2282                ))?;
2283                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2284                noxtls_aes_gcm_decrypt(&cipher, &nonce, aad, &record.ciphertext, &record.tag)?
2285            }
2286            CipherSuite::TlsEcdheEcdsaWithAes128CcmSha256
2287            | CipherSuite::TlsEcdheEcdsaWithAes256CcmSha256
2288            | CipherSuite::TlsEcdheEcdsaWithAes128Ccm8Sha256
2289            | CipherSuite::TlsEcdheEcdsaWithAes256Ccm8Sha256
2290            | CipherSuite::TlsRsaWithAes128CcmSha256
2291            | CipherSuite::TlsRsaWithAes256CcmSha256
2292            | CipherSuite::TlsRsaWithAes128Ccm8Sha256
2293            | CipherSuite::TlsRsaWithAes256Ccm8Sha256
2294            | CipherSuite::TlsDheRsaWithAes128CcmSha256
2295            | CipherSuite::TlsDheRsaWithAes256CcmSha256
2296            | CipherSuite::TlsDheRsaWithAes128Ccm8Sha256
2297            | CipherSuite::TlsDheRsaWithAes256Ccm8Sha256
2298            | CipherSuite::TlsPskWithAes128Ccm8Sha256
2299            | CipherSuite::TlsEcjpakeWithAes128Ccm8Sha256 => {
2300                let key_len = suite.noxtls_tls12_aead_key_len().ok_or(Error::StateError(
2301                    "tls 1.2 aes-ccm suites must define traffic key length",
2302                ))?;
2303                let tag_len = suite.noxtls_tls12_aead_tag_len().ok_or(Error::StateError(
2304                    "tls 1.2 aes-ccm suites must define tag length",
2305                ))?;
2306                let cipher = AesCipher::noxtls_new(&key[..key_len])?;
2307                noxtls_aes_ccm_decrypt_with_tag_len(
2308                    &cipher,
2309                    &nonce,
2310                    aad,
2311                    &record.ciphertext,
2312                    &record.tag,
2313                    tag_len,
2314                )?
2315            }
2316            CipherSuite::TlsEcdheRsaWithAes128CbcSha | CipherSuite::TlsRsaWithAes128CbcSha => {
2317                return Err(Error::UnsupportedFeature(
2318                    "tls 1.2 cbc uses block record protection",
2319                ));
2320            }
2321            CipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256
2322            | CipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256
2323            | CipherSuite::TlsDheRsaWithChacha20Poly1305Sha256 => {
2324                return Err(Error::UnsupportedFeature(
2325                    "tls 1.2 chacha20-poly1305 uses tls12 nonce record protection",
2326                ));
2327            }
2328        };
2329        if plaintext.len() > self.max_record_plaintext_len {
2330            return Err(Error::InvalidLength(
2331                "record plaintext exceeds configured limit",
2332            ));
2333        }
2334        Ok(plaintext)
2335    }
2336
2337    /// Seals one TLS 1.2 wire record packet from plaintext and outer content type.
2338    ///
2339    /// # Arguments
2340    /// * `plaintext`: Application plaintext bytes to protect.
2341    /// * `content_type`: TLS record content type for the outer TLS 1.2 header.
2342    ///
2343    /// # Returns
2344    /// Serialized TLSCiphertext packet bytes (`type || version || len || ciphertext || tag`).
2345    /// # Errors
2346    ///
2347    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2348    ///
2349    /// # Panics
2350    ///
2351    /// This function does not panic.
2352    ///
2353    pub fn noxtls_seal_tls12_record_packet(
2354        &mut self,
2355        plaintext: &[u8],
2356        content_type: RecordContentType,
2357    ) -> Result<Vec<u8>> {
2358        self.noxtls_ensure_tls12_wire_mode()?;
2359        if self.state != HandshakeState::Finished {
2360            return Err(Error::StateError(
2361                "cannot seal tls12 record packet before handshake completion",
2362            ));
2363        }
2364        if plaintext.len() > self.max_record_plaintext_len {
2365            return Err(Error::InvalidLength(
2366                "record plaintext exceeds configured limit",
2367            ));
2368        }
2369        if self.client_sequence == u64::MAX {
2370            return Err(Error::StateError("client record sequence exhausted"));
2371        }
2372        let sequence = self.client_sequence;
2373        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2374            "cipher suite must be selected before sealing tls12 record packet",
2375        ))?;
2376        if suite.noxtls_tls12_chacha20_poly1305_key_len().is_some() {
2377            let key = self
2378                .client_write_key
2379                .ok_or(Error::StateError("client write key is not installed"))?;
2380            let iv = self
2381                .client_write_iv
2382                .ok_or(Error::StateError("client write iv is not installed"))?;
2383            let nonce = Self::noxtls_build_tls12_chacha20_poly1305_nonce(&iv, sequence);
2384            let aad =
2385                self.noxtls_build_tls12_record_aad(sequence, content_type, plaintext.len())?;
2386            let (ciphertext, tag) =
2387                noxtls_chacha20_poly1305_encrypt(&key, &nonce, &aad, plaintext)?;
2388            let mut payload = Vec::with_capacity(ciphertext.len() + tag.len());
2389            payload.extend_from_slice(&ciphertext);
2390            payload.extend_from_slice(&tag);
2391            self.client_sequence = self.client_sequence.wrapping_add(1);
2392            return noxtls_encode_tls12_ciphertext_record(
2393                content_type.to_u8(),
2394                noxtls_legacy_wire_version(self.version),
2395                &payload,
2396            );
2397        }
2398        let key = self
2399            .client_write_key
2400            .ok_or(Error::StateError("client write key is not installed"))?;
2401        let iv = self
2402            .client_write_iv
2403            .ok_or(Error::StateError("client write iv is not installed"))?;
2404        let explicit_nonce = sequence.to_be_bytes();
2405        let nonce = Self::noxtls_build_tls12_aead_nonce(&iv, &explicit_nonce);
2406        let aad = self.noxtls_build_tls12_record_aad(sequence, content_type, plaintext.len())?;
2407        let (ciphertext, tag) =
2408            Self::noxtls_aead_encrypt_for_suite(suite, &key, &nonce, &aad, plaintext)?;
2409        let record = ProtectedRecord {
2410            sequence,
2411            ciphertext,
2412            tag,
2413        };
2414        self.client_sequence = self.client_sequence.wrapping_add(1);
2415        self.noxtls_encode_tls12_record_packet(&record, content_type, &explicit_nonce)
2416    }
2417
2418    /// Opens one inbound TLS 1.2 wire record packet using server traffic keys.
2419    ///
2420    /// # Arguments
2421    /// * `packet`: Serialized TLSCiphertext packet bytes.
2422    ///
2423    /// # Returns
2424    /// Tuple `(content_type, plaintext)` after successful authentication.
2425    /// # Errors
2426    ///
2427    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2428    ///
2429    /// # Panics
2430    ///
2431    /// This function does not panic.
2432    ///
2433    pub fn noxtls_open_tls12_record_packet(
2434        &mut self,
2435        packet: &[u8],
2436    ) -> Result<(RecordContentType, Vec<u8>)> {
2437        self.noxtls_ensure_tls12_wire_mode()?;
2438        if self.state != HandshakeState::Finished {
2439            return Err(Error::StateError(
2440                "cannot open tls12 record packet before handshake completion",
2441            ));
2442        }
2443        let sequence = self.server_sequence;
2444        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2445            "cipher suite must be selected before opening tls12 record packet",
2446        ))?;
2447        if suite.noxtls_tls12_chacha20_poly1305_key_len().is_some() {
2448            let (content_type_u8, version, payload) =
2449                noxtls_decode_tls12_ciphertext_record(packet)?;
2450            let strict_version = noxtls_legacy_wire_version(self.version);
2451            let legacy_compat_ok = self.tls12_allow_legacy_record_versions
2452                && (version == [0x03, 0x01] || version == [0x03, 0x02]);
2453            if version != strict_version && !legacy_compat_ok {
2454                return Err(Error::ParseFailure(
2455                    "tls12 record has invalid legacy version",
2456                ));
2457            }
2458            let content_type = RecordContentType::from_u8(content_type_u8)
2459                .ok_or(Error::ParseFailure("unknown tls12 record content type"))?;
2460            if payload.len() < 16 {
2461                return Err(Error::ParseFailure(
2462                    "tls12 chacha20-poly1305 record payload too short",
2463                ));
2464            }
2465            let tag_offset = payload.len() - 16;
2466            let ciphertext = &payload[..tag_offset];
2467            let mut tag = [0_u8; 16];
2468            tag.copy_from_slice(&payload[tag_offset..]);
2469            let key = self
2470                .server_write_key
2471                .ok_or(Error::StateError("server write key is not installed"))?;
2472            let iv = self
2473                .server_write_iv
2474                .ok_or(Error::StateError("server write iv is not installed"))?;
2475            let nonce = Self::noxtls_build_tls12_chacha20_poly1305_nonce(&iv, sequence);
2476            let aad =
2477                self.noxtls_build_tls12_record_aad(sequence, content_type, ciphertext.len())?;
2478            let plaintext = noxtls_chacha20_poly1305_decrypt(&key, &nonce, &aad, ciphertext, &tag)?;
2479            if plaintext.len() > self.max_record_plaintext_len {
2480                return Err(Error::InvalidLength(
2481                    "record plaintext exceeds configured limit",
2482                ));
2483            }
2484            self.server_sequence = self.server_sequence.wrapping_add(1);
2485            return Ok((content_type, plaintext));
2486        }
2487        let (record, content_type, explicit_nonce) =
2488            self.noxtls_decode_tls12_record_packet(packet, sequence)?;
2489        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2490            "cipher suite must be selected before opening tls12 record packet",
2491        ))?;
2492        let key = self
2493            .server_write_key
2494            .ok_or(Error::StateError("server write key is not installed"))?;
2495        let iv = self
2496            .server_write_iv
2497            .ok_or(Error::StateError("server write iv is not installed"))?;
2498        let nonce = Self::noxtls_build_tls12_aead_nonce(&iv, &explicit_nonce);
2499        let aad =
2500            self.noxtls_build_tls12_record_aad(sequence, content_type, record.ciphertext.len())?;
2501        let plaintext = Self::noxtls_aead_decrypt_for_suite(
2502            suite,
2503            &key,
2504            &nonce,
2505            &aad,
2506            &record.ciphertext,
2507            &record.tag,
2508        )?;
2509        if plaintext.len() > self.max_record_plaintext_len {
2510            return Err(Error::InvalidLength(
2511                "record plaintext exceeds configured limit",
2512            ));
2513        }
2514        self.server_sequence = self.server_sequence.wrapping_add(1);
2515        Ok((content_type, plaintext))
2516    }
2517
2518    /// Opens one locally-sealed TLS 1.2 wire packet using client traffic keys.
2519    ///
2520    /// # Arguments
2521    /// * `packet`: Serialized TLSCiphertext packet bytes.
2522    /// * `sequence`: Record sequence number used during sealing.
2523    ///
2524    /// # Returns
2525    /// Tuple `(content_type, plaintext)` after successful authentication.
2526    /// # Errors
2527    ///
2528    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2529    ///
2530    /// # Panics
2531    ///
2532    /// This function does not panic.
2533    ///
2534    pub fn noxtls_open_own_tls12_record_packet(
2535        &self,
2536        packet: &[u8],
2537        sequence: u64,
2538    ) -> Result<(RecordContentType, Vec<u8>)> {
2539        self.noxtls_ensure_tls12_wire_mode()?;
2540        let suite = self.noxtls_selected_cipher_suite.ok_or(Error::StateError(
2541            "cipher suite must be selected before opening own tls12 record packet",
2542        ))?;
2543        if suite.noxtls_tls12_chacha20_poly1305_key_len().is_some() {
2544            let (content_type_u8, version, payload) =
2545                noxtls_decode_tls12_ciphertext_record(packet)?;
2546            let strict_version = noxtls_legacy_wire_version(self.version);
2547            let legacy_compat_ok = self.tls12_allow_legacy_record_versions
2548                && (version == [0x03, 0x01] || version == [0x03, 0x02]);
2549            if version != strict_version && !legacy_compat_ok {
2550                return Err(Error::ParseFailure(
2551                    "tls12 record has invalid legacy version",
2552                ));
2553            }
2554            let content_type = RecordContentType::from_u8(content_type_u8)
2555                .ok_or(Error::ParseFailure("unknown tls12 record content type"))?;
2556            if payload.len() < 16 {
2557                return Err(Error::ParseFailure(
2558                    "tls12 chacha20-poly1305 record payload too short",
2559                ));
2560            }
2561            let tag_offset = payload.len() - 16;
2562            let ciphertext = &payload[..tag_offset];
2563            let mut tag = [0_u8; 16];
2564            tag.copy_from_slice(&payload[tag_offset..]);
2565            let key = self
2566                .client_write_key
2567                .ok_or(Error::StateError("client write key is not installed"))?;
2568            let iv = self
2569                .client_write_iv
2570                .ok_or(Error::StateError("client write iv is not installed"))?;
2571            let nonce = Self::noxtls_build_tls12_chacha20_poly1305_nonce(&iv, sequence);
2572            let aad =
2573                self.noxtls_build_tls12_record_aad(sequence, content_type, ciphertext.len())?;
2574            let plaintext = noxtls_chacha20_poly1305_decrypt(&key, &nonce, &aad, ciphertext, &tag)?;
2575            if plaintext.len() > self.max_record_plaintext_len {
2576                return Err(Error::InvalidLength(
2577                    "record plaintext exceeds configured limit",
2578                ));
2579            }
2580            return Ok((content_type, plaintext));
2581        }
2582        let (record, content_type, explicit_nonce) =
2583            self.noxtls_decode_tls12_record_packet(packet, sequence)?;
2584        let key = self
2585            .client_write_key
2586            .ok_or(Error::StateError("client write key is not installed"))?;
2587        let iv = self
2588            .client_write_iv
2589            .ok_or(Error::StateError("client write iv is not installed"))?;
2590        let nonce = Self::noxtls_build_tls12_aead_nonce(&iv, &explicit_nonce);
2591        let aad =
2592            self.noxtls_build_tls12_record_aad(sequence, content_type, record.ciphertext.len())?;
2593        let plaintext = Self::noxtls_aead_decrypt_for_suite(
2594            suite,
2595            &key,
2596            &nonce,
2597            &aad,
2598            &record.ciphertext,
2599            &record.tag,
2600        )?;
2601        if plaintext.len() > self.max_record_plaintext_len {
2602            return Err(Error::InvalidLength(
2603                "record plaintext exceeds configured limit",
2604            ));
2605        }
2606        Ok((content_type, plaintext))
2607    }
2608
2609    /// Seals a TLS 1.2 fatal/warning alert into an encrypted TLSCiphertext packet.
2610    ///
2611    /// # Arguments
2612    /// * `level`: TLS alert level byte semantic.
2613    /// * `description`: TLS alert description codepoint semantic.
2614    ///
2615    /// # Returns
2616    /// Serialized TLS 1.2 alert record packet bytes.
2617    /// # Errors
2618    ///
2619    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2620    ///
2621    /// # Panics
2622    ///
2623    /// This function does not panic.
2624    ///
2625    pub fn noxtls_send_tls12_alert_packet(
2626        &mut self,
2627        level: AlertLevel,
2628        description: AlertDescription,
2629    ) -> Result<Vec<u8>> {
2630        if self.version != TlsVersion::Tls12 {
2631            return Err(Error::StateError(
2632                "tls12 alert records require TLS 1.2 connection",
2633            ));
2634        }
2635        self.noxtls_seal_tls12_record_packet(
2636            &[level.to_u8(), description.to_u8()],
2637            RecordContentType::Alert,
2638        )
2639    }
2640
2641    /// Maps a TLS 1.2 handshake error and seals the corresponding fatal alert packet.
2642    ///
2643    /// # Arguments
2644    /// * `error`: Handshake processing error to map into alert semantics.
2645    ///
2646    /// # Returns
2647    /// Serialized TLS 1.2 alert record packet bytes.
2648    /// # Errors
2649    ///
2650    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2651    ///
2652    /// # Panics
2653    ///
2654    /// This function does not panic.
2655    ///
2656    pub fn noxtls_send_tls12_alert_for_handshake_error(
2657        &mut self,
2658        error: &Error,
2659    ) -> Result<Vec<u8>> {
2660        let (level, description) = Self::noxtls_tls12_alert_for_handshake_error(error);
2661        self.noxtls_send_tls12_alert_packet(level, description)
2662    }
2663
2664    /// Opens a peer TLS 1.2 alert packet and parses `(level, description)` semantics.
2665    ///
2666    /// # Arguments
2667    /// * `packet`: Serialized TLSCiphertext packet bytes carrying alert content.
2668    ///
2669    /// # Returns
2670    /// Parsed `(AlertLevel, AlertDescription)` tuple.
2671    /// # Errors
2672    ///
2673    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2674    ///
2675    /// # Panics
2676    ///
2677    /// This function does not panic.
2678    ///
2679    pub fn noxtls_recv_tls12_alert_packet(
2680        &mut self,
2681        packet: &[u8],
2682    ) -> Result<(AlertLevel, AlertDescription)> {
2683        let (content_type, payload) = self.noxtls_open_tls12_record_packet(packet)?;
2684        self.noxtls_parse_tls12_alert_payload(content_type, &payload)
2685    }
2686
2687    /// Opens a locally-sealed TLS 1.2 alert packet for deterministic loopback tests.
2688    ///
2689    /// # Arguments
2690    /// * `packet`: Serialized TLSCiphertext packet bytes from local alert sealing.
2691    /// * `sequence`: Sequence value used when the packet was sealed.
2692    ///
2693    /// # Returns
2694    /// Parsed `(AlertLevel, AlertDescription)` tuple.
2695    /// # Errors
2696    ///
2697    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2698    ///
2699    /// # Panics
2700    ///
2701    /// This function does not panic.
2702    ///
2703    pub fn noxtls_recv_own_tls12_alert_packet(
2704        &self,
2705        packet: &[u8],
2706        sequence: u64,
2707    ) -> Result<(AlertLevel, AlertDescription)> {
2708        let (content_type, payload) = self.noxtls_open_own_tls12_record_packet(packet, sequence)?;
2709        self.noxtls_parse_tls12_alert_payload(content_type, &payload)
2710    }
2711
2712    /// Seals plaintext into multiple records using caller-selected fragment size.
2713    ///
2714    /// # Arguments
2715    /// * `plaintext`: Full plaintext payload to fragment and seal.
2716    /// * `aad`: Additional authenticated data reused for each fragment.
2717    /// * `fragment_len`: Maximum plaintext bytes per sealed record fragment.
2718    ///
2719    /// # Returns
2720    /// Ordered protected-record fragments covering the full plaintext.
2721    /// # Errors
2722    ///
2723    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2724    ///
2725    /// # Panics
2726    ///
2727    /// This function does not panic.
2728    ///
2729    pub fn noxtls_seal_record_fragments(
2730        &mut self,
2731        plaintext: &[u8],
2732        aad: &[u8],
2733        fragment_len: usize,
2734    ) -> Result<Vec<ProtectedRecord>> {
2735        if fragment_len == 0 {
2736            return Err(Error::InvalidLength(
2737                "fragment length must be greater than zero",
2738            ));
2739        }
2740        if fragment_len > self.max_record_plaintext_len {
2741            return Err(Error::InvalidLength(
2742                "fragment length exceeds configured record plaintext limit",
2743            ));
2744        }
2745        if plaintext.is_empty() {
2746            return Ok(Vec::new());
2747        }
2748        let fragment_count = plaintext.len().div_ceil(fragment_len);
2749        let required_sequences = u64::try_from(fragment_count)
2750            .map_err(|_| Error::InvalidLength("too many record fragments requested"))?;
2751        let highest_sequence = self
2752            .client_sequence
2753            .checked_add(required_sequences.saturating_sub(1));
2754        if highest_sequence.is_none() {
2755            return Err(Error::StateError(
2756                "insufficient record sequence space for all fragments",
2757            ));
2758        }
2759
2760        let mut out = Vec::with_capacity(fragment_count);
2761        let mut offset = 0_usize;
2762        while offset < plaintext.len() {
2763            let end = (offset + fragment_len).min(plaintext.len());
2764            out.push(self.noxtls_seal_record(&plaintext[offset..end], aad)?);
2765            offset = end;
2766        }
2767        Ok(out)
2768    }
2769
2770    /// Opens and reassembles a sequence of protected record fragments.
2771    ///
2772    /// # Arguments
2773    /// * `records`: Ordered record fragments to decrypt and concatenate.
2774    /// * `aad`: Additional authenticated data reused for each fragment.
2775    ///
2776    /// # Returns
2777    /// Reassembled plaintext payload.
2778    /// # Errors
2779    ///
2780    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2781    ///
2782    /// # Panics
2783    ///
2784    /// This function does not panic.
2785    ///
2786    pub fn noxtls_open_record_fragments(
2787        &mut self,
2788        records: &[ProtectedRecord],
2789        aad: &[u8],
2790    ) -> Result<Vec<u8>> {
2791        if records.is_empty() {
2792            return Ok(Vec::new());
2793        }
2794        let base_sequence = self.server_sequence;
2795        for (index, record) in records.iter().enumerate() {
2796            let expected_sequence = base_sequence
2797                .checked_add(index as u64)
2798                .ok_or(Error::ParseFailure("record fragment sequence overflow"))?;
2799            if record.sequence != expected_sequence {
2800                return Err(Error::ParseFailure(
2801                    "record fragments must be contiguous sequences",
2802                ));
2803            }
2804        }
2805        let mut out = Vec::new();
2806        for record in records {
2807            out.extend_from_slice(&self.noxtls_open_record(record, aad)?);
2808        }
2809        Ok(out)
2810    }
2811
2812    /// Opens locally-sealed fragments with client keys and reassembles plaintext.
2813    ///
2814    /// # Arguments
2815    /// * `records`: Ordered local record fragments produced by `noxtls_seal_record_fragments`.
2816    /// * `aad`: Additional authenticated data reused for each fragment.
2817    ///
2818    /// # Returns
2819    /// Reassembled plaintext payload.
2820    /// # Errors
2821    ///
2822    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2823    ///
2824    /// # Panics
2825    ///
2826    /// This function does not panic.
2827    ///
2828    pub fn noxtls_open_own_record_fragments(
2829        &self,
2830        records: &[ProtectedRecord],
2831        aad: &[u8],
2832    ) -> Result<Vec<u8>> {
2833        if records.is_empty() {
2834            return Ok(Vec::new());
2835        }
2836        let base_sequence = records[0].sequence;
2837        for (index, record) in records.iter().enumerate() {
2838            let expected_sequence = base_sequence
2839                .checked_add(index as u64)
2840                .ok_or(Error::ParseFailure("record fragment sequence overflow"))?;
2841            if record.sequence != expected_sequence {
2842                return Err(Error::ParseFailure(
2843                    "record fragments must be contiguous sequences",
2844                ));
2845            }
2846        }
2847        let mut out = Vec::new();
2848        for record in records {
2849            out.extend_from_slice(&self.noxtls_open_own_record(record, aad)?);
2850        }
2851        Ok(out)
2852    }
2853
2854    /// Seals a TLS 1.3 record by encoding TLSInnerPlaintext with content type and padding.
2855    ///
2856    /// # Arguments
2857    /// * `content`: Inner plaintext content bytes.
2858    /// * `content_type`: TLS content type byte encoded at end of inner plaintext.
2859    /// * `aad`: Additional authenticated data for AEAD.
2860    /// * `padding_len`: Number of trailing zero padding bytes.
2861    ///
2862    /// # Returns
2863    /// Protected record carrying encrypted TLSInnerPlaintext.
2864    /// # Errors
2865    ///
2866    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2867    ///
2868    /// # Panics
2869    ///
2870    /// This function does not panic.
2871    ///
2872    pub fn noxtls_seal_tls13_inner_record(
2873        &mut self,
2874        content: &[u8],
2875        content_type: u8,
2876        aad: &[u8],
2877        padding_len: usize,
2878    ) -> Result<ProtectedRecord> {
2879        if !self.version.uses_tls13_handshake_semantics() {
2880            return Err(Error::StateError(
2881                "tls13 inner plaintext records require TLS 1.3 connection",
2882            ));
2883        }
2884        let inner = noxtls_encode_tls13_inner_plaintext(content, content_type, padding_len);
2885        self.noxtls_seal_record(&inner, aad)
2886    }
2887
2888    /// Opens a TLS 1.3 record and decodes TLSInnerPlaintext into content and content type.
2889    ///
2890    /// # Arguments
2891    /// * `record`: Protected record sealed with peer TLS 1.3 traffic keys.
2892    /// * `aad`: Additional authenticated data used during sealing.
2893    ///
2894    /// # Returns
2895    /// Tuple `(content, content_type)` extracted from decoded inner plaintext.
2896    /// # Errors
2897    ///
2898    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2899    ///
2900    /// # Panics
2901    ///
2902    /// This function does not panic.
2903    ///
2904    pub fn noxtls_open_tls13_inner_record(
2905        &mut self,
2906        record: &ProtectedRecord,
2907        aad: &[u8],
2908    ) -> Result<(Vec<u8>, u8)> {
2909        if !self.version.uses_tls13_handshake_semantics() {
2910            return Err(Error::StateError(
2911                "tls13 inner plaintext records require TLS 1.3 connection",
2912            ));
2913        }
2914        let inner = self.noxtls_open_record(record, aad)?;
2915        noxtls_decode_tls13_inner_plaintext(&inner)
2916    }
2917
2918    /// Opens a locally-sealed TLS 1.3 record and decodes TLSInnerPlaintext for tests.
2919    ///
2920    /// # Arguments
2921    /// * `record`: Protected record sealed via `noxtls_seal_tls13_inner_record`.
2922    /// * `aad`: Additional authenticated data used during sealing.
2923    ///
2924    /// # Returns
2925    /// Tuple `(content, content_type)` extracted from decoded inner plaintext.
2926    /// # Errors
2927    ///
2928    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2929    ///
2930    /// # Panics
2931    ///
2932    /// This function does not panic.
2933    ///
2934    pub fn noxtls_open_own_tls13_inner_record(
2935        &self,
2936        record: &ProtectedRecord,
2937        aad: &[u8],
2938    ) -> Result<(Vec<u8>, u8)> {
2939        if !self.version.uses_tls13_handshake_semantics() {
2940            return Err(Error::StateError(
2941                "tls13 inner plaintext records require TLS 1.3 connection",
2942            ));
2943        }
2944        let inner = self.noxtls_open_own_record(record, aad)?;
2945        noxtls_decode_tls13_inner_plaintext(&inner)
2946    }
2947
2948    /// Seals one TLS 1.3 wire record packet from TLSInnerPlaintext content.
2949    ///
2950    /// # Arguments
2951    /// * `content`: Inner plaintext content bytes.
2952    /// * `content_type`: Inner content type byte.
2953    /// * `aad`: Additional authenticated data for AEAD.
2954    /// * `padding_len`: Number of trailing zero padding bytes.
2955    ///
2956    /// # Returns
2957    /// Serialized TLSCiphertext packet bytes.
2958    /// # Errors
2959    ///
2960    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2961    ///
2962    /// # Panics
2963    ///
2964    /// This function does not panic.
2965    ///
2966    pub fn noxtls_seal_tls13_record_packet(
2967        &mut self,
2968        content: &[u8],
2969        content_type: u8,
2970        aad: &[u8],
2971        padding_len: usize,
2972    ) -> Result<Vec<u8>> {
2973        if !self.version.uses_tls13_handshake_semantics() {
2974            return Err(Error::StateError(
2975                "tls13 record packets require TLS 1.3 connection",
2976            ));
2977        }
2978        let record =
2979            self.noxtls_seal_tls13_inner_record(content, content_type, aad, padding_len)?;
2980        self.noxtls_encode_tls13_record_packet(&record)
2981    }
2982
2983    /// Opens one inbound TLS 1.3 wire record packet and decodes TLSInnerPlaintext.
2984    ///
2985    /// # Arguments
2986    /// * `packet`: Serialized TLSCiphertext packet bytes.
2987    /// * `aad`: Additional authenticated data used during sealing.
2988    ///
2989    /// # Returns
2990    /// Tuple `(content, content_type)` decoded from inner plaintext.
2991    /// # Errors
2992    ///
2993    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
2994    ///
2995    /// # Panics
2996    ///
2997    /// This function does not panic.
2998    ///
2999    pub fn noxtls_open_tls13_record_packet(
3000        &mut self,
3001        packet: &[u8],
3002        aad: &[u8],
3003    ) -> Result<(Vec<u8>, u8)> {
3004        if !self.version.uses_tls13_handshake_semantics() {
3005            return Err(Error::StateError(
3006                "tls13 record packets require TLS 1.3 connection",
3007            ));
3008        }
3009        let record = self.noxtls_decode_tls13_record_packet(packet, self.server_sequence)?;
3010        match self.noxtls_open_tls13_inner_record(&record, aad) {
3011            Ok(inner) => Ok(inner),
3012            Err(error) => {
3013                noxtls_tls13_debug_log("tls13.open_record.error", "failed to decrypt record");
3014                noxtls_tls13_debug_log_bytes("tls13.open_record.aad", aad);
3015                noxtls_tls13_debug_log_bytes("tls13.open_record.ciphertext", &record.ciphertext);
3016                noxtls_tls13_debug_log_bytes("tls13.open_record.tag", &record.tag);
3017                if let Some(key) = self.server_write_key.as_ref() {
3018                    noxtls_tls13_debug_log_bytes("tls13.open_record.server_write_key", key);
3019                }
3020                if let Some(iv) = self.server_write_iv.as_ref() {
3021                    noxtls_tls13_debug_log_bytes("tls13.open_record.server_write_iv", iv);
3022                }
3023                self.noxtls_debug_probe_tls13_open_record_failure(&record, aad);
3024                Err(error)
3025            }
3026        }
3027    }
3028
3029    /// Opens one locally-sealed TLS 1.3 wire packet using client traffic keys.
3030    ///
3031    /// # Arguments
3032    /// * `packet`: Serialized TLSCiphertext packet bytes.
3033    /// * `sequence`: Record sequence number used during sealing.
3034    /// * `aad`: Additional authenticated data used during sealing.
3035    ///
3036    /// # Returns
3037    /// Tuple `(content, content_type)` decoded from inner plaintext.
3038    /// # Errors
3039    ///
3040    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3041    ///
3042    /// # Panics
3043    ///
3044    /// This function does not panic.
3045    ///
3046    pub fn noxtls_open_own_tls13_record_packet(
3047        &self,
3048        packet: &[u8],
3049        sequence: u64,
3050        aad: &[u8],
3051    ) -> Result<(Vec<u8>, u8)> {
3052        if !self.version.uses_tls13_handshake_semantics() {
3053            return Err(Error::StateError(
3054                "tls13 record packets require TLS 1.3 connection",
3055            ));
3056        }
3057        let record = self.noxtls_decode_tls13_record_packet(packet, sequence)?;
3058        self.noxtls_open_own_tls13_inner_record(&record, aad)
3059    }
3060
3061    /// Seals a TLS 1.3 alert as TLSInnerPlaintext with alert content type.
3062    ///
3063    /// # Arguments
3064    /// * `level`: Alert severity level.
3065    /// * `description`: Alert description codepoint.
3066    /// * `aad`: Additional authenticated data used for AEAD.
3067    ///
3068    /// # Returns
3069    /// Protected record containing encoded alert bytes.
3070    /// # Errors
3071    ///
3072    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3073    ///
3074    /// # Panics
3075    ///
3076    /// This function does not panic.
3077    ///
3078    pub fn noxtls_send_tls13_alert(
3079        &mut self,
3080        level: AlertLevel,
3081        description: AlertDescription,
3082        aad: &[u8],
3083    ) -> Result<ProtectedRecord> {
3084        if !self.version.uses_tls13_handshake_semantics() {
3085            return Err(Error::StateError(
3086                "tls13 alert records require TLS 1.3 connection",
3087            ));
3088        }
3089        let payload = [level.to_u8(), description.to_u8()];
3090        let record = self.noxtls_seal_tls13_inner_record(
3091            &payload,
3092            RecordContentType::Alert.to_u8(),
3093            aad,
3094            0,
3095        )?;
3096        self.noxtls_apply_tls13_alert_effects(level, description, true);
3097        Ok(record)
3098    }
3099
3100    /// Seals a TLS 1.3 alert and encodes it into TLSCiphertext packet wire format.
3101    ///
3102    /// # Arguments
3103    /// * `level`: Alert severity level.
3104    /// * `description`: Alert description codepoint.
3105    /// * `aad`: Additional authenticated data used for AEAD.
3106    ///
3107    /// # Returns
3108    /// Serialized TLSCiphertext packet bytes carrying an alert inner record.
3109    /// # Errors
3110    ///
3111    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3112    ///
3113    /// # Panics
3114    ///
3115    /// This function does not panic.
3116    ///
3117    pub fn noxtls_send_tls13_alert_packet(
3118        &mut self,
3119        level: AlertLevel,
3120        description: AlertDescription,
3121        aad: &[u8],
3122    ) -> Result<Vec<u8>> {
3123        if !self.version.uses_tls13_handshake_semantics() {
3124            return Err(Error::StateError(
3125                "tls13 alert records require TLS 1.3 connection",
3126            ));
3127        }
3128        let record = self.noxtls_send_tls13_alert(level, description, aad)?;
3129        self.noxtls_encode_tls13_record_packet(&record)
3130    }
3131
3132    /// Opens and parses a peer TLS 1.3 alert record.
3133    ///
3134    /// # Arguments
3135    /// * `record`: Protected record carrying peer alert payload.
3136    /// * `aad`: Additional authenticated data used during sealing.
3137    ///
3138    /// # Returns
3139    /// Parsed `(AlertLevel, AlertDescription)` tuple.
3140    /// # Errors
3141    ///
3142    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3143    ///
3144    /// # Panics
3145    ///
3146    /// This function does not panic.
3147    ///
3148    pub fn noxtls_recv_tls13_alert(
3149        &mut self,
3150        record: &ProtectedRecord,
3151        aad: &[u8],
3152    ) -> Result<(AlertLevel, AlertDescription)> {
3153        if !self.version.uses_tls13_handshake_semantics() {
3154            return Err(Error::StateError(
3155                "tls13 alert records require TLS 1.3 connection",
3156            ));
3157        }
3158        let (payload, content_type) = self.noxtls_open_tls13_inner_record(record, aad)?;
3159        self.noxtls_process_parsed_tls13_alert(payload, content_type)
3160    }
3161
3162    /// Opens and parses a locally-sealed TLS 1.3 alert record for loopback tests.
3163    ///
3164    /// # Arguments
3165    /// * `record`: Protected record sealed via `noxtls_send_tls13_alert`.
3166    /// * `aad`: Additional authenticated data used during sealing.
3167    ///
3168    /// # Returns
3169    /// Parsed `(AlertLevel, AlertDescription)` tuple.
3170    /// # Errors
3171    ///
3172    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3173    ///
3174    /// # Panics
3175    ///
3176    /// This function does not panic.
3177    ///
3178    pub fn noxtls_recv_own_tls13_alert(
3179        &mut self,
3180        record: &ProtectedRecord,
3181        aad: &[u8],
3182    ) -> Result<(AlertLevel, AlertDescription)> {
3183        if !self.version.uses_tls13_handshake_semantics() {
3184            return Err(Error::StateError(
3185                "tls13 alert records require TLS 1.3 connection",
3186            ));
3187        }
3188        let (payload, content_type) = self.noxtls_open_own_tls13_inner_record(record, aad)?;
3189        self.noxtls_process_parsed_tls13_alert(payload, content_type)
3190    }
3191
3192    /// Opens and parses a peer TLS 1.3 alert TLSCiphertext packet.
3193    ///
3194    /// # Arguments
3195    /// * `packet`: Serialized TLSCiphertext packet bytes carrying an alert inner record.
3196    /// * `aad`: Additional authenticated data used during sealing.
3197    ///
3198    /// # Returns
3199    /// Parsed `(AlertLevel, AlertDescription)` tuple.
3200    /// # Errors
3201    ///
3202    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3203    ///
3204    /// # Panics
3205    ///
3206    /// This function does not panic.
3207    ///
3208    pub fn noxtls_recv_tls13_alert_packet(
3209        &mut self,
3210        packet: &[u8],
3211        aad: &[u8],
3212    ) -> Result<(AlertLevel, AlertDescription)> {
3213        if !self.version.uses_tls13_handshake_semantics() {
3214            return Err(Error::StateError(
3215                "tls13 alert records require TLS 1.3 connection",
3216            ));
3217        }
3218        let (payload, content_type) = self.noxtls_open_tls13_record_packet(packet, aad)?;
3219        self.noxtls_process_parsed_tls13_alert(payload, content_type)
3220    }
3221
3222    /// Opens and parses a locally-sealed TLS 1.3 alert TLSCiphertext packet for loopback tests.
3223    ///
3224    /// # Arguments
3225    /// * `packet`: Serialized TLSCiphertext packet bytes from local alert sealing.
3226    /// * `sequence`: Record sequence number used during sealing.
3227    /// * `aad`: Additional authenticated data used during sealing.
3228    ///
3229    /// # Returns
3230    /// Parsed `(AlertLevel, AlertDescription)` tuple.
3231    /// # Errors
3232    ///
3233    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3234    ///
3235    /// # Panics
3236    ///
3237    /// This function does not panic.
3238    ///
3239    pub fn noxtls_recv_own_tls13_alert_packet(
3240        &mut self,
3241        packet: &[u8],
3242        sequence: u64,
3243        aad: &[u8],
3244    ) -> Result<(AlertLevel, AlertDescription)> {
3245        if !self.version.uses_tls13_handshake_semantics() {
3246            return Err(Error::StateError(
3247                "tls13 alert records require TLS 1.3 connection",
3248            ));
3249        }
3250        let (payload, content_type) =
3251            self.noxtls_open_own_tls13_record_packet(packet, sequence, aad)?;
3252        self.noxtls_process_parsed_tls13_alert(payload, content_type)
3253    }
3254
3255    /// Applies decoded alert payload semantics to connection state.
3256    ///
3257    /// # Arguments
3258    ///
3259    /// * `self` — `&mut self`.
3260    /// * `payload` — `payload: Vec<u8>`.
3261    /// * `content_type` — `content_type: u8`.
3262    ///
3263    /// # Returns
3264    ///
3265    /// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3266    ///
3267    /// # Errors
3268    ///
3269    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3270    ///
3271    /// # Panics
3272    ///
3273    /// This function does not panic.
3274    ///
3275    fn noxtls_process_parsed_tls13_alert(
3276        &mut self,
3277        payload: Vec<u8>,
3278        content_type: u8,
3279    ) -> Result<(AlertLevel, AlertDescription)> {
3280        if RecordContentType::from_u8(content_type) != Some(RecordContentType::Alert) {
3281            return Err(Error::ParseFailure("record is not an alert content type"));
3282        }
3283        if payload.len() != 2 {
3284            return Err(Error::ParseFailure("tls13 alert payload must be two bytes"));
3285        }
3286        let level =
3287            AlertLevel::from_u8(payload[0]).ok_or(Error::ParseFailure("unknown alert level"))?;
3288        let description = AlertDescription::from_u8(payload[1])
3289            .ok_or(Error::ParseFailure("unknown alert description"))?;
3290        self.noxtls_apply_tls13_alert_effects(level, description, false);
3291        Ok((level, description))
3292    }
3293
3294    /// Applies modeled TLS 1.3 alert mapping effects for local-send and peer-receive paths.
3295    ///
3296    /// # Arguments
3297    ///
3298    /// * `self` — `&mut self`.
3299    /// * `level` — `level: AlertLevel`.
3300    /// * `description` — `description: AlertDescription`.
3301    /// * `from_local_send` — `from_local_send: bool`.
3302    ///
3303    /// # Panics
3304    ///
3305    /// This function does not panic.
3306    ///
3307    fn noxtls_apply_tls13_alert_effects(
3308        &mut self,
3309        level: AlertLevel,
3310        description: AlertDescription,
3311        from_local_send: bool,
3312    ) {
3313        if description == AlertDescription::CloseNotify {
3314            if from_local_send {
3315                self.noxtls_tls13_local_close_notify_sent = true;
3316            } else {
3317                self.noxtls_tls13_peer_close_notify_received = true;
3318            }
3319        }
3320        if level == AlertLevel::Fatal {
3321            self.state = HandshakeState::Idle;
3322        }
3323    }
3324
3325    /// Reports whether a peer close_notify alert has been processed.
3326    #[must_use]
3327    /// # Arguments
3328    ///
3329    /// * `&self` — `&self`.
3330    ///
3331    /// # Returns
3332    ///
3333    /// `true` or `false` according to the checks in the function body.
3334    ///
3335    /// # Panics
3336    ///
3337    /// This function does not panic.
3338    ///
3339    pub fn noxtls_tls13_peer_close_notify_received(&self) -> bool {
3340        self.noxtls_tls13_peer_close_notify_received
3341    }
3342
3343    /// Reports whether this endpoint has sent a close_notify alert.
3344    #[must_use]
3345    /// # Arguments
3346    ///
3347    /// * `&self` — `&self`.
3348    ///
3349    /// # Returns
3350    ///
3351    /// `true` or `false` according to the checks in the function body.
3352    ///
3353    /// # Panics
3354    ///
3355    /// This function does not panic.
3356    ///
3357    pub fn noxtls_tls13_local_close_notify_sent(&self) -> bool {
3358        self.noxtls_tls13_local_close_notify_sent
3359    }
3360
3361    /// Resets per-handshake certificate-auth tracking before a noxtls_new ClientHello.
3362    ///
3363    /// # Arguments
3364    ///
3365    /// * `self` — `&mut self`.
3366    ///
3367    /// # Panics
3368    ///
3369    /// This function does not panic.
3370    ///
3371    fn noxtls_reset_tls13_certificate_auth_state(&mut self) {
3372        self.tls13_server_leaf_public_key_der = None;
3373        self.tls13_server_certificate_chain_validated = false;
3374        self.tls13_negotiated_server_certificate_type = TLS_CERTIFICATE_TYPE_X509;
3375        self.noxtls_tls13_server_name_acknowledged = false;
3376        self.noxtls_tls13_selected_alpn_protocol = None;
3377        self.noxtls_tls13_server_ocsp_staple = None;
3378        self.noxtls_tls13_server_ocsp_staple_verified = false;
3379    }
3380
3381    /// Validates server certificate chain and caches leaf SPKI for CertificateVerify.
3382    ///
3383    /// # Arguments
3384    ///
3385    /// * `self` — `&mut self`.
3386    /// * `certificates` — `certificates: &[Vec<u8>]`.
3387    ///
3388    /// # Returns
3389    ///
3390    /// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3391    ///
3392    /// # Errors
3393    ///
3394    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3395    ///
3396    /// # Panics
3397    ///
3398    /// This function does not panic.
3399    ///
3400    fn noxtls_validate_tls13_server_raw_public_key(&mut self, spki_der: &[u8]) -> Result<()> {
3401        let expected = self
3402            .tls13_expected_server_raw_public_key_der
3403            .as_deref()
3404            .ok_or(Error::StateError(
3405                "expected tls13 server raw public key is not configured",
3406            ))?;
3407        if expected != spki_der {
3408            return Err(Error::CryptoFailure(
3409                "server raw public key validation failed",
3410            ));
3411        }
3412        let spki = noxtls_parse_spki_public_key_info_der(spki_der)?;
3413        self.tls13_server_leaf_public_key_der = Some(spki.subject_public_key);
3414        self.tls13_server_certificate_chain_validated = true;
3415        Ok(())
3416    }
3417
3418    fn noxtls_validate_tls13_server_certificate_chain(
3419        &mut self,
3420        certificates: &[Vec<u8>],
3421    ) -> Result<()> {
3422        if certificates.is_empty() {
3423            return Err(Error::ParseFailure(
3424                "certificate list must include leaf certificate",
3425            ));
3426        }
3427        if self.tls13_server_trust_anchors_der.is_empty() {
3428            return Err(Error::StateError(
3429                "tls13 server trust anchors are not configured",
3430            ));
3431        }
3432        let validation_time =
3433            self.tls13_server_validation_time
3434                .as_deref()
3435                .ok_or(Error::StateError(
3436                    "tls13 server validation time is not configured",
3437                ))?;
3438        let leaf = noxtls_parse_certificate(&certificates[0])?;
3439        if let Some(expected_hostname) = self.tls13_server_expected_hostname.as_deref() {
3440            if !noxtls_certificate_matches_hostname(&leaf, expected_hostname) {
3441                return Err(Error::CryptoFailure(
3442                    "server certificate hostname validation failed",
3443                ));
3444            }
3445        }
3446
3447        let mut parsed_intermediates = Vec::new();
3448        for der in &certificates[1..] {
3449            let parsed = noxtls_parse_certificate(der)?;
3450            parsed_intermediates.push(parsed);
3451        }
3452        for der in &self.tls13_server_intermediates_der {
3453            let parsed = noxtls_parse_certificate(der)?;
3454            parsed_intermediates.push(parsed);
3455        }
3456
3457        let mut parsed_anchors = Vec::new();
3458        for der in &self.tls13_server_trust_anchors_der {
3459            let parsed = noxtls_parse_certificate(der)?;
3460            parsed_anchors.push(parsed);
3461        }
3462
3463        noxtls_validate_certificate_chain(
3464            &leaf,
3465            &parsed_intermediates,
3466            &parsed_anchors,
3467            validation_time,
3468        )
3469        .map_err(noxtls_map_certificate_validation_error)?;
3470        self.tls13_server_leaf_public_key_der = Some(leaf.subject_public_key.clone());
3471        self.tls13_server_certificate_chain_validated = true;
3472        Ok(())
3473    }
3474
3475    /// Verifies TLS 1.3 CertificateVerify signature over transcript-based context bytes.
3476    ///
3477    /// # Arguments
3478    ///
3479    /// * `&self` — `&self`.
3480    /// * `signature_scheme` — `signature_scheme: u16`.
3481    /// * `signature` — `signature: &[u8]`.
3482    ///
3483    /// # Returns
3484    ///
3485    /// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3486    ///
3487    /// # Errors
3488    ///
3489    /// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3490    ///
3491    /// # Panics
3492    ///
3493    /// This function does not panic.
3494    ///
3495    fn noxtls_verify_tls13_server_certificate_verify_signature(
3496        &self,
3497        signature_scheme: u16,
3498        signature: &[u8],
3499    ) -> Result<()> {
3500        let leaf_spki =
3501            self.tls13_server_leaf_public_key_der
3502                .as_deref()
3503                .ok_or(Error::StateError(
3504                    "server leaf public key is unavailable for certificate verify",
3505                ))?;
3506        let signed_message =
3507            noxtls_build_tls13_server_certificate_verify_message(&self.noxtls_transcript_hash());
3508        match signature_scheme {
3509            TLS13_SIGALG_ECDSA_SECP256R1_SHA256 => {
3510                let public_key = P256PublicKey::from_uncompressed(leaf_spki)?;
3511                let (r, s) = if signature.len() == 64 {
3512                    let mut r = [0_u8; 32];
3513                    let mut s = [0_u8; 32];
3514                    r.copy_from_slice(&signature[..32]);
3515                    s.copy_from_slice(&signature[32..]);
3516                    (r, s)
3517                } else {
3518                    noxtls_parse_ecdsa_signature_der(signature)?
3519                };
3520                noxtls_p256_ecdsa_verify_sha256(&public_key, &signed_message, &r, &s).map_err(
3521                    |_| {
3522                        Error::CryptoFailure("tls13 certificate verify signature validation failed")
3523                    },
3524                )
3525            }
3526            TLS13_SIGALG_ECDSA_SECP384R1_SHA384 => {
3527                let public_key = P384PublicKey::from_uncompressed(leaf_spki)
3528                    .map_err(|_| Error::ParseFailure("failed to parse p384 server public key"))?;
3529                let (r, s) = noxtls_parse_ecdsa_p384_signature_der(signature)
3530                    .map_err(|_| Error::ParseFailure("failed to parse p384 ecdsa signature"))?;
3531                noxtls_p384_ecdsa_verify_sha384(&public_key, &signed_message, &r, &s).map_err(
3532                    |_| {
3533                        Error::CryptoFailure("tls13 certificate verify signature validation failed")
3534                    },
3535                )
3536            }
3537            TLS13_SIGALG_RSA_PSS_RSAE_SHA256 | TLS13_SIGALG_RSA_PSS_PSS_SHA256 => {
3538                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
3539                noxtls_rsassa_pss_sha256_verify(&public_key, &signed_message, signature, 32)
3540                    .map_err(|_| {
3541                        Error::CryptoFailure("tls13 certificate verify signature validation failed")
3542                    })
3543            }
3544            TLS13_SIGALG_RSA_PSS_RSAE_SHA384 | TLS13_SIGALG_RSA_PSS_PSS_SHA384 => {
3545                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
3546                noxtls_rsassa_pss_sha384_verify(&public_key, &signed_message, signature, 48)
3547                    .map_err(|_| {
3548                        Error::CryptoFailure("tls13 certificate verify signature validation failed")
3549                    })
3550            }
3551            TLS13_SIGALG_RSA_PSS_RSAE_SHA512 | TLS13_SIGALG_RSA_PSS_PSS_SHA512 => {
3552                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
3553                noxtls_rsassa_pss_verify(
3554                    &public_key,
3555                    &signed_message,
3556                    signature,
3557                    RsaPssHashAlgorithm::Sha512,
3558                    RsaPssHashAlgorithm::Sha512,
3559                    64,
3560                )
3561                .map_err(|_| {
3562                    Error::CryptoFailure("tls13 certificate verify signature validation failed")
3563                })
3564            }
3565            TLS13_SIGALG_ED25519 => {
3566                let public_key = noxtls_ed25519_public_key_from_subject_public_key_info(leaf_spki)?;
3567                noxtls_ed25519_verify(&public_key, &signed_message, signature).map_err(|_| {
3568                    Error::CryptoFailure("tls13 certificate verify signature validation failed")
3569                })
3570            }
3571            TLS13_SIGALG_MLDSA65 => {
3572                let public_key = MlDsaPublicKey::from_bytes(leaf_spki).map_err(|_| {
3573                    Error::ParseFailure("failed to parse mldsa server public key bytes")
3574                })?;
3575                noxtls_mldsa_verify(&public_key, &signed_message, signature).map_err(|_| {
3576                    Error::CryptoFailure("tls13 certificate verify signature validation failed")
3577                })
3578            }
3579            _ => Err(Error::UnsupportedFeature(
3580                "unsupported tls13 certificate verify signature scheme",
3581            )),
3582        }
3583    }
3584
3585    /// Overrides record sequence counters for external validation harness scenarios.
3586    /// # Arguments
3587    ///
3588    /// * `self` — `&mut self`.
3589    /// * `client_sequence` — `client_sequence: u64`.
3590    /// * `server_sequence` — `server_sequence: u64`.
3591    ///
3592    /// # Panics
3593    ///
3594    /// This function does not panic.
3595    ///
3596    pub fn noxtls_set_record_sequences_for_test(
3597        &mut self,
3598        client_sequence: u64,
3599        server_sequence: u64,
3600    ) {
3601        self.client_sequence = client_sequence;
3602        self.server_sequence = server_sequence;
3603    }
3604
3605    /// Installs CertificateVerify public-key material for validation harness testing flows.
3606    /// # Arguments
3607    ///
3608    /// * `self` — `&mut self`.
3609    /// * `leaf_spki_der` — `leaf_spki_der: Vec<u8>`.
3610    ///
3611    /// # Panics
3612    ///
3613    /// This function does not panic.
3614    ///
3615    pub fn noxtls_set_tls13_certificate_verify_material_for_test(
3616        &mut self,
3617        leaf_spki_der: Vec<u8>,
3618    ) {
3619        self.tls13_server_leaf_public_key_der = Some(leaf_spki_der);
3620        self.tls13_server_certificate_chain_validated = true;
3621    }
3622
3623    /// Builds TLS 1.3 server CertificateVerify transcript message bytes for external signing tests.
3624    /// # Arguments
3625    ///
3626    /// * `&self` — `&self`.
3627    ///
3628    /// # Returns
3629    ///
3630    /// The value described by the return type in the function signature.
3631    ///
3632    /// # Panics
3633    ///
3634    /// This function does not panic.
3635    ///
3636    pub fn noxtls_tls13_server_certificate_verify_message_for_test(&self) -> Vec<u8> {
3637        noxtls_build_tls13_server_certificate_verify_message(&self.noxtls_transcript_hash())
3638    }
3639
3640    /// Probes alternate decrypt hypotheses for TLS 1.3 record-open failures when debug is enabled.
3641    ///
3642    /// # Arguments
3643    ///
3644    /// * `record` — Decoded protected TLSCiphertext components.
3645    /// * `aad` — Additional authenticated data used for AEAD authentication.
3646    ///
3647    /// # Returns
3648    ///
3649    /// `()` after emitting optional debug hints.
3650    ///
3651    /// # Panics
3652    ///
3653    /// This function does not panic.
3654    fn noxtls_debug_probe_tls13_open_record_failure(&self, record: &ProtectedRecord, aad: &[u8]) {
3655        if !noxtls_tls13_debug_enabled() {
3656            return;
3657        }
3658        let Some(suite) = self.noxtls_selected_cipher_suite else {
3659            return;
3660        };
3661        let Some(key_len) = suite.noxtls_tls13_traffic_key_len() else {
3662            return;
3663        };
3664        let probe_key = |label: &str, key: &[u8; 32], iv: &[u8; 12], seq: u64| {
3665            let nonce = noxtls_build_record_nonce(iv, seq);
3666            let status = match AesCipher::noxtls_new(&key[..key_len]) {
3667                Ok(cipher) => {
3668                    noxtls_aes_gcm_decrypt(&cipher, &nonce, aad, &record.ciphertext, &record.tag)
3669                        .is_ok()
3670                }
3671                Err(_) => false,
3672            };
3673            if status {
3674                noxtls_tls13_debug_log(label, "success");
3675            } else {
3676                noxtls_tls13_debug_log(label, "fail");
3677            }
3678        };
3679        if let (Some(key), Some(iv)) = (
3680            self.server_write_key.as_ref(),
3681            self.server_write_iv.as_ref(),
3682        ) {
3683            probe_key(
3684                "tls13.open_record.probe.server_seq+1",
3685                key,
3686                iv,
3687                record.sequence.saturating_add(1),
3688            );
3689            probe_key(
3690                "tls13.open_record.probe.server_seq+2",
3691                key,
3692                iv,
3693                record.sequence.saturating_add(2),
3694            );
3695            let mut nonce_first8_be = *iv;
3696            for (idx, byte) in record.sequence.to_be_bytes().iter().enumerate() {
3697                nonce_first8_be[idx] ^= *byte;
3698            }
3699            let first8_be_ok = match AesCipher::noxtls_new(&key[..key_len]) {
3700                Ok(cipher) => noxtls_aes_gcm_decrypt(
3701                    &cipher,
3702                    &nonce_first8_be,
3703                    aad,
3704                    &record.ciphertext,
3705                    &record.tag,
3706                )
3707                .is_ok(),
3708                Err(_) => false,
3709            };
3710            noxtls_tls13_debug_log(
3711                "tls13.open_record.probe.server_nonce_first8_be",
3712                if first8_be_ok { "success" } else { "fail" },
3713            );
3714            let mut nonce_last8_le = *iv;
3715            for (idx, byte) in record.sequence.to_le_bytes().iter().enumerate() {
3716                nonce_last8_le[4 + idx] ^= *byte;
3717            }
3718            let last8_le_ok = match AesCipher::noxtls_new(&key[..key_len]) {
3719                Ok(cipher) => noxtls_aes_gcm_decrypt(
3720                    &cipher,
3721                    &nonce_last8_le,
3722                    aad,
3723                    &record.ciphertext,
3724                    &record.tag,
3725                )
3726                .is_ok(),
3727                Err(_) => false,
3728            };
3729            noxtls_tls13_debug_log(
3730                "tls13.open_record.probe.server_nonce_last8_le",
3731                if last8_le_ok { "success" } else { "fail" },
3732            );
3733            let mut nonce_first8_le = *iv;
3734            for (idx, byte) in record.sequence.to_le_bytes().iter().enumerate() {
3735                nonce_first8_le[idx] ^= *byte;
3736            }
3737            let first8_le_ok = match AesCipher::noxtls_new(&key[..key_len]) {
3738                Ok(cipher) => noxtls_aes_gcm_decrypt(
3739                    &cipher,
3740                    &nonce_first8_le,
3741                    aad,
3742                    &record.ciphertext,
3743                    &record.tag,
3744                )
3745                .is_ok(),
3746                Err(_) => false,
3747            };
3748            noxtls_tls13_debug_log(
3749                "tls13.open_record.probe.server_nonce_first8_le",
3750                if first8_le_ok { "success" } else { "fail" },
3751            );
3752        }
3753        if let (Some(key), Some(iv)) = (
3754            self.client_write_key.as_ref(),
3755            self.client_write_iv.as_ref(),
3756        ) {
3757            probe_key(
3758                "tls13.open_record.probe.client_seq+0",
3759                key,
3760                iv,
3761                record.sequence,
3762            );
3763        }
3764    }
3765}
3766
3767/// Extracts first PSK binder value from encoded ClientHello pre_shared_key extension.
3768///
3769/// # Arguments
3770///
3771/// * `client_hello` — `client_hello: &[u8]`.
3772///
3773/// # Returns
3774///
3775/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3776///
3777/// # Errors
3778///
3779/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3780///
3781/// # Panics
3782///
3783/// This function does not panic.
3784///
3785fn noxtls_extract_first_psk_binder_from_client_hello(client_hello: &[u8]) -> Result<Vec<u8>> {
3786    let info = noxtls_parse_client_hello_info(client_hello)?;
3787    info.extensions
3788        .psk_binders
3789        .first()
3790        .cloned()
3791        .ok_or(Error::ParseFailure(
3792            "client hello missing pre_shared_key binder",
3793        ))
3794}
3795
3796/// Returns ClientHello copy with all pre_shared_key binder bytes replaced with zeros.
3797///
3798/// # Arguments
3799///
3800/// * `client_hello` — `client_hello: &[u8]`.
3801///
3802/// # Returns
3803///
3804/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3805///
3806/// # Errors
3807///
3808/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3809///
3810/// # Panics
3811///
3812/// This function does not panic.
3813///
3814fn noxtls_zero_client_hello_psk_binders(client_hello: &[u8]) -> Result<Vec<u8>> {
3815    let (handshake_type, body) = noxtls_parse_handshake_message(client_hello)?;
3816    if handshake_type != HANDSHAKE_CLIENT_HELLO {
3817        return Err(Error::ParseFailure("invalid client hello type"));
3818    }
3819    if body.len() < 39 {
3820        return Err(Error::ParseFailure("client hello body too short"));
3821    }
3822    let mut out = client_hello.to_vec();
3823    let session_id_len = body[34] as usize;
3824    let suites_len_offset = 35 + session_id_len;
3825    if body.len() < suites_len_offset + 2 {
3826        return Err(Error::ParseFailure(
3827            "client hello missing cipher suites length",
3828        ));
3829    }
3830    let suites_len =
3831        u16::from_be_bytes([body[suites_len_offset], body[suites_len_offset + 1]]) as usize;
3832    let suites_end = suites_len_offset + 2 + suites_len;
3833    if body.len() < suites_end + 3 {
3834        return Err(Error::ParseFailure(
3835            "client hello missing compression methods",
3836        ));
3837    }
3838    let compression_methods_len = body[suites_end] as usize;
3839    let compression_methods_end = suites_end + 1 + compression_methods_len;
3840    if body.len() < compression_methods_end + 2 {
3841        return Err(Error::ParseFailure("client hello missing extension length"));
3842    }
3843    let extensions_len = u16::from_be_bytes([
3844        body[compression_methods_end],
3845        body[compression_methods_end + 1],
3846    ]) as usize;
3847    let extensions_start_in_body = compression_methods_end + 2;
3848    let extensions_end_in_body = extensions_start_in_body + extensions_len;
3849    if body.len() < extensions_end_in_body {
3850        return Err(Error::ParseFailure("client hello extensions truncated"));
3851    }
3852
3853    let body_offset = 4; // handshake header bytes in full message
3854    let mut ext_cursor = extensions_start_in_body;
3855    while ext_cursor < extensions_end_in_body {
3856        if extensions_end_in_body - ext_cursor < 4 {
3857            return Err(Error::ParseFailure(
3858                "client hello extension header truncated",
3859            ));
3860        }
3861        let ext_type = u16::from_be_bytes([body[ext_cursor], body[ext_cursor + 1]]);
3862        let ext_len = u16::from_be_bytes([body[ext_cursor + 2], body[ext_cursor + 3]]) as usize;
3863        let ext_data_start = ext_cursor + 4;
3864        let ext_data_end = ext_data_start + ext_len;
3865        if ext_data_end > extensions_end_in_body {
3866            return Err(Error::ParseFailure("client hello extension truncated"));
3867        }
3868        if ext_type == EXT_PRE_SHARED_KEY {
3869            if ext_len < 4 {
3870                return Err(Error::ParseFailure("pre_shared_key extension too short"));
3871            }
3872            let identities_len =
3873                u16::from_be_bytes([body[ext_data_start], body[ext_data_start + 1]]) as usize;
3874            if ext_len < 2 + identities_len + 2 {
3875                return Err(Error::ParseFailure("pre_shared_key identities truncated"));
3876            }
3877            let binders_len_offset = ext_data_start + 2 + identities_len;
3878            let binders_len =
3879                u16::from_be_bytes([body[binders_len_offset], body[binders_len_offset + 1]])
3880                    as usize;
3881            let mut binder_cursor = binders_len_offset + 2;
3882            let binders_end = binder_cursor + binders_len;
3883            if binders_end != ext_data_end {
3884                return Err(Error::ParseFailure(
3885                    "invalid pre_shared_key binder vector length",
3886                ));
3887            }
3888            while binder_cursor < binders_end {
3889                let binder_len = body[binder_cursor] as usize;
3890                binder_cursor += 1;
3891                if binder_cursor + binder_len > binders_end {
3892                    return Err(Error::ParseFailure("pre_shared_key binder bytes truncated"));
3893                }
3894                let start = body_offset + binder_cursor;
3895                let end = start + binder_len;
3896                out[start..end].fill(0);
3897                binder_cursor += binder_len;
3898            }
3899            return Ok(out);
3900        }
3901        ext_cursor = ext_data_end;
3902    }
3903
3904    Err(Error::ParseFailure(
3905        "client hello missing pre_shared_key extension",
3906    ))
3907}
3908
3909/// Returns default client-advertised suites for the current prototype version.
3910///
3911/// # Arguments
3912///
3913/// * `version` — `version: TlsVersion`.
3914///
3915/// # Returns
3916///
3917/// The value described by the return type in the function signature.
3918///
3919/// # Panics
3920///
3921/// This function does not panic.
3922///
3923fn noxtls_default_client_cipher_suites(version: TlsVersion) -> Vec<CipherSuite> {
3924    match version {
3925        TlsVersion::Tls13 | TlsVersion::Dtls13 => vec![
3926            CipherSuite::TlsAes256GcmSha384,
3927            CipherSuite::TlsAes128GcmSha256,
3928            CipherSuite::TlsChacha20Poly1305Sha256,
3929        ],
3930        TlsVersion::Tls10 | TlsVersion::Tls11 | TlsVersion::Tls12 | TlsVersion::Dtls12 => {
3931            vec![
3932                CipherSuite::TlsEcdheEcdsaWithAes128GcmSha256,
3933                CipherSuite::TlsEcdheEcdsaWithAes256GcmSha384,
3934                CipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256,
3935                CipherSuite::TlsEcdheRsaWithAes128GcmSha256,
3936                CipherSuite::TlsEcdheRsaWithAes256GcmSha384,
3937                CipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256,
3938                CipherSuite::TlsDheRsaWithChacha20Poly1305Sha256,
3939                CipherSuite::TlsDheRsaWithAes128GcmSha256,
3940                CipherSuite::TlsDheRsaWithAes256GcmSha384,
3941                CipherSuite::TlsEcdheEcdsaWithAes128CcmSha256,
3942                CipherSuite::TlsEcdheEcdsaWithAes256CcmSha256,
3943                CipherSuite::TlsEcdheEcdsaWithAes128Ccm8Sha256,
3944                CipherSuite::TlsEcdheEcdsaWithAes256Ccm8Sha256,
3945                CipherSuite::TlsRsaWithAes128CcmSha256,
3946                CipherSuite::TlsRsaWithAes256CcmSha256,
3947                CipherSuite::TlsRsaWithAes128Ccm8Sha256,
3948                CipherSuite::TlsRsaWithAes256Ccm8Sha256,
3949                CipherSuite::TlsDheRsaWithAes128CcmSha256,
3950                CipherSuite::TlsDheRsaWithAes256CcmSha256,
3951                CipherSuite::TlsDheRsaWithAes128Ccm8Sha256,
3952                CipherSuite::TlsDheRsaWithAes256Ccm8Sha256,
3953            ]
3954        }
3955    }
3956}
3957
3958/// Encodes a minimally structured TLS ClientHello body for prototype negotiation.
3959///
3960/// # Arguments
3961///
3962/// * `version` — `version: TlsVersion`.
3963/// * `random` — `random: &[u8]`.
3964/// * `suites` — `suites: &[CipherSuite]`.
3965/// * `key_shares` — `key_shares: &Tls13ClientPublicKeyShares`.
3966/// * `sni_server_name` — `sni_server_name: Option<&str>`.
3967/// * `alpn_protocols` — `alpn_protocols: &[Vec<u8>]`.
3968/// * `offer_early_data` — `offer_early_data: bool`.
3969/// * `psk_offer` — `psk_offer: Option<&PskClientOffer<'_>>`.
3970///
3971/// # Returns
3972///
3973/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
3974///
3975/// # Errors
3976///
3977/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
3978///
3979/// # Panics
3980///
3981/// This function does not panic.
3982#[allow(clippy::too_many_arguments)]
3983fn noxtls_encode_client_hello_body(
3984    version: TlsVersion,
3985    random: &[u8],
3986    suites: &[CipherSuite],
3987    key_shares: &Tls13ClientPublicKeyShares,
3988    sni_server_name: Option<&str>,
3989    alpn_protocols: &[Vec<u8>],
3990    request_ocsp_stapling: bool,
3991    offer_mldsa_signature: bool,
3992    offer_early_data: bool,
3993    psk_offer: Option<&PskClientOffer<'_>>,
3994    offer_raw_public_key: bool,
3995    noxtls_tls12_session_id: Option<&[u8]>,
3996    secure_renegotiation_info: Option<&[u8]>,
3997) -> Result<Vec<u8>> {
3998    if random.len() != 32 {
3999        return Err(Error::InvalidLength("client hello random must be 32 bytes"));
4000    }
4001    if suites.is_empty() {
4002        return Err(Error::InvalidLength(
4003            "client hello suite list must not be empty",
4004        ));
4005    }
4006    let mut body = Vec::new();
4007    body.extend_from_slice(&noxtls_legacy_wire_version(version));
4008    body.extend_from_slice(random);
4009    if version == TlsVersion::Tls12 {
4010        let session_id = noxtls_tls12_session_id.unwrap_or(&[]);
4011        if session_id.len() > 32 {
4012            return Err(Error::InvalidLength(
4013                "tls12 session id must not exceed 32 bytes",
4014            ));
4015        }
4016        body.push(session_id.len() as u8);
4017        body.extend_from_slice(session_id);
4018    } else {
4019        body.push(0x00); // session_id length
4020    }
4021    body.extend_from_slice(&((suites.len() * 2) as u16).to_be_bytes());
4022    for suite in suites {
4023        body.extend_from_slice(&suite.noxtls_to_u16().to_be_bytes());
4024    }
4025    body.extend_from_slice(&[0x01, 0x00]); // compression_methods: null
4026    let extensions = noxtls_build_client_hello_extensions(
4027        version,
4028        key_shares,
4029        sni_server_name,
4030        alpn_protocols,
4031        request_ocsp_stapling,
4032        offer_mldsa_signature,
4033        offer_early_data,
4034        psk_offer,
4035        offer_raw_public_key,
4036        secure_renegotiation_info,
4037    )?;
4038    body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
4039    body.extend_from_slice(&extensions);
4040    Ok(body)
4041}
4042
4043/// Encodes a minimally structured TLS ServerHello body for prototype parsing.
4044///
4045/// # Arguments
4046///
4047/// * `version` — `version: TlsVersion`.
4048/// * `suite` — `suite: CipherSuite`.
4049/// * `random` — `random: &[u8]`.
4050///
4051/// # Returns
4052///
4053/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4054///
4055/// # Errors
4056///
4057/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4058///
4059/// # Panics
4060///
4061/// This function does not panic.
4062///
4063fn noxtls_encode_server_hello_body(
4064    version: TlsVersion,
4065    suite: CipherSuite,
4066    random: &[u8],
4067) -> Result<Vec<u8>> {
4068    noxtls_encode_server_hello_body_with_key_share(version, suite, random, None, None, None)
4069}
4070
4071/// Encodes ServerHello with optional explicit `key_share` bytes (for tests and tooling).
4072///
4073/// # Arguments
4074///
4075/// * `version` — `version: TlsVersion`.
4076/// * `suite` — `suite: CipherSuite`.
4077/// * `random` — `random: &[u8]`.
4078/// * `key_share_override` — `key_share_override: Option<(u16, &[u8])>`.
4079///
4080/// # Returns
4081///
4082/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4083///
4084/// # Errors
4085///
4086/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4087///
4088/// # Panics
4089///
4090/// This function does not panic.
4091///
4092fn noxtls_encode_server_hello_body_with_key_share(
4093    version: TlsVersion,
4094    suite: CipherSuite,
4095    random: &[u8],
4096    key_share_override: Option<(u16, &[u8])>,
4097    legacy_session_id_echo: Option<&[u8]>,
4098    secure_renegotiation_info: Option<&[u8]>,
4099) -> Result<Vec<u8>> {
4100    if random.len() != 32 {
4101        return Err(Error::InvalidLength("server hello random must be 32 bytes"));
4102    }
4103    let mut body = Vec::new();
4104    body.extend_from_slice(&noxtls_legacy_wire_version(version));
4105    body.extend_from_slice(random);
4106    let session_id = legacy_session_id_echo.unwrap_or(&[]);
4107    if session_id.len() > 32 {
4108        return Err(Error::InvalidLength(
4109            "server hello session_id echo must not exceed 32 bytes",
4110        ));
4111    }
4112    body.push(session_id.len() as u8);
4113    body.extend_from_slice(session_id);
4114    body.extend_from_slice(&suite.noxtls_to_u16().to_be_bytes());
4115    body.push(0x00); // compression method
4116    let mut extensions = Vec::new();
4117    if version == TlsVersion::Tls12 {
4118        let renegotiation_info = secure_renegotiation_info.unwrap_or(&[]);
4119        if renegotiation_info.len() > u8::MAX as usize {
4120            return Err(Error::InvalidLength(
4121                "renegotiation_info length must not exceed 255 bytes",
4122            ));
4123        }
4124        let mut extension_data = Vec::with_capacity(1 + renegotiation_info.len());
4125        extension_data.push(renegotiation_info.len() as u8);
4126        extension_data.extend_from_slice(renegotiation_info);
4127        noxtls_push_extension(&mut extensions, EXT_RENEGOTIATION_INFO, &extension_data);
4128    }
4129    if version.uses_tls13_handshake_semantics() {
4130        noxtls_push_extension(
4131            &mut extensions,
4132            EXT_SUPPORTED_VERSIONS,
4133            &0x0304_u16.to_be_bytes(),
4134        );
4135        let mut key_share = Vec::new();
4136        if let Some((g, bytes)) = key_share_override {
4137            if g == TLS13_KEY_SHARE_GROUP_X25519 && bytes.len() != 32 {
4138                return Err(Error::ParseFailure(
4139                    "invalid x25519 server key_share key_exchange length",
4140                ));
4141            }
4142            if g == TLS13_KEY_SHARE_GROUP_SECP256R1 && bytes.len() != 65 {
4143                return Err(Error::ParseFailure(
4144                    "invalid secp256r1 server key_share key_exchange length",
4145                ));
4146            }
4147            if g == TLS13_KEY_SHARE_GROUP_SECP384R1 && bytes.len() != 97 {
4148                return Err(Error::ParseFailure(
4149                    "invalid secp384r1 server key_share key_exchange length",
4150                ));
4151            }
4152            if g == TLS13_KEY_SHARE_GROUP_SECP521R1 && bytes.len() != 133 {
4153                return Err(Error::ParseFailure(
4154                    "invalid secp521r1 server key_share key_exchange length",
4155                ));
4156            }
4157            if g == TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13 && bytes.len() != 65 {
4158                return Err(Error::ParseFailure(
4159                    "invalid brainpoolP256r1tls13 server key_share key_exchange length",
4160                ));
4161            }
4162            if g == TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13 && bytes.len() != 97 {
4163                return Err(Error::ParseFailure(
4164                    "invalid brainpoolP384r1tls13 server key_share key_exchange length",
4165                ));
4166            }
4167            if g == TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13 && bytes.len() != 129 {
4168                return Err(Error::ParseFailure(
4169                    "invalid brainpoolP512r1tls13 server key_share key_exchange length",
4170                ));
4171            }
4172            if g == TLS13_KEY_SHARE_GROUP_X448 && bytes.len() != 56 {
4173                return Err(Error::ParseFailure(
4174                    "invalid x448 server key_share key_exchange length",
4175                ));
4176            }
4177            if g == TLS13_KEY_SHARE_GROUP_MLKEM768 && bytes.len() != MLKEM_CIPHERTEXT_LEN {
4178                return Err(Error::ParseFailure(
4179                    "invalid mlkem768 server key_share key_exchange length",
4180                ));
4181            }
4182            if g == TLS13_KEY_SHARE_GROUP_SECP256R1_MLKEM768_HYBRID
4183                && bytes.len() != (65 + MLKEM_CIPHERTEXT_LEN)
4184            {
4185                return Err(Error::ParseFailure(
4186                    "invalid secp256r1mlkem768 hybrid server key_share key_exchange length",
4187                ));
4188            }
4189            if g == TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID
4190                && bytes.len() != (32 + MLKEM_CIPHERTEXT_LEN)
4191            {
4192                return Err(Error::ParseFailure(
4193                    "invalid x25519_mlkem768 hybrid server key_share key_exchange length",
4194                ));
4195            }
4196            if g == TLS13_KEY_SHARE_GROUP_SECP384R1_MLKEM1024_HYBRID
4197                && bytes.len() != (97 + MLKEM1024_CIPHERTEXT_LEN)
4198            {
4199                return Err(Error::ParseFailure(
4200                    "invalid secp384r1mlkem1024 hybrid server key_share key_exchange length",
4201                ));
4202            }
4203            if g != TLS13_KEY_SHARE_GROUP_X25519
4204                && g != TLS13_KEY_SHARE_GROUP_SECP256R1
4205                && g != TLS13_KEY_SHARE_GROUP_SECP384R1
4206                && g != TLS13_KEY_SHARE_GROUP_SECP521R1
4207                && g != TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13
4208                && g != TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13
4209                && g != TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13
4210                && (g != TLS13_KEY_SHARE_GROUP_X448 || !cfg!(feature = "hazardous-legacy-crypto"))
4211                && g != TLS13_KEY_SHARE_GROUP_MLKEM768
4212                && g != TLS13_KEY_SHARE_GROUP_SECP256R1_MLKEM768_HYBRID
4213                && g != TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID
4214                && g != TLS13_KEY_SHARE_GROUP_SECP384R1_MLKEM1024_HYBRID
4215            {
4216                return Err(Error::ParseFailure("unsupported server key_share group"));
4217            }
4218            key_share.extend_from_slice(&g.to_be_bytes());
4219            key_share.extend_from_slice(&(bytes.len() as u16).to_be_bytes());
4220            key_share.extend_from_slice(bytes);
4221        } else {
4222            let private =
4223                noxtls_derive_deterministic_x25519_private(random, b"tls13 server x25519");
4224            let public = private.public_key().bytes;
4225            key_share.extend_from_slice(&TLS13_KEY_SHARE_GROUP_X25519.to_be_bytes());
4226            key_share.extend_from_slice(&32_u16.to_be_bytes());
4227            key_share.extend_from_slice(&public);
4228        }
4229        noxtls_push_extension(&mut extensions, EXT_KEY_SHARE, &key_share);
4230    }
4231    body.extend_from_slice(&(extensions.len() as u16).to_be_bytes());
4232    body.extend_from_slice(&extensions);
4233    Ok(body)
4234}
4235
4236/// Extracts the legacy `session_id` field from a ClientHello handshake body.
4237///
4238/// # Arguments
4239///
4240/// * `body` — ClientHello handshake body bytes (without the four-byte handshake header).
4241///
4242/// # Returns
4243///
4244/// On success, the offered legacy session identifier bytes (possibly empty).
4245///
4246/// # Errors
4247///
4248/// Returns [`noxtls_core::Error`] when the body is truncated or the session id length is invalid.
4249///
4250/// # Panics
4251///
4252/// This function does not panic.
4253fn noxtls_extract_client_hello_legacy_session_id(body: &[u8]) -> Result<&[u8]> {
4254    if body.len() < 35 {
4255        return Err(Error::ParseFailure(
4256            "client hello body too short for session_id",
4257        ));
4258    }
4259    let session_id_len = body[34] as usize;
4260    if session_id_len > 32 {
4261        return Err(Error::ParseFailure(
4262            "client hello legacy session_id exceeds 32 bytes",
4263        ));
4264    }
4265    let end = 35_usize.saturating_add(session_id_len);
4266    if body.len() < end {
4267        return Err(Error::ParseFailure(
4268            "client hello legacy session_id bytes truncated",
4269        ));
4270    }
4271    Ok(&body[35..end])
4272}
4273
4274/// Parses supported server hello encoding and extracts selected cipher suite.
4275///
4276/// # Arguments
4277///
4278/// * `msg` — `msg: &[u8]`.
4279///
4280/// # Returns
4281///
4282/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4283///
4284/// # Errors
4285///
4286/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4287///
4288/// # Panics
4289///
4290/// This function does not panic.
4291///
4292fn noxtls_parse_server_hello(msg: &[u8]) -> Result<ParsedServerHello> {
4293    if msg.len() == 3 && msg.first().copied() == Some(HANDSHAKE_SERVER_HELLO) {
4294        let suite_id = u16::from_be_bytes([msg[1], msg[2]]);
4295        let suite = CipherSuite::noxtls_from_u16(suite_id)
4296            .ok_or(Error::ParseFailure("unsupported cipher suite"))?;
4297        return Ok(ParsedServerHello {
4298            suite,
4299            random: None,
4300            key_share: None,
4301            hello_retry_request: false,
4302            requested_group: None,
4303            secure_renegotiation_info: None,
4304        });
4305    }
4306
4307    let (handshake_type, body) = noxtls_parse_handshake_message(msg)?;
4308    if handshake_type != HANDSHAKE_SERVER_HELLO {
4309        return Err(Error::ParseFailure("invalid server hello type"));
4310    }
4311    if body.len() < 40 {
4312        return Err(Error::ParseFailure("server hello body too short"));
4313    }
4314    let session_id_len = body[34] as usize;
4315    let suite_start = 35 + session_id_len;
4316    let suite_end = suite_start + 2;
4317    if body.len() < suite_end + 3 {
4318        return Err(Error::ParseFailure("server hello missing cipher suite"));
4319    }
4320    let suite_id = u16::from_be_bytes([body[suite_start], body[suite_start + 1]]);
4321    let suite = CipherSuite::noxtls_from_u16(suite_id)
4322        .ok_or(Error::ParseFailure("unsupported cipher suite"))?;
4323    let legacy_version = u16::from_be_bytes([body[0], body[1]]);
4324    if noxtls_is_tls13_suite(suite) && legacy_version != 0x0303 && legacy_version != 0xFEFD {
4325        return Err(Error::ParseFailure(
4326            "invalid tls13 server hello legacy_version",
4327        ));
4328    }
4329    let compression_method = body[suite_end];
4330    if compression_method != 0x00 {
4331        return Err(Error::ParseFailure(
4332            "invalid server hello compression method",
4333        ));
4334    }
4335    let mut random_bytes = [0_u8; 32];
4336    random_bytes.copy_from_slice(&body[2..34]);
4337    let random = &random_bytes;
4338    let hello_retry_request = random == &TLS13_HRR_RANDOM;
4339    let mut key_share_parsed = None;
4340    let mut requested_group = None;
4341    let mut seen_key_share_extension = false;
4342    let mut seen_supported_versions_extension = false;
4343    let mut secure_renegotiation_info = None;
4344    let mut supports_tls13 = false;
4345    let mut seen_extension_types = Vec::new();
4346    let ext_len_offset = suite_end + 1;
4347    let ext_len = u16::from_be_bytes([body[ext_len_offset], body[ext_len_offset + 1]]) as usize;
4348    let ext_start = ext_len_offset + 2;
4349    let ext_end = ext_start + ext_len;
4350    if ext_end > body.len() {
4351        return Err(Error::ParseFailure("server hello extensions truncated"));
4352    }
4353    let mut cursor = &body[ext_start..ext_end];
4354    while !cursor.is_empty() {
4355        if cursor.len() < 4 {
4356            return Err(Error::ParseFailure(
4357                "server hello extension header truncated",
4358            ));
4359        }
4360        let ext_type = u16::from_be_bytes([cursor[0], cursor[1]]);
4361        let ext_data_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
4362        cursor = &cursor[4..];
4363        if cursor.len() < ext_data_len {
4364            return Err(Error::ParseFailure("server hello extension truncated"));
4365        }
4366        if seen_extension_types.contains(&ext_type) {
4367            return Err(Error::ParseFailure("duplicate server hello extension type"));
4368        }
4369        seen_extension_types.push(ext_type);
4370        let ext_data = &cursor[..ext_data_len];
4371        match ext_type {
4372            EXT_SIGNATURE_ALGORITHMS | EXT_PSK_KEY_EXCHANGE_MODES | EXT_SERVER_NAME => {
4373                return Err(Error::ParseFailure(
4374                    "server hello contains forbidden extension type",
4375                ));
4376            }
4377            EXT_SUPPORTED_VERSIONS => {
4378                if ext_data_len != 2 {
4379                    return Err(Error::ParseFailure(
4380                        "invalid server hello supported_versions length",
4381                    ));
4382                }
4383                seen_supported_versions_extension = true;
4384                let selected_version = u16::from_be_bytes([ext_data[0], ext_data[1]]);
4385                if selected_version != 0x0304 {
4386                    return Err(Error::ParseFailure(
4387                        "invalid tls13 server hello supported_versions value",
4388                    ));
4389                }
4390                supports_tls13 = true;
4391            }
4392            EXT_RENEGOTIATION_INFO => {
4393                if ext_data.is_empty() {
4394                    return Err(Error::ParseFailure(
4395                        "server hello renegotiation_info extension missing length",
4396                    ));
4397                }
4398                let renegotiated_len = ext_data[0] as usize;
4399                if ext_data.len() != 1 + renegotiated_len {
4400                    return Err(Error::ParseFailure(
4401                        "server hello renegotiation_info extension length mismatch",
4402                    ));
4403                }
4404                secure_renegotiation_info = Some(ext_data[1..].to_vec());
4405            }
4406            EXT_KEY_SHARE => {
4407                seen_key_share_extension = true;
4408                if hello_retry_request {
4409                    if ext_data_len != 2 {
4410                        return Err(Error::ParseFailure("invalid hrr key_share length"));
4411                    }
4412                    requested_group = Some(u16::from_be_bytes([ext_data[0], ext_data[1]]));
4413                } else {
4414                    if ext_data_len < 4 {
4415                        return Err(Error::ParseFailure("invalid server key_share length"));
4416                    }
4417                    let group = u16::from_be_bytes([ext_data[0], ext_data[1]]);
4418                    let key_len = u16::from_be_bytes([ext_data[2], ext_data[3]]) as usize;
4419                    if ext_data_len != 4 + key_len {
4420                        return Err(Error::ParseFailure("invalid server key_share length"));
4421                    }
4422                    key_share_parsed = Some(match group {
4423                        TLS13_KEY_SHARE_GROUP_X25519 => {
4424                            if key_len != 32 {
4425                                return Err(Error::ParseFailure(
4426                                    "invalid x25519 server key_share key_exchange length",
4427                                ));
4428                            }
4429                            let mut key = [0_u8; 32];
4430                            key.copy_from_slice(&ext_data[4..36]);
4431                            Tls13ServerKeyShareParsed::X25519(key)
4432                        }
4433                        TLS13_KEY_SHARE_GROUP_SECP256R1 => {
4434                            if key_len != 65 {
4435                                return Err(Error::ParseFailure(
4436                                    "invalid secp256r1 server key_share key_exchange length",
4437                                ));
4438                            }
4439                            let mut key = [0_u8; 65];
4440                            key.copy_from_slice(&ext_data[4..69]);
4441                            Tls13ServerKeyShareParsed::Secp256r1(key)
4442                        }
4443                        TLS13_KEY_SHARE_GROUP_MLKEM768 => {
4444                            if key_len != MLKEM_CIPHERTEXT_LEN {
4445                                return Err(Error::ParseFailure(
4446                                    "invalid mlkem768 server key_share key_exchange length",
4447                                ));
4448                            }
4449                            Tls13ServerKeyShareParsed::MlKem768(ext_data[4..].to_vec())
4450                        }
4451                        TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID => {
4452                            if key_len != (32 + MLKEM_CIPHERTEXT_LEN) {
4453                                return Err(Error::ParseFailure(
4454                                    "invalid x25519_mlkem768 hybrid server key_share key_exchange length",
4455                                ));
4456                            }
4457                            let mlkem768 = ext_data[4..(4 + MLKEM_CIPHERTEXT_LEN)].to_vec();
4458                            let mut x25519 = [0_u8; 32];
4459                            x25519.copy_from_slice(&ext_data[(4 + MLKEM_CIPHERTEXT_LEN)..]);
4460                            Tls13ServerKeyShareParsed::X25519MlKem768Hybrid { x25519, mlkem768 }
4461                        }
4462                        _ => {
4463                            return Err(Error::ParseFailure("unsupported server key_share"));
4464                        }
4465                    });
4466                }
4467            }
4468            _ => {}
4469        }
4470        cursor = &cursor[ext_data_len..];
4471    }
4472    if hello_retry_request && !seen_key_share_extension {
4473        return Err(Error::ParseFailure("hrr missing key_share extension"));
4474    }
4475    if !hello_retry_request
4476        && noxtls_is_tls13_suite(suite)
4477        && legacy_version == 0x0303
4478        && !seen_supported_versions_extension
4479    {
4480        return Err(Error::ParseFailure(
4481            "tls13 server hello missing supported_versions extension",
4482        ));
4483    }
4484    if !hello_retry_request
4485        && noxtls_is_tls13_suite(suite)
4486        && legacy_version == 0x0303
4487        && !supports_tls13
4488    {
4489        return Err(Error::ParseFailure(
4490            "invalid tls13 server hello supported_versions value",
4491        ));
4492    }
4493    if !hello_retry_request
4494        && noxtls_is_tls13_suite(suite)
4495        && legacy_version == 0x0303
4496        && !seen_key_share_extension
4497    {
4498        return Err(Error::ParseFailure(
4499            "tls13 server hello missing key_share extension",
4500        ));
4501    }
4502    Ok(ParsedServerHello {
4503        suite,
4504        random: Some(random_bytes),
4505        key_share: key_share_parsed,
4506        hello_retry_request,
4507        requested_group,
4508        secure_renegotiation_info,
4509    })
4510}
4511
4512/// Returns true when suite belongs to TLS 1.3 suite registry.
4513///
4514/// # Arguments
4515///
4516/// * `suite` — `suite: CipherSuite`.
4517///
4518/// # Returns
4519///
4520/// `true` or `false` according to the checks in the function body.
4521///
4522/// # Panics
4523///
4524/// This function does not panic.
4525///
4526fn noxtls_is_tls13_suite(suite: CipherSuite) -> bool {
4527    matches!(
4528        suite,
4529        CipherSuite::TlsAes128GcmSha256
4530            | CipherSuite::TlsAes256GcmSha384
4531            | CipherSuite::TlsChacha20Poly1305Sha256
4532    )
4533}
4534
4535/// Parses minimally-structured ClientHello and extracts suite + extension metadata.
4536///
4537/// # Arguments
4538///
4539/// * `msg` — `msg: &[u8]`.
4540///
4541/// # Returns
4542///
4543/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4544///
4545/// # Errors
4546///
4547/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4548///
4549/// # Panics
4550///
4551/// This function does not panic.
4552///
4553fn noxtls_parse_client_hello_info(msg: &[u8]) -> Result<ClientHelloInfo> {
4554    let (handshake_type, body) = noxtls_parse_handshake_message(msg)?;
4555    if handshake_type != HANDSHAKE_CLIENT_HELLO {
4556        return Err(Error::ParseFailure("invalid client hello type"));
4557    }
4558    if body.len() < 39 {
4559        return Err(Error::ParseFailure("client hello body too short"));
4560    }
4561    let legacy_version = u16::from_be_bytes([body[0], body[1]]);
4562    let session_id_len = body[34] as usize;
4563    let suites_len_offset = 35 + session_id_len;
4564    if body.len() < suites_len_offset + 2 {
4565        return Err(Error::ParseFailure(
4566            "client hello missing cipher suites length",
4567        ));
4568    }
4569    let suites_len =
4570        u16::from_be_bytes([body[suites_len_offset], body[suites_len_offset + 1]]) as usize;
4571    if suites_len == 0 || suites_len % 2 != 0 {
4572        return Err(Error::ParseFailure(
4573            "invalid client hello cipher suites length",
4574        ));
4575    }
4576    let suites_start = suites_len_offset + 2;
4577    let suites_end = suites_start + suites_len;
4578    if body.len() < suites_end + 3 {
4579        return Err(Error::ParseFailure("client hello cipher suites truncated"));
4580    }
4581
4582    let mut suites = Vec::new();
4583    let mut secure_renegotiation_scsv = false;
4584    let mut offered_tls_fallback_scsv = false;
4585    for chunk in body[suites_start..suites_end].chunks_exact(2) {
4586        let codepoint = u16::from_be_bytes([chunk[0], chunk[1]]);
4587        if codepoint == TLS_EMPTY_RENEGOTIATION_INFO_SCSV {
4588            secure_renegotiation_scsv = true;
4589            continue;
4590        }
4591        if codepoint == TLS_FALLBACK_SCSV {
4592            offered_tls_fallback_scsv = true;
4593            continue;
4594        }
4595        if let Some(suite) = CipherSuite::noxtls_from_u16(codepoint) {
4596            suites.push(suite);
4597        }
4598    }
4599    if suites.is_empty() {
4600        return Err(Error::ParseFailure(
4601            "client hello has no supported cipher suite",
4602        ));
4603    }
4604
4605    let compression_methods_len = body[suites_end] as usize;
4606    let compression_methods_start = suites_end + 1;
4607    let compression_methods_end = compression_methods_start + compression_methods_len;
4608    if body.len() < compression_methods_end + 2 {
4609        return Err(Error::ParseFailure(
4610            "client hello missing compression methods",
4611        ));
4612    }
4613    let extensions_len = u16::from_be_bytes([
4614        body[compression_methods_end],
4615        body[compression_methods_end + 1],
4616    ]) as usize;
4617    let extensions_start = compression_methods_end + 2;
4618    let extensions_end = extensions_start + extensions_len;
4619    if body.len() < extensions_end {
4620        return Err(Error::ParseFailure("client hello extensions truncated"));
4621    }
4622    if body.len() != extensions_end {
4623        return Err(Error::ParseFailure("client hello has trailing bytes"));
4624    }
4625    let mut extensions =
4626        noxtls_parse_client_hello_extensions(&body[extensions_start..extensions_end])?;
4627    extensions.secure_renegotiation_scsv = secure_renegotiation_scsv;
4628
4629    Ok(ClientHelloInfo {
4630        legacy_version,
4631        offered_cipher_suites: suites,
4632        offered_tls_fallback_scsv,
4633        extensions,
4634    })
4635}
4636
4637/// Chooses the first server-preferred suite also present in client offer.
4638///
4639/// # Arguments
4640///
4641/// * `hello` — `hello: &ClientHelloInfo`.
4642/// * `preferred` — `preferred: &[CipherSuite]`.
4643/// * `version` — `version: TlsVersion`.
4644///
4645/// # Returns
4646///
4647/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4648///
4649/// # Errors
4650///
4651/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4652///
4653/// # Panics
4654///
4655/// This function does not panic.
4656///
4657fn noxtls_pick_intersection_suite(
4658    hello: &ClientHelloInfo,
4659    preferred: &[CipherSuite],
4660    version: TlsVersion,
4661) -> Result<CipherSuite> {
4662    for suite in preferred {
4663        if !hello.offered_cipher_suites.contains(suite) {
4664            continue;
4665        }
4666        if !noxtls_suite_supported_by_version(*suite, version) {
4667            continue;
4668        }
4669        if noxtls_suite_allowed_by_extensions(*suite, version, &hello.extensions) {
4670            return Ok(*suite);
4671        }
4672    }
4673    Err(Error::ParseFailure("no mutually supported cipher suite"))
4674}
4675
4676/// Returns true when one suite is valid for the target protocol version family.
4677///
4678/// # Arguments
4679///
4680/// * `suite` — `suite: CipherSuite`.
4681/// * `version` — `version: TlsVersion`.
4682///
4683/// # Returns
4684///
4685/// `true` or `false` according to the checks in the function body.
4686///
4687/// # Panics
4688///
4689/// This function does not panic.
4690///
4691fn noxtls_suite_supported_by_version(suite: CipherSuite, version: TlsVersion) -> bool {
4692    match version {
4693        TlsVersion::Tls13 | TlsVersion::Dtls13 => matches!(
4694            suite,
4695            CipherSuite::TlsAes128GcmSha256
4696                | CipherSuite::TlsAes256GcmSha384
4697                | CipherSuite::TlsChacha20Poly1305Sha256
4698        ),
4699        TlsVersion::Tls10 | TlsVersion::Tls11 | TlsVersion::Tls12 | TlsVersion::Dtls12 => {
4700            !noxtls_is_tls13_suite(suite)
4701        }
4702    }
4703}
4704
4705/// Applies extension-level checks for negotiated suite acceptance.
4706///
4707/// # Arguments
4708///
4709/// * `suite` — `suite: CipherSuite`.
4710/// * `version` — `version: TlsVersion`.
4711/// * `extensions` — `extensions: &ClientHelloExtensions`.
4712///
4713/// # Returns
4714///
4715/// `true` or `false` according to the checks in the function body.
4716///
4717/// # Panics
4718///
4719/// This function does not panic.
4720///
4721fn noxtls_suite_allowed_by_extensions(
4722    suite: CipherSuite,
4723    version: TlsVersion,
4724    extensions: &ClientHelloExtensions,
4725) -> bool {
4726    match version {
4727        TlsVersion::Tls13 | TlsVersion::Dtls13 => {
4728            if matches!(
4729                suite,
4730                CipherSuite::TlsAes128GcmSha256
4731                    | CipherSuite::TlsAes256GcmSha384
4732                    | CipherSuite::TlsChacha20Poly1305Sha256
4733            ) {
4734                return noxtls_tls13_client_hello_offers_supported_key_exchange(
4735                    &extensions.supported_versions,
4736                    &extensions.key_share_groups,
4737                    &extensions.signature_algorithms,
4738                );
4739            }
4740            true
4741        }
4742        TlsVersion::Tls10 | TlsVersion::Tls11 | TlsVersion::Tls12 | TlsVersion::Dtls12 => true,
4743    }
4744}
4745
4746/// Returns whether TLS13 debug tracing is enabled at runtime.
4747///
4748/// # Arguments
4749///
4750/// * _(none)_ — This function takes no parameters.
4751///
4752/// # Returns
4753///
4754/// `true` when environment variable `NOXTLS_TLS13_DEBUG` is present; `false` otherwise.
4755///
4756/// # Panics
4757///
4758/// This function does not panic.
4759fn noxtls_tls13_debug_enabled() -> bool {
4760    #[cfg(feature = "std")]
4761    {
4762        std::env::var_os("NOXTLS_TLS13_DEBUG").is_some()
4763    }
4764    #[cfg(not(feature = "std"))]
4765    {
4766        false
4767    }
4768}
4769
4770/// Logs one TLS13 debug key/value pair when debug tracing is enabled.
4771///
4772/// # Arguments
4773///
4774/// * `label` — Short field identifier describing the logged value.
4775/// * `value` — Human-readable value string to print.
4776///
4777/// # Returns
4778///
4779/// `()` after optionally emitting one debug line.
4780///
4781/// # Panics
4782///
4783/// This function does not panic.
4784fn noxtls_tls13_debug_log(_label: &str, _value: &str) {
4785    if !noxtls_tls13_debug_enabled() {
4786        return;
4787    }
4788    #[cfg(feature = "std")]
4789    {
4790        eprintln!("tls13_debug::{_label}={_value}");
4791    }
4792    #[cfg(not(feature = "std"))]
4793    {
4794        let _ = (_label, _value);
4795    }
4796}
4797
4798/// Logs one TLS13 debug byte slice as lowercase hexadecimal.
4799///
4800/// # Arguments
4801///
4802/// * `label` — Short field identifier describing the logged bytes.
4803/// * `bytes` — Opaque bytes to encode in hexadecimal.
4804///
4805/// # Returns
4806///
4807/// `()` after optionally emitting one debug line.
4808///
4809/// # Panics
4810///
4811/// This function does not panic.
4812fn noxtls_tls13_debug_log_bytes(label: &str, bytes: &[u8]) {
4813    if !noxtls_tls13_debug_enabled() {
4814        return;
4815    }
4816    noxtls_tls13_debug_log(label, &noxtls_encode_hex(bytes));
4817}
4818
4819/// Formats a byte slice as lowercase hexadecimal without separators.
4820///
4821/// # Arguments
4822///
4823/// * `bytes` — Byte slice to encode.
4824///
4825/// # Returns
4826///
4827/// Lowercase hexadecimal string with length `bytes.len() * 2`.
4828///
4829/// # Panics
4830///
4831/// This function does not panic.
4832fn noxtls_encode_hex(bytes: &[u8]) -> String {
4833    const HEX: &[u8; 16] = b"0123456789abcdef";
4834    let mut out = String::with_capacity(bytes.len() * 2);
4835    for byte in bytes {
4836        out.push(HEX[(byte >> 4) as usize] as char);
4837        out.push(HEX[(byte & 0x0f) as usize] as char);
4838    }
4839    out
4840}
4841
4842/// Returns a stable display label for hash algorithms used in TLS key schedule logs.
4843///
4844/// # Arguments
4845///
4846/// * `noxtls_hash_algorithm` — Hash algorithm enum value to render.
4847///
4848/// # Returns
4849///
4850/// Static string label for the provided algorithm.
4851///
4852/// # Panics
4853///
4854/// This function does not panic.
4855fn noxtls_hash_algorithm_name(noxtls_hash_algorithm: HashAlgorithm) -> &'static str {
4856    match noxtls_hash_algorithm {
4857        HashAlgorithm::Sha256 => "sha256",
4858        HashAlgorithm::Sha384 => "sha384",
4859    }
4860}
4861
4862/// Extracts TLS 1.3 ClientHello X25519 key_share bytes from encoded handshake message.
4863///
4864/// # Arguments
4865///
4866/// * `message` — Encoded `ClientHello` handshake message bytes (`type || len || body`).
4867///
4868/// # Returns
4869///
4870/// `Ok(Some(key_exchange))` when one X25519 key share is present, `Ok(None)` when absent.
4871///
4872/// # Errors
4873///
4874/// Returns [`noxtls_core::Error`] when the handshake shape or extension encoding is malformed.
4875///
4876/// # Panics
4877///
4878/// This function does not panic.
4879fn noxtls_extract_tls13_client_hello_x25519_key_share(message: &[u8]) -> Result<Option<[u8; 32]>> {
4880    let (handshake_type, body) = noxtls_parse_handshake_message(message)?;
4881    if handshake_type != HANDSHAKE_CLIENT_HELLO {
4882        return Err(Error::ParseFailure(
4883            "expected client hello while extracting x25519 key share",
4884        ));
4885    }
4886    if body.len() < 39 {
4887        return Err(Error::ParseFailure("client hello body too short"));
4888    }
4889    let mut offset = 0_usize;
4890    offset = offset.saturating_add(2); // legacy_version
4891    offset = offset.saturating_add(32); // random
4892    let session_id_len = body.get(offset).copied().ok_or(Error::ParseFailure(
4893        "client hello missing session_id length",
4894    ))? as usize;
4895    offset = offset.saturating_add(1 + session_id_len);
4896    if body.len().saturating_sub(offset) < 2 {
4897        return Err(Error::ParseFailure(
4898            "client hello missing cipher_suites length",
4899        ));
4900    }
4901    let suites_len = u16::from_be_bytes([body[offset], body[offset + 1]]) as usize;
4902    offset = offset.saturating_add(2 + suites_len);
4903    if body.len().saturating_sub(offset) < 1 {
4904        return Err(Error::ParseFailure(
4905            "client hello missing compression_methods length",
4906        ));
4907    }
4908    let compression_len = body[offset] as usize;
4909    offset = offset.saturating_add(1 + compression_len);
4910    if body.len().saturating_sub(offset) < 2 {
4911        return Err(Error::ParseFailure(
4912            "client hello missing extensions length",
4913        ));
4914    }
4915    let extensions_len = u16::from_be_bytes([body[offset], body[offset + 1]]) as usize;
4916    offset = offset.saturating_add(2);
4917    if body.len().saturating_sub(offset) < extensions_len {
4918        return Err(Error::ParseFailure("client hello extensions truncated"));
4919    }
4920    let mut cursor = &body[offset..offset + extensions_len];
4921    while !cursor.is_empty() {
4922        if cursor.len() < 4 {
4923            return Err(Error::ParseFailure(
4924                "client hello extension header truncated",
4925            ));
4926        }
4927        let extension_type = u16::from_be_bytes([cursor[0], cursor[1]]);
4928        let extension_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
4929        cursor = &cursor[4..];
4930        if cursor.len() < extension_len {
4931            return Err(Error::ParseFailure("client hello extension truncated"));
4932        }
4933        let extension_data = &cursor[..extension_len];
4934        if extension_type == EXT_KEY_SHARE {
4935            if extension_data.len() < 2 {
4936                return Err(Error::ParseFailure(
4937                    "client hello key_share extension missing vector length",
4938                ));
4939            }
4940            let key_share_list_len =
4941                u16::from_be_bytes([extension_data[0], extension_data[1]]) as usize;
4942            if extension_data.len() != key_share_list_len + 2 {
4943                return Err(Error::ParseFailure(
4944                    "client hello key_share extension length mismatch",
4945                ));
4946            }
4947            let mut shares = &extension_data[2..];
4948            while !shares.is_empty() {
4949                if shares.len() < 4 {
4950                    return Err(Error::ParseFailure(
4951                        "client hello key_share entry truncated",
4952                    ));
4953                }
4954                let group = u16::from_be_bytes([shares[0], shares[1]]);
4955                let key_exchange_len = u16::from_be_bytes([shares[2], shares[3]]) as usize;
4956                shares = &shares[4..];
4957                if shares.len() < key_exchange_len {
4958                    return Err(Error::ParseFailure(
4959                        "client hello key_share key_exchange truncated",
4960                    ));
4961                }
4962                if group == TLS13_KEY_SHARE_GROUP_X25519 {
4963                    if key_exchange_len != 32 {
4964                        return Err(Error::ParseFailure(
4965                            "client hello x25519 key_share length must be 32",
4966                        ));
4967                    }
4968                    let mut key_exchange = [0_u8; 32];
4969                    key_exchange.copy_from_slice(&shares[..32]);
4970                    return Ok(Some(key_exchange));
4971                }
4972                shares = &shares[key_exchange_len..];
4973            }
4974            return Ok(None);
4975        }
4976        cursor = &cursor[extension_len..];
4977    }
4978    Ok(None)
4979}
4980
4981/// Builds minimally required ClientHello extensions per protocol version.
4982///
4983/// # Arguments
4984///
4985/// * `version` — `version: TlsVersion`.
4986/// * `key_shares` — `key_shares: &Tls13ClientPublicKeyShares`.
4987/// * `sni_server_name` — `sni_server_name: Option<&str>`.
4988/// * `alpn_protocols` — `alpn_protocols: &[Vec<u8>]`.
4989/// * `offer_early_data` — `offer_early_data: bool`.
4990/// * `psk_offer` — `psk_offer: Option<&PskClientOffer<'_>>`.
4991///
4992/// # Returns
4993///
4994/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
4995///
4996/// # Errors
4997///
4998/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
4999///
5000/// # Panics
5001///
5002/// This function does not panic.
5003///
5004#[allow(clippy::too_many_arguments)]
5005fn noxtls_build_client_hello_extensions(
5006    version: TlsVersion,
5007    key_shares: &Tls13ClientPublicKeyShares,
5008    sni_server_name: Option<&str>,
5009    alpn_protocols: &[Vec<u8>],
5010    request_ocsp_stapling: bool,
5011    offer_mldsa_signature: bool,
5012    offer_early_data: bool,
5013    psk_offer: Option<&PskClientOffer<'_>>,
5014    offer_raw_public_key: bool,
5015    secure_renegotiation_info: Option<&[u8]>,
5016) -> Result<Vec<u8>> {
5017    let mut extensions = Vec::new();
5018    match version {
5019        TlsVersion::Tls13 | TlsVersion::Dtls13 => {
5020            // supported_versions: TLS 1.3 plus TLS 1.2 fallback marker.
5021            let mut supported_versions = Vec::new();
5022            supported_versions.push(4_u8);
5023            supported_versions.extend_from_slice(&0x0304_u16.to_be_bytes());
5024            supported_versions.extend_from_slice(&0x0303_u16.to_be_bytes());
5025            noxtls_push_extension(&mut extensions, EXT_SUPPORTED_VERSIONS, &supported_versions);
5026
5027            // signature_algorithms: modeled TLS 1.3 schemes aligned with verify support.
5028            let mut sigalgs = Vec::new();
5029            let mut supported_sigalgs = vec![
5030                TLS13_SIGALG_ECDSA_SECP256R1_SHA256,
5031                TLS13_SIGALG_ECDSA_SECP384R1_SHA384,
5032                TLS13_SIGALG_ECDSA_SECP521R1_SHA512,
5033                TLS13_SIGALG_RSA_PSS_RSAE_SHA256,
5034                TLS13_SIGALG_RSA_PSS_RSAE_SHA384,
5035                TLS13_SIGALG_RSA_PSS_RSAE_SHA512,
5036                TLS13_SIGALG_RSA_PSS_PSS_SHA256,
5037                TLS13_SIGALG_RSA_PSS_PSS_SHA384,
5038                TLS13_SIGALG_RSA_PSS_PSS_SHA512,
5039                TLS13_SIGALG_ED25519,
5040            ];
5041            if offer_mldsa_signature {
5042                supported_sigalgs.push(TLS13_SIGALG_MLDSA65);
5043            }
5044            sigalgs.extend_from_slice(&((supported_sigalgs.len() * 2) as u16).to_be_bytes());
5045            for sigalg in supported_sigalgs {
5046                sigalgs.extend_from_slice(&sigalg.to_be_bytes());
5047            }
5048            noxtls_push_extension(&mut extensions, EXT_SIGNATURE_ALGORITHMS, &sigalgs);
5049
5050            // supported_groups: advertise all groups we may select in key_share.
5051            let mut supported_groups = Vec::new();
5052            let mut supported_group_ids = Vec::new();
5053            if key_shares.x25519.is_some() {
5054                supported_group_ids.push(TLS13_KEY_SHARE_GROUP_X25519);
5055            }
5056            if key_shares.secp256r1_uncompressed.is_some() {
5057                supported_group_ids.push(TLS13_KEY_SHARE_GROUP_SECP256R1);
5058            }
5059            if key_shares.mlkem768.is_some() {
5060                supported_group_ids.push(TLS13_KEY_SHARE_GROUP_MLKEM768);
5061            }
5062            if key_shares.x25519_mlkem768_hybrid.is_some() {
5063                supported_group_ids.push(TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID);
5064            }
5065            if supported_group_ids.is_empty() {
5066                return Err(Error::InvalidLength(
5067                    "tls13 client hello supported_groups extension must not be empty",
5068                ));
5069            }
5070            supported_groups
5071                .extend_from_slice(&((supported_group_ids.len() * 2) as u16).to_be_bytes());
5072            for group in supported_group_ids {
5073                supported_groups.extend_from_slice(&group.to_be_bytes());
5074            }
5075            noxtls_push_extension(&mut extensions, EXT_SUPPORTED_GROUPS, &supported_groups);
5076
5077            // key_share: X25519 and optional secp256r1 entries for modeled ECDHE breadth.
5078            let mut key_share_list = Vec::new();
5079            if let Some(public) = key_shares.x25519 {
5080                key_share_list.extend_from_slice(&TLS13_KEY_SHARE_GROUP_X25519.to_be_bytes());
5081                key_share_list.extend_from_slice(&32_u16.to_be_bytes());
5082                key_share_list.extend_from_slice(&public);
5083            }
5084            if let Some(public) = key_shares.secp256r1_uncompressed {
5085                key_share_list.extend_from_slice(&TLS13_KEY_SHARE_GROUP_SECP256R1.to_be_bytes());
5086                key_share_list.extend_from_slice(&65_u16.to_be_bytes());
5087                key_share_list.extend_from_slice(&public);
5088            }
5089            if let Some(public) = key_shares.mlkem768.as_ref() {
5090                key_share_list.extend_from_slice(&TLS13_KEY_SHARE_GROUP_MLKEM768.to_be_bytes());
5091                key_share_list.extend_from_slice(&(public.len() as u16).to_be_bytes());
5092                key_share_list.extend_from_slice(public);
5093            }
5094            if let Some(public) = key_shares.x25519_mlkem768_hybrid.as_ref() {
5095                key_share_list
5096                    .extend_from_slice(&TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID.to_be_bytes());
5097                key_share_list.extend_from_slice(&(public.len() as u16).to_be_bytes());
5098                key_share_list.extend_from_slice(public);
5099            }
5100            if key_share_list.is_empty() {
5101                return Err(Error::InvalidLength(
5102                    "tls13 client hello key_share extension must not be empty",
5103                ));
5104            }
5105            let mut key_share_ext = Vec::new();
5106            key_share_ext.extend_from_slice(&(key_share_list.len() as u16).to_be_bytes());
5107            key_share_ext.extend_from_slice(&key_share_list);
5108            noxtls_push_extension(&mut extensions, EXT_KEY_SHARE, &key_share_ext);
5109            if let Some(server_name) = sni_server_name {
5110                let server_name_extension_data =
5111                    noxtls_encode_server_name_extension_data(server_name)?;
5112                noxtls_push_extension(
5113                    &mut extensions,
5114                    EXT_SERVER_NAME,
5115                    &server_name_extension_data,
5116                );
5117            }
5118            if request_ocsp_stapling {
5119                let status_request_data = noxtls_encode_status_request_ocsp_extension_data()?;
5120                noxtls_push_extension(&mut extensions, EXT_STATUS_REQUEST, &status_request_data);
5121            }
5122            if !alpn_protocols.is_empty() {
5123                let alpn_extension_data = noxtls_encode_alpn_extension_data(alpn_protocols)?;
5124                noxtls_push_extension(&mut extensions, EXT_ALPN, &alpn_extension_data);
5125            }
5126            if let Some(renegotiation_info) = secure_renegotiation_info {
5127                if renegotiation_info.len() > u8::MAX as usize {
5128                    return Err(Error::InvalidLength(
5129                        "renegotiation_info length must not exceed 255 bytes",
5130                    ));
5131                }
5132                let mut extension_data = Vec::with_capacity(1 + renegotiation_info.len());
5133                extension_data.push(renegotiation_info.len() as u8);
5134                extension_data.extend_from_slice(renegotiation_info);
5135                noxtls_push_extension(&mut extensions, EXT_RENEGOTIATION_INFO, &extension_data);
5136            }
5137            if offer_raw_public_key {
5138                noxtls_push_extension(
5139                    &mut extensions,
5140                    EXT_CLIENT_CERTIFICATE_TYPE,
5141                    &[1, TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY],
5142                );
5143                noxtls_push_extension(
5144                    &mut extensions,
5145                    EXT_SERVER_CERTIFICATE_TYPE,
5146                    &[
5147                        2,
5148                        TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY,
5149                        TLS_CERTIFICATE_TYPE_X509,
5150                    ],
5151                );
5152            }
5153            if offer_early_data {
5154                if psk_offer.is_none() {
5155                    return Err(Error::StateError(
5156                        "tls13 early_data extension requires pre_shared_key offer",
5157                    ));
5158                }
5159                noxtls_push_extension(&mut extensions, EXT_EARLY_DATA, &[]);
5160            }
5161            if let Some(psk) = psk_offer {
5162                let psk_key_exchange_modes = [1_u8, TLS13_PSK_KEY_EXCHANGE_MODE_PSK_DHE_KE];
5163                noxtls_push_extension(
5164                    &mut extensions,
5165                    EXT_PSK_KEY_EXCHANGE_MODES,
5166                    &psk_key_exchange_modes,
5167                );
5168                let psk_extension = noxtls_encode_pre_shared_key_extension(psk)?;
5169                noxtls_push_extension(&mut extensions, EXT_PRE_SHARED_KEY, &psk_extension);
5170            }
5171        }
5172        TlsVersion::Tls10 | TlsVersion::Tls11 | TlsVersion::Tls12 | TlsVersion::Dtls12 => {
5173            // signature_algorithms: placeholder vector for non-TLS1.3 paths.
5174            let mut sigalgs = Vec::new();
5175            sigalgs.extend_from_slice(&2_u16.to_be_bytes());
5176            sigalgs.extend_from_slice(&0x0401_u16.to_be_bytes());
5177            noxtls_push_extension(&mut extensions, EXT_SIGNATURE_ALGORITHMS, &sigalgs);
5178            if let Some(renegotiation_info) = secure_renegotiation_info {
5179                if renegotiation_info.len() > u8::MAX as usize {
5180                    return Err(Error::InvalidLength(
5181                        "renegotiation_info length must not exceed 255 bytes",
5182                    ));
5183                }
5184                let mut extension_data = Vec::with_capacity(1 + renegotiation_info.len());
5185                extension_data.push(renegotiation_info.len() as u8);
5186                extension_data.extend_from_slice(renegotiation_info);
5187                noxtls_push_extension(&mut extensions, EXT_RENEGOTIATION_INFO, &extension_data);
5188            }
5189            if offer_raw_public_key {
5190                noxtls_push_extension(
5191                    &mut extensions,
5192                    EXT_CLIENT_CERTIFICATE_TYPE,
5193                    &[1, TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY],
5194                );
5195                noxtls_push_extension(
5196                    &mut extensions,
5197                    EXT_SERVER_CERTIFICATE_TYPE,
5198                    &[
5199                        2,
5200                        TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY,
5201                        TLS_CERTIFICATE_TYPE_X509,
5202                    ],
5203                );
5204            }
5205        }
5206    }
5207    Ok(extensions)
5208}
5209
5210/// Appends one `Extension` (type + length + value) to output buffer.
5211///
5212/// # Arguments
5213///
5214/// * `out` — `out: &mut Vec<u8>`.
5215/// * `ext_type` — `ext_type: u16`.
5216/// * `ext_data` — `ext_data: &[u8]`.
5217///
5218/// # Panics
5219///
5220/// This function does not panic.
5221///
5222fn noxtls_push_extension(out: &mut Vec<u8>, ext_type: u16, ext_data: &[u8]) {
5223    out.extend_from_slice(&ext_type.to_be_bytes());
5224    out.extend_from_slice(&(ext_data.len() as u16).to_be_bytes());
5225    out.extend_from_slice(ext_data);
5226}
5227
5228/// Parses selected ClientHello extensions needed for current prototype checks.
5229///
5230/// # Arguments
5231///
5232/// * `input` — `input: &[u8]`.
5233///
5234/// # Returns
5235///
5236/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5237///
5238/// # Errors
5239///
5240/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5241///
5242/// # Panics
5243///
5244/// This function does not panic.
5245///
5246fn noxtls_parse_client_hello_extensions(input: &[u8]) -> Result<ClientHelloExtensions> {
5247    let mut out = ClientHelloExtensions::default();
5248    let mut cursor = input;
5249    let mut seen_supported_versions = false;
5250    let mut seen_signature_algorithms = false;
5251    let mut seen_key_share = false;
5252    let mut seen_psk_key_exchange_modes = false;
5253    let mut seen_pre_shared_key = false;
5254    let mut seen_early_data = false;
5255    let mut seen_extension_types = Vec::new();
5256    while !cursor.is_empty() {
5257        if cursor.len() < 4 {
5258            return Err(Error::ParseFailure(
5259                "client hello extension header truncated",
5260            ));
5261        }
5262        let ext_type = u16::from_be_bytes([cursor[0], cursor[1]]);
5263        let ext_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
5264        cursor = &cursor[4..];
5265        if cursor.len() < ext_len {
5266            return Err(Error::ParseFailure("client hello extension truncated"));
5267        }
5268        let ext_data = &cursor[..ext_len];
5269        if seen_extension_types.contains(&ext_type) {
5270            return Err(Error::ParseFailure("duplicate client hello extension type"));
5271        }
5272        seen_extension_types.push(ext_type);
5273        if seen_pre_shared_key {
5274            return Err(Error::ParseFailure(
5275                "pre_shared_key extension must be the last extension",
5276            ));
5277        }
5278        match ext_type {
5279            EXT_SUPPORTED_VERSIONS => {
5280                if seen_supported_versions {
5281                    return Err(Error::ParseFailure(
5282                        "duplicate supported_versions extension",
5283                    ));
5284                }
5285                out.supported_versions = noxtls_parse_supported_versions_extension(ext_data)?;
5286                seen_supported_versions = true;
5287            }
5288            EXT_SIGNATURE_ALGORITHMS => {
5289                if seen_signature_algorithms {
5290                    return Err(Error::ParseFailure(
5291                        "duplicate signature_algorithms extension",
5292                    ));
5293                }
5294                out.signature_algorithms = noxtls_parse_u16_vector_with_len(ext_data)?;
5295                if out.signature_algorithms.is_empty() {
5296                    return Err(Error::ParseFailure(
5297                        "signature_algorithms extension must not be empty",
5298                    ));
5299                }
5300                seen_signature_algorithms = true;
5301            }
5302            EXT_SUPPORTED_GROUPS => {
5303                out.supported_groups = noxtls_parse_u16_vector_with_len(ext_data)?;
5304            }
5305            EXT_KEY_SHARE => {
5306                if seen_key_share {
5307                    return Err(Error::ParseFailure("duplicate key_share extension"));
5308                }
5309                out.key_share_groups = noxtls_parse_key_share_groups_extension(ext_data)?;
5310                out.key_share_offered = true;
5311                seen_key_share = true;
5312            }
5313            EXT_SERVER_NAME => {
5314                out.sni_server_name = Some(noxtls_parse_server_name_extension(ext_data)?);
5315            }
5316            EXT_ALPN => {
5317                out.alpn_protocols = noxtls_parse_alpn_protocol_name_list(ext_data)?;
5318            }
5319            EXT_STATUS_REQUEST => {
5320                out.status_request_ocsp = noxtls_parse_status_request_ocsp_extension(ext_data)?;
5321            }
5322            EXT_COMPRESS_CERTIFICATE => {
5323                out.certificate_compression_algorithms =
5324                    noxtls_parse_compress_certificate_extension(ext_data)?;
5325            }
5326            EXT_CLIENT_CERTIFICATE_TYPE => {
5327                out.client_certificate_types = noxtls_parse_u8_vector_with_len(ext_data)?;
5328                if out.client_certificate_types.is_empty() {
5329                    return Err(Error::ParseFailure(
5330                        "client_certificate_type extension must not be empty",
5331                    ));
5332                }
5333            }
5334            EXT_SERVER_CERTIFICATE_TYPE => {
5335                out.server_certificate_types = noxtls_parse_u8_vector_with_len(ext_data)?;
5336                if out.server_certificate_types.is_empty() {
5337                    return Err(Error::ParseFailure(
5338                        "server_certificate_type extension must not be empty",
5339                    ));
5340                }
5341            }
5342            EXT_PSK_KEY_EXCHANGE_MODES => {
5343                if seen_psk_key_exchange_modes {
5344                    return Err(Error::ParseFailure(
5345                        "duplicate psk_key_exchange_modes extension",
5346                    ));
5347                }
5348                out.psk_key_exchange_modes = noxtls_parse_u8_vector_with_len(ext_data)?;
5349                if !out
5350                    .psk_key_exchange_modes
5351                    .contains(&TLS13_PSK_KEY_EXCHANGE_MODE_PSK_DHE_KE)
5352                {
5353                    return Err(Error::ParseFailure(
5354                        "psk_key_exchange_modes must include psk_dhe_ke",
5355                    ));
5356                }
5357                seen_psk_key_exchange_modes = true;
5358            }
5359            EXT_PRE_SHARED_KEY => {
5360                if seen_pre_shared_key {
5361                    return Err(Error::ParseFailure("duplicate pre_shared_key extension"));
5362                }
5363                let (identity_count, identities, obfuscated_ages, binders) =
5364                    noxtls_parse_pre_shared_key_extension(ext_data)?;
5365                out.psk_identity_count = identity_count;
5366                out.psk_identities = identities;
5367                out.psk_obfuscated_ticket_ages = obfuscated_ages;
5368                out.psk_binders = binders;
5369                seen_pre_shared_key = true;
5370            }
5371            EXT_RENEGOTIATION_INFO => {
5372                if ext_data.is_empty() {
5373                    return Err(Error::ParseFailure(
5374                        "renegotiation_info extension missing length",
5375                    ));
5376                }
5377                let renegotiated_len = ext_data[0] as usize;
5378                if ext_data.len() != 1 + renegotiated_len {
5379                    return Err(Error::ParseFailure(
5380                        "renegotiation_info extension length mismatch",
5381                    ));
5382                }
5383                out.secure_renegotiation_info = Some(ext_data[1..].to_vec());
5384            }
5385            EXT_EARLY_DATA => {
5386                if seen_early_data {
5387                    return Err(Error::ParseFailure("duplicate early_data extension"));
5388                }
5389                if !ext_data.is_empty() {
5390                    return Err(Error::ParseFailure(
5391                        "client hello early_data extension must be empty",
5392                    ));
5393                }
5394                out.early_data_offered = true;
5395                seen_early_data = true;
5396            }
5397            _ => {}
5398        }
5399        cursor = &cursor[ext_len..];
5400    }
5401    if seen_pre_shared_key && !seen_psk_key_exchange_modes {
5402        return Err(Error::ParseFailure(
5403            "pre_shared_key extension requires psk_key_exchange_modes extension",
5404        ));
5405    }
5406    if seen_early_data && !seen_pre_shared_key {
5407        return Err(Error::ParseFailure(
5408            "early_data extension requires pre_shared_key extension",
5409        ));
5410    }
5411    let advertises_tls13 = out.supported_versions.contains(&0x0304);
5412    if seen_pre_shared_key && !advertises_tls13 {
5413        return Err(Error::ParseFailure(
5414            "pre_shared_key extension requires tls13 supported_versions entry",
5415        ));
5416    }
5417    if seen_key_share && !advertises_tls13 {
5418        return Err(Error::ParseFailure(
5419            "key_share extension requires tls13 supported_versions entry",
5420        ));
5421    }
5422    if advertises_tls13 && !seen_signature_algorithms {
5423        return Err(Error::ParseFailure(
5424            "tls13 supported_versions requires signature_algorithms extension",
5425        ));
5426    }
5427    if advertises_tls13 && !seen_key_share {
5428        return Err(Error::ParseFailure(
5429            "tls13 supported_versions requires key_share extension",
5430        ));
5431    }
5432    if advertises_tls13
5433        && noxtls_tls13_supported_groups_has_explicit_obsolete_offer(&out.supported_groups)
5434    {
5435        return Err(Error::ParseFailure(
5436            "tls13 client hello offered obsolete supported_group",
5437        ));
5438    }
5439    if advertises_tls13
5440        && out
5441            .key_share_groups
5442            .iter()
5443            .copied()
5444            .any(noxtls_tls13_named_group_is_obsolete)
5445    {
5446        return Err(Error::ParseFailure(
5447            "tls13 client hello offered obsolete key_share group",
5448        ));
5449    }
5450    if seen_pre_shared_key && !seen_key_share {
5451        return Err(Error::ParseFailure(
5452            "pre_shared_key with psk_dhe_ke requires key_share extension",
5453        ));
5454    }
5455    Ok(out)
5456}
5457
5458fn noxtls_tls13_named_group_is_obsolete(group: u16) -> bool {
5459    (0x0001..=0x0016).contains(&group)
5460        || (0x001A..=0x001C).contains(&group)
5461        || (0xFF01..=0xFF02).contains(&group)
5462}
5463
5464fn noxtls_tls13_supported_groups_has_explicit_obsolete_offer(groups: &[u16]) -> bool {
5465    groups.len() <= 2
5466        && groups
5467            .iter()
5468            .copied()
5469            .any(noxtls_tls13_named_group_is_obsolete)
5470}
5471
5472/// Parses `<len:u8><versions:u16...>` form used by client supported_versions extension.
5473///
5474/// # Arguments
5475///
5476/// * `input` — `input: &[u8]`.
5477///
5478/// # Returns
5479///
5480/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5481///
5482/// # Errors
5483///
5484/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5485///
5486/// # Panics
5487///
5488/// This function does not panic.
5489///
5490fn noxtls_parse_supported_versions_extension(input: &[u8]) -> Result<Vec<u16>> {
5491    if input.is_empty() {
5492        return Err(Error::ParseFailure("supported_versions extension is empty"));
5493    }
5494    let declared = input[0] as usize;
5495    if input.len() != declared + 1 || declared % 2 != 0 {
5496        return Err(Error::ParseFailure(
5497            "invalid supported_versions extension length",
5498        ));
5499    }
5500    let mut versions = Vec::new();
5501    for chunk in input[1..].chunks_exact(2) {
5502        let version = u16::from_be_bytes([chunk[0], chunk[1]]);
5503        if versions.contains(&version) {
5504            return Err(Error::ParseFailure(
5505                "duplicate supported_versions entry in extension body",
5506            ));
5507        }
5508        versions.push(version);
5509    }
5510    Ok(versions)
5511}
5512
5513/// Validates SNI DNS host syntax used by modeled server_name extension hooks.
5514///
5515/// # Arguments
5516///
5517/// * `name` — `name: &str`.
5518///
5519/// # Returns
5520///
5521/// `true` or `false` according to the checks in the function body.
5522///
5523/// # Panics
5524///
5525/// This function does not panic.
5526///
5527fn noxtls_is_valid_sni_dns_name(name: &str) -> bool {
5528    if name.is_empty() || !name.is_ascii() {
5529        return false;
5530    }
5531    let trimmed = if let Some(stripped) = name.strip_suffix('.') {
5532        stripped
5533    } else {
5534        name
5535    };
5536    if trimmed.is_empty() || trimmed.len() > u16::MAX as usize {
5537        return false;
5538    }
5539    if trimmed
5540        .as_bytes()
5541        .iter()
5542        .any(|byte| *byte <= 0x20 || *byte >= 0x7f)
5543    {
5544        return false;
5545    }
5546    for label in trimmed.split('.') {
5547        if label.is_empty() || label.len() > 63 {
5548            return false;
5549        }
5550        let bytes = label.as_bytes();
5551        if bytes.first() == Some(&b'-') || bytes.last() == Some(&b'-') {
5552            return false;
5553        }
5554        if !bytes
5555            .iter()
5556            .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'-')
5557        {
5558            return false;
5559        }
5560    }
5561    true
5562}
5563
5564/// Parses one SNI server_name extension payload into a DNS host_name string.
5565///
5566/// # Arguments
5567///
5568/// * `input` — `input: &[u8]`.
5569///
5570/// # Returns
5571///
5572/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5573///
5574/// # Errors
5575///
5576/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5577///
5578/// # Panics
5579///
5580/// This function does not panic.
5581///
5582fn noxtls_parse_server_name_extension(input: &[u8]) -> Result<String> {
5583    if input.len() < 5 {
5584        return Err(Error::ParseFailure("server_name extension too short"));
5585    }
5586    let list_len = u16::from_be_bytes([input[0], input[1]]) as usize;
5587    if list_len == 0 || input.len() != list_len + 2 {
5588        return Err(Error::ParseFailure("invalid server_name extension length"));
5589    }
5590    if input[2] != 0x00 {
5591        return Err(Error::ParseFailure("unsupported server_name type"));
5592    }
5593    let name_len = u16::from_be_bytes([input[3], input[4]]) as usize;
5594    if name_len == 0 || input.len() != 5 + name_len {
5595        return Err(Error::ParseFailure("invalid server_name host_name length"));
5596    }
5597    let name = core::str::from_utf8(&input[5..])
5598        .map_err(|_| Error::ParseFailure("invalid sni server_name"))?;
5599    if !noxtls_is_valid_sni_dns_name(name) {
5600        return Err(Error::ParseFailure("invalid sni server_name"));
5601    }
5602    Ok(name.to_owned())
5603}
5604
5605/// Encodes one SNI host_name string into TLS server_name extension payload bytes.
5606///
5607/// # Arguments
5608///
5609/// * `server_name` — `server_name: &str`.
5610///
5611/// # Returns
5612///
5613/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5614///
5615/// # Errors
5616///
5617/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5618///
5619/// # Panics
5620///
5621/// This function does not panic.
5622///
5623fn noxtls_encode_server_name_extension_data(server_name: &str) -> Result<Vec<u8>> {
5624    if !noxtls_is_valid_sni_dns_name(server_name) {
5625        return Err(Error::ParseFailure("invalid sni server_name"));
5626    }
5627    let name_bytes = server_name.as_bytes();
5628    let mut entry = Vec::new();
5629    entry.push(0x00); // host_name
5630    entry.extend_from_slice(&(name_bytes.len() as u16).to_be_bytes());
5631    entry.extend_from_slice(name_bytes);
5632    let mut out = Vec::new();
5633    out.extend_from_slice(&(entry.len() as u16).to_be_bytes());
5634    out.extend_from_slice(&entry);
5635    Ok(out)
5636}
5637
5638/// Encodes RFC 6066/8446 `status_request` data for OCSP stapling support.
5639fn noxtls_encode_status_request_ocsp_extension_data() -> Result<Vec<u8>> {
5640    let mut out = Vec::new();
5641    out.push(0x01); // status_type=ocsp
5642    out.extend_from_slice(&0_u16.to_be_bytes()); // responder_id_list length
5643    out.extend_from_slice(&0_u16.to_be_bytes()); // request_extensions length
5644    Ok(out)
5645}
5646
5647/// Parses `status_request` extension and accepts the OCSP form.
5648fn noxtls_parse_status_request_ocsp_extension(input: &[u8]) -> Result<bool> {
5649    if input.len() != 5 {
5650        return Err(Error::ParseFailure(
5651            "invalid status_request extension length",
5652        ));
5653    }
5654    if input[0] != 0x01 {
5655        return Err(Error::ParseFailure(
5656            "status_request extension must use ocsp status type",
5657        ));
5658    }
5659    let responder_id_list_len = u16::from_be_bytes([input[1], input[2]]) as usize;
5660    let request_extensions_len = u16::from_be_bytes([input[3], input[4]]) as usize;
5661    if responder_id_list_len != 0 || request_extensions_len != 0 {
5662        return Err(Error::ParseFailure(
5663            "status_request extension non-empty responder/request vectors are unsupported",
5664        ));
5665    }
5666    Ok(true)
5667}
5668
5669/// Parses ALPN extension payload into ordered protocol-name vector.
5670///
5671/// # Arguments
5672///
5673/// * `input` — `input: &[u8]`.
5674///
5675/// # Returns
5676///
5677/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5678///
5679/// # Errors
5680///
5681/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5682///
5683/// # Panics
5684///
5685/// This function does not panic.
5686///
5687fn noxtls_parse_alpn_protocol_name_list(input: &[u8]) -> Result<Vec<Vec<u8>>> {
5688    if input.len() < 2 {
5689        return Err(Error::ParseFailure(
5690            "alpn extension missing protocol_name_list",
5691        ));
5692    }
5693    let declared_len = u16::from_be_bytes([input[0], input[1]]) as usize;
5694    if declared_len == 0 || input.len() != declared_len + 2 {
5695        return Err(Error::ParseFailure("invalid alpn extension length"));
5696    }
5697    let mut cursor = &input[2..];
5698    let mut protocols = Vec::new();
5699    while !cursor.is_empty() {
5700        let protocol_len = cursor[0] as usize;
5701        cursor = &cursor[1..];
5702        if protocol_len == 0 {
5703            return Err(Error::ParseFailure("alpn protocol must not be empty"));
5704        }
5705        if cursor.len() < protocol_len {
5706            return Err(Error::ParseFailure("alpn protocol truncated"));
5707        }
5708        let protocol = cursor[..protocol_len].to_vec();
5709        if protocols.contains(&protocol) {
5710            return Err(Error::ParseFailure("duplicate alpn protocol"));
5711        }
5712        protocols.push(protocol);
5713        cursor = &cursor[protocol_len..];
5714    }
5715    Ok(protocols)
5716}
5717
5718/// Encodes ordered ALPN protocol names into TLS extension payload bytes.
5719///
5720/// # Arguments
5721///
5722/// * `protocols` — `protocols: &[Vec<u8>]`.
5723///
5724/// # Returns
5725///
5726/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5727///
5728/// # Errors
5729///
5730/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5731///
5732/// # Panics
5733///
5734/// This function does not panic.
5735///
5736fn noxtls_encode_alpn_extension_data(protocols: &[Vec<u8>]) -> Result<Vec<u8>> {
5737    if protocols.is_empty() {
5738        return Err(Error::InvalidLength(
5739            "alpn extension must include at least one protocol",
5740        ));
5741    }
5742    let mut protocol_name_list = Vec::new();
5743    let mut seen_protocols = Vec::new();
5744    for protocol in protocols {
5745        if protocol.is_empty() {
5746            return Err(Error::InvalidLength("alpn protocol must not be empty"));
5747        }
5748        if protocol.len() > u8::MAX as usize {
5749            return Err(Error::InvalidLength(
5750                "alpn protocol length must not exceed 255 bytes",
5751            ));
5752        }
5753        if seen_protocols.contains(protocol) {
5754            return Err(Error::ParseFailure("duplicate alpn protocol"));
5755        }
5756        seen_protocols.push(protocol.clone());
5757        protocol_name_list.push(protocol.len() as u8);
5758        protocol_name_list.extend_from_slice(protocol);
5759    }
5760    let mut extension_data = Vec::new();
5761    extension_data.extend_from_slice(&(protocol_name_list.len() as u16).to_be_bytes());
5762    extension_data.extend_from_slice(&protocol_name_list);
5763    Ok(extension_data)
5764}
5765
5766/// Parses `<len:u16><items:u16...>` style vector and returns u16 items.
5767///
5768/// # Arguments
5769///
5770/// * `input` — `input: &[u8]`.
5771///
5772/// # Returns
5773///
5774/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5775///
5776/// # Errors
5777///
5778/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5779///
5780/// # Panics
5781///
5782/// This function does not panic.
5783///
5784fn noxtls_parse_u16_vector_with_len(input: &[u8]) -> Result<Vec<u16>> {
5785    if input.len() < 2 {
5786        return Err(Error::ParseFailure("u16 vector missing length prefix"));
5787    }
5788    let len = u16::from_be_bytes([input[0], input[1]]) as usize;
5789    if input.len() != len + 2 || len % 2 != 0 {
5790        return Err(Error::ParseFailure("invalid u16 vector length"));
5791    }
5792    let mut out = Vec::with_capacity(len / 2);
5793    for chunk in input[2..].chunks_exact(2) {
5794        let value = u16::from_be_bytes([chunk[0], chunk[1]]);
5795        if !out.contains(&value) {
5796            out.push(value);
5797        }
5798    }
5799    Ok(out)
5800}
5801
5802fn noxtls_parse_compress_certificate_extension(input: &[u8]) -> Result<Vec<u16>> {
5803    if input.is_empty() {
5804        return Err(Error::ParseFailure(
5805            "certificate compression extension missing length prefix",
5806        ));
5807    }
5808    let len = input[0] as usize;
5809    if len == 0 {
5810        return Err(Error::ParseFailure(
5811            "certificate compression extension algorithm list must not be empty",
5812        ));
5813    }
5814    if input.len() != len + 1 || len % 2 != 0 {
5815        return Err(Error::ParseFailure(
5816            "invalid certificate compression extension length",
5817        ));
5818    }
5819    let mut algorithms = Vec::with_capacity(len / 2);
5820    for chunk in input[1..].chunks_exact(2) {
5821        let algorithm = u16::from_be_bytes([chunk[0], chunk[1]]);
5822        if !algorithms.contains(&algorithm) {
5823            algorithms.push(algorithm);
5824        }
5825    }
5826    Ok(algorithms)
5827}
5828
5829/// Parses `<len:u8><items:u8...>` style vector and returns u8 items.
5830///
5831/// # Arguments
5832///
5833/// * `input` — `input: &[u8]`.
5834///
5835/// # Returns
5836///
5837/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5838///
5839/// # Errors
5840///
5841/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5842///
5843/// # Panics
5844///
5845/// This function does not panic.
5846///
5847fn noxtls_parse_u8_vector_with_len(input: &[u8]) -> Result<Vec<u8>> {
5848    if input.is_empty() {
5849        return Err(Error::ParseFailure("u8 vector missing length prefix"));
5850    }
5851    let len = input[0] as usize;
5852    if input.len() != len + 1 {
5853        return Err(Error::ParseFailure("invalid u8 vector length"));
5854    }
5855    if len == 0 {
5856        return Err(Error::ParseFailure("u8 vector must not be empty"));
5857    }
5858    let mut out = Vec::new();
5859    for value in &input[1..] {
5860        if out.contains(value) {
5861            return Err(Error::ParseFailure("duplicate u8 vector entry"));
5862        }
5863        out.push(*value);
5864    }
5865    Ok(out)
5866}
5867
5868/// Parses CertificateRequest body shape used by TLS 1.3.
5869///
5870/// # Arguments
5871///
5872/// * `body` — `body: &[u8]`.
5873///
5874/// # Returns
5875///
5876/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5877///
5878/// # Errors
5879///
5880/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5881///
5882/// # Panics
5883///
5884/// This function does not panic.
5885///
5886fn noxtls_parse_certificate_request_body(body: &[u8]) -> Result<()> {
5887    if body.len() < 3 {
5888        return Err(Error::ParseFailure("certificate request body too short"));
5889    }
5890    let context_len = body[0] as usize;
5891    let ext_len_offset = 1 + context_len;
5892    if body.len() < ext_len_offset + 2 {
5893        return Err(Error::ParseFailure("certificate request context truncated"));
5894    }
5895    let ext_len = u16::from_be_bytes([body[ext_len_offset], body[ext_len_offset + 1]]) as usize;
5896    let ext_start = ext_len_offset + 2;
5897    if body.len() != ext_start + ext_len {
5898        return Err(Error::ParseFailure(
5899            "certificate request extensions truncated",
5900        ));
5901    }
5902    noxtls_parse_certificate_request_extensions(&body[ext_start..])?;
5903    Ok(())
5904}
5905
5906/// Parses CertificateRequest extensions vector and validates entry structure.
5907///
5908/// # Arguments
5909///
5910/// * `input` — `input: &[u8]`.
5911///
5912/// # Returns
5913///
5914/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5915///
5916/// # Errors
5917///
5918/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5919///
5920/// # Panics
5921///
5922/// This function does not panic.
5923///
5924fn noxtls_parse_certificate_request_extensions(input: &[u8]) -> Result<()> {
5925    let mut cursor = input;
5926    let mut seen_extension_types = Vec::new();
5927    let mut seen_signature_algorithms = false;
5928    while !cursor.is_empty() {
5929        if cursor.len() < 4 {
5930            return Err(Error::ParseFailure(
5931                "certificate request extension header truncated",
5932            ));
5933        }
5934        let ext_type = u16::from_be_bytes([cursor[0], cursor[1]]);
5935        let ext_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
5936        if seen_extension_types.contains(&ext_type) {
5937            return Err(Error::ParseFailure(
5938                "duplicate certificate request extension type",
5939            ));
5940        }
5941        if matches!(
5942            ext_type,
5943            EXT_SUPPORTED_VERSIONS
5944                | EXT_KEY_SHARE
5945                | EXT_PRE_SHARED_KEY
5946                | EXT_PSK_KEY_EXCHANGE_MODES
5947                | EXT_SERVER_NAME
5948        ) {
5949            return Err(Error::ParseFailure(
5950                "certificate request contains forbidden extension type",
5951            ));
5952        }
5953        seen_extension_types.push(ext_type);
5954        cursor = &cursor[4..];
5955        if cursor.len() < ext_len {
5956            return Err(Error::ParseFailure(
5957                "certificate request extension truncated",
5958            ));
5959        }
5960        if ext_type == EXT_SIGNATURE_ALGORITHMS {
5961            let signature_algorithms = noxtls_parse_u16_vector_with_len(&cursor[..ext_len])?;
5962            if signature_algorithms.is_empty() {
5963                return Err(Error::ParseFailure(
5964                    "certificate request signature_algorithms must not be empty",
5965                ));
5966            }
5967            seen_signature_algorithms = true;
5968        }
5969        cursor = &cursor[ext_len..];
5970    }
5971    if !seen_signature_algorithms {
5972        return Err(Error::ParseFailure(
5973            "certificate request missing signature_algorithms extension",
5974        ));
5975    }
5976    Ok(())
5977}
5978
5979/// Parses EncryptedExtensions body and validates extension vector structure.
5980///
5981/// # Arguments
5982///
5983/// * `body` — `body: &[u8]`.
5984///
5985/// # Returns
5986///
5987/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
5988///
5989/// # Errors
5990///
5991/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
5992///
5993/// # Panics
5994///
5995/// This function does not panic.
5996///
5997fn noxtls_parse_encrypted_extensions_body(body: &[u8]) -> Result<ParsedEncryptedExtensions> {
5998    if body.len() < 2 {
5999        return Err(Error::ParseFailure("encrypted extensions body too short"));
6000    }
6001    let extensions_len = u16::from_be_bytes([body[0], body[1]]) as usize;
6002    if body.len() != 2 + extensions_len {
6003        return Err(Error::ParseFailure("encrypted extensions malformed length"));
6004    }
6005    let mut cursor = &body[2..];
6006    let mut seen_extension_types = Vec::new();
6007    let mut selected_alpn_protocol = None;
6008    let mut server_name_acknowledged = false;
6009    let mut early_data_accepted = false;
6010    let mut server_certificate_type = None;
6011    while !cursor.is_empty() {
6012        if cursor.len() < 4 {
6013            return Err(Error::ParseFailure(
6014                "encrypted extensions entry header truncated",
6015            ));
6016        }
6017        let ext_type = u16::from_be_bytes([cursor[0], cursor[1]]);
6018        let ext_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
6019        if ext_len > TLS13_MAX_EXTENSION_VALUE_BYTES {
6020            return Err(Error::ParseFailure(
6021                "encrypted extensions extension value exceeds modeled maximum",
6022            ));
6023        }
6024        if seen_extension_types.contains(&ext_type) {
6025            return Err(Error::ParseFailure("duplicate encrypted extensions type"));
6026        }
6027        seen_extension_types.push(ext_type);
6028        cursor = &cursor[4..];
6029        if cursor.len() < ext_len {
6030            return Err(Error::ParseFailure("encrypted extensions entry truncated"));
6031        }
6032        let ext_data = &cursor[..ext_len];
6033        match ext_type {
6034            EXT_SERVER_NAME => {
6035                if !ext_data.is_empty() {
6036                    return Err(Error::ParseFailure(
6037                        "encrypted extensions server_name must be empty",
6038                    ));
6039                }
6040                server_name_acknowledged = true;
6041            }
6042            EXT_ALPN => {
6043                let protocols = noxtls_parse_alpn_protocol_name_list(ext_data)?;
6044                if protocols.len() != 1 {
6045                    return Err(Error::ParseFailure(
6046                        "encrypted extensions alpn must select exactly one protocol",
6047                    ));
6048                }
6049                selected_alpn_protocol = protocols.first().cloned();
6050            }
6051            EXT_EARLY_DATA => {
6052                if !ext_data.is_empty() {
6053                    return Err(Error::ParseFailure(
6054                        "encrypted extensions early_data must be empty",
6055                    ));
6056                }
6057                early_data_accepted = true;
6058            }
6059            EXT_SERVER_CERTIFICATE_TYPE => {
6060                if ext_data.len() != 1 {
6061                    return Err(Error::ParseFailure(
6062                        "encrypted extensions server_certificate_type must be one byte",
6063                    ));
6064                }
6065                match ext_data[0] {
6066                    TLS_CERTIFICATE_TYPE_X509 | TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY => {
6067                        server_certificate_type = Some(ext_data[0]);
6068                    }
6069                    _ => {
6070                        return Err(Error::UnsupportedFeature(
6071                            "unsupported server_certificate_type selected",
6072                        ));
6073                    }
6074                }
6075            }
6076            EXT_SUPPORTED_VERSIONS
6077            | EXT_KEY_SHARE
6078            | EXT_PRE_SHARED_KEY
6079            | EXT_PSK_KEY_EXCHANGE_MODES => {
6080                return Err(Error::ParseFailure(
6081                    "encrypted extensions contains forbidden extension type",
6082                ));
6083            }
6084            _ => {}
6085        }
6086        cursor = &cursor[ext_len..];
6087    }
6088    Ok(ParsedEncryptedExtensions {
6089        selected_alpn_protocol,
6090        server_name_acknowledged,
6091        early_data_accepted,
6092        server_certificate_type,
6093    })
6094}
6095
6096/// Parses Certificate body shape used by TLS 1.3 and extracts certificate entries.
6097///
6098/// # Arguments
6099///
6100/// * `body` — `body: &[u8]`.
6101///
6102/// # Returns
6103///
6104/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
6105///
6106/// # Errors
6107///
6108/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
6109///
6110/// # Panics
6111///
6112/// This function does not panic.
6113///
6114/// Maps X.509 chain-validation errors into stable protocol-layer failure strings.
6115///
6116/// # Arguments
6117///
6118/// * `err` — Validation error returned by `noxtls-x509`.
6119///
6120/// # Returns
6121///
6122/// `noxtls_core::Error` preserving the underlying failure category.
6123///
6124/// # Panics
6125///
6126/// This function does not panic.
6127fn noxtls_map_certificate_validation_error(err: ValidationError) -> Error {
6128    let message = match err {
6129        ValidationError::InvalidNowTimeFormat => "server certificate validation time is invalid",
6130        ValidationError::CertificateNotYetValid => "server certificate is not yet valid",
6131        ValidationError::CertificateExpired => "server certificate is expired",
6132        ValidationError::IssuerNotFound => "server certificate issuer not found",
6133        ValidationError::IssuerNotCa => "server certificate issuer is not a CA",
6134        ValidationError::IssuerMissingKeyCertSign => {
6135            "server certificate issuer missing keyCertSign usage"
6136        }
6137        ValidationError::PathLenExceeded => "server certificate path length exceeded",
6138        ValidationError::UntrustedRoot => {
6139            "server certificate chain does not terminate at trust anchor"
6140        }
6141        ValidationError::ChainLoopDetected => "server certificate chain loop detected",
6142        ValidationError::MaxChainDepthExceeded => "server certificate chain depth exceeded",
6143        ValidationError::SignatureAlgorithmMismatch => {
6144            "server certificate signature algorithm mismatch"
6145        }
6146        ValidationError::UnsupportedSignatureAlgorithm => {
6147            "server certificate signature algorithm unsupported"
6148        }
6149        ValidationError::UnsupportedPublicKeyAlgorithm => {
6150            "server certificate issuer public key algorithm unsupported"
6151        }
6152        ValidationError::PublicKeyDecodeFailed => {
6153            "server certificate issuer public key decode failed"
6154        }
6155        ValidationError::SignatureVerificationFailed => {
6156            "server certificate signature verification failed"
6157        }
6158        ValidationError::MissingRequiredPolicy => "server certificate missing required policy OID",
6159        ValidationError::MissingRequiredExtendedKeyUsage => {
6160            "server certificate missing required extended key usage"
6161        }
6162        ValidationError::ExplicitPolicyRequired => {
6163            "server certificate policy set is empty under explicit policy mode"
6164        }
6165        ValidationError::PolicyMappingInhibited => {
6166            "server certificate policy mappings are inhibited"
6167        }
6168        ValidationError::NameConstraintsViolation => {
6169            "server certificate violates issuer name constraints"
6170        }
6171        ValidationError::MissingRevocationInfo => {
6172            "server certificate missing revocation distribution info"
6173        }
6174        ValidationError::MissingRevocationLocator => {
6175            "server certificate missing revocation locator"
6176        }
6177    };
6178    Error::CryptoFailure(message)
6179}
6180
6181fn noxtls_parse_certificate_body(
6182    body: &[u8],
6183    certificate_type: u8,
6184) -> Result<ParsedTls13CertificateBody> {
6185    if body.len() < 4 {
6186        return Err(Error::ParseFailure("certificate body too short"));
6187    }
6188    match certificate_type {
6189        TLS_CERTIFICATE_TYPE_X509 | TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY => {}
6190        _ => {
6191            return Err(Error::UnsupportedFeature(
6192                "unsupported certificate message type",
6193            ));
6194        }
6195    }
6196    let context_len = body[0] as usize;
6197    let list_len_offset = 1 + context_len;
6198    if body.len() < list_len_offset + 3 {
6199        return Err(Error::ParseFailure("certificate list length missing"));
6200    }
6201    let cert_list_len = u32::from_be_bytes([
6202        0x00,
6203        body[list_len_offset],
6204        body[list_len_offset + 1],
6205        body[list_len_offset + 2],
6206    ]) as usize;
6207    let cert_list_start = list_len_offset + 3;
6208    let cert_list_end = cert_list_start + cert_list_len;
6209    if cert_list_end > body.len() {
6210        return Err(Error::ParseFailure("certificate list truncated"));
6211    }
6212    let mut certificates = Vec::new();
6213    let mut cursor = &body[cert_list_start..cert_list_end];
6214    let mut leaf_ocsp_staple = None;
6215    while !cursor.is_empty() {
6216        if cursor.len() < 5 {
6217            return Err(Error::ParseFailure("certificate entry truncated"));
6218        }
6219        let cert_len = u32::from_be_bytes([0x00, cursor[0], cursor[1], cursor[2]]) as usize;
6220        let cert_end = 3 + cert_len;
6221        if cursor.len() < cert_end + 2 {
6222            return Err(Error::ParseFailure("certificate bytes truncated"));
6223        }
6224        certificates.push(cursor[3..cert_end].to_vec());
6225        let ext_len = u16::from_be_bytes([cursor[cert_end], cursor[cert_end + 1]]) as usize;
6226        let ext_end = cert_end + 2 + ext_len;
6227        if cursor.len() < ext_end {
6228            return Err(Error::ParseFailure(
6229                "certificate entry extensions truncated",
6230            ));
6231        }
6232        let parsed_staple =
6233            noxtls_parse_certificate_entry_extensions(&cursor[cert_end + 2..ext_end])?;
6234        if certificates.len() == 1 {
6235            leaf_ocsp_staple = parsed_staple;
6236        }
6237        cursor = &cursor[ext_end..];
6238    }
6239    if certificates.is_empty() {
6240        return Err(Error::ParseFailure("certificate list must not be empty"));
6241    }
6242    if certificate_type == TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY {
6243        if certificates.len() != 1 {
6244            return Err(Error::ParseFailure(
6245                "raw public key certificate list must contain exactly one entry",
6246            ));
6247        }
6248        noxtls_parse_spki_public_key_info_der(&certificates[0])?;
6249    }
6250    if cert_list_end != body.len() {
6251        return Err(Error::ParseFailure("certificate body trailing bytes"));
6252    }
6253    Ok(ParsedTls13CertificateBody {
6254        certificate_type,
6255        certificates,
6256        leaf_ocsp_staple,
6257    })
6258}
6259
6260/// Parses a TLS 1.2 Certificate body and extracts DER certificate entries.
6261///
6262/// # Arguments
6263///
6264/// * `body` — Handshake body bytes for a TLS 1.2 `Certificate` message.
6265///
6266/// # Returns
6267///
6268/// On success, non-empty DER certificate entries in wire order (leaf first).
6269///
6270/// # Errors
6271///
6272/// Returns [`noxtls_core::Error`] for malformed list framing, truncated entries, or trailing bytes.
6273///
6274/// # Panics
6275///
6276/// This function does not panic.
6277fn noxtls_parse_tls12_certificate_list(body: &[u8]) -> Result<Vec<Vec<u8>>> {
6278    if body.len() < 3 {
6279        return Err(Error::ParseFailure(
6280            "tls12 certificate message is malformed",
6281        ));
6282    }
6283    let list_len = ((body[0] as usize) << 16) | ((body[1] as usize) << 8) | body[2] as usize;
6284    if list_len == 0 || list_len != body.len() - 3 {
6285        return Err(Error::ParseFailure(
6286            "tls12 certificate list length is malformed",
6287        ));
6288    }
6289    let mut certificates = Vec::new();
6290    let mut cursor = &body[3..];
6291    while !cursor.is_empty() {
6292        if cursor.len() < 3 {
6293            return Err(Error::ParseFailure(
6294                "tls12 certificate entry length is truncated",
6295            ));
6296        }
6297        let cert_len =
6298            ((cursor[0] as usize) << 16) | ((cursor[1] as usize) << 8) | cursor[2] as usize;
6299        if cert_len == 0 {
6300            return Err(Error::ParseFailure(
6301                "tls12 certificate entry must not be empty",
6302            ));
6303        }
6304        if cursor.len() < 3 + cert_len {
6305            return Err(Error::ParseFailure("tls12 certificate entry is truncated"));
6306        }
6307        certificates.push(cursor[3..3 + cert_len].to_vec());
6308        cursor = &cursor[3 + cert_len..];
6309    }
6310    if certificates.is_empty() {
6311        return Err(Error::ParseFailure(
6312            "tls12 certificate list must not be empty",
6313        ));
6314    }
6315    Ok(certificates)
6316}
6317
6318#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6319enum Tls12KeyExchangeKind {
6320    EcdheSigned,
6321    DheSigned,
6322    StaticRsa,
6323    Psk,
6324    Ecjpake,
6325}
6326
6327fn noxtls_tls12_key_exchange_kind(suite: CipherSuite) -> Result<Tls12KeyExchangeKind> {
6328    match suite {
6329        CipherSuite::TlsEcdheRsaWithAes128GcmSha256
6330        | CipherSuite::TlsEcdheRsaWithAes256GcmSha384
6331        | CipherSuite::TlsEcdheRsaWithAes128CbcSha
6332        | CipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256
6333        | CipherSuite::TlsEcdheEcdsaWithAes128GcmSha256
6334        | CipherSuite::TlsEcdheEcdsaWithAes256GcmSha384
6335        | CipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256
6336        | CipherSuite::TlsEcdheEcdsaWithAes128CcmSha256
6337        | CipherSuite::TlsEcdheEcdsaWithAes256CcmSha256
6338        | CipherSuite::TlsEcdheEcdsaWithAes128Ccm8Sha256
6339        | CipherSuite::TlsEcdheEcdsaWithAes256Ccm8Sha256 => Ok(Tls12KeyExchangeKind::EcdheSigned),
6340        CipherSuite::TlsDheRsaWithChacha20Poly1305Sha256
6341        | CipherSuite::TlsDheRsaWithAes128GcmSha256
6342        | CipherSuite::TlsDheRsaWithAes256GcmSha384
6343        | CipherSuite::TlsDheRsaWithAes128CcmSha256
6344        | CipherSuite::TlsDheRsaWithAes256CcmSha256
6345        | CipherSuite::TlsDheRsaWithAes128Ccm8Sha256
6346        | CipherSuite::TlsDheRsaWithAes256Ccm8Sha256 => Ok(Tls12KeyExchangeKind::DheSigned),
6347        CipherSuite::TlsRsaWithAes128CbcSha
6348        | CipherSuite::TlsRsaWithAes128GcmSha256
6349        | CipherSuite::TlsRsaWithAes256GcmSha384
6350        | CipherSuite::TlsRsaWithAes128CcmSha256
6351        | CipherSuite::TlsRsaWithAes256CcmSha256
6352        | CipherSuite::TlsRsaWithAes128Ccm8Sha256
6353        | CipherSuite::TlsRsaWithAes256Ccm8Sha256 => Ok(Tls12KeyExchangeKind::StaticRsa),
6354        CipherSuite::TlsPskWithAes128Ccm8Sha256 => Ok(Tls12KeyExchangeKind::Psk),
6355        CipherSuite::TlsEcjpakeWithAes128Ccm8Sha256 => Ok(Tls12KeyExchangeKind::Ecjpake),
6356        _ => Err(Error::UnsupportedFeature(
6357            "tls12 handshake key exchange requires a tls 1.2 cipher suite",
6358        )),
6359    }
6360}
6361
6362fn noxtls_parse_tls12_server_key_exchange_body(suite: CipherSuite, body: &[u8]) -> Result<()> {
6363    match noxtls_tls12_key_exchange_kind(suite)? {
6364        Tls12KeyExchangeKind::StaticRsa => {
6365            if body.is_empty() {
6366                Ok(())
6367            } else {
6368                Err(Error::ParseFailure(
6369                    "tls12 rsa key transport must omit server key exchange",
6370                ))
6371            }
6372        }
6373        Tls12KeyExchangeKind::Psk => noxtls_parse_tls12_psk_server_key_exchange_body(body),
6374        Tls12KeyExchangeKind::Ecjpake => noxtls_parse_tls12_ecjpake_key_exchange_body(
6375            body,
6376            "tls12 ecjpake server key exchange body must not be empty",
6377        ),
6378        Tls12KeyExchangeKind::EcdheSigned => {
6379            noxtls_parse_tls12_ecdhe_server_key_exchange_body(body)
6380        }
6381        Tls12KeyExchangeKind::DheSigned => noxtls_parse_tls12_dhe_server_key_exchange_body(body),
6382    }
6383}
6384
6385fn noxtls_parse_tls12_client_key_exchange_body(suite: CipherSuite, body: &[u8]) -> Result<()> {
6386    match noxtls_tls12_key_exchange_kind(suite)? {
6387        Tls12KeyExchangeKind::StaticRsa => {
6388            noxtls_parse_tls12_u16_opaque(body, "tls12 rsa encrypted pre-master secret")
6389        }
6390        Tls12KeyExchangeKind::EcdheSigned => {
6391            noxtls_parse_tls12_u8_opaque(body, "tls12 ecdhe client key exchange public key")
6392        }
6393        Tls12KeyExchangeKind::DheSigned => {
6394            noxtls_parse_tls12_u16_opaque(body, "tls12 dhe client public key")
6395        }
6396        Tls12KeyExchangeKind::Psk => noxtls_parse_tls12_u16_opaque(body, "tls12 psk identity"),
6397        Tls12KeyExchangeKind::Ecjpake => noxtls_parse_tls12_ecjpake_key_exchange_body(
6398            body,
6399            "tls12 ecjpake client key exchange body must not be empty",
6400        ),
6401    }
6402}
6403
6404fn noxtls_parse_tls12_ecdhe_server_key_exchange_body(body: &[u8]) -> Result<()> {
6405    if body.len() < 8 {
6406        return Err(Error::ParseFailure(
6407            "tls12 ecdhe server key exchange body must include key share and signature fields",
6408        ));
6409    }
6410    if body[0] != 0x03 {
6411        return Err(Error::ParseFailure(
6412            "tls12 ecdhe server key exchange requires named_curve parameters",
6413        ));
6414    }
6415    let public_len = body[3] as usize;
6416    if public_len == 0 {
6417        return Err(Error::ParseFailure(
6418            "tls12 ecdhe server key exchange public key must not be empty",
6419        ));
6420    }
6421    let signature_header_offset = 4 + public_len;
6422    noxtls_parse_tls12_signature_tail(body, signature_header_offset)
6423}
6424
6425fn noxtls_parse_tls12_dhe_server_key_exchange_body(body: &[u8]) -> Result<()> {
6426    let _ = noxtls_tls12_dhe_server_key_exchange_values(body)?;
6427    Ok(())
6428}
6429
6430fn noxtls_tls12_dhe_server_key_exchange_values(body: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
6431    let (prime, cursor) = noxtls_parse_tls12_u16_opaque_value(body, "tls12 dhe prime")?;
6432    let (generator, cursor) = noxtls_parse_tls12_u16_opaque_value(cursor, "tls12 dhe generator")?;
6433    let (server_public, cursor) =
6434        noxtls_parse_tls12_u16_opaque_value(cursor, "tls12 dhe server public key")?;
6435    let signature_header_offset = body.len() - cursor.len();
6436    noxtls_parse_tls12_signature_tail(body, signature_header_offset)?;
6437    Ok((prime.to_vec(), generator.to_vec(), server_public.to_vec()))
6438}
6439
6440fn noxtls_parse_tls12_psk_server_key_exchange_body(body: &[u8]) -> Result<()> {
6441    if body.len() < 2 {
6442        return Err(Error::ParseFailure(
6443            "tls12 psk server key exchange missing identity hint length",
6444        ));
6445    }
6446    let hint_len = u16::from_be_bytes([body[0], body[1]]) as usize;
6447    if body.len() != 2 + hint_len {
6448        return Err(Error::ParseFailure(
6449            "tls12 psk server key exchange identity hint length is malformed",
6450        ));
6451    }
6452    Ok(())
6453}
6454
6455fn noxtls_parse_tls12_ecjpake_key_exchange_body(body: &[u8], message: &'static str) -> Result<()> {
6456    if body.is_empty() {
6457        Err(Error::ParseFailure(message))
6458    } else {
6459        Ok(())
6460    }
6461}
6462
6463fn noxtls_parse_tls12_u8_opaque(body: &[u8], _label: &'static str) -> Result<()> {
6464    if body.is_empty() {
6465        return Err(Error::ParseFailure(
6466            "tls12 client key exchange body must not be empty",
6467        ));
6468    }
6469    let len = body[0] as usize;
6470    if len == 0 {
6471        return Err(Error::ParseFailure(
6472            "tls12 client key exchange value must not be empty",
6473        ));
6474    }
6475    if body.len() != 1 + len {
6476        return Err(Error::ParseFailure(
6477            "tls12 client key exchange length is malformed",
6478        ));
6479    }
6480    Ok(())
6481}
6482
6483fn noxtls_parse_tls12_u16_opaque(body: &[u8], label: &'static str) -> Result<()> {
6484    let (_value, remaining) = noxtls_parse_tls12_u16_opaque_value(body, label)?;
6485    if remaining.is_empty() {
6486        Ok(())
6487    } else {
6488        Err(Error::ParseFailure(
6489            "tls12 client key exchange has trailing bytes",
6490        ))
6491    }
6492}
6493
6494fn noxtls_tls12_u16_opaque_value(body: &[u8], label: &'static str) -> Result<Vec<u8>> {
6495    let (value, remaining) = noxtls_parse_tls12_u16_opaque_value(body, label)?;
6496    if !remaining.is_empty() {
6497        return Err(Error::ParseFailure(
6498            "tls12 client key exchange has trailing bytes",
6499        ));
6500    }
6501    Ok(value.to_vec())
6502}
6503
6504fn noxtls_parse_tls12_u16_opaque_value<'a>(
6505    body: &'a [u8],
6506    _label: &'static str,
6507) -> Result<(&'a [u8], &'a [u8])> {
6508    if body.len() < 2 {
6509        return Err(Error::ParseFailure("tls12 opaque vector missing length"));
6510    }
6511    let len = u16::from_be_bytes([body[0], body[1]]) as usize;
6512    if len == 0 {
6513        return Err(Error::ParseFailure("tls12 opaque vector must not be empty"));
6514    }
6515    if body.len() < 2 + len {
6516        return Err(Error::ParseFailure("tls12 opaque vector is truncated"));
6517    }
6518    Ok((&body[2..2 + len], &body[2 + len..]))
6519}
6520
6521fn noxtls_parse_tls12_signature_tail(body: &[u8], offset: usize) -> Result<()> {
6522    noxtls_parse_tls12_signature_fields(body, offset).map(|_| ())
6523}
6524
6525fn noxtls_parse_tls12_certificate_verify_body(body: &[u8]) -> Result<()> {
6526    if body.len() < 4 {
6527        return Err(Error::ParseFailure(
6528            "tls12 client certificate verify body must include signature scheme and length",
6529        ));
6530    }
6531    let signature_scheme = u16::from_be_bytes([body[0], body[1]]);
6532    if !noxtls_tls12_signature_scheme_is_modern(signature_scheme) {
6533        return Err(Error::ParseFailure(
6534            "tls12 client certificate verify uses unsupported signature scheme",
6535        ));
6536    }
6537    let signature_len = u16::from_be_bytes([body[2], body[3]]) as usize;
6538    if signature_len == 0 {
6539        return Err(Error::ParseFailure(
6540            "tls12 client certificate verify signature must not be empty",
6541        ));
6542    }
6543    if body.len() != 4 + signature_len {
6544        return Err(Error::ParseFailure(
6545            "tls12 client certificate verify signature length is malformed",
6546        ));
6547    }
6548    Ok(())
6549}
6550
6551/// Returns whether TLS 1.2 signature noxtls_algorithm is allowed by default-safe policy.
6552///
6553/// # Arguments
6554///
6555/// * `signature_scheme` — TLS SignatureScheme identifier.
6556///
6557/// # Returns
6558///
6559/// `true` for modern schemes enabled by default, `false` otherwise.
6560///
6561/// # Panics
6562///
6563/// This function does not panic.
6564fn noxtls_tls12_signature_scheme_is_modern(signature_scheme: u16) -> bool {
6565    matches!(
6566        signature_scheme,
6567        TLS13_SIGALG_ECDSA_SECP256R1_SHA256
6568            | TLS13_SIGALG_ECDSA_SECP384R1_SHA384
6569            | TLS13_SIGALG_ECDSA_SECP521R1_SHA512
6570            | TLS13_SIGALG_RSA_PSS_RSAE_SHA256
6571            | TLS13_SIGALG_RSA_PSS_RSAE_SHA384
6572            | TLS13_SIGALG_RSA_PSS_RSAE_SHA512
6573            | TLS13_SIGALG_RSA_PSS_PSS_SHA256
6574            | TLS13_SIGALG_RSA_PSS_PSS_SHA384
6575            | TLS13_SIGALG_RSA_PSS_PSS_SHA512
6576            | TLS13_SIGALG_ED25519
6577            | TLS13_SIGALG_MLDSA65
6578    )
6579}
6580
6581/// Parses one CertificateEntry extension vector and validates structure.
6582///
6583/// # Arguments
6584///
6585/// * `input` — `input: &[u8]`.
6586///
6587/// # Returns
6588///
6589/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
6590///
6591/// # Errors
6592///
6593/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
6594///
6595/// # Panics
6596///
6597/// This function does not panic.
6598///
6599fn noxtls_parse_certificate_entry_extensions(input: &[u8]) -> Result<Option<Vec<u8>>> {
6600    let mut cursor = input;
6601    let mut seen_extension_types = Vec::new();
6602    let mut status_request_ocsp = None;
6603    while !cursor.is_empty() {
6604        if cursor.len() < 4 {
6605            return Err(Error::ParseFailure(
6606                "certificate entry extension header truncated",
6607            ));
6608        }
6609        let ext_type = u16::from_be_bytes([cursor[0], cursor[1]]);
6610        let ext_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
6611        if seen_extension_types.contains(&ext_type) {
6612            return Err(Error::ParseFailure(
6613                "duplicate certificate entry extension type",
6614            ));
6615        }
6616        seen_extension_types.push(ext_type);
6617        cursor = &cursor[4..];
6618        if cursor.len() < ext_len {
6619            return Err(Error::ParseFailure("certificate entry extension truncated"));
6620        }
6621        let ext_data = &cursor[..ext_len];
6622        if ext_type == EXT_STATUS_REQUEST {
6623            if status_request_ocsp.is_some() {
6624                return Err(Error::ParseFailure(
6625                    "duplicate certificate entry status_request extension",
6626                ));
6627            }
6628            status_request_ocsp = Some(noxtls_parse_certificate_entry_status_request_extension(
6629                ext_data,
6630            )?);
6631        }
6632        cursor = &cursor[ext_len..];
6633    }
6634    Ok(status_request_ocsp)
6635}
6636
6637/// Encodes one CertificateEntry `status_request` extension with OCSP staple payload.
6638fn noxtls_encode_certificate_entry_status_request_extension(ocsp_staple: &[u8]) -> Result<Vec<u8>> {
6639    if ocsp_staple.is_empty() {
6640        return Err(Error::InvalidLength("ocsp staple must not be empty"));
6641    }
6642    if ocsp_staple.len() > 0x00FF_FFFF {
6643        return Err(Error::InvalidLength("ocsp staple is too large"));
6644    }
6645    let mut status_request_payload = Vec::new();
6646    status_request_payload.push(0x01); // status_type=ocsp
6647    let staple_len = ocsp_staple.len() as u32;
6648    status_request_payload.extend_from_slice(&staple_len.to_be_bytes()[1..4]);
6649    status_request_payload.extend_from_slice(ocsp_staple);
6650
6651    let mut extension = Vec::new();
6652    extension.extend_from_slice(&EXT_STATUS_REQUEST.to_be_bytes());
6653    extension.extend_from_slice(&(status_request_payload.len() as u16).to_be_bytes());
6654    extension.extend_from_slice(&status_request_payload);
6655    Ok(extension)
6656}
6657
6658/// Parses and validates a stapled OCSP response against a validation time.
6659pub fn noxtls_parse_tls13_ocsp_staple_info(
6660    ocsp_response_der: &[u8],
6661    validation_time: &str,
6662) -> Result<Tls13OcspStapleInfo> {
6663    let now = noxtls_canonical_ocsp_time(validation_time)
6664        .ok_or(Error::ParseFailure("invalid ocsp validation time"))?;
6665    let (outer, rest) = noxtls_parse_der_node(ocsp_response_der)?;
6666    if outer.tag != 0x30 || !rest.is_empty() {
6667        return Err(Error::ParseFailure("ocsp response must be a DER sequence"));
6668    }
6669    let (status, cursor) = noxtls_parse_der_node(outer.body)?;
6670    if status.tag != 0x0A || status.body.len() != 1 {
6671        return Err(Error::ParseFailure("ocsp response status is malformed"));
6672    }
6673    if status.body[0] != 0 {
6674        return Err(Error::ParseFailure(
6675            "ocsp response status is not successful",
6676        ));
6677    }
6678    if cursor.is_empty() {
6679        return Err(Error::ParseFailure(
6680            "successful ocsp response missing responseBytes",
6681        ));
6682    }
6683    let (response_bytes_explicit, after_response_bytes) = noxtls_parse_der_node(cursor)?;
6684    if response_bytes_explicit.tag != 0xA0 || !after_response_bytes.is_empty() {
6685        return Err(Error::ParseFailure("ocsp responseBytes is malformed"));
6686    }
6687    let (response_bytes, response_bytes_rest) =
6688        noxtls_parse_der_node(response_bytes_explicit.body)?;
6689    if response_bytes.tag != 0x30 || !response_bytes_rest.is_empty() {
6690        return Err(Error::ParseFailure("ocsp ResponseBytes must be a sequence"));
6691    }
6692    let (response_type, response_payload_cursor) = noxtls_parse_der_node(response_bytes.body)?;
6693    if response_type.tag != 0x06 || response_type.body != OCSP_BASIC_RESPONSE_OID_DER {
6694        return Err(Error::UnsupportedFeature("unsupported ocsp response type"));
6695    }
6696    let (response_octets, response_payload_rest) = noxtls_parse_der_node(response_payload_cursor)?;
6697    if response_octets.tag != 0x04 || !response_payload_rest.is_empty() {
6698        return Err(Error::ParseFailure(
6699            "ocsp basic response octets are malformed",
6700        ));
6701    }
6702    noxtls_parse_tls13_basic_ocsp_response(response_octets.body, &now)
6703}
6704
6705/// Validates a stapled OCSP response and returns its revocation/freshness status.
6706pub fn noxtls_verify_tls13_ocsp_staple(
6707    ocsp_response_der: &[u8],
6708    validation_time: &str,
6709) -> Result<Tls13OcspStapleVerification> {
6710    Ok(noxtls_parse_tls13_ocsp_staple_info(ocsp_response_der, validation_time)?.status)
6711}
6712
6713const OCSP_BASIC_RESPONSE_OID_DER: &[u8] = &[0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01];
6714
6715fn noxtls_parse_tls13_basic_ocsp_response(
6716    basic_response_der: &[u8],
6717    now: &str,
6718) -> Result<Tls13OcspStapleInfo> {
6719    let (basic_response, rest) = noxtls_parse_der_node(basic_response_der)?;
6720    if basic_response.tag != 0x30 || !rest.is_empty() {
6721        return Err(Error::ParseFailure(
6722            "ocsp BasicOCSPResponse must be a sequence",
6723        ));
6724    }
6725    let (response_data, _) = noxtls_parse_der_node(basic_response.body)?;
6726    if response_data.tag != 0x30 {
6727        return Err(Error::ParseFailure("ocsp ResponseData must be a sequence"));
6728    }
6729    noxtls_parse_tls13_ocsp_response_data(response_data.body, now)
6730}
6731
6732fn noxtls_parse_tls13_ocsp_response_data(
6733    mut cursor: &[u8],
6734    now: &str,
6735) -> Result<Tls13OcspStapleInfo> {
6736    if cursor.is_empty() {
6737        return Err(Error::ParseFailure("ocsp ResponseData is empty"));
6738    }
6739    let (first, rest_after_first) = noxtls_parse_der_node(cursor)?;
6740    cursor = if first.tag == 0xA0 {
6741        rest_after_first
6742    } else {
6743        cursor
6744    };
6745    let (_responder_id, after_responder_id) = noxtls_parse_der_node(cursor)?;
6746    let (produced_at_node, after_produced_at) = noxtls_parse_der_node(after_responder_id)?;
6747    let produced_at = noxtls_ocsp_time_from_node(&produced_at_node)?;
6748    if produced_at.as_str() > now {
6749        return Err(Error::ParseFailure(
6750            "ocsp response producedAt is in the future",
6751        ));
6752    }
6753    let (responses, _after_responses) = noxtls_parse_der_node(after_produced_at)?;
6754    if responses.tag != 0x30 {
6755        return Err(Error::ParseFailure("ocsp responses must be a sequence"));
6756    }
6757    let mut response_cursor = responses.body;
6758    let mut first_info: Option<Tls13OcspStapleInfo> = None;
6759    while !response_cursor.is_empty() {
6760        let (single_response, rest) = noxtls_parse_der_node(response_cursor)?;
6761        if single_response.tag != 0x30 {
6762            return Err(Error::ParseFailure(
6763                "ocsp SingleResponse must be a sequence",
6764            ));
6765        }
6766        let info =
6767            noxtls_parse_tls13_ocsp_single_response(single_response.body, now, &produced_at)?;
6768        if info.status == Tls13OcspStapleVerification::Revoked {
6769            return Ok(info);
6770        }
6771        if first_info.is_none() {
6772            first_info = Some(info);
6773        }
6774        response_cursor = rest;
6775    }
6776    first_info.ok_or(Error::ParseFailure(
6777        "ocsp response contains no SingleResponse",
6778    ))
6779}
6780
6781fn noxtls_parse_tls13_ocsp_single_response(
6782    mut cursor: &[u8],
6783    now: &str,
6784    produced_at: &str,
6785) -> Result<Tls13OcspStapleInfo> {
6786    let (_cert_id, after_cert_id) = noxtls_parse_der_node(cursor)?;
6787    cursor = after_cert_id;
6788    let (cert_status, after_status) = noxtls_parse_der_node(cursor)?;
6789    cursor = after_status;
6790    let status = match cert_status.tag {
6791        0x80 => Tls13OcspStapleVerification::Good,
6792        0xA1 | 0x81 => Tls13OcspStapleVerification::Revoked,
6793        0x82 | 0xA2 => return Err(Error::ParseFailure("ocsp certificate status is unknown")),
6794        _ => return Err(Error::ParseFailure("ocsp certificate status is malformed")),
6795    };
6796    let (this_update_node, after_this_update) = noxtls_parse_der_node(cursor)?;
6797    let this_update = noxtls_ocsp_time_from_node(&this_update_node)?;
6798    let mut next_update = None;
6799    cursor = after_this_update;
6800    if !cursor.is_empty() {
6801        let (maybe_next_update, _after_next_update) = noxtls_parse_der_node(cursor)?;
6802        if maybe_next_update.tag == 0xA0 {
6803            let (time_node, time_rest) = noxtls_parse_der_node(maybe_next_update.body)?;
6804            if !time_rest.is_empty() {
6805                return Err(Error::ParseFailure("ocsp nextUpdate has trailing bytes"));
6806            }
6807            next_update = Some(noxtls_ocsp_time_from_node(&time_node)?);
6808        }
6809    }
6810    if status == Tls13OcspStapleVerification::Revoked {
6811        return Ok(Tls13OcspStapleInfo {
6812            produced_at: produced_at.to_owned(),
6813            this_update,
6814            next_update,
6815            status,
6816        });
6817    }
6818    let is_fresh = this_update.as_str() <= now
6819        && next_update
6820            .as_deref()
6821            .map(|value| now <= value)
6822            .unwrap_or(true);
6823    Ok(Tls13OcspStapleInfo {
6824        produced_at: produced_at.to_owned(),
6825        this_update,
6826        next_update,
6827        status: if is_fresh {
6828            Tls13OcspStapleVerification::Good
6829        } else {
6830            Tls13OcspStapleVerification::Expired
6831        },
6832    })
6833}
6834
6835fn noxtls_ocsp_time_from_node(node: &noxtls_x509::DerNode) -> Result<String> {
6836    if node.tag != 0x17 && node.tag != 0x18 {
6837        return Err(Error::ParseFailure(
6838            "ocsp time must be UTCTime or GeneralizedTime",
6839        ));
6840    }
6841    let text = core::str::from_utf8(node.body)
6842        .map_err(|_| Error::ParseFailure("ocsp time is not valid utf-8"))?;
6843    noxtls_canonical_ocsp_time(text).ok_or(Error::ParseFailure("invalid ocsp time"))
6844}
6845
6846fn noxtls_canonical_ocsp_time(input: &str) -> Option<String> {
6847    if input.len() == 15 && input.ends_with("Z") {
6848        let body = &input[..14];
6849        if body.chars().all(|c| c.is_ascii_digit()) {
6850            return Some(input.to_owned());
6851        }
6852        return None;
6853    }
6854    if input.len() == 13 && input.ends_with("Z") {
6855        let yy = &input[..2];
6856        let rest = &input[2..12];
6857        if !yy.chars().all(|c| c.is_ascii_digit()) || !rest.chars().all(|c| c.is_ascii_digit()) {
6858            return None;
6859        }
6860        let yy_value = yy.parse::<u32>().ok()?;
6861        let century = if yy_value >= 50 { "19" } else { "20" };
6862        return Some(format!("{century}{yy}{rest}Z"));
6863    }
6864    None
6865}
6866
6867/// Parses one CertificateEntry `status_request` extension and extracts OCSP staple bytes.
6868fn noxtls_parse_certificate_entry_status_request_extension(input: &[u8]) -> Result<Vec<u8>> {
6869    if input.len() < 4 {
6870        return Err(Error::ParseFailure(
6871            "certificate entry status_request extension is truncated",
6872        ));
6873    }
6874    if input[0] != 0x01 {
6875        return Err(Error::ParseFailure(
6876            "certificate entry status_request must use ocsp status type",
6877        ));
6878    }
6879    let ocsp_len = ((input[1] as usize) << 16) | ((input[2] as usize) << 8) | input[3] as usize;
6880    if ocsp_len == 0 {
6881        return Err(Error::ParseFailure(
6882            "certificate entry status_request ocsp response must not be empty",
6883        ));
6884    }
6885    if input.len() != 4 + ocsp_len {
6886        return Err(Error::ParseFailure(
6887            "certificate entry status_request ocsp response is truncated",
6888        ));
6889    }
6890    Ok(input[4..].to_vec())
6891}
6892
6893/// Parses CertificateVerify body shape used by TLS 1.3.
6894///
6895/// # Arguments
6896///
6897/// * `body` — `body: &[u8]`.
6898///
6899/// # Returns
6900///
6901/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
6902///
6903/// # Errors
6904///
6905/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
6906///
6907/// # Panics
6908///
6909/// This function does not panic.
6910///
6911fn noxtls_parse_certificate_verify_fields(body: &[u8]) -> Result<(u16, &[u8])> {
6912    if body.len() < 4 {
6913        return Err(Error::ParseFailure("certificate verify body too short"));
6914    }
6915    let signature_scheme = u16::from_be_bytes([body[0], body[1]]);
6916    let sig_len = u16::from_be_bytes([body[2], body[3]]) as usize;
6917    if body.len() != 4 + sig_len {
6918        return Err(Error::ParseFailure(
6919            "certificate verify signature truncated",
6920        ));
6921    }
6922    Ok((signature_scheme, &body[4..]))
6923}
6924
6925/// Returns true when CertificateVerify scheme is supported by current TLS13 implementation.
6926///
6927/// # Arguments
6928///
6929/// * `signature_scheme` — `signature_scheme: u16`.
6930///
6931/// # Returns
6932///
6933/// `true` or `false` according to the checks in the function body.
6934///
6935/// # Panics
6936///
6937/// This function does not panic.
6938///
6939fn noxtls_tls13_supported_certificate_verify_signature_scheme(signature_scheme: u16) -> bool {
6940    matches!(
6941        signature_scheme,
6942        TLS13_SIGALG_ECDSA_SECP256R1_SHA256
6943            | TLS13_SIGALG_ECDSA_SECP384R1_SHA384
6944            | TLS13_SIGALG_ECDSA_SECP521R1_SHA512
6945            | TLS13_SIGALG_RSA_PSS_RSAE_SHA256
6946            | TLS13_SIGALG_RSA_PSS_RSAE_SHA384
6947            | TLS13_SIGALG_RSA_PSS_RSAE_SHA512
6948            | TLS13_SIGALG_RSA_PSS_PSS_SHA256
6949            | TLS13_SIGALG_RSA_PSS_PSS_SHA384
6950            | TLS13_SIGALG_RSA_PSS_PSS_SHA512
6951            | TLS13_SIGALG_ED25519
6952            | TLS13_SIGALG_MLDSA65
6953    )
6954}
6955
6956/// Parses NewSessionTicket body shape used by TLS 1.3.
6957///
6958/// # Arguments
6959///
6960/// * `body` — `body: &[u8]`.
6961///
6962/// # Returns
6963///
6964/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
6965///
6966/// # Errors
6967///
6968/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
6969///
6970/// # Panics
6971///
6972/// This function does not panic.
6973///
6974fn noxtls_parse_new_session_ticket_body(body: &[u8]) -> Result<()> {
6975    if body.len() < 11 {
6976        return Err(Error::ParseFailure(
6977            "noxtls_new session ticket body too short",
6978        ));
6979    }
6980    let nonce_len = body[8] as usize;
6981    let ticket_len_offset = 9 + nonce_len;
6982    if body.len() < ticket_len_offset + 2 {
6983        return Err(Error::ParseFailure(
6984            "noxtls_new session ticket nonce truncated",
6985        ));
6986    }
6987    let ticket_len =
6988        u16::from_be_bytes([body[ticket_len_offset], body[ticket_len_offset + 1]]) as usize;
6989    let ext_len_offset = ticket_len_offset + 2 + ticket_len;
6990    if body.len() < ext_len_offset + 2 {
6991        return Err(Error::ParseFailure(
6992            "noxtls_new session ticket bytes truncated",
6993        ));
6994    }
6995    let ext_len = u16::from_be_bytes([body[ext_len_offset], body[ext_len_offset + 1]]) as usize;
6996    if body.len() != ext_len_offset + 2 + ext_len {
6997        return Err(Error::ParseFailure(
6998            "noxtls_new session ticket extensions truncated",
6999        ));
7000    }
7001    Ok(())
7002}
7003
7004/// Builds TLS 1.3 CertificateVerify signed message for server role.
7005///
7006/// # Arguments
7007///
7008/// * `noxtls_transcript_hash` — `noxtls_transcript_hash: &[u8]`.
7009///
7010/// # Returns
7011///
7012/// The value described by the return type in the function signature.
7013///
7014/// # Panics
7015///
7016/// This function does not panic.
7017///
7018fn noxtls_build_tls13_server_certificate_verify_message(noxtls_transcript_hash: &[u8]) -> Vec<u8> {
7019    const PREFIX_LEN: usize = 64;
7020    const CONTEXT: &[u8] = b"TLS 1.3, server CertificateVerify";
7021    let mut out = Vec::with_capacity(PREFIX_LEN + CONTEXT.len() + 1 + noxtls_transcript_hash.len());
7022    out.extend(core::iter::repeat(0x20_u8).take(PREFIX_LEN));
7023    out.extend_from_slice(CONTEXT);
7024    out.push(0x00);
7025    out.extend_from_slice(noxtls_transcript_hash);
7026    out
7027}
7028
7029fn noxtls_build_tls13_client_certificate_verify_message(noxtls_transcript_hash: &[u8]) -> Vec<u8> {
7030    const PREFIX_LEN: usize = 64;
7031    const CONTEXT: &[u8] = b"TLS 1.3, client CertificateVerify";
7032    let mut out = Vec::with_capacity(PREFIX_LEN + CONTEXT.len() + 1 + noxtls_transcript_hash.len());
7033    out.extend(core::iter::repeat(0x20_u8).take(PREFIX_LEN));
7034    out.extend_from_slice(CONTEXT);
7035    out.push(0x00);
7036    out.extend_from_slice(noxtls_transcript_hash);
7037    out
7038}
7039
7040fn noxtls_encode_ecdsa_signature_der(r: &[u8], s: &[u8]) -> Result<Vec<u8>> {
7041    let r_der = noxtls_write_der_integer(r)?;
7042    let s_der = noxtls_write_der_integer(s)?;
7043    let mut body = Vec::with_capacity(r_der.len() + s_der.len());
7044    body.extend_from_slice(&r_der);
7045    body.extend_from_slice(&s_der);
7046    noxtls_write_der_sequence(&body)
7047}
7048
7049/// Parses DER RSAPublicKey bytes and constructs a `RsaPublicKey`.
7050///
7051/// # Arguments
7052///
7053/// * `public_key_der` — `public_key_der: &[u8]`.
7054///
7055/// # Returns
7056///
7057/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
7058///
7059/// # Errors
7060///
7061/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
7062///
7063/// # Panics
7064///
7065/// This function does not panic.
7066///
7067fn noxtls_parse_rsa_public_key_der(public_key_der: &[u8]) -> Result<RsaPublicKey> {
7068    let (rsa_seq, rem) = noxtls_parse_der_node(public_key_der)
7069        .map_err(|_| Error::ParseFailure("failed to parse server RSA public key"))?;
7070    if rsa_seq.tag != 0x30 || !rem.is_empty() {
7071        return Err(Error::ParseFailure(
7072            "invalid server RSA public key sequence",
7073        ));
7074    }
7075    let (modulus_node, rest) = noxtls_parse_der_node(rsa_seq.body)
7076        .map_err(|_| Error::ParseFailure("failed to parse server RSA modulus"))?;
7077    let (exponent_node, tail) = noxtls_parse_der_node(rest)
7078        .map_err(|_| Error::ParseFailure("failed to parse server RSA exponent"))?;
7079    if modulus_node.tag != 0x02 || exponent_node.tag != 0x02 || !tail.is_empty() {
7080        return Err(Error::ParseFailure(
7081            "invalid server RSA public key integer fields",
7082        ));
7083    }
7084    RsaPublicKey::from_be_bytes(modulus_node.body, exponent_node.body)
7085        .map_err(|_| Error::CryptoFailure("failed to construct server RSA public key"))
7086}
7087
7088/// Parses key_share extension and returns advertised key exchange group IDs.
7089///
7090/// # Arguments
7091///
7092/// * `input` — `input: &[u8]`.
7093///
7094/// # Returns
7095///
7096/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
7097///
7098/// # Errors
7099///
7100/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
7101///
7102/// # Panics
7103///
7104/// This function does not panic.
7105///
7106fn noxtls_parse_key_share_groups_extension(input: &[u8]) -> Result<Vec<u16>> {
7107    if input.len() < 2 {
7108        return Err(Error::ParseFailure(
7109            "key_share extension missing list length",
7110        ));
7111    }
7112    let list_len = u16::from_be_bytes([input[0], input[1]]) as usize;
7113    if input.len() != list_len + 2 {
7114        return Err(Error::ParseFailure("invalid key_share extension length"));
7115    }
7116    let mut cursor = &input[2..];
7117    let mut groups = Vec::new();
7118    while !cursor.is_empty() {
7119        if cursor.len() < 4 {
7120            return Err(Error::ParseFailure("key_share entry truncated"));
7121        }
7122        let group = u16::from_be_bytes([cursor[0], cursor[1]]);
7123        let key_len = u16::from_be_bytes([cursor[2], cursor[3]]) as usize;
7124        if groups.contains(&group) {
7125            return Err(Error::ParseFailure("duplicate key_share group"));
7126        }
7127        if key_len == 0 {
7128            return Err(Error::ParseFailure(
7129                "key_share key_exchange must not be empty",
7130            ));
7131        }
7132        cursor = &cursor[4..];
7133        if cursor.len() < key_len {
7134            return Err(Error::ParseFailure("key_share key_exchange truncated"));
7135        }
7136        groups.push(group);
7137        cursor = &cursor[key_len..];
7138    }
7139    Ok(groups)
7140}
7141
7142fn noxtls_validate_tls13_key_share_entry(group: u16, key_exchange: &[u8]) -> Result<()> {
7143    match group {
7144        TLS13_KEY_SHARE_GROUP_SECP256R1 => {
7145            P256PublicKey::from_uncompressed(key_exchange)?;
7146        }
7147        TLS13_KEY_SHARE_GROUP_SECP384R1 => {
7148            P384PublicKey::from_uncompressed(key_exchange)?;
7149        }
7150        TLS13_KEY_SHARE_GROUP_SECP521R1 => {
7151            NamedEcPublicKey::from_uncompressed(NamedCurve::Secp521R1, key_exchange)?;
7152        }
7153        TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13 => {
7154            NamedEcPublicKey::from_uncompressed(NamedCurve::BrainpoolP256R1, key_exchange)?;
7155        }
7156        TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13 => {
7157            NamedEcPublicKey::from_uncompressed(NamedCurve::BrainpoolP384R1, key_exchange)?;
7158        }
7159        TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13 => {
7160            NamedEcPublicKey::from_uncompressed(NamedCurve::BrainpoolP512R1, key_exchange)?;
7161        }
7162        TLS13_KEY_SHARE_GROUP_X25519 if key_exchange.len() != 32 => {
7163            return Err(Error::ParseFailure(
7164                "tls13 key_share entry must contain 32-byte x25519 key_exchange",
7165            ));
7166        }
7167        TLS13_KEY_SHARE_GROUP_X25519 => {}
7168        TLS13_KEY_SHARE_GROUP_X448 => {
7169            if key_exchange.len() != 56 {
7170                return Err(Error::ParseFailure(
7171                    "tls13 key_share entry must contain 56-byte x448 key_exchange",
7172                ));
7173            }
7174            let is_zero = key_exchange.iter().all(|byte| *byte == 0);
7175            let is_one = key_exchange[0] == 1 && key_exchange[1..].iter().all(|byte| *byte == 0);
7176            if is_zero || is_one {
7177                return Err(Error::ParseFailure(
7178                    "noxtls_x448 peer public key is low-order",
7179                ));
7180            }
7181        }
7182        _ => {}
7183    }
7184    Ok(())
7185}
7186
7187/// Encodes TLS 1.3 pre_shared_key extension with one identity and one binder.
7188///
7189/// # Arguments
7190///
7191/// * `offer` — `offer: &PskClientOffer<'_>`.
7192///
7193/// # Returns
7194///
7195/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
7196///
7197/// # Errors
7198///
7199/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
7200///
7201/// # Panics
7202///
7203/// This function does not panic.
7204///
7205fn noxtls_encode_pre_shared_key_extension(offer: &PskClientOffer<'_>) -> Result<Vec<u8>> {
7206    if offer.identities.is_empty() || offer.binders.is_empty() {
7207        return Err(Error::InvalidLength(
7208            "psk identity/binder list must not be empty",
7209        ));
7210    }
7211    if offer.identities.len() != offer.binders.len() {
7212        return Err(Error::InvalidLength(
7213            "psk identity and binder list lengths must match",
7214        ));
7215    }
7216    let mut identities = Vec::new();
7217    let mut binders = Vec::new();
7218    for (identity, binder) in offer.identities.iter().zip(offer.binders.iter()) {
7219        if identity.identity.is_empty() || binder.is_empty() {
7220            return Err(Error::InvalidLength(
7221                "psk identity and binder must not be empty",
7222            ));
7223        }
7224        if identity.identity.len() > u16::MAX as usize || binder.len() > u8::MAX as usize {
7225            return Err(Error::InvalidLength("psk identity or binder too long"));
7226        }
7227        identities.extend_from_slice(&(identity.identity.len() as u16).to_be_bytes());
7228        identities.extend_from_slice(identity.identity);
7229        identities.extend_from_slice(&identity.obfuscated_ticket_age.to_be_bytes());
7230        binders.push(binder.len() as u8);
7231        binders.extend_from_slice(binder);
7232    }
7233
7234    let mut out = Vec::new();
7235    out.extend_from_slice(&(identities.len() as u16).to_be_bytes());
7236    out.extend_from_slice(&identities);
7237    out.extend_from_slice(&(binders.len() as u16).to_be_bytes());
7238    out.extend_from_slice(&binders);
7239    Ok(out)
7240}
7241
7242/// Parses TLS 1.3 pre_shared_key extension and returns identity count and binders.
7243///
7244/// # Arguments
7245///
7246/// * `input` — `input: &[u8]`.
7247///
7248/// # Returns
7249///
7250/// On success, the `Ok` payload described by the return type; see the function body for the concrete value.
7251///
7252/// # Errors
7253///
7254/// Returns [`noxtls_core::Error`] when inputs or handshake state invalidate the operation; see the function body for specific error construction sites.
7255///
7256/// # Panics
7257///
7258/// This function does not panic.
7259///
7260fn noxtls_parse_pre_shared_key_extension(
7261    input: &[u8],
7262) -> Result<(usize, Vec<Vec<u8>>, Vec<u32>, Vec<Vec<u8>>)> {
7263    if input.len() < 4 {
7264        return Err(Error::ParseFailure("pre_shared_key extension too short"));
7265    }
7266    let identities_len = u16::from_be_bytes([input[0], input[1]]) as usize;
7267    if input.len() < 2 + identities_len + 2 {
7268        return Err(Error::ParseFailure("pre_shared_key identities truncated"));
7269    }
7270    let identities_end = 2 + identities_len;
7271    let mut id_cursor = &input[2..identities_end];
7272    let mut identity_count = 0_usize;
7273    let mut identities = Vec::new();
7274    let mut obfuscated_ages = Vec::new();
7275    while !id_cursor.is_empty() {
7276        if id_cursor.len() < 6 {
7277            return Err(Error::ParseFailure(
7278                "pre_shared_key identity entry truncated",
7279            ));
7280        }
7281        let id_len = u16::from_be_bytes([id_cursor[0], id_cursor[1]]) as usize;
7282        if id_len == 0 {
7283            return Err(Error::ParseFailure(
7284                "pre_shared_key identity must not be empty",
7285            ));
7286        }
7287        if id_cursor.len() < 2 + id_len + 4 {
7288            return Err(Error::ParseFailure(
7289                "pre_shared_key identity bytes truncated",
7290            ));
7291        }
7292        let identity = id_cursor[2..2 + id_len].to_vec();
7293        if identities.iter().any(|existing| existing == &identity) {
7294            return Err(Error::ParseFailure("duplicate pre_shared_key identity"));
7295        }
7296        identities.push(identity);
7297        obfuscated_ages.push(u32::from_be_bytes([
7298            id_cursor[2 + id_len],
7299            id_cursor[3 + id_len],
7300            id_cursor[4 + id_len],
7301            id_cursor[5 + id_len],
7302        ]));
7303        identity_count = identity_count.saturating_add(1);
7304        id_cursor = &id_cursor[2 + id_len + 4..];
7305    }
7306
7307    let binders_len =
7308        u16::from_be_bytes([input[identities_end], input[identities_end + 1]]) as usize;
7309    let binders_start = identities_end + 2;
7310    let binders_end = binders_start + binders_len;
7311    if input.len() != binders_end {
7312        return Err(Error::ParseFailure(
7313            "invalid pre_shared_key binder vector length",
7314        ));
7315    }
7316    let mut binders = Vec::new();
7317    let mut binder_cursor = &input[binders_start..binders_end];
7318    while !binder_cursor.is_empty() {
7319        let binder_len = binder_cursor[0] as usize;
7320        if binder_len == 0 {
7321            return Err(Error::ParseFailure(
7322                "pre_shared_key binder must not be empty",
7323            ));
7324        }
7325        binder_cursor = &binder_cursor[1..];
7326        if binder_cursor.len() < binder_len {
7327            return Err(Error::ParseFailure("pre_shared_key binder bytes truncated"));
7328        }
7329        binders.push(binder_cursor[..binder_len].to_vec());
7330        binder_cursor = &binder_cursor[binder_len..];
7331    }
7332    if identity_count != binders.len() {
7333        return Err(Error::ParseFailure(
7334            "pre_shared_key identity and binder counts differ",
7335        ));
7336    }
7337    if identity_count == 0 {
7338        return Err(Error::ParseFailure(
7339            "pre_shared_key extension must include at least one identity",
7340        ));
7341    }
7342    Ok((identity_count, identities, obfuscated_ages, binders))
7343}
7344
7345/// Returns legacy TLS version bytes used in ClientHello/ServerHello structures.
7346///
7347/// # Arguments
7348///
7349/// * `version` — `version: TlsVersion`.
7350///
7351/// # Returns
7352///
7353/// The value described by the return type in the function signature.
7354///
7355/// # Panics
7356///
7357/// This function does not panic.
7358///
7359fn noxtls_legacy_wire_version(version: TlsVersion) -> [u8; 2] {
7360    match version {
7361        TlsVersion::Tls10 => [0x03, 0x01],
7362        TlsVersion::Tls11 => [0x03, 0x02],
7363        TlsVersion::Tls12 | TlsVersion::Tls13 => [0x03, 0x03],
7364        TlsVersion::Dtls12 | TlsVersion::Dtls13 => [0xFE, 0xFD],
7365    }
7366}