use crate::protocols::Protocol;
#[cfg(kani)]
#[kani::proof]
fn proof_known_protocol_versions() {
let versions: [(u16, &str); 6] = [
(0x0002, "SSLv2"),
(0x0300, "SSLv3"),
(0x0301, "TLS 1.0"),
(0x0302, "TLS 1.1"),
(0x0303, "TLS 1.2"),
(0x0304, "TLS 1.3"),
];
for (version, expected_name) in versions {
let protocol = Protocol::from(version);
let name = protocol.name();
kani::assert(name == expected_name, "Protocol name should match");
}
}
#[cfg(kani)]
#[kani::proof]
fn proof_deprecated_protocols() {
let idx: u8 = kani::any();
kani::assume(idx < 7);
let protocol = match idx {
0 => Protocol::SSLv2,
1 => Protocol::SSLv3,
2 => Protocol::TLS10,
3 => Protocol::TLS11,
4 => Protocol::TLS12,
5 => Protocol::TLS13,
_ => Protocol::QUIC,
};
let is_deprecated = protocol.is_deprecated();
match protocol {
Protocol::SSLv2 | Protocol::SSLv3 | Protocol::TLS10 | Protocol::TLS11 => {
kani::assert(is_deprecated, "Old protocols should be deprecated");
}
Protocol::TLS12 | Protocol::TLS13 | Protocol::QUIC => {
kani::assert(!is_deprecated, "Modern protocols should not be deprecated");
}
}
}
#[cfg(kani)]
#[kani::proof]
fn proof_protocol_ordering() {
let idx1: u8 = kani::any();
let idx2: u8 = kani::any();
kani::assume(idx1 < 7);
kani::assume(idx2 < 7);
let p1 = match idx1 {
0 => Protocol::SSLv2,
1 => Protocol::SSLv3,
2 => Protocol::TLS10,
3 => Protocol::TLS11,
4 => Protocol::TLS12,
5 => Protocol::TLS13,
_ => Protocol::QUIC,
};
let p2 = match idx2 {
0 => Protocol::SSLv2,
1 => Protocol::SSLv3,
2 => Protocol::TLS10,
3 => Protocol::TLS11,
4 => Protocol::TLS12,
5 => Protocol::TLS13,
_ => Protocol::QUIC,
};
kani::assert(p1 == p1, "Protocol should equal itself");
kani::assert((p1 == p2) == (p2 == p1), "Equality should be symmetric");
if p1 == p2 {
let h1 = p1.as_hex();
let h2 = p2.as_hex();
kani::assert(h1 == h2, "Equal protocols should have same hex");
}
}
#[cfg(kani)]
#[kani::proof]
fn proof_unknown_version_handling() {
let version: u16 = kani::any();
kani::assume(
version != 0x0002
&& version != 0x0300
&& version != 0x0301
&& version != 0x0302
&& version != 0x0303
&& version != 0x0304
);
let protocol = Protocol::from(version);
kani::assert(protocol == Protocol::TLS12, "Unknown versions should default to TLS 1.2");
}
#[cfg(kani)]
#[kani::proof]
fn proof_record_layer_version() {
use crate::constants::{VERSION_SSL_3_0, VERSION_TLS_1_0};
let idx: u8 = kani::any();
kani::assume(idx < 7);
let protocol = match idx {
0 => Protocol::SSLv2,
1 => Protocol::SSLv3,
2 => Protocol::TLS10,
3 => Protocol::TLS11,
4 => Protocol::TLS12,
5 => Protocol::TLS13,
_ => Protocol::QUIC,
};
let record_version = match protocol {
Protocol::SSLv3 => VERSION_SSL_3_0,
_ => VERSION_TLS_1_0,
};
kani::assert(
record_version == VERSION_SSL_3_0 || record_version == VERSION_TLS_1_0,
"Record version should be SSL 3.0 or TLS 1.0"
);
}
#[cfg(kani)]
#[kani::proof]
fn proof_client_version_tls13() {
use crate::constants::VERSION_TLS_1_2;
let idx: u8 = kani::any();
kani::assume(idx < 7);
let protocol = match idx {
0 => Protocol::SSLv2,
1 => Protocol::SSLv3,
2 => Protocol::TLS10,
3 => Protocol::TLS11,
4 => Protocol::TLS12,
5 => Protocol::TLS13,
_ => Protocol::QUIC,
};
let client_version = if matches!(protocol, Protocol::TLS13) {
VERSION_TLS_1_2
} else {
protocol.as_hex()
};
if matches!(protocol, Protocol::TLS13) {
kani::assert(client_version == VERSION_TLS_1_2, "TLS 1.3 should use 0x0303 for compatibility");
}
}
#[cfg(kani)]
#[kani::proof]
fn proof_extension_name_lookup() {
let extension_type: u16 = kani::any();
let name = match extension_type {
0x0000 => "server_name (SNI)",
0x0001 => "max_fragment_length",
0x0005 => "status_request (OCSP stapling)",
0x000a => "supported_groups",
0x000b => "ec_point_formats",
0x000d => "signature_algorithms",
0x000f => "heartbeat",
0x0010 => "application_layer_protocol_negotiation (ALPN)",
0x0012 => "signed_certificate_timestamp",
0x0015 => "padding",
0x0017 => "extended_master_secret",
0x0023 => "session_ticket",
0x002b => "supported_versions",
0x002d => "psk_key_exchange_modes",
0x0033 => "key_share",
0xff01 => "renegotiation_info",
_ => "unknown",
};
kani::assert(!name.is_empty(), "Extension name should not be empty");
}
#[cfg(kani)]
#[kani::proof]
fn proof_version_byte_extraction() {
let high: u8 = kani::any();
let low: u8 = kani::any();
let version = u16::from_be_bytes([high, low]);
let protocol = Protocol::from(version);
let _name = protocol.name(); }
#[cfg(kani)]
#[kani::proof]
#[kani::unwind(33)]
fn proof_cipher_suite_parsing_bounds() {
let suite_count: usize = kani::any();
kani::assume(suite_count <= 16);
let mut parsed_count: usize = 0;
for _ in 0..suite_count {
let high: u8 = kani::any();
let low: u8 = kani::any();
let _suite = u16::from_be_bytes([high, low]);
parsed_count += 1;
}
kani::assert(parsed_count == suite_count, "Should have expected number of suites");
}
#[cfg(kani)]
#[kani::proof]
fn proof_sni_length_calculation() {
let hostname_len: usize = kani::any();
kani::assume(hostname_len > 0 && hostname_len <= 255);
let list_len = 3 + hostname_len; let ext_len = 2 + list_len;
kani::assert(list_len <= 258, "List length should be bounded");
kani::assert(ext_len <= 260, "Extension length should be bounded");
kani::assert(ext_len <= 65535, "Should fit in u16 length field");
}
#[cfg(kani)]
#[kani::proof]
#[kani::unwind(11)]
fn proof_alpn_construction() {
let protocol_count: usize = kani::any();
kani::assume(protocol_count > 0 && protocol_count <= 5);
let mut total_len: usize = 0;
for _ in 0..protocol_count {
let proto_len: usize = kani::any();
kani::assume(proto_len > 0 && proto_len <= 255);
match total_len.checked_add(1 + proto_len) {
Some(new_len) => {
total_len = new_len;
}
None => {
return;
}
}
}
kani::assert(total_len <= 65535, "ALPN list should fit in extension");
}
#[cfg(kani)]
#[kani::proof]
#[kani::unwind(33)]
fn proof_session_id_handling() {
let session_id_len: u8 = kani::any();
kani::assume(session_id_len <= 32);
let mut session_id: [u8; 32] = [0u8; 32];
for i in 0..(session_id_len as usize) {
session_id[i] = kani::any();
}
let actual_len = session_id_len as usize;
let _ = session_id;
kani::assert(actual_len == session_id_len as usize, "Session ID should match length");
kani::assert(actual_len <= 32, "Session ID should not exceed max");
}
#[cfg(kani)]
#[kani::proof]
fn proof_handshake_length_encoding() {
let length: u32 = kani::any();
kani::assume(length <= 0x00FFFFFF);
let byte0 = ((length >> 16) & 0xff) as u8;
let byte1 = ((length >> 8) & 0xff) as u8;
let byte2 = (length & 0xff) as u8;
let decoded = ((byte0 as u32) << 16) | ((byte1 as u32) << 8) | (byte2 as u32);
kani::assert(decoded == length, "Handshake length roundtrip should match");
}