use crate::internal_alloc::Vec;
use crate::platform_bridge::noxtls_fill_random;
use crate::protocol::{
noxtls_tls13_key_share_group_supported, CipherSuite, Connection, HandshakeState,
RecordContentType, Tls13ServerIdentityKey, TlsRecordDeframer, TLS_MAX_RECORD_PAYLOAD_LEN,
TLS_RECORD_HEADER_LEN,
};
type TransportResult<T> = core::result::Result<T, TransportError>;
use noxtls_core::{Error, Result as TlsResult};
use noxtls_io::transport::drive_async::{noxtls_read_record_async, noxtls_write_all_async};
use noxtls_io::transport::stream_async::AsyncByteStream;
use noxtls_io::transport::TransportError;
use noxtls_platform::EntropySource;
const TLS_RECORD_HANDSHAKE: u8 = 0x16;
const TLS_RECORD_APPLICATION_DATA: u8 = 0x17;
const TLS_RECORD_CHANGE_CIPHER_SPEC: u8 = 0x14;
const TLS13_HANDSHAKE_CLIENT_HELLO: u8 = 0x01;
const TLS13_GROUP_SECP256R1: u16 = 0x0017;
fn tls_err<T>(result: TlsResult<T>) -> TransportResult<T> {
result.map_err(|_| TransportError::IoFailed("tls protocol error"))
}
pub struct AsyncTlsServerConfig<'a> {
pub cert_chain_der: &'a [Vec<u8>],
pub signing_key: Tls13ServerIdentityKey,
pub cipher_suites: &'a [CipherSuite],
pub alpn_protocols: &'a [&'a [u8]],
pub key_exchange_groups: &'a [u16],
pub request_client_cert: bool,
pub server_random: [u8; 32],
}
pub struct AsyncTls13Connection<S> {
stream: S,
pub conn: Connection,
deframer: TlsRecordDeframer,
}
impl<S: AsyncByteStream> AsyncTls13Connection<S> {
#[must_use]
pub fn stream(&self) -> &S {
&self.stream
}
pub fn stream_mut(&mut self) -> &mut S {
&mut self.stream
}
pub async fn read_record(&mut self) -> TransportResult<Vec<u8>> {
noxtls_read_record_async(&mut self.stream, &mut self.deframer).await
}
pub async fn write_record(&mut self, packet: &[u8]) -> TransportResult<()> {
noxtls_write_all_async(&mut self.stream, packet).await
}
#[must_use]
pub fn selected_cipher_suite(&self) -> Option<CipherSuite> {
self.conn.noxtls_selected_cipher_suite()
}
#[must_use]
pub fn cipher_suite_display_name(&self) -> Option<&'static str> {
self.conn.noxtls_cipher_suite_display_name()
}
#[must_use]
pub fn selected_alpn_protocol(&self) -> Option<&[u8]> {
self.conn.noxtls_selected_alpn_protocol()
}
#[must_use]
pub fn selected_key_exchange_group(&self) -> Option<u16> {
self.conn.noxtls_negotiated_key_exchange_group()
}
#[must_use]
pub fn selected_certificate_signature_scheme(&self) -> Option<u16> {
self.conn
.noxtls_negotiated_certificate_verify_signature_scheme()
}
pub fn open_client_application(&mut self, packet: &[u8]) -> TlsResult<Vec<u8>> {
let aad = Connection::noxtls_tls13_packet_header_aad(packet)?;
let (plaintext, content_type) = self
.conn
.noxtls_open_client_tls13_record_packet(packet, &aad)?;
if content_type != RecordContentType::ApplicationData.to_u8() {
return Err(Error::ParseFailure(
"expected application_data from client record",
));
}
Ok(plaintext)
}
pub async fn read_application(&mut self) -> TransportResult<Vec<u8>> {
let packet = self.read_record().await?;
self.open_client_application(&packet)
.map_err(|_| TransportError::IoFailed("open application record failed"))
}
pub fn seal_server_application(&mut self, plaintext: &[u8]) -> TlsResult<Vec<u8>> {
let inner_len = plaintext
.len()
.checked_add(1)
.ok_or(Error::InvalidLength("tls13 inner plaintext overflow"))?;
let payload_len = inner_len
.checked_add(16)
.ok_or(Error::InvalidLength("tls13 ciphertext length overflow"))?;
let mut aad = [0_u8; TLS_RECORD_HEADER_LEN];
aad[0] = TLS_RECORD_APPLICATION_DATA;
aad[1] = 0x03;
aad[2] = 0x03;
aad[3] = ((payload_len >> 8) & 0xff) as u8;
aad[4] = (payload_len & 0xff) as u8;
self.conn.noxtls_seal_server_tls13_record_packet(
plaintext,
RecordContentType::ApplicationData.to_u8(),
&aad,
0,
)
}
pub async fn write_application(&mut self, plaintext: &[u8]) -> TransportResult<()> {
let packet = self
.seal_server_application(plaintext)
.map_err(|_| TransportError::IoFailed("seal application record failed"))?;
self.write_record(&packet).await
}
}
pub async fn noxtls_accept_tls13_async<S: AsyncByteStream>(
mut stream: S,
config: &AsyncTlsServerConfig<'_>,
_entropy: &mut dyn EntropySource,
) -> TransportResult<AsyncTls13Connection<S>> {
let mut deframer = TlsRecordDeframer::noxtls_new();
let first_record = noxtls_read_record_async(&mut stream, &mut deframer).await?;
if tls_record_content_type(&first_record) != TLS_RECORD_HANDSHAKE {
return Err(TransportError::IoFailed(
"expected ClientHello handshake record",
));
}
if tls_record_payload_len(&first_record) > TLS_MAX_RECORD_PAYLOAD_LEN {
return Err(TransportError::IoFailed("ClientHello record too large"));
}
let client_hello = read_complete_handshake_message_async(
&mut stream,
&mut deframer,
first_record,
TLS13_HANDSHAKE_CLIENT_HELLO,
)
.await?;
let mut conn = Connection::noxtls_new_tls13_server();
tls_err(conn.noxtls_configure_tls13_server_identity(
config.cert_chain_der,
config.signing_key.clone(),
))?;
tls_err(conn.noxtls_set_tls13_server_cipher_suites(config.cipher_suites))?;
if !config.key_exchange_groups.is_empty() {
tls_err(conn.noxtls_set_tls13_server_key_exchange_groups(config.key_exchange_groups))?;
}
if !config.alpn_protocols.is_empty() {
tls_err(conn.noxtls_set_tls13_server_alpn_protocols(config.alpn_protocols))?;
}
let mut client_hello_msg: Vec<u8> = client_hello;
let mut sent_hrr = false;
let mut parsed_client_hello =
Connection::noxtls_parse_client_hello_info(&client_hello_msg).ok();
if let Some(hello_info) = parsed_client_hello.as_ref() {
let has_supported_key_share = conn
.noxtls_select_tls13_server_key_exchange_group(&hello_info.extensions.key_share_groups)
.is_some();
if hello_info.extensions.key_share_offered
&& !has_supported_key_share
&& hello_info
.extensions
.supported_groups
.iter()
.any(|g| noxtls_tls13_key_share_group_supported(*g))
{
let requested_group = conn
.noxtls_select_tls13_server_key_exchange_group(
&hello_info.extensions.supported_groups,
)
.unwrap_or(TLS13_GROUP_SECP256R1);
let hrr = tls_err(conn.noxtls_start_tls13_hello_retry_request_with_info(
&client_hello_msg,
hello_info,
requested_group,
))?;
noxtls_write_all_async(&mut stream, &tls_err(encode_tls13_handshake_record(&hrr))?)
.await?;
noxtls_write_all_async(
&mut stream,
&Connection::noxtls_build_tls13_compatibility_change_cipher_spec(),
)
.await?;
sent_hrr = true;
let retry_record = read_retry_client_hello_async(&mut stream, &mut deframer).await?;
if tls_record_content_type(&retry_record) != TLS_RECORD_HANDSHAKE {
return Err(TransportError::IoFailed("expected retry ClientHello"));
}
client_hello_msg =
extract_handshake_message(&retry_record, TLS13_HANDSHAKE_CLIENT_HELLO)
.map_err(|_| TransportError::IoFailed("retry ClientHello missing"))?;
parsed_client_hello =
Connection::noxtls_parse_client_hello_info(&client_hello_msg).ok();
}
}
let server_hello = if let Some(hello_info) = parsed_client_hello.as_ref() {
tls_err(conn.noxtls_accept_tls13_client_hello_with_info(
&client_hello_msg,
hello_info,
&config.server_random,
))?
} else {
tls_err(conn.noxtls_accept_tls13_client_hello(&client_hello_msg, &config.server_random))?
};
noxtls_write_all_async(
&mut stream,
&tls_err(encode_tls13_handshake_record(&server_hello))?,
)
.await?;
if !sent_hrr {
noxtls_write_all_async(
&mut stream,
&Connection::noxtls_build_tls13_compatibility_change_cipher_spec(),
)
.await?;
}
tls_err(conn.noxtls_derive_handshake_secret())?;
let server_flight = tls_err(
conn.noxtls_build_tls13_server_handshake_flight_with_client_certificate_request(
config.request_client_cert,
),
)?;
noxtls_write_all_async(&mut stream, &server_flight).await?;
while conn.state != HandshakeState::Finished {
let packet = noxtls_read_record_async(&mut stream, &mut deframer).await?;
match tls_record_content_type(&packet) {
TLS_RECORD_CHANGE_CIPHER_SPEC => {
if !is_tls13_compatibility_ccs(&packet) {
return Err(TransportError::IoFailed("invalid compatibility CCS"));
}
continue;
}
TLS_RECORD_APPLICATION_DATA => {
if config.request_client_cert {
tls_err(conn.noxtls_recv_tls13_client_authentication_packet(&packet))?;
} else {
tls_err(conn.noxtls_recv_client_finished_packet(&packet))?;
}
let _ = tls_err(conn.noxtls_activate_tls13_application_traffic_keys());
}
_ => {
return Err(TransportError::IoFailed(
"unexpected record during handshake",
))
}
}
}
Ok(AsyncTls13Connection {
stream,
conn,
deframer,
})
}
pub fn noxtls_server_random(entropy: &mut dyn EntropySource) -> [u8; 32] {
let mut random = [0_u8; 32];
noxtls_fill_random(entropy, &mut random);
random
}
fn tls_record_content_type(packet: &[u8]) -> u8 {
packet.first().copied().unwrap_or(0)
}
fn tls_record_payload_len(packet: &[u8]) -> usize {
if packet.len() < TLS_RECORD_HEADER_LEN {
return 0;
}
u16::from_be_bytes([packet[3], packet[4]]) as usize
}
fn encode_tls13_handshake_record(handshake_message: &[u8]) -> TlsResult<Vec<u8>> {
if handshake_message.len() > usize::from(u16::MAX) {
return Err(Error::InvalidLength("handshake record too large"));
}
let mut packet = Vec::with_capacity(TLS_RECORD_HEADER_LEN + handshake_message.len());
packet.push(TLS_RECORD_HANDSHAKE);
packet.extend_from_slice(&[0x03, 0x03]);
packet.extend_from_slice(&(handshake_message.len() as u16).to_be_bytes());
packet.extend_from_slice(handshake_message);
Ok(packet)
}
async fn read_complete_handshake_message_async<S: AsyncByteStream>(
stream: &mut S,
deframer: &mut TlsRecordDeframer,
first_record: Vec<u8>,
message_type: u8,
) -> TransportResult<Vec<u8>> {
let mut payload = first_record[TLS_RECORD_HEADER_LEN..].to_vec();
loop {
if payload.len() >= 4 {
let message_len =
((payload[1] as usize) << 16) | ((payload[2] as usize) << 8) | payload[3] as usize;
let full_len = 4_usize.saturating_add(message_len);
if payload.len() >= full_len {
if payload[0] != message_type {
return Err(TransportError::IoFailed(
"handshake record missing requested message",
));
}
return Ok(payload[..full_len].to_vec());
}
}
let next = noxtls_read_record_async(stream, deframer).await?;
if tls_record_content_type(&next) != TLS_RECORD_HANDSHAKE {
return Err(TransportError::IoFailed("fragmented handshake expected"));
}
payload.extend_from_slice(&next[TLS_RECORD_HEADER_LEN..]);
}
}
async fn read_retry_client_hello_async<S: AsyncByteStream>(
stream: &mut S,
deframer: &mut TlsRecordDeframer,
) -> TransportResult<Vec<u8>> {
loop {
let record = noxtls_read_record_async(stream, deframer).await?;
if tls_record_content_type(&record) == TLS_RECORD_APPLICATION_DATA {
continue;
}
return Ok(record);
}
}
fn extract_handshake_message(record: &[u8], message_type: u8) -> TlsResult<Vec<u8>> {
if record.len() < TLS_RECORD_HEADER_LEN {
return Err(Error::ParseFailure("handshake record too short"));
}
let payload = &record[TLS_RECORD_HEADER_LEN..];
let mut cursor = 0_usize;
while cursor < payload.len() {
if payload.len().saturating_sub(cursor) < 4 {
return Err(Error::ParseFailure("truncated handshake header"));
}
let message_len = ((payload[cursor + 1] as usize) << 16)
| ((payload[cursor + 2] as usize) << 8)
| payload[cursor + 3] as usize;
let full_len = 4_usize.saturating_add(message_len);
if payload.len().saturating_sub(cursor) < full_len {
return Err(Error::ParseFailure("truncated handshake body"));
}
if payload[cursor] == message_type {
return Ok(payload[cursor..cursor + full_len].to_vec());
}
cursor = cursor.saturating_add(full_len);
}
Err(Error::ParseFailure("handshake message not found"))
}
fn is_tls13_compatibility_ccs(packet: &[u8]) -> bool {
packet.len() == TLS_RECORD_HEADER_LEN + 1
&& packet[0] == TLS_RECORD_CHANGE_CIPHER_SPEC
&& packet[3] == 0
&& packet[4] == 1
&& packet[TLS_RECORD_HEADER_LEN] == 0x01
}