use std::io::{self, Read, Write};
use aws_lc_rs::{agreement, digest};
use rand::Rng;
use super::common::{
ALERT_DESC_CLOSE_NOTIFY, ALERT_LEVEL_WARNING, CIPHERTEXT_READ_BUF_CAPACITY, CONTENT_TYPE_ALERT,
CONTENT_TYPE_APPLICATION_DATA, CONTENT_TYPE_CHANGE_CIPHER_SPEC, CONTENT_TYPE_HANDSHAKE,
HANDSHAKE_TYPE_CERTIFICATE, HANDSHAKE_TYPE_CERTIFICATE_VERIFY,
HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, HANDSHAKE_TYPE_FINISHED, MAX_TLS_PLAINTEXT_LEN,
PLAINTEXT_READ_BUF_CAPACITY, TLS_MAX_RECORD_SIZE, TLS_RECORD_HEADER_SIZE,
};
use super::reality_aead::{AeadKey, decrypt_handshake_message};
use super::reality_auth::{derive_auth_key, encrypt_session_id, perform_ecdh};
use super::reality_cipher_suite::{CipherSuite, DEFAULT_CIPHER_SUITES};
use super::reality_client_verify::{
extract_certificate_der, extract_certificate_verify_signature, extract_ed25519_public_key,
verify_certificate_hmac, verify_certificate_verify_signature,
};
use super::reality_io_state::RealityIoState;
use super::reality_reader_writer::{RealityReader, RealityWriter};
use super::reality_records::{RecordDecryptor, RecordEncryptor};
use super::reality_tls13_keys::{
compute_finished_verify_data, derive_application_secrets, derive_handshake_keys,
derive_traffic_keys,
};
use super::reality_tls13_messages::{
DEFAULT_ALPN_PROTOCOLS, construct_client_hello, construct_finished, write_record_header,
};
use super::reality_util::{extract_server_cipher_suite, extract_server_public_key};
use crate::slide_buffer::SlideBuffer;
use crate::util::allocate_vec;
const MAX_HANDSHAKE_PLAINTEXT: usize = 4 * MAX_TLS_PLAINTEXT_LEN;
fn append_handshake_plaintext(accumulated: &mut Vec<u8>, plaintext: &[u8]) -> io::Result<usize> {
let previous_len = accumulated.len();
if previous_len.saturating_add(plaintext.len()) > MAX_HANDSHAKE_PLAINTEXT {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"REALITY handshake exceeds maximum size",
));
}
accumulated.extend_from_slice(plaintext);
Ok(previous_len)
}
#[derive(Clone)]
pub struct RealityClientConfig {
pub public_key: [u8; 32],
pub short_id: [u8; 8],
pub server_name: String,
pub cipher_suites: Vec<CipherSuite>,
}
enum HandshakeState {
AwaitingServerHello {
client_hello_bytes: Vec<u8>, client_private_key: [u8; 32],
auth_key: [u8; 32], },
ProcessingHandshake {
client_handshake_traffic_secret: Vec<u8>,
server_handshake_traffic_secret: Vec<u8>,
master_secret: Vec<u8>,
cipher_suite: CipherSuite,
handshake_transcript_bytes: Vec<u8>, auth_key: [u8; 32], handshake_seq: u64, accumulated_plaintext: Vec<u8>, messages_found: u8, certificate_verified: bool, ed25519_public_key: Option<[u8; 32]>, cert_verify_offset: Option<usize>, },
Complete,
}
pub struct RealityClientConnection {
config: RealityClientConfig,
handshake_state: HandshakeState,
app_read_key: Option<AeadKey>,
app_read_iv: Option<Vec<u8>>,
app_write_key: Option<AeadKey>,
app_write_iv: Option<Vec<u8>>,
read_seq: u64,
write_seq: u64,
cipher_suite: Option<CipherSuite>,
tls_read_buffer: Box<[u8]>,
ciphertext_read_buf: SlideBuffer, ciphertext_write_buf: Vec<u8>, plaintext_read_buf: SlideBuffer, plaintext_write_buf: Vec<u8>,
received_close_notify: bool, fatal_error: Option<io::ErrorKind>, }
impl RealityClientConnection {
pub fn new(config: RealityClientConfig) -> io::Result<Self> {
let mut conn = RealityClientConnection {
config,
handshake_state: HandshakeState::AwaitingServerHello {
client_hello_bytes: Vec::new(),
client_private_key: [0u8; 32],
auth_key: [0u8; 32],
},
app_read_key: None,
app_read_iv: None,
app_write_key: None,
app_write_iv: None,
read_seq: 0,
write_seq: 0,
cipher_suite: None,
tls_read_buffer: allocate_vec(TLS_MAX_RECORD_SIZE).into_boxed_slice(),
ciphertext_read_buf: SlideBuffer::new(CIPHERTEXT_READ_BUF_CAPACITY),
ciphertext_write_buf: Vec::new(),
plaintext_read_buf: SlideBuffer::new(PLAINTEXT_READ_BUF_CAPACITY),
plaintext_write_buf: Vec::new(),
received_close_notify: false,
fatal_error: None,
};
conn.generate_client_hello()?;
Ok(conn)
}
fn generate_client_hello(&mut self) -> io::Result<()> {
let mut rng = rand::rng();
let mut our_private_bytes = [0u8; 32];
rng.fill_bytes(&mut our_private_bytes);
let our_private_key =
agreement::PrivateKey::from_private_key(&agreement::X25519, &our_private_bytes)
.map_err(|_| io::Error::other("Failed to create X25519 key"))?;
let our_public_key_bytes = our_private_key
.compute_public_key()
.map_err(|_| io::Error::other("Failed to compute public key"))?;
let mut client_random = [0u8; 32];
rng.fill_bytes(&mut client_random);
let shared_secret = perform_ecdh(&our_private_bytes, &self.config.public_key)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
let auth_key = derive_auth_key(&shared_secret, &client_random[0..20], b"REALITY")
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|_| io::Error::other("System time error"))?
.as_secs();
let mut session_id_plaintext = [0u8; 16];
session_id_plaintext[0] = 1; session_id_plaintext[1] = 8; session_id_plaintext[2] = 0; session_id_plaintext[3] = 0; session_id_plaintext[4..8].copy_from_slice(&(timestamp as u32).to_be_bytes());
session_id_plaintext[8..16].copy_from_slice(&self.config.short_id);
let mut session_id_for_hello = [0u8; 32];
session_id_for_hello[0..16].copy_from_slice(&session_id_plaintext);
let cipher_suites = if self.config.cipher_suites.is_empty() {
DEFAULT_CIPHER_SUITES.to_vec()
} else {
self.config.cipher_suites.clone()
};
let cipher_suite_ids: Vec<u16> = cipher_suites.iter().map(|cs| cs.id()).collect();
let mut client_hello = construct_client_hello(
&client_random,
&session_id_for_hello,
our_public_key_bytes.as_ref(),
&self.config.server_name,
&cipher_suite_ids,
DEFAULT_ALPN_PROTOCOLS,
)?;
let nonce = &client_random[20..32];
client_hello[39..71].fill(0);
log::debug!("REALITY CLIENT: Encrypting SessionId");
log::debug!(
" aad_len={} (ClientHello with zero SessionId)",
client_hello.len()
);
log::debug!(" aad[0..4]={:02x?}", &client_hello[0..4]);
let encrypted_session_id =
encrypt_session_id(&session_id_plaintext, &auth_key, nonce, &client_hello)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
log::debug!(
"REALITY CLIENT: Encrypted SessionId={:02x?}",
encrypted_session_id
);
client_hello[39..71].copy_from_slice(&encrypted_session_id);
let mut record = write_record_header(CONTENT_TYPE_HANDSHAKE, client_hello.len() as u16);
record.extend_from_slice(&client_hello);
self.ciphertext_write_buf.extend_from_slice(&record);
self.handshake_state = HandshakeState::AwaitingServerHello {
client_hello_bytes: client_hello, client_private_key: our_private_bytes,
auth_key, };
log::debug!(
"REALITY: ClientHello generated and buffered ({} bytes)",
record.len()
);
Ok(())
}
pub fn read_tls(&mut self, rd: &mut dyn Read) -> io::Result<usize> {
if self.ciphertext_read_buf.remaining_capacity() < TLS_MAX_RECORD_SIZE {
self.ciphertext_read_buf.compact();
}
let n = rd.read(&mut self.tls_read_buffer[..])?;
if n > 0 {
self.ciphertext_read_buf
.extend_from_slice(&self.tls_read_buffer[..n]);
}
Ok(n)
}
pub fn process_new_packets(&mut self) -> io::Result<RealityIoState> {
if let Some(error_kind) = self.fatal_error {
return Err(io::Error::new(error_kind, "connection previously failed"));
}
if self.received_close_notify {
return Ok(RealityIoState::new(self.plaintext_read_buf.len()));
}
let result = self.process_new_packets_inner();
if let Err(ref e) = result {
match e.kind() {
io::ErrorKind::InvalidData
| io::ErrorKind::PermissionDenied
| io::ErrorKind::ConnectionAborted => {
self.fatal_error = Some(e.kind());
}
_ => {}
}
}
result
}
fn process_new_packets_inner(&mut self) -> io::Result<RealityIoState> {
loop {
match &self.handshake_state {
HandshakeState::AwaitingServerHello { .. } => {
if !self.process_server_hello()? {
break;
}
}
HandshakeState::ProcessingHandshake { .. } => {
if !self.process_encrypted_handshake()? {
break;
}
}
HandshakeState::Complete => {
self.process_application_data()?;
break;
}
}
}
Ok(RealityIoState::new(self.plaintext_read_buf.len()))
}
#[inline]
fn process_server_hello(&mut self) -> io::Result<bool> {
let HandshakeState::AwaitingServerHello {
client_hello_bytes,
client_private_key,
auth_key,
} = &self.handshake_state
else {
unreachable!()
};
if self.ciphertext_read_buf.len() < TLS_RECORD_HEADER_SIZE {
return Ok(false);
}
let record_len = self
.ciphertext_read_buf
.get_u16_be(3)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Buffer too short"))?
as usize;
let total_record_len = TLS_RECORD_HEADER_SIZE + record_len;
if self.ciphertext_read_buf.len() < total_record_len {
return Ok(false);
}
let client_hello_bytes = client_hello_bytes.clone();
let record: Vec<u8> = self.ciphertext_read_buf[..total_record_len].to_vec();
self.ciphertext_read_buf.consume(total_record_len);
let server_hello = &record[TLS_RECORD_HEADER_SIZE..];
log::debug!(
"REALITY CLIENT: ServerHello for transcript: len={}, bytes={:02x?}",
server_hello.len(),
server_hello
);
let server_public_key = extract_server_public_key(&record)?;
let cipher_suite_id = extract_server_cipher_suite(&record)?;
let cipher_suite = CipherSuite::from_id(cipher_suite_id).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Server selected unsupported cipher suite: 0x{:04x}",
cipher_suite_id
),
)
})?;
log::debug!(
"REALITY CLIENT: Server selected cipher suite 0x{:04x}",
cipher_suite_id
);
let mut full_transcript = digest::Context::new(cipher_suite.digest_algorithm());
log::debug!(
"REALITY CLIENT: Transcript includes ClientHello ({} bytes), first bytes: {:02x?}",
client_hello_bytes.len(),
&client_hello_bytes[..client_hello_bytes.len().min(20)]
);
log::debug!(
"REALITY CLIENT: Transcript includes ServerHello ({} bytes), first bytes: {:02x?}",
server_hello.len(),
&server_hello[..server_hello.len().min(20)]
);
full_transcript.update(&client_hello_bytes); full_transcript.update(server_hello); let server_hello_hash = full_transcript.finish();
let server_hello_hash_vec: Vec<u8> = server_hello_hash.as_ref().to_vec();
let client_hello_hash_vec: Vec<u8> = {
let mut ctx = digest::Context::new(cipher_suite.digest_algorithm());
ctx.update(&client_hello_bytes);
ctx.finish().as_ref().to_vec()
};
let peer_public_key =
agreement::UnparsedPublicKey::new(&agreement::X25519, &server_public_key);
let my_private_key =
agreement::PrivateKey::from_private_key(&agreement::X25519, client_private_key)
.map_err(|_| io::Error::other("Failed to create private key"))?;
let mut tls_shared_secret = [0u8; 32];
agreement::agree(
&my_private_key,
peer_public_key,
io::Error::other("ECDH failed"),
|key_material| {
tls_shared_secret.copy_from_slice(key_material);
Ok(())
},
)?;
let hs_keys = derive_handshake_keys(
cipher_suite,
&tls_shared_secret,
&client_hello_hash_vec,
&server_hello_hash_vec,
)?;
log::debug!("REALITY: ServerHello processed, handshake keys derived");
let mut transcript_bytes = Vec::new();
transcript_bytes.extend_from_slice(&client_hello_bytes);
transcript_bytes.extend_from_slice(server_hello);
self.handshake_state = HandshakeState::ProcessingHandshake {
client_handshake_traffic_secret: hs_keys.client_handshake_traffic_secret.clone(),
server_handshake_traffic_secret: hs_keys.server_handshake_traffic_secret.clone(),
master_secret: hs_keys.master_secret.clone(),
cipher_suite,
handshake_transcript_bytes: transcript_bytes,
auth_key: *auth_key, handshake_seq: 0,
accumulated_plaintext: Vec::new(),
messages_found: 0,
certificate_verified: false,
ed25519_public_key: None,
cert_verify_offset: None,
};
Ok(true)
}
#[inline]
fn process_encrypted_handshake(&mut self) -> io::Result<bool> {
let HandshakeState::ProcessingHandshake {
client_handshake_traffic_secret,
server_handshake_traffic_secret,
master_secret,
cipher_suite,
handshake_transcript_bytes,
auth_key,
handshake_seq,
accumulated_plaintext,
messages_found,
certificate_verified,
ed25519_public_key,
cert_verify_offset,
} = &self.handshake_state
else {
unreachable!()
};
let (server_hs_key, server_hs_iv) =
derive_traffic_keys(server_handshake_traffic_secret, *cipher_suite)?;
if *handshake_seq == 0 {
log::debug!(
"REALITY CLIENT: Server HS key={:02x?}, iv={:02x?}",
&server_hs_key[..16],
server_hs_iv
);
}
if self.ciphertext_read_buf.len() < TLS_RECORD_HEADER_SIZE {
return Ok(false);
}
let record_type = self.ciphertext_read_buf[0];
let tls_version = self
.ciphertext_read_buf
.get_u16_be(1)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Buffer too short"))?;
let record_len = self
.ciphertext_read_buf
.get_u16_be(3)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Buffer too short"))?
as usize;
log::debug!(
"REALITY CLIENT: TLS record header: type=0x{:02x}, version=0x{:04x}, len={}",
record_type,
tls_version,
record_len
);
let total_record_len = TLS_RECORD_HEADER_SIZE + record_len;
if self.ciphertext_read_buf.len() < total_record_len {
return Ok(false);
}
if record_type == CONTENT_TYPE_CHANGE_CIPHER_SPEC {
log::debug!(
"REALITY CLIENT: Skipping ChangeCipherSpec record ({} bytes)",
record_len
);
self.ciphertext_read_buf.consume(total_record_len);
return self.process_encrypted_handshake();
}
if record_type != CONTENT_TYPE_APPLICATION_DATA {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected Application Data record, got 0x{:02x}",
record_type
),
));
}
let client_hs_secret = client_handshake_traffic_secret.clone();
let server_hs_secret = server_handshake_traffic_secret.clone();
let master_secret = master_secret.clone();
let transcript_bytes = handshake_transcript_bytes.clone();
let mut accumulated_plaintext = accumulated_plaintext.clone();
let cipher_suite = *cipher_suite;
let auth_key = *auth_key;
let mut handshake_seq = *handshake_seq;
let mut messages_found = *messages_found;
let mut certificate_verified = *certificate_verified;
let mut ed25519_public_key = *ed25519_public_key;
let mut cert_verify_offset = *cert_verify_offset;
let ciphertext: Vec<u8> =
self.ciphertext_read_buf[TLS_RECORD_HEADER_SIZE..total_record_len].to_vec();
self.ciphertext_read_buf.consume(total_record_len);
log::debug!(
"REALITY CLIENT: Decrypting handshake record #{} - record_len={}",
handshake_seq,
record_len
);
let plaintext = decrypt_handshake_message(
cipher_suite,
&server_hs_key,
&server_hs_iv,
handshake_seq,
&ciphertext,
record_len as u16,
)?;
log::debug!(
"REALITY CLIENT: Decrypted record #{} ({} bytes plaintext)",
handshake_seq,
plaintext.len()
);
handshake_seq += 1;
let prev_accumulated_len =
append_handshake_plaintext(&mut accumulated_plaintext, &plaintext)?;
let mut offset = prev_accumulated_len;
while offset < accumulated_plaintext.len() && messages_found < 4 {
if offset + 4 > accumulated_plaintext.len() {
break; }
let msg_type = accumulated_plaintext[offset];
let msg_len = u32::from_be_bytes([
0,
accumulated_plaintext[offset + 1],
accumulated_plaintext[offset + 2],
accumulated_plaintext[offset + 3],
]) as usize;
if offset + 4 + msg_len > accumulated_plaintext.len() {
break; }
let msg_name = match msg_type {
HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS => "EncryptedExtensions",
HANDSHAKE_TYPE_CERTIFICATE => "Certificate",
HANDSHAKE_TYPE_CERTIFICATE_VERIFY => "CertificateVerify",
HANDSHAKE_TYPE_FINISHED => "Finished",
_ => "Unknown",
};
log::debug!(
"REALITY CLIENT: Found {} message (type={}, len={})",
msg_name,
msg_type,
msg_len
);
if msg_type == HANDSHAKE_TYPE_CERTIFICATE {
let cert_der =
extract_certificate_der(&accumulated_plaintext[offset..offset + 4 + msg_len])?;
verify_certificate_hmac(cert_der, &auth_key)?;
ed25519_public_key = Some(extract_ed25519_public_key(cert_der)?);
certificate_verified = true;
}
if msg_type == HANDSHAKE_TYPE_CERTIFICATE_VERIFY {
cert_verify_offset = Some(offset);
}
messages_found += 1;
offset += 4 + msg_len;
}
if messages_found < 4 {
log::debug!(
"REALITY CLIENT: Received {} of 4 handshake messages, waiting for more records",
messages_found
);
self.handshake_state = HandshakeState::ProcessingHandshake {
client_handshake_traffic_secret: client_hs_secret,
server_handshake_traffic_secret: server_hs_secret,
master_secret,
cipher_suite,
handshake_transcript_bytes: transcript_bytes,
auth_key,
handshake_seq,
accumulated_plaintext,
messages_found,
certificate_verified,
ed25519_public_key,
cert_verify_offset,
};
return Ok(true); }
log::debug!(
"REALITY CLIENT: All 4 handshake messages received ({} bytes total)",
accumulated_plaintext.len()
);
if !certificate_verified {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"REALITY handshake failed: Certificate message not received or not verified",
));
}
let mut cert_verify_verified = false;
if let (Some(public_key), Some(cv_offset)) = (ed25519_public_key, cert_verify_offset) {
let mut cv_transcript = digest::Context::new(cipher_suite.digest_algorithm());
cv_transcript.update(&transcript_bytes);
cv_transcript.update(&accumulated_plaintext[..cv_offset]);
let cv_transcript_hash = cv_transcript.finish();
let cv_msg_len = u32::from_be_bytes([
0,
accumulated_plaintext[cv_offset + 1],
accumulated_plaintext[cv_offset + 2],
accumulated_plaintext[cv_offset + 3],
]) as usize;
let cv_message = &accumulated_plaintext[cv_offset..cv_offset + 4 + cv_msg_len];
let signature = extract_certificate_verify_signature(cv_message)?;
verify_certificate_verify_signature(
&public_key,
&signature,
cv_transcript_hash.as_ref(),
)?;
cert_verify_verified = true;
}
if !cert_verify_verified {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"REALITY handshake failed: CertificateVerify not verified",
));
}
let mut handshake_transcript = digest::Context::new(cipher_suite.digest_algorithm());
handshake_transcript.update(&transcript_bytes);
handshake_transcript.update(&accumulated_plaintext);
let handshake_hash = handshake_transcript.finish();
let handshake_hash_vec: Vec<u8> = handshake_hash.as_ref().to_vec();
log::debug!(
"REALITY CLIENT: Handshake hash for client Finished: {:02x?}",
handshake_hash_vec
);
log::debug!(
"REALITY CLIENT: Transcript bytes len={}, accumulated_plaintext len={}",
transcript_bytes.len(),
accumulated_plaintext.len()
);
let client_verify_data =
compute_finished_verify_data(cipher_suite, &client_hs_secret, &handshake_hash_vec)?;
log::debug!(
"REALITY CLIENT: Client verify data: {:02x?}",
client_verify_data
);
let client_finished = construct_finished(&client_verify_data)?;
let (client_hs_key, client_hs_iv) = derive_traffic_keys(&client_hs_secret, cipher_suite)?;
let mut client_hs_seq = 0u64;
let buf_len_before = self.ciphertext_write_buf.len();
let hs_aead_key = AeadKey::new(cipher_suite, &client_hs_key)?;
{
let mut encryptor =
RecordEncryptor::new(&hs_aead_key, &client_hs_iv, &mut client_hs_seq);
encryptor.encrypt_handshake(&client_finished, &mut self.ciphertext_write_buf)?;
}
log::debug!(
"REALITY CLIENT: Client Finished message generated and buffered ({} bytes)",
self.ciphertext_write_buf.len() - buf_len_before
);
let (client_app_secret, server_app_secret) =
derive_application_secrets(cipher_suite, &master_secret, &handshake_hash_vec)?;
let (client_app_key_bytes, client_app_iv) =
derive_traffic_keys(&client_app_secret, cipher_suite)?;
let (server_app_key_bytes, server_app_iv) =
derive_traffic_keys(&server_app_secret, cipher_suite)?;
let client_app_key = AeadKey::new(cipher_suite, &client_app_key_bytes)?;
let server_app_key = AeadKey::new(cipher_suite, &server_app_key_bytes)?;
self.app_read_key = Some(server_app_key);
self.app_read_iv = Some(server_app_iv);
self.app_write_key = Some(client_app_key);
self.app_write_iv = Some(client_app_iv);
self.read_seq = 0;
self.write_seq = 0;
self.cipher_suite = Some(cipher_suite);
self.handshake_state = HandshakeState::Complete;
log::debug!("REALITY CLIENT: Handshake complete, application keys derived");
Ok(true)
}
#[inline]
fn process_application_data(&mut self) -> io::Result<()> {
let (app_read_key, app_read_iv) = match (&self.app_read_key, &self.app_read_iv) {
(Some(key), Some(iv)) => (key, iv),
_ => unreachable!(), };
while self.ciphertext_read_buf.len() >= TLS_RECORD_HEADER_SIZE {
let record_len = self
.ciphertext_read_buf
.get_u16_be(3)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Buffer too short"))?
as usize;
let total_record_len = TLS_RECORD_HEADER_SIZE + record_len;
if self.ciphertext_read_buf.len() < total_record_len {
break;
}
let ciphertext_slice = self
.ciphertext_read_buf
.slice_mut(TLS_RECORD_HEADER_SIZE..total_record_len);
let mut decryptor = RecordDecryptor::new(app_read_key, app_read_iv, &mut self.read_seq);
let (content_type, plaintext) =
decryptor.decrypt_record_in_place(ciphertext_slice, record_len as u16)?;
match content_type {
CONTENT_TYPE_APPLICATION_DATA => {
self.plaintext_read_buf.maybe_compact(4096);
self.plaintext_read_buf.extend_from_slice(plaintext);
}
CONTENT_TYPE_ALERT => {
if plaintext.len() >= 2 {
let alert_level = plaintext[0];
let alert_desc = plaintext[1];
if alert_desc == ALERT_DESC_CLOSE_NOTIFY {
log::debug!("REALITY CLIENT: Received close_notify alert");
self.received_close_notify = true;
return Ok(());
} else if alert_level != ALERT_LEVEL_WARNING {
log::warn!(
"REALITY CLIENT: Received fatal alert: level={}, desc={}",
alert_level,
alert_desc
);
return Err(io::Error::new(
io::ErrorKind::ConnectionAborted,
format!("received fatal alert: {}", alert_desc),
));
} else {
log::debug!(
"REALITY CLIENT: Received warning alert: desc={}",
alert_desc
);
}
}
}
_ => unreachable!(
"strip_content_type validates content type; unexpected: 0x{:02x}",
content_type
),
}
self.ciphertext_read_buf.consume(total_record_len);
}
Ok(())
}
pub fn reader(&mut self) -> RealityReader<'_> {
self.plaintext_read_buf.maybe_compact(4096);
RealityReader::new(&mut self.plaintext_read_buf, self.received_close_notify)
}
pub fn writer(&mut self) -> RealityWriter<'_> {
RealityWriter::new(
&mut self.plaintext_write_buf,
self.ciphertext_write_buf.len(),
)
}
pub fn write_tls(&mut self, wr: &mut dyn Write) -> io::Result<usize> {
if !matches!(self.handshake_state, HandshakeState::Complete) {
let n = wr.write(&self.ciphertext_write_buf)?;
self.ciphertext_write_buf.drain(..n);
return Ok(n);
}
if !self.plaintext_write_buf.is_empty() {
let (app_write_key, app_write_iv) = match (&self.app_write_key, &self.app_write_iv) {
(Some(key), Some(iv)) => (key, iv),
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Application keys not available",
));
}
};
let mut encryptor =
RecordEncryptor::new(app_write_key, app_write_iv, &mut self.write_seq);
encryptor.encrypt_app_data(
&mut self.plaintext_write_buf,
&mut self.ciphertext_write_buf,
)?;
}
let n = wr.write(&self.ciphertext_write_buf)?;
self.ciphertext_write_buf.drain(..n);
Ok(n)
}
pub fn wants_write(&self) -> bool {
!self.ciphertext_write_buf.is_empty() || !self.plaintext_write_buf.is_empty()
}
pub fn is_handshaking(&self) -> bool {
!matches!(self.handshake_state, HandshakeState::Complete)
}
pub fn wants_read(&self) -> bool {
if self.received_close_notify {
return false;
}
if self.fatal_error.is_some() {
return false;
}
if self.is_handshaking() {
return true;
}
self.plaintext_read_buf.is_empty()
}
pub fn send_close_notify(&mut self) {
if !matches!(self.handshake_state, HandshakeState::Complete) {
log::debug!("REALITY CLIENT: Cannot send close_notify - handshake not complete");
return;
}
let (app_write_key, app_write_iv) = match (&self.app_write_key, &self.app_write_iv) {
(Some(key), Some(iv)) => (key, iv),
_ => {
log::debug!(
"REALITY CLIENT: Cannot send close_notify - application keys not available"
);
return;
}
};
let mut encryptor = RecordEncryptor::new(app_write_key, app_write_iv, &mut self.write_seq);
match encryptor.encrypt_close_notify(&mut self.ciphertext_write_buf) {
Ok(()) => {
log::debug!("REALITY CLIENT: Encrypted close_notify alert queued");
}
Err(e) => {
log::error!("REALITY CLIENT: Failed to encrypt close_notify: {}", e);
}
}
}
}
#[inline]
pub fn feed_reality_client_connection(
client_connection: &mut RealityClientConnection,
data: &[u8],
) -> std::io::Result<()> {
let mut cursor = std::io::Cursor::new(data);
let mut i = 0;
while i < data.len() {
let n = client_connection.read_tls(&mut cursor).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("failed to feed TLS connection: {e}"),
)
})?;
i += n;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handshake_plaintext_is_bounded() {
let mut accumulated = vec![0; MAX_HANDSHAKE_PLAINTEXT];
let error = append_handshake_plaintext(&mut accumulated, &[0]).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidData);
assert_eq!(accumulated.len(), MAX_HANDSHAKE_PLAINTEXT);
}
#[test]
fn client_hello_transcript_bytes_use_encrypted_session_id() {
let server_private = [0x42u8; 32];
let server_private_key =
agreement::PrivateKey::from_private_key(&agreement::X25519, &server_private).unwrap();
let server_public_key = server_private_key.compute_public_key().unwrap();
let mut server_public = [0u8; 32];
server_public.copy_from_slice(server_public_key.as_ref());
let config = RealityClientConfig {
public_key: server_public,
short_id: [1, 2, 3, 4, 5, 6, 7, 8],
server_name: "example.com".to_string(),
cipher_suites: vec![],
};
let mut conn = RealityClientConnection::new(config).unwrap();
let stored_client_hello = match &conn.handshake_state {
HandshakeState::AwaitingServerHello {
client_hello_bytes, ..
} => client_hello_bytes.clone(),
_ => panic!("new client must be awaiting ServerHello"),
};
let mut wire = Vec::new();
conn.write_tls(&mut wire).unwrap();
assert_eq!(&wire[5..], stored_client_hello.as_slice());
assert_ne!(
&stored_client_hello[39..71],
&[0u8; 32],
"transcript ClientHello must retain encrypted wire SessionId"
);
}
}