use crate::buffer::CryptoBuffer;
use crate::config::Aes128GcmSha256;
use crate::extensions::extension_data::pre_shared_key::PreSharedKeyServerHello;
use crate::extensions::extension_data::supported_versions::{
SupportedVersionsServerHello, TLS13,
};
use crate::extensions::messages::{ClientHelloExtension, ServerHelloExtension};
use crate::handshake::client_hello::ClientHelloRef;
use crate::handshake::encrypted_extensions::EncryptedExtensionsEmit;
use crate::handshake::server_hello::ServerHelloEmit;
use crate::key_schedule::KeySchedule;
use crate::parse_buffer::ParseBuffer;
use heapless::Vec as HVec;
use sha2::{Digest, Sha256};
const HS_TYPE_CLIENT_HELLO: u8 = 0x01;
const LEGACY_VERSION: u16 = 0x0303;
const TLS13_VERSION: u16 = 0x0304;
const TLS_AES_128_GCM_SHA256: u16 = 0x1301;
const EXT_SUPPORTED_VERSIONS: u16 = 0x002b;
const EXT_PSK_KEY_EXCHANGE_MODES: u16 = 0x002d;
const EXT_PRE_SHARED_KEY: u16 = 0x0029;
const PSK_KE_MODE: u8 = 0;
const SHA256_LEN: usize = 32;
struct ChLayout {
bytes: std::vec::Vec<u8>,
binders_start_offset: usize,
binder_value_pos: usize,
}
fn build_synthetic_client_hello(identity: &[u8]) -> ChLayout {
let mut out = std::vec::Vec::with_capacity(256);
out.push(HS_TYPE_CLIENT_HELLO);
out.extend_from_slice(&[0, 0, 0]);
let body_start = out.len();
out.extend_from_slice(&LEGACY_VERSION.to_be_bytes());
out.extend_from_slice(&[0xa5; 32]);
out.push(0);
out.extend_from_slice(&2u16.to_be_bytes());
out.extend_from_slice(&TLS_AES_128_GCM_SHA256.to_be_bytes());
out.push(1);
out.push(0);
let ext_list_len_pos = out.len();
out.extend_from_slice(&[0, 0]); let ext_content_start = out.len();
out.extend_from_slice(&EXT_SUPPORTED_VERSIONS.to_be_bytes());
out.extend_from_slice(&3u16.to_be_bytes()); out.push(2); out.extend_from_slice(&TLS13_VERSION.to_be_bytes());
out.extend_from_slice(&EXT_PSK_KEY_EXCHANGE_MODES.to_be_bytes());
out.extend_from_slice(&2u16.to_be_bytes()); out.push(1); out.push(PSK_KE_MODE);
out.extend_from_slice(&EXT_PRE_SHARED_KEY.to_be_bytes());
let psk_data_len_pos = out.len();
out.extend_from_slice(&[0, 0]); let psk_data_start = out.len();
let id_entry_len = 2 + identity.len() + 4;
out.extend_from_slice(&(id_entry_len as u16).to_be_bytes());
out.extend_from_slice(&(identity.len() as u16).to_be_bytes());
out.extend_from_slice(identity);
out.extend_from_slice(&0u32.to_be_bytes());
let binders_start_offset = out.len();
let binders_section_len: u16 = 1 + SHA256_LEN as u16; out.extend_from_slice(&binders_section_len.to_be_bytes());
out.push(SHA256_LEN as u8);
let binder_value_pos = out.len();
out.extend_from_slice(&[0u8; SHA256_LEN]); let psk_data_end = out.len();
let psk_data_len = (psk_data_end - psk_data_start) as u16;
out[psk_data_len_pos..psk_data_len_pos + 2]
.copy_from_slice(&psk_data_len.to_be_bytes());
let ext_content_end = out.len();
let ext_list_len = (ext_content_end - ext_content_start) as u16;
out[ext_list_len_pos..ext_list_len_pos + 2]
.copy_from_slice(&ext_list_len.to_be_bytes());
let body_end = out.len();
let body_len = (body_end - body_start) as u32;
out[1] = (body_len >> 16) as u8;
out[2] = (body_len >> 8) as u8;
out[3] = body_len as u8;
ChLayout {
bytes: out,
binders_start_offset,
binder_value_pos,
}
}
#[test]
fn full_psk_ke_handshake_self_loop() {
let psk = [0x42u8; 32];
let identity: &[u8] = b"smartbox-house-1";
let mut layout = build_synthetic_client_hello(identity);
let mut client_ks = KeySchedule::<Aes128GcmSha256>::new();
client_ks
.initialize_early_secret(Some(&psk))
.expect("client init early secret");
let mut prefix_hash: Sha256 = Digest::new();
prefix_hash.update(&layout.bytes[..layout.binders_start_offset]);
let (write_state, _read_state) = client_ks.as_split();
let binder = write_state
.create_psk_binder(&prefix_hash)
.expect("client create binder");
layout.bytes[layout.binder_value_pos..layout.binder_value_pos + SHA256_LEN]
.copy_from_slice(binder.verify.as_slice());
let mut parse_buf = ParseBuffer::new(&layout.bytes);
let hello: ClientHelloRef<'_, 16> =
ClientHelloRef::parse(&mut parse_buf).expect("server parse CH");
assert_eq!(
hello.binders_start_offset, layout.binders_start_offset,
"parser and synthesiser must agree on the binder boundary",
);
let (parsed_identity, parsed_binder) = hello
.extensions
.iter()
.find_map(|ext| {
if let ClientHelloExtension::PreSharedKey(p) = ext {
Some((p.identities[0], p.binders[0]))
} else {
None
}
})
.expect("CH has pre_shared_key extension");
assert_eq!(parsed_identity, identity);
assert_eq!(parsed_binder.len(), SHA256_LEN);
let mut server_ks = KeySchedule::<Aes128GcmSha256>::new();
server_ks
.initialize_early_secret(Some(&psk))
.expect("server init early secret");
let mut server_prefix_hash: Sha256 = Digest::new();
server_prefix_hash.update(&layout.bytes[..hello.binders_start_offset]);
let binder_ok = server_ks
.verify_psk_binder(&server_prefix_hash, parsed_binder)
.expect("server verify binder");
assert!(binder_ok, "server must accept the client's binder");
server_ks.transcript_hash().update(&layout.bytes);
let server_random = [0xc3u8; 32];
let mut sh_extensions: HVec<ServerHelloExtension<'_>, 4> = HVec::new();
sh_extensions
.push(ServerHelloExtension::SupportedVersions(
SupportedVersionsServerHello {
selected_version: TLS13,
},
))
.unwrap();
sh_extensions
.push(ServerHelloExtension::PreSharedKey(PreSharedKeyServerHello {
selected_identity: 0,
}))
.unwrap();
let sh_emit = ServerHelloEmit {
random: server_random,
legacy_session_id_echo: hello.legacy_session_id,
cipher_suite: TLS_AES_128_GCM_SHA256,
extensions: sh_extensions,
};
let mut sh_backing = [0u8; 256];
let mut sh_buf = CryptoBuffer::wrap(&mut sh_backing);
sh_emit.encode(&mut sh_buf).expect("encode ServerHello");
let sh_body_len = sh_buf.len();
let mut sh_message = std::vec::Vec::with_capacity(4 + sh_body_len);
sh_message.push(0x02); sh_message.extend_from_slice(&[
0,
(sh_body_len >> 8) as u8,
sh_body_len as u8,
]);
sh_message.extend_from_slice(&sh_backing[..sh_body_len]);
server_ks.transcript_hash().update(&sh_message);
server_ks
.initialize_handshake_secret(&[0u8; 32])
.expect("server init handshake secret");
let ee_emit: EncryptedExtensionsEmit<'_, 4> = EncryptedExtensionsEmit::default();
let mut ee_backing = [0u8; 16];
let mut ee_buf = CryptoBuffer::wrap(&mut ee_backing);
ee_emit.encode(&mut ee_buf).expect("encode EE");
let ee_body_len = ee_buf.len();
assert_eq!(&ee_backing[..ee_body_len], &[0x00, 0x00]);
let mut ee_message = std::vec::Vec::with_capacity(4 + ee_body_len);
ee_message.push(0x08); ee_message.extend_from_slice(&[
0,
(ee_body_len >> 8) as u8,
ee_body_len as u8,
]);
ee_message.extend_from_slice(&ee_backing[..ee_body_len]);
server_ks.transcript_hash().update(&ee_message);
let mut server_finished = server_ks
.create_server_finished()
.expect("server create Finished");
client_ks.transcript_hash().update(&layout.bytes); client_ks.transcript_hash().update(&sh_message);
client_ks
.initialize_handshake_secret(&[0u8; 32])
.expect("client init handshake secret");
client_ks.transcript_hash().update(&ee_message);
server_finished.hash = Some(client_ks.transcript_hash().clone().finalize());
let (_w, client_read) = client_ks.as_split();
assert!(
client_read
.verify_server_finished(&server_finished)
.expect("client verify server Finished"),
"server's Finished must verify under the client's parallel schedule",
);
}