2108 lines
97 KiB
Rust
2108 lines
97 KiB
Rust
use smoltcp::socket::TcpSocket;
|
|
use smoltcp::socket::TcpState;
|
|
use smoltcp::socket::SocketHandle;
|
|
use smoltcp::socket::SocketSet;
|
|
use smoltcp::wire::IpEndpoint;
|
|
use smoltcp::Result;
|
|
use smoltcp::Error;
|
|
use smoltcp::phy::Device;
|
|
use smoltcp::iface::EthernetInterface;
|
|
use smoltcp::time::Instant;
|
|
|
|
use byteorder::{ByteOrder, NetworkEndian};
|
|
use generic_array::GenericArray;
|
|
|
|
use core::convert::TryFrom;
|
|
use core::convert::TryInto;
|
|
use core::cell::RefCell;
|
|
|
|
use p256::{EncodedPoint, ecdh::EphemeralSecret};
|
|
use ccm::consts::*;
|
|
|
|
use nom::bytes::complete::take;
|
|
use nom::error::ErrorKind;
|
|
use nom::combinator::complete;
|
|
|
|
use alloc::vec::Vec;
|
|
use heapless::Vec as HeaplessVec;
|
|
|
|
use crate::tls_packet::*;
|
|
use crate::parse::{
|
|
parse_tls_repr,
|
|
parse_inner_plaintext_for_handshake,
|
|
get_content_type_inner_plaintext
|
|
};
|
|
use crate::buffer::TlsBuffer;
|
|
use crate::session::{Session, TlsRole, DiffieHellmanPublicKey, DiffieHellmanPrivateKey};
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
#[allow(non_camel_case_types)]
|
|
pub(crate) enum TlsState {
|
|
DEFAULT, // The default state of the TLS socket
|
|
// Client state machine diagram
|
|
CLIENT_START,
|
|
WAIT_SH,
|
|
WAIT_EE,
|
|
WAIT_CERT_CR,
|
|
CLIENT_WAIT_CERT,
|
|
CLIENT_WAIT_CV,
|
|
CLIENT_WAIT_FINISHED,
|
|
SERVER_COMPLETED, // Additional state, for client to send Finished after server Finished
|
|
CLIENT_CONNECTED,
|
|
// Server state machine diagram
|
|
SERVER_START,
|
|
NEGOTIATED,
|
|
WAIT_FLIGHT,
|
|
SERVER_WAIT_CERT,
|
|
SERVER_WAIT_CV,
|
|
SERVER_WAIT_FINISHED,
|
|
SERVER_CONNECTED
|
|
}
|
|
|
|
pub struct TlsSocket<'a>
|
|
{
|
|
// Locally owned SocketSet, solely containing 1 TCP socket
|
|
sockets: SocketSet<'a>,
|
|
tcp_handle: SocketHandle,
|
|
rng: &'a mut dyn crate::TlsRng,
|
|
session: RefCell<Session<'a>>,
|
|
}
|
|
|
|
impl<'a> TlsSocket<'a> {
|
|
pub fn new(
|
|
tcp_socket: TcpSocket<'a>,
|
|
rng: &'a mut dyn crate::TlsRng,
|
|
certificate_with_key: Option<(
|
|
crate::session::CertificatePrivateKey,
|
|
Vec<&'a [u8]>
|
|
)>
|
|
) -> Self
|
|
{
|
|
let socket_set_entries: [_; 1] = Default::default();
|
|
let mut sockets = SocketSet::new(socket_set_entries);
|
|
let tcp_handle = sockets.add(tcp_socket);
|
|
TlsSocket {
|
|
sockets,
|
|
tcp_handle,
|
|
rng,
|
|
session: RefCell::new(
|
|
Session::new(TlsRole::Unknown, certificate_with_key)
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn connect<T, U>(
|
|
&mut self,
|
|
remote_endpoint: T,
|
|
local_endpoint: U,
|
|
) -> Result<()>
|
|
where
|
|
T: Into<IpEndpoint>,
|
|
U: Into<IpEndpoint>,
|
|
{
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
let mut session = self.session.borrow_mut();
|
|
|
|
// Start TCP handshake
|
|
if !tcp_socket.is_open() {
|
|
tcp_socket.connect(remote_endpoint, local_endpoint)?;
|
|
// Start TLS handshake if TCP handshake will commence
|
|
session.connect(
|
|
tcp_socket.remote_endpoint(),
|
|
tcp_socket.local_endpoint()
|
|
);
|
|
} else {
|
|
// Also start TLS handshake if for some reason TCP is ready,
|
|
// and TLS is idle
|
|
if session.get_tls_state() == TlsState::DEFAULT {
|
|
session.connect(
|
|
tcp_socket.remote_endpoint(),
|
|
tcp_socket.local_endpoint()
|
|
);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn listen<T>(
|
|
&mut self,
|
|
issue_client_verification: bool,
|
|
local_endpoint: T
|
|
) -> Result<()>
|
|
where
|
|
T: Into<IpEndpoint>
|
|
{
|
|
// Listen from TCP socket
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
tcp_socket.listen(local_endpoint)?;
|
|
|
|
// Update tls session to server_start
|
|
let mut session = self.session.borrow_mut();
|
|
session.listen(issue_client_verification);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn update_handshake<DeviceT>(
|
|
&mut self,
|
|
iface: &mut EthernetInterface<DeviceT>,
|
|
now: Instant
|
|
) -> Result<bool>
|
|
where
|
|
DeviceT: for<'d> Device<'d>
|
|
{
|
|
// Poll the TCP socket, no matter what
|
|
let propagated_poll = iface.poll(&mut self.sockets, now)?;
|
|
|
|
// Handle TLS handshake through TLS states
|
|
let tls_state = {
|
|
self.session.borrow().get_tls_state()
|
|
};
|
|
|
|
let need_send_alert = {
|
|
self.session.borrow().get_need_send_alert()
|
|
};
|
|
|
|
// Check TCP socket/ TLS session
|
|
{
|
|
let tcp_state = self.sockets.get::<TcpSocket>(self.tcp_handle).state();
|
|
|
|
// Close TCP socket if necessary
|
|
if tcp_state == TcpState::Established && tls_state == TlsState::DEFAULT {
|
|
self.sockets.get::<TcpSocket>(self.tcp_handle).close();
|
|
return Ok(propagated_poll);
|
|
}
|
|
|
|
// Skip handshake processing if it is already completed
|
|
// However, redo TCP handshake if TLS socket is trying to connect and
|
|
// TCP socket is not connected <= seems like a bad piece of idea to me
|
|
// Reset TLS state to DEFAULT if TCP session is interrupted
|
|
// This is to close off hanged TLS sockets, when its dependent TCP session
|
|
// has already ended.
|
|
if tcp_state != TcpState::Established {
|
|
use TlsState::*;
|
|
match tls_state {
|
|
// Do nothing on the starting states
|
|
// Namely those immediate precedes TCP handshake,
|
|
// as handshake can legitimately be incomplete
|
|
DEFAULT |
|
|
SERVER_START => {},
|
|
|
|
// Attempt to reconnect if the socket went down before TLS socket sent anything
|
|
CLIENT_START => {
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
let session = self.session.borrow();
|
|
if !tcp_socket.is_open() {
|
|
log::info!("Socket closed initially");
|
|
tcp_socket.connect(
|
|
session.get_remote_endpoint(),
|
|
session.get_local_endpoint()
|
|
)?;
|
|
}
|
|
}
|
|
|
|
// For any other functioning state, the TCP connection being not
|
|
// established should imply that the TLS connection had been derailed
|
|
// Reset TLS state to DEFAULT to allow terminate a dead link
|
|
_ => {
|
|
let mut session = self.session.borrow_mut();
|
|
session.reset_state();
|
|
log::info!("TLS socket resets after TCP socket closed");
|
|
}
|
|
}
|
|
|
|
// Terminate the procedure, as no processing is necessary
|
|
return Ok(propagated_poll);
|
|
}
|
|
}
|
|
|
|
// Send alert to start terminating TLS session if necessary
|
|
if let Some(alert) = need_send_alert {
|
|
match tls_state {
|
|
// Client side socket:
|
|
// States that expects plaintext payload
|
|
TlsState::WAIT_SH | TlsState::SERVER_START => {
|
|
// Send the cooresponding alert in plaintext
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
tcp_socket.send(
|
|
|data| {
|
|
// Set up a TLS buffer on the internal buffer of TCP socket
|
|
let mut buffer = TlsBuffer::new(data);
|
|
// Instantiate a TLS bytes-representation with pre-determined alert
|
|
let tls_repr = TlsRepr::new().alert(alert);
|
|
if buffer.enqueue_tls_repr(tls_repr).is_err() {
|
|
return (0, ())
|
|
}
|
|
|
|
let slice: &[u8] = buffer.into();
|
|
(slice.len(), ())
|
|
}
|
|
)?;
|
|
},
|
|
// States that expects enrypted payload using handshake secret
|
|
TlsState::WAIT_EE |
|
|
TlsState::WAIT_CERT_CR |
|
|
TlsState::CLIENT_WAIT_CERT |
|
|
TlsState::CLIENT_WAIT_CV |
|
|
TlsState::CLIENT_WAIT_FINISHED |
|
|
TlsState::SERVER_COMPLETED |
|
|
TlsState::NEGOTIATED |
|
|
TlsState::WAIT_FLIGHT |
|
|
TlsState::SERVER_WAIT_CERT |
|
|
TlsState::SERVER_WAIT_CV => {
|
|
// Send the corresponding alert in ciphertext using handshake secret
|
|
let severity: u8 = match alert {
|
|
AlertType::CloseNotify | AlertType::UserCanceled => {
|
|
1
|
|
},
|
|
_ => 2
|
|
};
|
|
let mut alert_array: [u8; 3] = [
|
|
severity,
|
|
u8::try_from(alert).unwrap(),
|
|
21 // Alert content type
|
|
];
|
|
self.send_application_slice(&mut alert_array)?;
|
|
},
|
|
// States that expects enrypted payload using application data secret
|
|
TlsState::CLIENT_CONNECTED |
|
|
TlsState::SERVER_WAIT_FINISHED |
|
|
TlsState::SERVER_CONNECTED => {
|
|
// Send the corresponding alert in ciphertext using application data secret
|
|
// Sending order:
|
|
// 1. Associated data/ TLS Record layer
|
|
// 2. Encrypted { Alert }
|
|
// 3. Authentication tag (16 bytes for all supported AEADs)
|
|
let mut associated_data: [u8; 5] = [
|
|
0x17, // Application data
|
|
0x03, 0x03, // TLS 1.3 record disguised as TLS 1.2
|
|
0x00, 0x00 // Length of encrypted data, yet to be determined conveniently
|
|
];
|
|
|
|
NetworkEndian::write_u16(&mut associated_data[3..5],
|
|
2 // Payload length
|
|
+ 1 // Content type length
|
|
+ 16 // Auth tag length
|
|
);
|
|
|
|
// Alert: Warning (1) , Close notify (0)
|
|
let severity: u8 = match alert {
|
|
AlertType::CloseNotify | AlertType::UserCanceled => {
|
|
1
|
|
},
|
|
_ => 2
|
|
};
|
|
let mut alert_array: [u8; 3] = [
|
|
severity,
|
|
u8::try_from(alert).unwrap(),
|
|
21 // Alert content type
|
|
];
|
|
|
|
let mut session = self.session.borrow_mut();
|
|
let tag = session.encrypt_application_data_in_place_detached(
|
|
&associated_data,
|
|
&mut alert_array
|
|
).unwrap();
|
|
session.increment_local_sequence_number();
|
|
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
if !tcp_socket.can_send() {
|
|
return Err(Error::Illegal);
|
|
}
|
|
|
|
tcp_socket.send_slice(&associated_data)?;
|
|
tcp_socket.send_slice(&alert_array)?;
|
|
tcp_socket.send_slice(&tag)?;
|
|
},
|
|
// Other states, such as client_start and default should never send alert
|
|
// These stages are too early to raise exceptions
|
|
_ => unreachable!()
|
|
}
|
|
|
|
// Finally, revert the FSM to DEFAULT to signal an invokation of
|
|
// `close()` to the TCP socket
|
|
self.session.borrow_mut().reset_state();
|
|
|
|
return Ok(propagated_poll);
|
|
}
|
|
|
|
// Handle TLS handshake through TLS states
|
|
match tls_state {
|
|
// Do nothing on the default state
|
|
// Socket has not been assigned to be a client or server
|
|
TlsState::DEFAULT => {},
|
|
// Initiate TLS handshake
|
|
TlsState::CLIENT_START => {
|
|
// Prepare field that is randomised,
|
|
// Supply it to the TLS repr builder.
|
|
let ecdh_secret = EphemeralSecret::random(&mut self.rng);
|
|
let x25519_secret = x25519_dalek::EphemeralSecret::new(&mut self.rng);
|
|
let mut random: [u8; 32] = [0; 32];
|
|
let mut session_id: [u8; 32] = [0; 32];
|
|
self.rng.fill_bytes(&mut random);
|
|
self.rng.fill_bytes(&mut session_id);
|
|
let repr = TlsRepr::new()
|
|
.client_hello(&ecdh_secret, &x25519_secret, random, session_id.clone());
|
|
|
|
{
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
let mut session = self.session.borrow_mut();
|
|
tcp_socket.send(
|
|
|data| {
|
|
// Enqueue tls representation without extra allocation
|
|
let mut buffer = TlsBuffer::new(data);
|
|
if buffer.enqueue_tls_repr(repr).is_err() {
|
|
return (0, ())
|
|
}
|
|
let slice: &[u8] = buffer.into();
|
|
|
|
// Update the session
|
|
// No sequence number calculation in CH
|
|
// because there is no encryption
|
|
// Still, data needs to be hashed
|
|
session.client_update_for_ch(
|
|
ecdh_secret,
|
|
x25519_secret,
|
|
session_id,
|
|
&slice[5..]
|
|
);
|
|
|
|
// Finally send the data
|
|
(slice.len(), ())
|
|
}
|
|
)?;
|
|
}
|
|
},
|
|
|
|
// TLS Client wait for Server Hello
|
|
// No need to send anything
|
|
TlsState::WAIT_SH => {},
|
|
|
|
// TLS Client wait for certificate from TLS server
|
|
// No need to send anything
|
|
// Note: TLS server should normally send SH alongside EE
|
|
// TLS client should jump from WAIT_SH directly to WAIT_CERT_CR directly.
|
|
TlsState::WAIT_EE => {},
|
|
|
|
// TLS Client wait for server's certificate/ certificate request
|
|
// No need to send anything
|
|
TlsState::WAIT_CERT_CR => {},
|
|
|
|
// TLS Client wait for server's certificate after receiveing a request
|
|
// No need to send anything
|
|
TlsState::CLIENT_WAIT_CERT => {},
|
|
|
|
// TLS Client wait for server's certificate cerify
|
|
// No need to send anything
|
|
TlsState::CLIENT_WAIT_CV => {},
|
|
|
|
// Last step of server authentication
|
|
// TLS Client wait for server's Finished handshake
|
|
// No need to send anything
|
|
TlsState::CLIENT_WAIT_FINISHED => {}
|
|
|
|
// Send client Finished to end handshake
|
|
// Also send certificate and certificate verify before client Finished if
|
|
// server sent a CertificateRequest beforehand
|
|
TlsState::SERVER_COMPLETED => {
|
|
// Certificate & CertificateVerify
|
|
let need_to_send_client_cert = {
|
|
self.session.borrow().need_to_send_client_certificate()
|
|
};
|
|
if need_to_send_client_cert {
|
|
let (certificates_total_length, buffer_vec) = {
|
|
let session = self.session.borrow();
|
|
let mut buffer_vec: Vec<u8> = Vec::new();
|
|
let certificates = session
|
|
.get_private_certificate_slices()
|
|
.clone();
|
|
|
|
// Handshake level, client certificate byte followed by length (u24)
|
|
// Certificate struct:
|
|
// request_context = X509: 0 (u8),
|
|
// certificate_list to be determined (u24)
|
|
let mut certificates_total_length: u32 = 0;
|
|
|
|
// Append place holder bytes (8 of them) in the buffer vector
|
|
// Simpily copy the the headers back into the vector
|
|
// when all certificates are appended into the vector
|
|
buffer_vec.extend_from_slice(&[11, 0, 0, 0, 0, 0, 0, 0]);
|
|
|
|
// Certificate Entry struct(s)
|
|
if let Some(certificate_list) = certificates {
|
|
for cert in certificate_list.iter() {
|
|
// cert_data length, to be determined (u24)
|
|
let mut cert_data_length: [u8; 3] = [0, 0, 0];
|
|
// extensions: no extension needed
|
|
let extension: [u8; 2] = [0, 0];
|
|
|
|
let certificate_length: u32 = u32::try_from(cert.len()).unwrap();
|
|
|
|
NetworkEndian::write_u24(
|
|
&mut cert_data_length,
|
|
certificate_length
|
|
);
|
|
|
|
// Update length in Certificate struct
|
|
certificates_total_length +=
|
|
// cert_data (len & data) AND extension (len & data)
|
|
3 + certificate_length + 2 + 0;
|
|
|
|
buffer_vec.extend_from_slice(&cert_data_length);
|
|
buffer_vec.extend_from_slice(cert);
|
|
buffer_vec.extend_from_slice(&extension);
|
|
}
|
|
}
|
|
|
|
// Write total certificate length into Certificate struct
|
|
NetworkEndian::write_u24(
|
|
&mut buffer_vec[5..8],
|
|
certificates_total_length
|
|
);
|
|
|
|
// Write the length of the entire handshake
|
|
NetworkEndian::write_u24(
|
|
&mut buffer_vec[1..4],
|
|
// 4 bytes for the Certificate struct header
|
|
certificates_total_length + 4
|
|
);
|
|
|
|
// Inner plaintext: record type
|
|
buffer_vec.push(22);
|
|
(certificates_total_length, buffer_vec)
|
|
};
|
|
|
|
self.send_application_slice(&mut buffer_vec.clone())?;
|
|
// Update session
|
|
let buffer_vec_length = buffer_vec.len();
|
|
|
|
{
|
|
self.session.borrow_mut()
|
|
.client_update_for_certificate_in_server_completed(
|
|
&buffer_vec[..(buffer_vec_length-1)]
|
|
);
|
|
}
|
|
|
|
// Send a CertificateVerify as well if any certificates
|
|
// were just sent by the client
|
|
if certificates_total_length != 0 {
|
|
// Serialize CertificateVerify
|
|
|
|
// Handshake bytes:
|
|
// msg_type = 15, CertificateVerify (u8)
|
|
// handshake_data_length = to be determined (u24)
|
|
// signature algorithm (u16)
|
|
// signature_length (u16)
|
|
// signature, the rest
|
|
|
|
let mut verify_buffer_vec: Vec<u8> = Vec::new();
|
|
// Leave bytes from Handshake struct as placeholders
|
|
verify_buffer_vec.extend_from_slice(&[
|
|
15,
|
|
0, 0, 0,
|
|
0, 0,
|
|
0, 0
|
|
]);
|
|
{
|
|
let session = self.session.borrow();
|
|
let (sig_alg, signature) = session
|
|
.get_certificate_verify_signature(
|
|
&mut self.rng,
|
|
TlsRole::Client
|
|
);
|
|
|
|
let signature_length: u16 = u16::try_from(signature.len()).unwrap();
|
|
NetworkEndian::write_u24(
|
|
&mut verify_buffer_vec[1..4],
|
|
(signature_length + 4).into()
|
|
);
|
|
NetworkEndian::write_u16(
|
|
&mut verify_buffer_vec[4..6],
|
|
sig_alg.try_into().unwrap()
|
|
);
|
|
NetworkEndian::write_u16(
|
|
&mut verify_buffer_vec[6..8],
|
|
signature_length
|
|
);
|
|
verify_buffer_vec.extend_from_slice(&signature);
|
|
}
|
|
// Push content byte (handshake: 22)
|
|
verify_buffer_vec.push(22);
|
|
|
|
self.send_application_slice(&mut verify_buffer_vec.clone())?;
|
|
// Update session
|
|
let cert_verify_len = verify_buffer_vec.len();
|
|
|
|
{
|
|
self.session.borrow_mut()
|
|
.client_update_for_cert_verify_in_server_completed(
|
|
&verify_buffer_vec[..(cert_verify_len-1)]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Client Finished
|
|
let inner_plaintext: HeaplessVec<u8, U64> = {
|
|
let verify_data = self.session.borrow()
|
|
.get_client_finished_verify_data();
|
|
let mut handshake_header: [u8; 4] = [20, 0, 0, 0];
|
|
NetworkEndian::write_u24(
|
|
&mut handshake_header[1..4],
|
|
u32::try_from(verify_data.len()).unwrap()
|
|
);
|
|
let mut buffer = HeaplessVec::from_slice(&handshake_header).unwrap();
|
|
buffer.extend_from_slice(&verify_data).unwrap();
|
|
// Inner plaintext: record type
|
|
buffer.push(22).unwrap();
|
|
buffer
|
|
};
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
self.session.borrow_mut()
|
|
.client_update_for_server_completed(&inner_plaintext[..(inner_plaintext_length-1)]);
|
|
}
|
|
|
|
// There is no need to care about handshake if it was completed
|
|
TlsState::CLIENT_CONNECTED => {
|
|
return Ok(propagated_poll);
|
|
}
|
|
|
|
// This state waits for Client Hello handshake from a client
|
|
// There is nothing to send
|
|
TlsState::SERVER_START => {}
|
|
|
|
// Respond to a Client Hello initiation with:
|
|
// - Server Hello
|
|
// - Encrypted Extensions
|
|
// - (Possible) Certificate Request
|
|
// - Certificate
|
|
// - CertificateVerify
|
|
// - Finished
|
|
TlsState::NEGOTIATED => {
|
|
let mut random: [u8; 32] = [0; 32];
|
|
self.rng.fill_bytes(&mut random);
|
|
let (session_id, cipher_suite, server_ecdhe_public_key) = {
|
|
let session = self.session.borrow();
|
|
(
|
|
session.get_session_id(),
|
|
session.get_cipher_suite(),
|
|
session.get_server_ecdhe_public_key()
|
|
)
|
|
};
|
|
let ecdhe_private_key = match server_ecdhe_public_key {
|
|
DiffieHellmanPublicKey::SECP256R1 { .. } => {
|
|
DiffieHellmanPrivateKey::SECP256R1 {
|
|
ephemeral_secret: {
|
|
p256::ecdh::EphemeralSecret::random(&mut self.rng)
|
|
}
|
|
}
|
|
},
|
|
DiffieHellmanPublicKey::X25519 { .. } => {
|
|
DiffieHellmanPrivateKey::X25519 {
|
|
ephemeral_secret: {
|
|
x25519_dalek::EphemeralSecret::new(&mut self.rng)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let ecdhe_public_key = ecdhe_private_key.to_public_key();
|
|
|
|
// Construct and send SH
|
|
let repr = TlsRepr::new().server_hello(
|
|
&random,
|
|
&session_id,
|
|
cipher_suite,
|
|
ecdhe_public_key
|
|
);
|
|
{
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
let mut session = self.session.borrow_mut();
|
|
tcp_socket.send(
|
|
|data| {
|
|
// Enqueue the TLS representation
|
|
let mut buffer = TlsBuffer::new(data);
|
|
if buffer.enqueue_tls_repr(repr).is_err() {
|
|
return (0, ())
|
|
}
|
|
let slice: &[u8] = buffer.into();
|
|
|
|
// Update session after sending only SH
|
|
session.server_update_for_server_hello(
|
|
ecdhe_private_key,
|
|
&slice[5..]
|
|
);
|
|
|
|
// Send the data
|
|
(slice.len(), ())
|
|
}
|
|
)?;
|
|
}
|
|
|
|
log::info!("sent server hello");
|
|
|
|
// Construct and send minimalistic EE
|
|
let inner_plaintext: [u8; 7] = [
|
|
0x08, // EE type
|
|
0x00, 0x00, 0x02, // Length: 2
|
|
0x00, 0x00, // Length of extensions: 0
|
|
22 // Content type of InnerPlainText
|
|
];
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
{
|
|
let mut session = self.session.borrow_mut();
|
|
session.server_update_for_encrypted_extension(
|
|
&inner_plaintext[..(inner_plaintext_length-1)]
|
|
);
|
|
}
|
|
|
|
log::info!("sent encrypted extension");
|
|
|
|
// Send certificate request to client, on user's discretion
|
|
if self.session.borrow().need_to_send_cert_request() {
|
|
let mut inner_plaintext: Vec<u8> = Vec::new();
|
|
inner_plaintext.extend_from_slice(&[
|
|
13, // Certificate request
|
|
0, 0, 0, // Dummy length
|
|
0, // No certificate request context
|
|
0, 0, // Dummy extensions length
|
|
0, 13, // Signature Algorithm extension type
|
|
0, 0, // Dummy extension length
|
|
0, 0 // Dummy signature scheme list length
|
|
]);
|
|
let supported_sig_algs = [
|
|
SignatureScheme::ecdsa_secp256r1_sha256,
|
|
SignatureScheme::ed25519,
|
|
SignatureScheme::rsa_pss_pss_sha256,
|
|
SignatureScheme::rsa_pkcs1_sha256,
|
|
SignatureScheme::rsa_pss_rsae_sha256,
|
|
SignatureScheme::rsa_pss_pss_sha384,
|
|
SignatureScheme::rsa_pkcs1_sha384,
|
|
SignatureScheme::rsa_pss_rsae_sha384,
|
|
SignatureScheme::rsa_pss_pss_sha512,
|
|
SignatureScheme::rsa_pkcs1_sha512,
|
|
SignatureScheme::rsa_pss_rsae_sha512
|
|
];
|
|
|
|
NetworkEndian::write_u16(
|
|
&mut inner_plaintext[11..13],
|
|
u16::try_from(supported_sig_algs.len() * 2).unwrap()
|
|
);
|
|
|
|
NetworkEndian::write_u16(
|
|
&mut inner_plaintext[9..11],
|
|
u16::try_from(supported_sig_algs.len() * 2 + 2).unwrap()
|
|
);
|
|
|
|
NetworkEndian::write_u16(
|
|
&mut inner_plaintext[5..7],
|
|
u16::try_from(supported_sig_algs.len() * 2 + 6).unwrap()
|
|
);
|
|
|
|
NetworkEndian::write_u24(
|
|
&mut inner_plaintext[1..4],
|
|
u32::try_from(supported_sig_algs.len() * 2 + 9).unwrap()
|
|
);
|
|
|
|
for sig_alg in supported_sig_algs.iter() {
|
|
inner_plaintext.extend_from_slice(
|
|
&u16::try_from(*sig_alg).unwrap().to_be_bytes()
|
|
);
|
|
}
|
|
|
|
// Push content type: Handshake
|
|
inner_plaintext.push(22);
|
|
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
{
|
|
let mut session = self.session.borrow_mut();
|
|
session.server_update_for_sent_certificate_request(
|
|
&inner_plaintext[..(inner_plaintext_length-1)]
|
|
);
|
|
}
|
|
|
|
log::info!("sent certificate request");
|
|
}
|
|
|
|
// Construct and send server certificate handshake content
|
|
let inner_plaintext = {
|
|
let mut inner_plaintext: Vec<u8> = Vec::new();
|
|
let session = self.session.borrow();
|
|
let certificates = session.get_private_certificate_slices().clone();
|
|
|
|
// Handshake level, client certificate byte followed by length (u24)
|
|
// Certificate struct:
|
|
// request_context = X509: 0 (u8),
|
|
// certificate_list to be determined (u24)
|
|
let mut certificates_total_length: u32 = 0;
|
|
|
|
// Append place holder bytes (8 of them) in the buffer vector
|
|
// Simpily copy the the headers back into the vector
|
|
// when all certificates are appended into the vector
|
|
inner_plaintext.extend_from_slice(&[11, 0, 0, 0, 0, 0, 0, 0]);
|
|
|
|
// Certificate Entry struct(s)
|
|
if let Some(certificate_list) = certificates {
|
|
for cert in certificate_list.iter() {
|
|
// cert_data length, to be determined (u24)
|
|
let mut cert_data_length: [u8; 3] = [0, 0, 0];
|
|
// extensions: no extension needed
|
|
let extension: [u8; 2] = [0, 0];
|
|
|
|
let certificate_length: u32 = u32::try_from(cert.len()).unwrap();
|
|
|
|
NetworkEndian::write_u24(
|
|
&mut cert_data_length,
|
|
certificate_length
|
|
);
|
|
|
|
// Update length in Certificate struct
|
|
certificates_total_length +=
|
|
// cert_data (len & data) AND extension (len & data)
|
|
3 + certificate_length + 2 + 0;
|
|
|
|
inner_plaintext.extend_from_slice(&cert_data_length);
|
|
inner_plaintext.extend_from_slice(cert);
|
|
inner_plaintext.extend_from_slice(&extension);
|
|
}
|
|
}
|
|
|
|
// Write total certificate length into Certificate struct
|
|
NetworkEndian::write_u24(
|
|
&mut inner_plaintext[5..8],
|
|
certificates_total_length
|
|
);
|
|
|
|
// Write the length of the entire handshake
|
|
NetworkEndian::write_u24(
|
|
&mut inner_plaintext[1..4],
|
|
// 4 bytes for the Certificate struct header
|
|
certificates_total_length + 4
|
|
);
|
|
|
|
// Inner plaintext: record type
|
|
inner_plaintext.push(22);
|
|
inner_plaintext
|
|
};
|
|
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
// Update session
|
|
{
|
|
self.session.borrow_mut()
|
|
.server_update_for_sent_certificate(&inner_plaintext[..(inner_plaintext_length-1)]);
|
|
}
|
|
log::info!("sent certificate");
|
|
|
|
// Construct and send certificate verify
|
|
let inner_plaintext = {
|
|
let mut inner_plaintext = Vec::new();
|
|
inner_plaintext.extend_from_slice(&[
|
|
15,
|
|
0, 0, 0,
|
|
0, 0,
|
|
0, 0
|
|
]);
|
|
let session = self.session.borrow();
|
|
let (sig_alg, signature) = session.get_certificate_verify_signature(
|
|
&mut self.rng,
|
|
TlsRole::Server
|
|
);
|
|
let signature_length: u16 = u16::try_from(signature.len()).unwrap();
|
|
NetworkEndian::write_u24(
|
|
&mut inner_plaintext[1..4],
|
|
(signature_length + 4).into()
|
|
);
|
|
NetworkEndian::write_u16(
|
|
&mut inner_plaintext[4..6],
|
|
sig_alg.try_into().unwrap()
|
|
);
|
|
NetworkEndian::write_u16(
|
|
&mut inner_plaintext[6..8],
|
|
signature_length
|
|
);
|
|
inner_plaintext.extend_from_slice(&signature);
|
|
inner_plaintext.push(22); // Content type byte
|
|
inner_plaintext
|
|
};
|
|
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
{
|
|
self.session.borrow_mut()
|
|
.server_update_for_sent_certificate_verify(
|
|
&inner_plaintext[..(inner_plaintext_length-1)]
|
|
);
|
|
}
|
|
log::info!("sent certificate verify");
|
|
|
|
// Construct and send server finished
|
|
let inner_plaintext: HeaplessVec<u8, U64> = {
|
|
let verify_data = self.session.borrow()
|
|
.get_server_finished_verify_data();
|
|
let mut handshake_header: [u8; 4] = [20, 0, 0, 0];
|
|
NetworkEndian::write_u24(
|
|
&mut handshake_header[1..4],
|
|
u32::try_from(verify_data.len()).unwrap()
|
|
);
|
|
let mut buffer = HeaplessVec::from_slice(&handshake_header).unwrap();
|
|
buffer.extend_from_slice(&verify_data).unwrap();
|
|
// Inner plaintext: record type
|
|
buffer.push(22).unwrap();
|
|
buffer
|
|
};
|
|
self.send_application_slice(&mut inner_plaintext.clone())?;
|
|
|
|
let inner_plaintext_length = inner_plaintext.len();
|
|
{
|
|
self.session.borrow_mut()
|
|
.server_update_for_server_finished(&inner_plaintext[..(inner_plaintext_length-1)]);
|
|
}
|
|
log::info!("sent client finished");
|
|
}
|
|
|
|
// There is no need to care about handshake if it was completed
|
|
// This is to prevent accidental dequeing of application data
|
|
TlsState::SERVER_CONNECTED => {
|
|
return Ok(propagated_poll);
|
|
}
|
|
|
|
// Other states
|
|
_ => {}
|
|
}
|
|
|
|
// Read for TLS packet
|
|
// Proposition: Decouple all data from TLS record layer before processing
|
|
// Recouple a brand new TLS record wrapper
|
|
// Use peek & recv to avoid buffer allocation
|
|
{
|
|
let tls_repr_vec = {
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
|
|
// Check if there are bytes enqueued in the recv buffer
|
|
// No need to do further dequeuing if there are no receivable bytes
|
|
if !tcp_socket.can_recv() {
|
|
return Ok(propagated_poll)
|
|
}
|
|
|
|
// Peak into the first 5 bytes (TLS record layer)
|
|
// This tells the length of the entire record
|
|
let length = match tcp_socket.peek(5) {
|
|
Ok(bytes) => NetworkEndian::read_u16(&bytes[3..5]),
|
|
_ => return Ok(propagated_poll)
|
|
};
|
|
|
|
// Recv the entire TLS record
|
|
tcp_socket.recv(
|
|
|buffer| ((length + 5).into(), Vec::from(&buffer[..(length + 5).into()]))
|
|
).unwrap()
|
|
};
|
|
|
|
// Parse the bytes representation of a TLS record
|
|
let (repr_slice, mut repr) = match parse_tls_repr(&tls_repr_vec) {
|
|
Ok((_, (repr_slice, repr))) => (repr_slice, repr),
|
|
_ => return Ok(propagated_poll)
|
|
};
|
|
|
|
// Process record base on content type
|
|
log::info!("Record type: {:?}", repr.content_type);
|
|
|
|
// Handle TLS represenatation according to the content type:
|
|
// Handshake & ChangeCipherSpec: Directly process the handshake
|
|
// Alert: Reset session immediately
|
|
// ApplicationData: Decrypt and then handle, with similar criteria
|
|
// Reject invalid contents by invalidating the TLS session
|
|
match repr.content_type {
|
|
TlsContentType::ApplicationData => {
|
|
log::info!("Found application data");
|
|
// Take the payload out of TLS Record and decrypt
|
|
let mut app_data = repr.payload.take().unwrap();
|
|
let mut associated_data = [0; 5];
|
|
associated_data[0] = repr.content_type.into();
|
|
NetworkEndian::write_u16(
|
|
&mut associated_data[1..3],
|
|
repr.version.into()
|
|
);
|
|
NetworkEndian::write_u16(
|
|
&mut associated_data[3..5],
|
|
repr.length
|
|
);
|
|
{
|
|
let mut session = self.session.borrow_mut();
|
|
session.decrypt_in_place_detached(
|
|
&associated_data,
|
|
&mut app_data
|
|
).unwrap();
|
|
session.increment_remote_sequence_number();
|
|
}
|
|
|
|
// Discard last 16 bytes (auth tag)
|
|
let inner_plaintext = &app_data[..app_data.len()-16];
|
|
let (inner_content_type, begin_zero) = get_content_type_inner_plaintext(
|
|
inner_plaintext
|
|
);
|
|
// Find the index of the content type byte
|
|
let content_type_index = match begin_zero {
|
|
Some(index) => index - 1,
|
|
None => app_data.len() - 16 - 1
|
|
};
|
|
|
|
// Process contents that are not handshakes differently
|
|
// Invalid: Raise an alert to remote side
|
|
// ChangeCipherSpec: It should not be encrypted, raise alert
|
|
// Alert: Reset TLS session and terminate TCP session directly
|
|
// Handshake: Normal procedure as below
|
|
// ApplicationData: Early data is silently ignored and wont be processed
|
|
match inner_content_type {
|
|
TlsContentType::Invalid | TlsContentType::ChangeCipherSpec => {
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
&inner_plaintext[..content_type_index]
|
|
);
|
|
return Ok(propagated_poll);
|
|
},
|
|
TlsContentType::Alert => {
|
|
self.session.borrow_mut().reset_state();
|
|
return Ok(propagated_poll);
|
|
},
|
|
TlsContentType::ApplicationData => {
|
|
return Ok(propagated_poll);
|
|
},
|
|
_ => ()
|
|
}
|
|
|
|
let (_, mut inner_handshakes) = complete(
|
|
parse_inner_plaintext_for_handshake
|
|
)(inner_plaintext).unwrap();
|
|
|
|
// Sequentially process all handshakes
|
|
let num_of_handshakes = inner_handshakes.len();
|
|
for _ in 0..num_of_handshakes {
|
|
let (handshake_slice, handshake_repr) = inner_handshakes.remove(0);
|
|
if self.process(
|
|
handshake_slice,
|
|
TlsRepr {
|
|
content_type: TlsContentType::Handshake,
|
|
version: repr.version,
|
|
length: u16::try_from(handshake_repr.length).unwrap() + 4,
|
|
payload: None,
|
|
handshake: Some(handshake_repr)
|
|
}
|
|
).is_err() {
|
|
return Ok(propagated_poll)
|
|
}
|
|
}
|
|
},
|
|
|
|
TlsContentType::ChangeCipherSpec |
|
|
TlsContentType::Handshake => {
|
|
if self.process(repr_slice, repr).is_err() {
|
|
return Ok(propagated_poll)
|
|
}
|
|
log::info!("Processed record");
|
|
},
|
|
|
|
TlsContentType::Alert => {
|
|
self.session.borrow_mut().reset_state();
|
|
log::info!("Received alert, closing TCP socket..");
|
|
},
|
|
|
|
TlsContentType::Invalid => {
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
&repr.payload.unwrap_or(Vec::new())
|
|
);
|
|
log::info!("Received invalid TLS records, terminate immediately..");
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(propagated_poll)
|
|
}
|
|
|
|
// Process TLS ingress during handshake
|
|
// The slice should ONLY include handshake overhead
|
|
// i.e. Exclude 5 bytes of TLS Record
|
|
// Include 4 bytes of HandshakeRepr, everything within the same handshake
|
|
fn process(&self, handshake_slice: &[u8], mut repr: TlsRepr) -> Result<()> {
|
|
// Change_cipher_spec check:
|
|
// Must receive CCS before recv peer's FINISH message
|
|
// i.e. Must happen after START and before CONNECTED
|
|
//
|
|
// CCS message only exist for compatibility reason,
|
|
// Drop the message and update `received_change_cipher_spec`
|
|
// Note: CSS doesn't count as a proper record, no need to increment sequence number
|
|
if repr.is_change_cipher_spec() {
|
|
let mut session = self.session.try_borrow_mut().expect("Cannot borrow mut");
|
|
session.receive_change_cipher_spec();
|
|
return Ok(())
|
|
}
|
|
|
|
let tls_state = {
|
|
self.session.borrow().get_tls_state()
|
|
};
|
|
match tls_state {
|
|
// During WAIT_SH for a TLS client, client should wait for ServerHello
|
|
TlsState::WAIT_SH => {
|
|
if repr.is_server_hello() {
|
|
// Check SH content:
|
|
// random: Cannot represent HelloRequestRetry
|
|
// session_id_echo: should be same as the one sent by client
|
|
// cipher_suite: Store
|
|
// (TODO: Check if such suite was offered)
|
|
// compression_method: Must be null, not supported in TLS 1.3
|
|
//
|
|
// Check extensions:
|
|
// supported_version: Must be TLS 1.3
|
|
// key_share: Store key, must be in secp256r1 or x25519
|
|
|
|
// "Cache" for ECDHE server public info
|
|
let mut p256_public: Option<EncodedPoint> = None;
|
|
let mut x25519_public: Option<x25519_dalek::PublicKey> = None;
|
|
let mut selected_cipher: Option<CipherSuite> = None;
|
|
|
|
// Process the handshake data within ServerHello
|
|
let handshake_data = &repr.handshake.as_ref().unwrap().handshake_data;
|
|
if let HandshakeData::ServerHello(server_hello) = handshake_data {
|
|
|
|
// Check random: Cannot be SHA-256 of "HelloRetryRequest"
|
|
if server_hello.random == HRR_RANDOM {
|
|
// Abort communication with illegal parameter alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Check session_id_echo
|
|
// The socket should have a session_id after moving from START state
|
|
if !self.session.borrow().verify_session_id_echo(server_hello.session_id_echo) {
|
|
// Abort communication with illegal parameter alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Note the selected cipher suite
|
|
selected_cipher.replace(server_hello.cipher_suite);
|
|
|
|
// TLSv13 forbidden key compression
|
|
if server_hello.compression_method != 0 {
|
|
// Abort communication with illegal parameter alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
for extension in server_hello.extensions.iter() {
|
|
if extension.extension_type == ExtensionType::SupportedVersions {
|
|
if let ExtensionData::SupportedVersions(
|
|
SupportedVersions::ServerHello {
|
|
selected_version
|
|
}
|
|
) = extension.extension_data {
|
|
if selected_version != TlsVersion::Tls13 {
|
|
// Abort for choosing not offered TLS version,
|
|
// with illegal parameter alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Abort for malformatted extension, with decode error alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
if extension.extension_type == ExtensionType::KeyShare {
|
|
if let ExtensionData::KeyShareEntry(
|
|
KeyShareEntryContent::KeyShareServerHello {
|
|
server_share
|
|
}
|
|
) = &extension.extension_data {
|
|
match server_share.group {
|
|
NamedGroup::secp256r1 => {
|
|
p256_public.replace(
|
|
EncodedPoint::from_untagged_bytes(
|
|
GenericArray::from_slice(&server_share.key_exchange[1..])
|
|
)
|
|
);
|
|
},
|
|
NamedGroup::x25519 => {
|
|
let mut x25519_server_key: [u8; 32] = [0; 32];
|
|
x25519_server_key.clone_from_slice(&server_share.key_exchange);
|
|
x25519_public.replace(
|
|
x25519_dalek::PublicKey::from(
|
|
x25519_server_key
|
|
)
|
|
);
|
|
},
|
|
// The client side implementation of this TLS socket only offers
|
|
// P-256 and x25519 as ECDHE key exchange algorithms
|
|
// Respond with illegal parameter alert and then terminate
|
|
_ => {
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// Handle invalid TLS packet
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Check that both selected_cipher and (p256_public OR x25519_public) were received
|
|
// The case where key_share extension exists but no appropriate keys are returned
|
|
// is considered in above. The only remaining case is that the `key share` entry extension
|
|
// is not sent at all.
|
|
if selected_cipher.is_none() || (p256_public.is_none() && x25519_public.is_none()) {
|
|
// Abort communication
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Get slice without reserialization
|
|
let mut session = self.session.borrow_mut();
|
|
session.client_update_for_sh(
|
|
selected_cipher.unwrap(),
|
|
p256_public,
|
|
x25519_public,
|
|
handshake_slice
|
|
);
|
|
// Key exchange occurred, seq_num is set to 0
|
|
// Do NOT update seq_num again. Early return.
|
|
return Ok(());
|
|
}
|
|
},
|
|
|
|
// Expect encrypted extensions after receiving SH
|
|
TlsState::WAIT_EE => {
|
|
// Verify that it is indeed an EE
|
|
let might_be_ee = repr.handshake.take().unwrap();
|
|
if might_be_ee.get_msg_type() != HandshakeType::EncryptedExtensions {
|
|
// Unexpected message types
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Possiblity: Process payload
|
|
// Practically, nothing will be done about cookies/server name
|
|
// These fields are typically not session related
|
|
// Extension processing is therefore skipped
|
|
|
|
// Update hash of the session, get EE by taking appropriate length of data
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, ee_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_ee.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
self.session.borrow_mut()
|
|
.client_update_for_ee(
|
|
&ee_slice
|
|
);
|
|
|
|
log::info!("Received EE");
|
|
},
|
|
|
|
// In this stage, wait for a certificate from server
|
|
// Parse the certificate and check its content
|
|
TlsState::WAIT_CERT_CR => {
|
|
// Verify that it is indeed an Certificate, or CertificateRequest
|
|
let might_be_cert = repr.handshake.take().unwrap();
|
|
if might_be_cert.get_msg_type() == HandshakeType::Certificate {
|
|
// Process certificates
|
|
|
|
// let all_certificates = might_be_cert.get_all_asn1_der_certificates().unwrap();
|
|
// log::info!("Number of certificates: {:?}", all_certificates.len());
|
|
// log::info!("All certificates: {:?}", all_certificates);
|
|
|
|
// TODO: Process all certificates
|
|
let cert = might_be_cert.get_asn1_der_certificate().unwrap();
|
|
|
|
// TODO: Replace this block after implementing a proper
|
|
// certificate verification procdeure
|
|
cert.validate_self_signed_signature().expect("Signature mismatched");
|
|
|
|
// Update session TLS state to WAIT_CV
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
self.session.borrow_mut()
|
|
.client_update_for_wait_cert_cr(
|
|
&cert_slice,
|
|
cert.get_cert_public_key().unwrap()
|
|
);
|
|
log::info!("Received WAIT_CERT_CR");
|
|
} else if might_be_cert.get_msg_type() == HandshakeType::CertificateRequest {
|
|
// Process signature algorithm extension
|
|
// Signature algorithm for the private key of client cert must be included
|
|
// within the list of signature algorithms
|
|
//
|
|
// Client is STRONGLY RECOMMENDED to use a signature algorithm within
|
|
// the list of `mandatory to implement` signature algorithms, including:
|
|
// rsa_pkcs1_sha256, rsa_pss_rsae_sha256, ecdsa_secp256r1_sha256
|
|
//
|
|
// Update client state
|
|
let cert_req_extensions: &Vec<Extension> = might_be_cert
|
|
.get_cert_request_extensions()
|
|
.unwrap();
|
|
let sig_algs_ext: Option<&crate::tls_packet::Extension>
|
|
= cert_req_extensions
|
|
.iter()
|
|
.find( |&extension| {
|
|
extension.extension_type
|
|
== crate::tls_packet::ExtensionType::SignatureAlgorithms
|
|
}
|
|
);
|
|
if sig_algs_ext.is_some() {
|
|
// Convert extension into SignatureScheme
|
|
if let crate::tls_packet::ExtensionData::SignatureAlgorithms(
|
|
scheme_list
|
|
) = &sig_algs_ext.unwrap().extension_data {
|
|
// Update session TLS state to WAIT_CERT
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_req_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
self.session.borrow_mut()
|
|
.client_update_for_certificate_request(
|
|
&cert_req_slice,
|
|
&scheme_list.supported_signature_algorithms
|
|
);
|
|
}
|
|
|
|
} else {
|
|
// Reject connection, CertificateRequest must have
|
|
// SignatureAlgorithm extension
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
log::info!("Received WAIT_CERT_CR");
|
|
} else {
|
|
// Throw alert for not recving certificate/certificate request from server side
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
},
|
|
|
|
// In this stage, server will send a certificate chain
|
|
// Verify the certificate
|
|
TlsState::CLIENT_WAIT_CERT => {
|
|
// Verify that it is indeed an Certificate
|
|
let might_be_cert = repr.handshake.take().unwrap();
|
|
|
|
if might_be_cert.get_msg_type() == HandshakeType::Certificate {
|
|
// Process certificates
|
|
|
|
// let all_certificates = might_be_cert.get_all_asn1_der_certificates().unwrap();
|
|
// log::info!("Number of certificates: {:?}", all_certificates.len());
|
|
// log::info!("All certificates: {:?}", all_certificates);
|
|
|
|
// TODO: Process all certificates
|
|
let cert = might_be_cert.get_asn1_der_certificate().unwrap();
|
|
|
|
// TODO: Replace this block after implementing a proper
|
|
// certificate verification procdeure
|
|
cert.validate_self_signed_signature().expect("Signature mismatched");
|
|
|
|
// Update session TLS state to WAIT_CV
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
self.session.borrow_mut()
|
|
.client_update_for_wait_cert_cr(
|
|
&cert_slice,
|
|
cert.get_cert_public_key().unwrap()
|
|
);
|
|
log::info!("Received WAIT_CERT");
|
|
} else {
|
|
// Unexpected handshakes
|
|
// Throw alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
},
|
|
|
|
// In this stage, server will eventually send a CertificateVerify
|
|
// Verify that the signature is indeed correct
|
|
TlsState::CLIENT_WAIT_CV => {
|
|
// Ensure that it is CertificateVerify
|
|
let might_be_cert_verify = repr.handshake.take().unwrap();
|
|
if might_be_cert_verify.get_msg_type() != HandshakeType::CertificateVerify {
|
|
// Process the other handshakes in "handshake_vec"
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Take out the portion for CertificateVerify
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_verify_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert_verify.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
// Perform verification, update TLS state if successful
|
|
let (sig_alg, signature) = might_be_cert_verify.get_signature().unwrap();
|
|
{
|
|
self.session.borrow_mut()
|
|
.client_update_for_wait_cv(
|
|
cert_verify_slice,
|
|
sig_alg,
|
|
signature
|
|
);
|
|
}
|
|
log::info!("Received CV");
|
|
},
|
|
|
|
// Client will receive a Finished handshake from server
|
|
TlsState::CLIENT_WAIT_FINISHED => {
|
|
// Ensure that it is Finished
|
|
let might_be_server_finished = repr.handshake.take().unwrap();
|
|
if might_be_server_finished.get_msg_type() != HandshakeType::Finished {
|
|
// Server Finished is expected.
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Take out the portion for server Finished
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, server_finished_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_server_finished.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
// Perform verification, update TLS state if successful
|
|
// Update traffic secret, reset sequence number
|
|
self.session.borrow_mut()
|
|
.client_update_for_wait_finished(
|
|
server_finished_slice,
|
|
might_be_server_finished.get_verify_data().unwrap()
|
|
);
|
|
log::info!("Received server FIN");
|
|
},
|
|
|
|
// Server will reveice a Client Hello initiating the TLS handshake
|
|
TlsState::SERVER_START => {
|
|
// Ensure that is a Client Hello
|
|
let might_be_client_hello = repr.handshake.take().unwrap();
|
|
if might_be_client_hello.get_msg_type() != HandshakeType::ClientHello {
|
|
// Throw alert. Client Hello is expected.
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Process as Client Hello
|
|
if let HandshakeData::ClientHello(client_hello)
|
|
= might_be_client_hello.handshake_data
|
|
{
|
|
// Checks on the client hello structure
|
|
// Read session ID
|
|
// Select acceptable TLS 1.3 cipher suite
|
|
let session_id = client_hello.session_id;
|
|
let accepted_cipher_suite = {
|
|
let recognized_cipher_suite = client_hello.cipher_suites
|
|
.iter()
|
|
.find(|cipher_suite| {
|
|
// CCM_8 is the only unsupported cipher suite
|
|
if let Some(cipher) = cipher_suite {
|
|
cipher != &CipherSuite::TLS_AES_128_CCM_8_SHA256
|
|
} else {
|
|
false
|
|
}
|
|
});
|
|
if let Some(Some(nominated_cipher_suite)) = recognized_cipher_suite {
|
|
nominated_cipher_suite
|
|
} else {
|
|
// No appropriate cipher found,
|
|
// the full set of security measures cannot be set up.
|
|
// Send alert for this
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::HandshakeFailure,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
// Check on the handshakes
|
|
// `supported_version` extension: only support TLS 1.3 (SSL 3.4)
|
|
// `supported_groups` extension: select an acceptable ECDHE group
|
|
// `key_share` extension: find the corresponding ECDHE shared key
|
|
// `signature_algorithm`: pick a signature algorithm
|
|
// Will not handle PSK, no 0-RTT
|
|
let mut offered_p256 = false;
|
|
let mut offered_x25519 = false;
|
|
let mut ecdhe_public_key: Option<DiffieHellmanPublicKey> = None;
|
|
let signature_algorithm: Option<SignatureScheme>;
|
|
|
|
// Verify that TLS 1.3 is offered by the client
|
|
if let Some(supported_version_extension) = client_hello.extensions.iter().find(
|
|
|extension| extension.extension_type == ExtensionType::SupportedVersions
|
|
) {
|
|
if let ExtensionData::SupportedVersions(supported_version)
|
|
= &supported_version_extension.extension_data
|
|
{
|
|
if let SupportedVersions::ClientHello { versions, .. }
|
|
= supported_version
|
|
{
|
|
if versions.iter().find(
|
|
|&&version| version == TlsVersion::Tls13
|
|
).is_none()
|
|
{
|
|
// TLS 1.3 was not offered by client
|
|
// Reject connection immediately
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::IllegalParameter,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Wrong variant appeared, probably malformed
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Malformed TLS packet
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// No supported_version extension was found,
|
|
// Terminate by sending alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Check offered ECDHE algorithm
|
|
if let Some(supported_groups) = client_hello.extensions.iter().find(
|
|
|extension| extension.extension_type == ExtensionType::SupportedGroups
|
|
) {
|
|
if let ExtensionData::NegotiatedGroups(NamedGroupList { named_group_list, .. })
|
|
= &supported_groups.extension_data
|
|
{
|
|
// Mark down the offered and acceptable group
|
|
if let Some(_group) = named_group_list.iter().find(
|
|
|&&named_group| {
|
|
named_group == NamedGroup::secp256r1
|
|
}
|
|
) {
|
|
offered_p256 = true;
|
|
}
|
|
|
|
if let Some(_group) = named_group_list.iter().find(
|
|
|&&named_group| {
|
|
named_group == NamedGroup::x25519
|
|
}
|
|
) {
|
|
offered_x25519 = true;
|
|
}
|
|
} else {
|
|
// Malformed TLS packet
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Client did not offer ECDHE algorithm within `supported version` extension
|
|
// While it is allowed, HRR is not handled as acceptable parameters
|
|
// should have been offered already initially.
|
|
// Possibility: Tolerate minor mismatch of client hello, and send HRR instead
|
|
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Select usable key
|
|
if let Some(key_shares) = client_hello.extensions.iter().find(
|
|
|extension| extension.extension_type == ExtensionType::KeyShare
|
|
) {
|
|
if let ExtensionData::KeyShareEntry(
|
|
KeyShareEntryContent::KeyShareClientHello { client_shares, .. }
|
|
) = &key_shares.extension_data {
|
|
// Try P-256 first, if offered by client
|
|
if offered_p256 {
|
|
if let Some(p256_key) = client_shares.iter().find(
|
|
|key| {
|
|
key.group == NamedGroup::secp256r1
|
|
}
|
|
) {
|
|
ecdhe_public_key.replace(
|
|
DiffieHellmanPublicKey::SECP256R1 {
|
|
encoded_point: p256::EncodedPoint::from_untagged_bytes(
|
|
GenericArray::from_slice(
|
|
&p256_key.key_exchange[1..]
|
|
)
|
|
)
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
// Then try X25519, if P-256 key is not found and x25519 is offered
|
|
if offered_x25519 && ecdhe_public_key.is_none() {
|
|
if let Some(x25519_key) = client_shares.iter().find(
|
|
|key| {
|
|
key.group == NamedGroup::x25519
|
|
}
|
|
) {
|
|
// Prepare a 32-bytes buffer for the public key
|
|
let mut key_content: [u8; 32] = [0; 32];
|
|
|
|
key_content.clone_from_slice(&x25519_key.key_exchange);
|
|
ecdhe_public_key.replace(
|
|
DiffieHellmanPublicKey::X25519 {
|
|
public_key: x25519_dalek::PublicKey::from(
|
|
key_content
|
|
)
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
// There are no applicable offered client key,
|
|
// Proper way of handling: Send a HelloRetryRequest with key generated
|
|
if ecdhe_public_key.is_none() {
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::HandshakeFailure,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Malformed packet
|
|
// Send alert to client
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// The key_share extension was not sent
|
|
// Proper way of handling: Send a HelloRetryRequest with key generated
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Select signature algorithm
|
|
if let Some(signature_algorithms) = client_hello.extensions.iter().find(
|
|
|extension| extension.extension_type == ExtensionType::SignatureAlgorithms
|
|
) {
|
|
if let ExtensionData::SignatureAlgorithms(
|
|
SignatureSchemeList { supported_signature_algorithms, .. }
|
|
) = &signature_algorithms.extension_data {
|
|
// Check compatibility of signature algorithms
|
|
if let Some(certificate_private_key) = self.session.borrow().get_certificate_private_key() {
|
|
use crate::session::CertificatePrivateKey::*;
|
|
if let Some(server_signature_algorithm) = match certificate_private_key {
|
|
// Try RSA keys:
|
|
RSA { .. } => {
|
|
supported_signature_algorithms.iter().find(
|
|
|&&signature_algorithm| {
|
|
signature_algorithm == SignatureScheme::rsa_pkcs1_sha256 ||
|
|
signature_algorithm == SignatureScheme::rsa_pkcs1_sha384 ||
|
|
signature_algorithm == SignatureScheme::rsa_pkcs1_sha512 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_rsae_sha256 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_rsae_sha384 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_rsae_sha512 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_pss_sha256 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_pss_sha384 ||
|
|
signature_algorithm == SignatureScheme::rsa_pss_pss_sha512
|
|
}
|
|
)
|
|
},
|
|
ECDSA_SECP256R1_SHA256 { .. } => {
|
|
supported_signature_algorithms.iter().find(
|
|
|&&signature_algorithm| {
|
|
signature_algorithm == SignatureScheme::ecdsa_secp256r1_sha256
|
|
}
|
|
)
|
|
},
|
|
ED25519 { .. } => {
|
|
supported_signature_algorithms.iter().find(
|
|
|&&signature_algorithm| {
|
|
signature_algorithm == SignatureScheme::ed25519
|
|
}
|
|
)
|
|
}
|
|
} {
|
|
signature_algorithm = Some(*server_signature_algorithm);
|
|
} else {
|
|
// Cannot find a suitable signature algorithm for the server side
|
|
// Terminate the negotiation with an alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::HandshakeFailure,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
} else {
|
|
// Server must have a certificate ready
|
|
// Through this should be enforced when entering listening stage
|
|
unreachable!()
|
|
}
|
|
} else {
|
|
// Malformed packet, type does not match content
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::DecodeError,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
} else {
|
|
// Will only accept authentication through certificate
|
|
// Send alert if there are no signature algorithms extension
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::MissingExtension,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
{
|
|
let mut session = self.session.borrow_mut();
|
|
session.server_update_for_begin(
|
|
*accepted_cipher_suite,
|
|
ecdhe_public_key.unwrap(),
|
|
session_id,
|
|
signature_algorithm.unwrap(),
|
|
handshake_slice
|
|
)
|
|
}
|
|
|
|
log::info!("Processed client hello")
|
|
}
|
|
},
|
|
|
|
// Receive client certificate as server
|
|
TlsState::SERVER_WAIT_CERT => {
|
|
// Verify that it is indeed an Certificate
|
|
let might_be_cert = repr.handshake.take().unwrap();
|
|
|
|
if might_be_cert.get_msg_type() == HandshakeType::Certificate {
|
|
// Process certificates
|
|
|
|
// let all_certificates = might_be_cert.get_all_asn1_der_certificates().unwrap();
|
|
// log::info!("Number of certificates: {:?}", all_certificates.len());
|
|
// log::info!("All certificates: {:?}", all_certificates);
|
|
|
|
// TODO: Process all certificates
|
|
// TODO: Conditionally allow client to send empty certificate
|
|
let cert = might_be_cert.get_asn1_der_certificate().unwrap();
|
|
|
|
// TODO: Replace this block after implementing a proper
|
|
// certificate verification procdeure
|
|
cert.validate_self_signed_signature().expect("Signature mismatched");
|
|
|
|
// Update session TLS state to WAIT_CV
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
self.session.borrow_mut()
|
|
.server_update_for_wait_cert_cr(
|
|
&cert_slice,
|
|
cert.get_cert_public_key().unwrap()
|
|
);
|
|
log::info!("Received WAIT_CERT");
|
|
} else {
|
|
// Unexpected handshakes
|
|
// Throw alert
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
},
|
|
|
|
// Receive client certificate verify as server
|
|
// Verify the signature to the hash
|
|
TlsState::SERVER_WAIT_CV => {
|
|
// Ensure that it is CertificateVerify
|
|
let might_be_cert_verify = repr.handshake.take().unwrap();
|
|
if might_be_cert_verify.get_msg_type() != HandshakeType::CertificateVerify {
|
|
// Throw alert to terminate handshake if it is not certificate verify
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Take out the portion for CertificateVerify
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, cert_verify_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_cert_verify.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
// Perform verification, update TLS state if successful
|
|
let (sig_alg, signature) = might_be_cert_verify.get_signature().unwrap();
|
|
{
|
|
self.session.borrow_mut()
|
|
.server_update_for_wait_cv(
|
|
cert_verify_slice,
|
|
sig_alg,
|
|
signature
|
|
);
|
|
}
|
|
log::info!("Received CV");
|
|
},
|
|
|
|
TlsState::SERVER_WAIT_FINISHED => {
|
|
// Ensure that it is Finished
|
|
let might_be_client_finished = repr.handshake.take().unwrap();
|
|
if might_be_client_finished.get_msg_type() != HandshakeType::Finished {
|
|
// Expected to recv client finished
|
|
self.session.borrow_mut().invalidate_session(
|
|
AlertType::UnexpectedMessage,
|
|
handshake_slice
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Take out the portion for server Finished
|
|
// Length of handshake header is 4
|
|
let (_handshake_slice, client_finished_slice) =
|
|
take::<_, _, (&[u8], ErrorKind)>(
|
|
might_be_client_finished.length + 4
|
|
)(handshake_slice)
|
|
.map_err(|_| Error::Unrecognized)?;
|
|
|
|
// Perform verification, update TLS state if successful
|
|
// Update traffic secret, reset sequence number
|
|
self.session.borrow_mut()
|
|
.server_update_for_wait_finished(
|
|
client_finished_slice,
|
|
might_be_client_finished.get_verify_data().unwrap()
|
|
);
|
|
log::info!("Received client FIN");
|
|
}
|
|
|
|
_ => {},
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Send method for TLS Handshake that needs to be encrypted.
|
|
// Does the following things:
|
|
// 1. Encryption
|
|
// 2. Add TLS header in front of application data
|
|
// Input should be inner plaintext
|
|
// Note: Do not put this slice into the transcript hash. It is polluted.
|
|
// TODO: Rename this function. It is only good for client finished
|
|
fn send_application_slice(&mut self, slice: &mut [u8]) -> Result<()> {
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
if !tcp_socket.can_send() {
|
|
return Err(Error::Illegal);
|
|
}
|
|
// Borrow session in advance
|
|
let mut session = self.session.borrow_mut();
|
|
|
|
// Pre-compute TLS record layer as associated_data
|
|
let mut associated_data: [u8; 5] = [0x17, 0x03, 0x03, 0x00, 0x00];
|
|
let auth_tag_length: u16 = match session.get_cipher_suite_type() {
|
|
Some(CipherSuite::TLS_AES_128_GCM_SHA256) |
|
|
Some(CipherSuite::TLS_AES_256_GCM_SHA384) |
|
|
Some(CipherSuite::TLS_AES_128_CCM_SHA256) |
|
|
Some(CipherSuite::TLS_CHACHA20_POLY1305_SHA256) => {
|
|
16
|
|
},
|
|
_ => return Err(Error::Illegal),
|
|
};
|
|
|
|
NetworkEndian::write_u16(
|
|
&mut associated_data[3..5],
|
|
auth_tag_length + u16::try_from(slice.len()).unwrap()
|
|
);
|
|
|
|
let auth_tag = session.encrypt_in_place_detached(
|
|
&associated_data,
|
|
slice
|
|
).map_err(|_| Error::Illegal)?;
|
|
|
|
tcp_socket.send_slice(&associated_data)?;
|
|
tcp_socket.send_slice(&slice)?;
|
|
tcp_socket.send_slice(&auth_tag)?;
|
|
|
|
session.increment_local_sequence_number();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<usize> {
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
if !tcp_socket.can_recv() {
|
|
return Ok(0);
|
|
}
|
|
|
|
let mut session = self.session.borrow_mut();
|
|
|
|
// If the handshake is not completed, do not pull bytes out of the buffer
|
|
// through TlsSocket.recv_slice()
|
|
// Handshake recv should be through TCPSocket directly.
|
|
if session.get_tls_state() != TlsState::CLIENT_CONNECTED &&
|
|
session.get_tls_state() != TlsState::SERVER_CONNECTED {
|
|
return Ok(0);
|
|
}
|
|
|
|
let (recv_slice_size, acceptable) = tcp_socket.recv(
|
|
|buffer| {
|
|
// Read the size of the TLS record beforehand
|
|
let record_length: usize = NetworkEndian::read_u16(&buffer[3..5]).into();
|
|
let provided_data_capacity: usize = data.len();
|
|
// Copy the entire byte representation of TLS packet into the
|
|
// user-provided buffer, if possible
|
|
if provided_data_capacity >= (record_length + 5) {
|
|
&data[..(record_length + 5)].clone_from_slice(&buffer[..(record_length + 5)]);
|
|
}
|
|
(
|
|
(record_length + 5),
|
|
(
|
|
(record_length + 5),
|
|
provided_data_capacity >= (record_length + 5)
|
|
)
|
|
)
|
|
}
|
|
)?;
|
|
|
|
if !acceptable {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Encrypted data need a TLS record wrapper (5 bytes)
|
|
// Authentication tag (16 bytes, for all supported AEADs)
|
|
// Content type byte (1 byte)
|
|
// Zero paddings (>=0 bytes)
|
|
if recv_slice_size < 22 {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Get Associated Data
|
|
let mut associated_data: [u8; 5] = [0; 5];
|
|
associated_data.clone_from_slice(&data[..5]);
|
|
|
|
// Dump association data (TLS Record wrapper)
|
|
// Only decrypt application data
|
|
// Always increment sequence number after decrpytion
|
|
log::info!("Sequence number: {:?}", session.server_sequence_number);
|
|
session.decrypt_application_data_in_place(
|
|
&associated_data,
|
|
&mut data[5..recv_slice_size]
|
|
).unwrap();
|
|
session.increment_remote_sequence_number();
|
|
|
|
// Make sure it is application data
|
|
let (content_type, padding_start_index) =
|
|
get_content_type_inner_plaintext(&data[..(recv_slice_size-16)]);
|
|
|
|
// If it is not application data, handle it internally
|
|
if content_type != TlsContentType::ApplicationData {
|
|
// TODO: Implement key update here, as it could be a key update
|
|
log::info!("Other decrypted: {:?}", &data[..(recv_slice_size-16)]);
|
|
return Ok(0);
|
|
}
|
|
|
|
// Otherwise, it is surely application data.
|
|
// Prune TLS record wrapper (5 bytes) from data.
|
|
data.rotate_left(5);
|
|
|
|
// Remove extra length:
|
|
// 5 bytes of TLS record header
|
|
// 16 bytes of authentication tag (included in zero padding search fn)
|
|
// 1 byte of content type
|
|
// zero paddings, variated length
|
|
let actual_application_data_length = recv_slice_size - 5 - 1
|
|
- padding_start_index.map_or(16,
|
|
|start| recv_slice_size - start
|
|
);
|
|
|
|
Ok(actual_application_data_length)
|
|
}
|
|
|
|
pub fn send_slice(&mut self, data: &[u8]) -> Result<usize> {
|
|
// If the handshake is not completed, do not push bytes onto the buffer
|
|
// through TlsSocket.send_slice()
|
|
// Handshake send should be through TCPSocket directly.
|
|
let mut session = self.session.borrow_mut();
|
|
if session.get_tls_state() != TlsState::CLIENT_CONNECTED &&
|
|
session.get_tls_state() != TlsState::SERVER_CONNECTED {
|
|
return Ok(0);
|
|
}
|
|
let data_length = data.len();
|
|
|
|
// Sending order:
|
|
// 1. Associated data/ TLS Record layer
|
|
// 2. Encrypted { Payload (data) | Content type: Application Data }
|
|
// 3. Authentication tag (16 bytes for all supported AEADs)
|
|
let mut associated_data: [u8; 5] = [
|
|
0x17, // Application data
|
|
0x03, 0x03, // TLS 1.3 record disguised as TLS 1.2
|
|
0x00, 0x00 // Length of encrypted data, yet to be determined
|
|
];
|
|
|
|
NetworkEndian::write_u16(&mut associated_data[3..5],
|
|
u16::try_from(data_length).unwrap() // Payload length
|
|
+ 1 // Content type length
|
|
+ 16 // Auth tag length
|
|
);
|
|
|
|
let mut vec: HeaplessVec<u8, U1024> = HeaplessVec::from_slice(data).unwrap();
|
|
vec.push(0x17).unwrap(); // Content type
|
|
|
|
let tag = session.encrypt_application_data_in_place_detached(
|
|
&associated_data,
|
|
&mut vec
|
|
).unwrap();
|
|
session.increment_local_sequence_number();
|
|
|
|
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
|
|
if !tcp_socket.can_send() {
|
|
return Err(Error::Illegal);
|
|
}
|
|
|
|
tcp_socket.send_slice(&associated_data)?;
|
|
tcp_socket.send_slice(&vec)?;
|
|
tcp_socket.send_slice(&tag)?;
|
|
|
|
Ok(data_length)
|
|
}
|
|
|
|
pub fn is_connected(&self) -> Result<bool> {
|
|
let session = self.session.borrow();
|
|
Ok(
|
|
session.get_tls_state() == TlsState::CLIENT_CONNECTED ||
|
|
session.get_tls_state() == TlsState::SERVER_CONNECTED
|
|
)
|
|
}
|
|
|
|
// Send `Close notify` alert to remote side
|
|
// Set state to `CLOSED`
|
|
// Leave TCP termination to polling method
|
|
pub fn close(&mut self) -> Result<()> {
|
|
let mut session = self.session.borrow_mut();
|
|
match session.get_tls_state() {
|
|
// Send a `close notify` if handshake is established
|
|
TlsState::CLIENT_CONNECTED | TlsState::SERVER_CONNECTED => {
|
|
session.invalidate_session(
|
|
AlertType::CloseNotify,
|
|
&[]
|
|
);
|
|
},
|
|
// Do nothing if handshake hasn't even started
|
|
TlsState::DEFAULT => {},
|
|
// Send `user cancaled` to cancel the handshake negotiation
|
|
// if it is currently in the middle of one
|
|
_ => {
|
|
session.invalidate_session(
|
|
AlertType::UserCanceled,
|
|
&[]
|
|
);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
use core::fmt;
|
|
impl<'a> fmt::Write for TlsSocket<'a> {
|
|
fn write_str(&mut self, slice: &str) -> fmt::Result {
|
|
let slice = slice.as_bytes();
|
|
if self.send_slice(slice) == Ok(slice.len()) {
|
|
Ok(())
|
|
} else {
|
|
Err(fmt::Error)
|
|
}
|
|
}
|
|
}
|