tls: use session
This commit is contained in:
parent
ec0662e752
commit
404804809e
|
@ -1,226 +0,0 @@
|
|||
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret, ecdh::SharedSecret};
|
||||
use aes_gcm::{Aes128Gcm, Aes256Gcm};
|
||||
use chacha20poly1305::{ChaCha20Poly1305, Key};
|
||||
use ccm::{Ccm, consts::*};
|
||||
use aes_gcm::aes::Aes128;
|
||||
use aes_gcm::{AeadInPlace, NewAead};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{ RngCore, CryptoRng };
|
||||
use sha2::{ Digest, Sha256, Sha384, Sha512 };
|
||||
use heapless::Vec;
|
||||
use hkdf::Hkdf;
|
||||
|
||||
use crate::Error as TlsError;
|
||||
use crate::tls_packet::CipherSuite as CipherSuiteField;
|
||||
use crate::key::*;
|
||||
|
||||
// A structure representing the block cipher and the hashes
|
||||
pub(crate) enum CipherSuite {
|
||||
// Handshake is still proceeding, no cipher can be produced yet,
|
||||
// Though hashes still has to be prepared for deriving key later,
|
||||
// This enum offers all possible hashes that could be needed
|
||||
// i.e. SHA256 and SHA384
|
||||
Undetermined {
|
||||
sha_256: Sha256,
|
||||
sha_384: Sha384,
|
||||
},
|
||||
|
||||
// Established cipher suites
|
||||
// Contains a block cipher (GCM/CCM/ChaChaPoly)
|
||||
// Contains a hash function (SHA256/SHA384)
|
||||
TLS_AES_128_GCM_SHA256 {
|
||||
aes_128_gcm: Aes128Gcm,
|
||||
sha_256: Sha256,
|
||||
},
|
||||
TLS_AES_256_GCM_SHA384 {
|
||||
aes_256_gcm: Aes256Gcm,
|
||||
sha_384: Sha384,
|
||||
},
|
||||
TLS_CHACHA20_POLY1305_SHA256 {
|
||||
chacha20_poly1305: ChaCha20Poly1305,
|
||||
sha_256: Sha256,
|
||||
},
|
||||
TLS_AES_128_CCM_SHA256 {
|
||||
ccm: Ccm<Aes128, U16, U12>,
|
||||
sha_256: Sha256,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
impl CipherSuite {
|
||||
pub(crate) fn new() -> Self {
|
||||
CipherSuite::Undetermined {
|
||||
sha_256: Sha256::new(),
|
||||
sha_384: Sha384::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Assume no PSK, establish ciphersuite along side handshake secret
|
||||
// Need to update hash function before calling
|
||||
pub(crate) fn establish(
|
||||
self,
|
||||
field: CipherSuiteField,
|
||||
ecdhe_shared: SharedSecret
|
||||
) -> Self {
|
||||
use CipherSuiteField::*;
|
||||
|
||||
let (sha_256, sha_384) = {
|
||||
if let CipherSuite::Undetermined {
|
||||
sha_256,
|
||||
sha_384,
|
||||
} = self {
|
||||
(sha_256, sha_384)
|
||||
} else {
|
||||
// TODO: Implement key change
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
||||
match field {
|
||||
TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 |
|
||||
TLS_AES_128_CCM_SHA256 => {
|
||||
// Compute early_secret in HKDF, without PSK
|
||||
let empty_hash = Sha256::new().chain("");
|
||||
let early_secret = Hkdf::<Sha256>::new(None, &[0; 32]);
|
||||
|
||||
// Calculate derived secret
|
||||
let derived_secret = derive_secret(
|
||||
&early_secret,
|
||||
"derived",
|
||||
empty_hash
|
||||
);
|
||||
|
||||
// Calculate handshake secret in HKDF
|
||||
let handshake_secret = Hkdf::<Sha256>::new(
|
||||
Some(&derived_secret),
|
||||
ecdhe_shared.as_bytes()
|
||||
);
|
||||
|
||||
// Calculate client_handshake_traffic_secret
|
||||
let client_handshake_traffic_secret = derive_secret(
|
||||
&handshake_secret,
|
||||
"c hs traffic",
|
||||
sha_256.clone()
|
||||
);
|
||||
|
||||
// Calculate server_handshake_traffic_secret
|
||||
let server_handshake_traffic_secret = derive_secret(
|
||||
&handshake_secret,
|
||||
"c hs traffic",
|
||||
sha_256.clone()
|
||||
);
|
||||
|
||||
// let client_write_key = hkdf_expand_label(
|
||||
//
|
||||
// );
|
||||
}
|
||||
_ => todo!()
|
||||
}
|
||||
|
||||
todo!()
|
||||
|
||||
// // Compute HKDF
|
||||
// let (hash, empty_hash, hkdf) = match field {
|
||||
// TLS_AES_128_GCM_SHA256 |
|
||||
// TLS_CHACHA20_POLY1305_SHA256 |
|
||||
// TLS_AES_128_CCM_SHA256 => {
|
||||
// (
|
||||
// sha_256,
|
||||
// Sha256::new().chain(""),
|
||||
// Hkdf::<Sha256>::new(None, &[0; 32])
|
||||
// )
|
||||
// },
|
||||
// TLS_AES_256_GCM_SHA384 => {
|
||||
// (
|
||||
// sha_384,
|
||||
// Sha384::new().chain(""),
|
||||
// Hkdf::<Sha384>::new(None, &[0; 48])
|
||||
// )
|
||||
// }
|
||||
// };
|
||||
|
||||
// // get_derived_secret, then insert ECDHE shared secret
|
||||
// let derived_secret = derive_secret(hkdf, "derived", empty_hash);
|
||||
|
||||
// let (key, iv) = match field {
|
||||
// TLS_AES_128_GCM_SHA256 |
|
||||
// TLS_CHACHA20_POLY1305_SHA256 |
|
||||
// TLS_AES_128_CCM_SHA256 => {
|
||||
// let hkdf = Hkdf::<Sha256>::new(
|
||||
// Some(&derived_secret),
|
||||
// ecdhe_shared.as_bytes()
|
||||
// );
|
||||
// let client_handshake_traffic_secret = derive_secret(
|
||||
// hkdf,
|
||||
// "c hs traffic",
|
||||
// sha256.clone(),
|
||||
// );
|
||||
// },
|
||||
// TLS_AES_256_GCM_SHA384 => {
|
||||
// Hkdf::<Sha384>::new(
|
||||
// Some(&derived_secret),
|
||||
// ecdhe_shared.as_bytes()
|
||||
// )
|
||||
// }
|
||||
// };
|
||||
}
|
||||
}
|
||||
|
||||
// macro_rules! impl_cipher {
|
||||
// ($($cipher_name: ident),+) => {
|
||||
// impl Cipher {
|
||||
// pub(crate) fn encrypt<T>(&self, rng: &mut T, associated_data: &[u8], buffer: &mut Vec<u8>) -> core::result::Result<(), TlsError>
|
||||
// where
|
||||
// T: RngCore + CryptoRng
|
||||
// {
|
||||
// // All 4 supported Ciphers use a nonce of 12 bytes
|
||||
// let mut nonce_array: [u8; 12] = [0; 12];
|
||||
// rng.fill_bytes(&mut nonce_array);
|
||||
// use Cipher::*;
|
||||
// match self {
|
||||
// $(
|
||||
// $cipher_name(cipher) => {
|
||||
// cipher.encrypt_in_place(
|
||||
// &GenericArray::from_slice(&nonce_array),
|
||||
// associated_data,
|
||||
// buffer
|
||||
// ).map_err(
|
||||
// |_| TlsError::EncryptionError
|
||||
// )
|
||||
// }
|
||||
// )+
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub(crate) fn decrypt<T>(&self, rng: &mut T, associated_data: &[u8], buffer: &mut Vec<u8>) -> core::result::Result<(), TlsError>
|
||||
// where
|
||||
// T: RngCore + CryptoRng
|
||||
// {
|
||||
// // All 4 supported Ciphers use a nonce of 12 bytes
|
||||
// let mut nonce_array: [u8; 12] = [0; 12];
|
||||
// rng.fill_bytes(&mut nonce_array);
|
||||
// use Cipher::*;
|
||||
// match self {
|
||||
// $(
|
||||
// $cipher_name(cipher) => {
|
||||
// cipher.decrypt_in_place(
|
||||
// &GenericArray::from_slice(&nonce_array),
|
||||
// associated_data,
|
||||
// buffer
|
||||
// ).map_err(
|
||||
// |_| TlsError::EncryptionError
|
||||
// )
|
||||
// }
|
||||
// )+
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl_cipher!(
|
||||
// TLS_AES_128_GCM_SHA256,
|
||||
// TLS_AES_256_GCM_SHA384,
|
||||
// TLS_CHACHA20_POLY1305_SHA256,
|
||||
// TLS_AES_128_CCM_SHA256
|
||||
// );
|
|
@ -6,7 +6,6 @@ extern crate alloc;
|
|||
pub mod tls;
|
||||
pub mod tls_packet;
|
||||
pub mod parse;
|
||||
pub mod cipher_suite;
|
||||
pub mod buffer;
|
||||
pub mod key;
|
||||
pub mod session;
|
||||
|
|
|
@ -411,6 +411,14 @@ impl Session {
|
|||
pub(crate) fn get_tls_state(&self) -> TlsState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub(crate) fn has_completed_handshake(&self) -> bool {
|
||||
self.state == TlsState::CONNECTED
|
||||
}
|
||||
|
||||
pub(crate) fn receive_change_cipher_spec(&mut self) {
|
||||
self.changed_cipher_spec = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
|
|
129
src/tls.rs
129
src/tls.rs
|
@ -35,7 +35,6 @@ use alloc::vec::{ self, Vec };
|
|||
use crate::Error as TlsError;
|
||||
use crate::tls_packet::*;
|
||||
use crate::parse::parse_tls_repr;
|
||||
use crate::cipher_suite::CipherSuite;
|
||||
use crate::buffer::TlsBuffer;
|
||||
use crate::session::{Session, TlsRole};
|
||||
|
||||
|
@ -55,14 +54,8 @@ pub(crate) enum TlsState {
|
|||
// TODO: Group up all session_specific parameters into a separate structure
|
||||
pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
|
||||
{
|
||||
state: RefCell<TlsState>,
|
||||
tcp_handle: SocketHandle,
|
||||
rng: R,
|
||||
secret: RefCell<Option<EphemeralSecret>>, // Used enum Option to allow later init
|
||||
session_id: RefCell<Option<[u8; 32]>>, // init session specific field later
|
||||
received_change_cipher_spec: RefCell<Option<bool>>,
|
||||
cipher: RefCell<Option<CipherSuite>>,
|
||||
handshake_sha256: RefCell<Sha256>,
|
||||
session: RefCell<Session>,
|
||||
}
|
||||
|
||||
|
@ -79,14 +72,8 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
let tcp_socket = TcpSocket::new(rx_buffer, tx_buffer);
|
||||
let tcp_handle = sockets.add(tcp_socket);
|
||||
TlsSocket {
|
||||
state: RefCell::new(TlsState::START),
|
||||
tcp_handle,
|
||||
rng,
|
||||
secret: RefCell::new(None),
|
||||
session_id: RefCell::new(None),
|
||||
received_change_cipher_spec: RefCell::new(None),
|
||||
cipher: RefCell::new(None),
|
||||
handshake_sha256: RefCell::new(Sha256::new()),
|
||||
session: RefCell::new(
|
||||
Session::new(TlsRole::Client)
|
||||
),
|
||||
|
@ -130,8 +117,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
}
|
||||
|
||||
// Handle TLS handshake through TLS states
|
||||
let state = self.state.clone().into_inner();
|
||||
match state {
|
||||
match self.session.borrow().get_tls_state() {
|
||||
// Initiate TLS handshake
|
||||
TlsState::START => {
|
||||
// Prepare field that is randomised,
|
||||
|
@ -142,7 +128,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
self.rng.fill_bytes(&mut random);
|
||||
self.rng.fill_bytes(&mut session_id);
|
||||
let repr = TlsRepr::new()
|
||||
.client_hello(&ecdh_secret, random, session_id);
|
||||
.client_hello(&ecdh_secret, random, session_id.clone());
|
||||
|
||||
// Update hash function with client hello handshake
|
||||
let mut array = [0; 2048];
|
||||
|
@ -150,29 +136,27 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
buffer.enqueue_tls_repr(repr)?;
|
||||
let slice: &[u8] = buffer.into();
|
||||
|
||||
// Update hash by handshake
|
||||
// Update with entire packet except the record layer
|
||||
{
|
||||
self.handshake_sha256.borrow_mut().update(&slice[5..]);
|
||||
}
|
||||
// Send the packet
|
||||
self.send_tls_slice(sockets, slice)?;
|
||||
|
||||
// Store session settings, i.e. secret, session_id
|
||||
self.secret.replace(Some(ecdh_secret));
|
||||
self.session_id.replace(Some(session_id));
|
||||
self.received_change_cipher_spec.replace(Some(false));
|
||||
|
||||
// Update the TLS state
|
||||
self.state.replace(TlsState::WAIT_SH);
|
||||
// Update TLS session
|
||||
self.session.borrow_mut().client_update_for_ch(
|
||||
ecdh_secret,
|
||||
session_id,
|
||||
&slice[5..]
|
||||
);
|
||||
},
|
||||
|
||||
// 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 normall send SH alongside EE
|
||||
// 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 => {},
|
||||
|
||||
_ => todo!()
|
||||
}
|
||||
|
||||
|
@ -191,13 +175,11 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
self.process(repr)?;
|
||||
}
|
||||
|
||||
Ok(self.state.clone().into_inner() == TlsState::CONNECTED)
|
||||
Ok(self.session.borrow().has_completed_handshake())
|
||||
}
|
||||
|
||||
// Process TLS ingress during handshake
|
||||
fn process(&self, repr: TlsRepr) -> Result<()> {
|
||||
let state = self.state.clone().into_inner();
|
||||
|
||||
// Change_cipher_spec check:
|
||||
// Must receive CCS before recv peer's FINISH message
|
||||
// i.e. Must happen after START and before CONNECTED
|
||||
|
@ -205,11 +187,11 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
// CCS message only exist for compatibility reason,
|
||||
// Drop the message and update `received_change_cipher_spec`
|
||||
if repr.is_change_cipher_spec() {
|
||||
self.received_change_cipher_spec.replace(Some(true));
|
||||
self.session.borrow_mut().receive_change_cipher_spec();
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
match state {
|
||||
match self.session.borrow().get_tls_state() {
|
||||
// During WAIT_SH for a TLS client, client should wait for ServerHello
|
||||
TlsState::WAIT_SH => {
|
||||
// Legacy_protocol must be TLS 1.2
|
||||
|
@ -232,25 +214,37 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
// supported_version: Must be TLS 1.3
|
||||
// key_share: Store key, must be in secp256r1
|
||||
// (TODO: Support other key shares ^)
|
||||
|
||||
// "Cache" for ECDHE server public info
|
||||
let mut server_public: Option<EncodedPoint> = 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
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Check session_id_echo
|
||||
// The socket should have a session_id after moving from START state
|
||||
if self.session_id.clone().into_inner().unwrap() != server_hello.session_id_echo {
|
||||
if !self.session.borrow().verify_session_id_echo(server_hello.session_id_echo) {
|
||||
// Abort communication
|
||||
todo!()
|
||||
}
|
||||
// Store the cipher suite
|
||||
let selected_cipher = server_hello.cipher_suite;
|
||||
|
||||
// Note the selected cipher suite
|
||||
selected_cipher.replace(server_hello.cipher_suite);
|
||||
|
||||
// TLSv13 forbidden key compression
|
||||
if server_hello.compression_method != 0 {
|
||||
// Abort communciation
|
||||
todo!()
|
||||
}
|
||||
|
||||
for extension in server_hello.extensions.iter() {
|
||||
if extension.extension_type == ExtensionType::SupportedVersions {
|
||||
if let ExtensionData::SupportedVersions(
|
||||
|
@ -285,43 +279,11 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
// It is surely from secp256r1, no other groups are permitted
|
||||
// Convert untagged bytes into encoded point on p256 eliptic curve
|
||||
// Slice the first byte out of the bytes
|
||||
let server_public = EncodedPoint::from_untagged_bytes(
|
||||
GenericArray::from_slice(&server_share.key_exchange[1..])
|
||||
server_public.replace(
|
||||
EncodedPoint::from_untagged_bytes(
|
||||
GenericArray::from_slice(&server_share.key_exchange[1..])
|
||||
)
|
||||
);
|
||||
// TODO: Handle improper shared key
|
||||
// Right now is causes a panic, only socket abort is needed
|
||||
let secret = self.secret.replace(None);
|
||||
let shared = secret.unwrap()
|
||||
.diffie_hellman(&server_public)
|
||||
.expect("Unsupported key");
|
||||
// let cipher = match selected_cipher {
|
||||
// CipherSuite::TLS_AES_128_GCM_SHA256 => {
|
||||
// Cipher::TLS_AES_128_GCM_SHA256(
|
||||
// todo!()
|
||||
// )
|
||||
// },
|
||||
// CipherSuite::TLS_AES_256_GCM_SHA384 => {
|
||||
// Cipher::TLS_AES_256_GCM_SHA384(
|
||||
// Aes256Gcm::new(shared.as_bytes())
|
||||
// )
|
||||
// },
|
||||
// CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => {
|
||||
// Cipher::TLS_CHACHA20_POLY1305_SHA256(
|
||||
// ChaCha20Poly1305::new(shared.as_bytes())
|
||||
// )
|
||||
// },
|
||||
// CipherSuite::TLS_AES_128_CCM_SHA256 => {
|
||||
// Cipher::TLS_AES_128_CCM_SHA256(
|
||||
// todo!()
|
||||
// )
|
||||
// },
|
||||
// // CCM_8 is not supported
|
||||
// // TODO: Abort communication
|
||||
// CipherSuite::TLS_AES_128_CCM_8_SHA256 => {
|
||||
// todo!()
|
||||
// }
|
||||
// };
|
||||
// self.cipher.replace(Some(cipher));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,21 +293,24 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
todo!()
|
||||
}
|
||||
|
||||
// Check that both selected_cipher and server_public were received
|
||||
if selected_cipher.is_none() || server_public.is_none() {
|
||||
// Abort communication
|
||||
todo!()
|
||||
}
|
||||
|
||||
// This is indeed a desirable ServerHello TLS repr
|
||||
// Reprocess ServerHello into a slice
|
||||
// Update SHA256 hasher with the slice
|
||||
// Update session with required parameter
|
||||
let mut array = [0; 2048];
|
||||
let mut buffer = TlsBuffer::new(&mut array);
|
||||
buffer.enqueue_tls_repr(repr);
|
||||
let slice: &[u8] = buffer.into();
|
||||
{
|
||||
self.handshake_sha256
|
||||
.borrow_mut()
|
||||
.update(&slice[5..]);
|
||||
}
|
||||
|
||||
// Update TLS session state
|
||||
self.state.replace(TlsState::WAIT_EE);
|
||||
self.session.borrow_mut().client_update_for_sh(
|
||||
selected_cipher.unwrap(),
|
||||
server_public.unwrap(),
|
||||
&slice[5..]
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue