key: init
This commit is contained in:
parent
912feac263
commit
ec53973aec
|
@ -7,11 +7,13 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes-gcm = "0.7.0"
|
aes-gcm = "0.7.0"
|
||||||
ccm = "0.2.0"
|
ccm = "0.2.0"
|
||||||
|
hkdf = "0.9.0"
|
||||||
sha2 = { version = "0.9.1", default-features = false }
|
sha2 = { version = "0.9.1", default-features = false }
|
||||||
byteorder = { version = "1.3.4", default-features = false }
|
byteorder = { version = "1.3.4", default-features = false }
|
||||||
num_enum = { version = "0.5.1", default-features = false }
|
num_enum = { version = "0.5.1", default-features = false }
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
generic-array = "0.14.4"
|
generic-array = "0.14.4"
|
||||||
|
heapless = "0.5.6"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
|
|
@ -8,8 +8,10 @@ use alloc::vec::Vec;
|
||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
||||||
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
|
use crate::key::*;
|
||||||
|
|
||||||
// Only designed to support read or write the entire buffer
|
// Only designed to support read or write the entire buffer
|
||||||
|
// TODO: Stricter visibility
|
||||||
pub(crate) struct TlsBuffer<'a> {
|
pub(crate) struct TlsBuffer<'a> {
|
||||||
buffer: &'a mut [u8],
|
buffer: &'a mut [u8],
|
||||||
index: RefCell<usize>,
|
index: RefCell<usize>,
|
||||||
|
@ -22,7 +24,7 @@ impl<'a> Into<&'a [u8]> for TlsBuffer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TlsBuffer<'a> {
|
impl<'a> TlsBuffer<'a> {
|
||||||
pub(crate) fn new(buffer: &'a mut [u8]) -> Self {
|
pub fn new(buffer: &'a mut [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
index: RefCell::new(0),
|
index: RefCell::new(0),
|
||||||
|
@ -199,6 +201,14 @@ impl<'a> TlsBuffer<'a> {
|
||||||
self.write_u16(entry.length)?;
|
self.write_u16(entry.length)?;
|
||||||
self.write(entry.key_exchange.as_slice())
|
self.write(entry.key_exchange.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn enqueue_hkdf_label(&mut self, hkdf_label: HkdfLabel) -> Result<()> {
|
||||||
|
self.write_u16(hkdf_label.length)?;
|
||||||
|
self.write_u8(hkdf_label.label_length)?;
|
||||||
|
self.write(hkdf_label.label)?;
|
||||||
|
self.write_u8(hkdf_label.context_length)?;
|
||||||
|
self.write(hkdf_label.context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! export_byte_order_fn {
|
macro_rules! export_byte_order_fn {
|
||||||
|
|
|
@ -1,76 +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 alloc::vec::Vec;
|
|
||||||
use crate::Error as TlsError;
|
|
||||||
|
|
||||||
pub(crate) enum Cipher {
|
|
||||||
TLS_AES_128_GCM_SHA256(Aes128Gcm),
|
|
||||||
TLS_AES_256_GCM_SHA384(Aes256Gcm),
|
|
||||||
TLS_CHACHA20_POLY1305_SHA256(ChaCha20Poly1305),
|
|
||||||
TLS_AES_128_CCM_SHA256(Ccm<Aes128, U16, U12>)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
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
|
||||||
|
// );
|
|
@ -0,0 +1,111 @@
|
||||||
|
use hkdf::Hkdf;
|
||||||
|
use sha2::{ Digest, Sha256, Sha384, Sha512 };
|
||||||
|
use sha2::digest::{BlockInput, FixedOutput, Reset, Update};
|
||||||
|
use generic_array::{ GenericArray, ArrayLength };
|
||||||
|
use heapless::{ String, Vec, consts::* };
|
||||||
|
|
||||||
|
use crate::buffer::TlsBuffer;
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct HkdfLabel<'a> {
|
||||||
|
// Length of hash function
|
||||||
|
pub(crate) length: u16,
|
||||||
|
// Label vector: "tls13 " + label
|
||||||
|
pub(crate) label_length: u8,
|
||||||
|
pub(crate) label: &'a [u8],
|
||||||
|
// Context vector: Hashed message
|
||||||
|
pub(crate) context_length: u8,
|
||||||
|
pub(crate) context: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of Derive-Secret function in RFC8446
|
||||||
|
pub(crate) fn derive_secret<Hash>(
|
||||||
|
hkdf: &Hkdf<Hash>,
|
||||||
|
label: &str,
|
||||||
|
hash: Hash
|
||||||
|
) -> GenericArray<u8, Hash::OutputSize>
|
||||||
|
where
|
||||||
|
Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone,
|
||||||
|
Hash::OutputSize: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
// Build a string using heapless
|
||||||
|
// label size:
|
||||||
|
// prefix: "tls13 " => 6 chars
|
||||||
|
// suffix: at most 12 chars as per RFC8446, section 7.1
|
||||||
|
let mut label_string: String<U32> = String::new();
|
||||||
|
label_string.push_str("tls13 ").unwrap();
|
||||||
|
label_string.push_str(label);
|
||||||
|
|
||||||
|
let length = u16::try_from(Hash::output_size()).unwrap();
|
||||||
|
let label_length = u8::try_from(label_string.len()).unwrap();
|
||||||
|
|
||||||
|
let hkdf_label = HkdfLabel {
|
||||||
|
length,
|
||||||
|
label_length,
|
||||||
|
label: label_string.as_ref(),
|
||||||
|
context_length: u8::try_from(length).unwrap(),
|
||||||
|
context: &hash.finalize(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build info from HKDF label using Buffer
|
||||||
|
// length: 2 bytes,
|
||||||
|
// label_vec: 18 bytes (label) + 1 byte (len)
|
||||||
|
// context_vec: 48 bytes for SHA384 + 1 byte (len)
|
||||||
|
let mut array = [0; 100];
|
||||||
|
let mut buffer = TlsBuffer::new(&mut array);
|
||||||
|
buffer.enqueue_hkdf_label(hkdf_label);
|
||||||
|
let info: &[u8] = buffer.into();
|
||||||
|
|
||||||
|
// Define output key material (OKM), dynamically sized by hash
|
||||||
|
let mut okm: GenericArray<u8, Hash::OutputSize> = GenericArray::default();
|
||||||
|
hkdf.expand(info, &mut okm).unwrap();
|
||||||
|
okm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of HKDF-Expand-Label function in RFC8446
|
||||||
|
// Secret is embedded inside hkdf through salt and input key material (IKM)
|
||||||
|
pub(crate) fn hkdf_expand_label<Hash>(
|
||||||
|
hkdf: &Hkdf<Hash>,
|
||||||
|
label: &str,
|
||||||
|
context: &str,
|
||||||
|
okm: &mut [u8],
|
||||||
|
)
|
||||||
|
where
|
||||||
|
Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone,
|
||||||
|
{
|
||||||
|
// Build a string using heapless
|
||||||
|
// label size:
|
||||||
|
// prefix: "tls13 " => 6 chars
|
||||||
|
// suffix: at most 12 chars as per RFC8446, section 7.1
|
||||||
|
let mut label_string: String<U32> = String::new();
|
||||||
|
label_string.push_str("tls13 ").unwrap();
|
||||||
|
label_string.push_str(label);
|
||||||
|
let label_length = u8::try_from(label_string.len()).unwrap();
|
||||||
|
|
||||||
|
let context_slice = context.as_bytes();
|
||||||
|
let context_length = u8::try_from(context_slice.len()).unwrap();
|
||||||
|
|
||||||
|
let length = u16::try_from(okm.len()).unwrap();
|
||||||
|
|
||||||
|
// Build HKDF label
|
||||||
|
let hkdf_label = HkdfLabel {
|
||||||
|
length,
|
||||||
|
label_length,
|
||||||
|
label: label_string.as_ref(),
|
||||||
|
context_length: context_length,
|
||||||
|
context: context_slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build info from HKDF label using Buffer
|
||||||
|
// length: 2 bytes,
|
||||||
|
// label_vec: 18 bytes (label) + 1 byte (len)
|
||||||
|
// context_vec: 48 bytes for SHA384 + 1 byte (len)
|
||||||
|
let mut array = [0; 100];
|
||||||
|
let mut buffer = TlsBuffer::new(&mut array);
|
||||||
|
buffer.enqueue_hkdf_label(hkdf_label);
|
||||||
|
let info: &[u8] = buffer.into();
|
||||||
|
|
||||||
|
hkdf.expand(info, okm).unwrap();
|
||||||
|
}
|
|
@ -6,8 +6,9 @@ extern crate alloc;
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
pub mod tls_packet;
|
pub mod tls_packet;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod cipher;
|
pub mod cipher_suite;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
pub mod key;
|
||||||
|
|
||||||
// TODO: Implement errors
|
// TODO: Implement errors
|
||||||
// Details: Encapsulate smoltcp & nom errors
|
// Details: Encapsulate smoltcp & nom errors
|
||||||
|
|
120
src/tls.rs
120
src/tls.rs
|
@ -28,13 +28,14 @@ use chacha20poly1305::{ChaCha20Poly1305, Key};
|
||||||
use ccm::{Ccm, consts::*};
|
use ccm::{Ccm, consts::*};
|
||||||
use aes_gcm::aes::Aes128;
|
use aes_gcm::aes::Aes128;
|
||||||
use aes_gcm::{AeadInPlace, NewAead};
|
use aes_gcm::{AeadInPlace, NewAead};
|
||||||
|
use sha2::{Sha256, Sha384, Sha512, Digest};
|
||||||
|
|
||||||
use alloc::vec::{ self, Vec };
|
use alloc::vec::{ self, Vec };
|
||||||
|
|
||||||
use crate::Error as TlsError;
|
use crate::Error as TlsError;
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
use crate::parse::parse_tls_repr;
|
use crate::parse::parse_tls_repr;
|
||||||
use crate::cipher::Cipher;
|
use crate::cipher_suite::CipherSuite;
|
||||||
use crate::buffer::TlsBuffer;
|
use crate::buffer::TlsBuffer;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
@ -59,7 +60,8 @@ pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
|
||||||
secret: RefCell<Option<EphemeralSecret>>, // Used enum Option to allow later init
|
secret: RefCell<Option<EphemeralSecret>>, // Used enum Option to allow later init
|
||||||
session_id: RefCell<Option<[u8; 32]>>, // init session specific field later
|
session_id: RefCell<Option<[u8; 32]>>, // init session specific field later
|
||||||
received_change_cipher_spec: RefCell<Option<bool>>,
|
received_change_cipher_spec: RefCell<Option<bool>>,
|
||||||
cipher: RefCell<Option<Cipher>>,
|
cipher: RefCell<Option<CipherSuite>>,
|
||||||
|
handshake_sha256: RefCell<Sha256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
|
@ -82,6 +84,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
session_id: RefCell::new(None),
|
session_id: RefCell::new(None),
|
||||||
received_change_cipher_spec: RefCell::new(None),
|
received_change_cipher_spec: RefCell::new(None),
|
||||||
cipher: RefCell::new(None),
|
cipher: RefCell::new(None),
|
||||||
|
handshake_sha256: RefCell::new(Sha256::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +138,19 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
self.rng.fill_bytes(&mut session_id);
|
self.rng.fill_bytes(&mut session_id);
|
||||||
let repr = TlsRepr::new()
|
let repr = TlsRepr::new()
|
||||||
.client_hello(&ecdh_secret, random, session_id);
|
.client_hello(&ecdh_secret, random, session_id);
|
||||||
self.send_tls_repr(sockets, repr)?;
|
|
||||||
|
// Update hash function with client hello handshake
|
||||||
|
let mut array = [0; 2048];
|
||||||
|
let mut buffer = TlsBuffer::new(&mut array);
|
||||||
|
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..]);
|
||||||
|
}
|
||||||
|
self.send_tls_slice(sockets, slice)?;
|
||||||
|
|
||||||
// Store session settings, i.e. secret, session_id
|
// Store session settings, i.e. secret, session_id
|
||||||
self.secret.replace(Some(ecdh_secret));
|
self.secret.replace(Some(ecdh_secret));
|
||||||
|
@ -161,9 +176,13 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
|
|
||||||
// Read for TLS packet
|
// Read for TLS packet
|
||||||
let mut array: [u8; 2048] = [0; 2048];
|
let mut array: [u8; 2048] = [0; 2048];
|
||||||
let tls_repr_vec = self.recv_tls_repr(sockets, &mut array)?;
|
let mut tls_repr_vec = self.recv_tls_repr(sockets, &mut array)?;
|
||||||
|
|
||||||
for repr in tls_repr_vec.iter() {
|
// Take the TLS representation out of the vector,
|
||||||
|
// Process as a queue
|
||||||
|
let tls_repr_vec_size = tls_repr_vec.len();
|
||||||
|
for index in 0..tls_repr_vec_size {
|
||||||
|
let repr = tls_repr_vec.remove(0);
|
||||||
self.process(repr)?;
|
self.process(repr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +190,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process TLS ingress during handshake
|
// Process TLS ingress during handshake
|
||||||
fn process(&self, repr: &TlsRepr) -> Result<()> {
|
fn process(&self, repr: TlsRepr) -> Result<()> {
|
||||||
let state = self.state.clone().into_inner();
|
let state = self.state.clone().into_inner();
|
||||||
|
|
||||||
// Change_cipher_spec check:
|
// Change_cipher_spec check:
|
||||||
|
@ -270,44 +289,58 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
let shared = secret.unwrap()
|
let shared = secret.unwrap()
|
||||||
.diffie_hellman(&server_public)
|
.diffie_hellman(&server_public)
|
||||||
.expect("Unsupported key");
|
.expect("Unsupported key");
|
||||||
let cipher = match selected_cipher {
|
// let cipher = match selected_cipher {
|
||||||
CipherSuite::TLS_AES_128_GCM_SHA256 => {
|
// CipherSuite::TLS_AES_128_GCM_SHA256 => {
|
||||||
Cipher::TLS_AES_128_GCM_SHA256(
|
// Cipher::TLS_AES_128_GCM_SHA256(
|
||||||
todo!()
|
// todo!()
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
CipherSuite::TLS_AES_256_GCM_SHA384 => {
|
// CipherSuite::TLS_AES_256_GCM_SHA384 => {
|
||||||
Cipher::TLS_AES_256_GCM_SHA384(
|
// Cipher::TLS_AES_256_GCM_SHA384(
|
||||||
Aes256Gcm::new(shared.as_bytes())
|
// Aes256Gcm::new(shared.as_bytes())
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => {
|
// CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => {
|
||||||
Cipher::TLS_CHACHA20_POLY1305_SHA256(
|
// Cipher::TLS_CHACHA20_POLY1305_SHA256(
|
||||||
ChaCha20Poly1305::new(shared.as_bytes())
|
// ChaCha20Poly1305::new(shared.as_bytes())
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
CipherSuite::TLS_AES_128_CCM_SHA256 => {
|
// CipherSuite::TLS_AES_128_CCM_SHA256 => {
|
||||||
Cipher::TLS_AES_128_CCM_SHA256(
|
// Cipher::TLS_AES_128_CCM_SHA256(
|
||||||
todo!()
|
// todo!()
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
// CCM_8 is not supported
|
// // CCM_8 is not supported
|
||||||
// TODO: Abort communication
|
// // TODO: Abort communication
|
||||||
CipherSuite::TLS_AES_128_CCM_8_SHA256 => {
|
// CipherSuite::TLS_AES_128_CCM_8_SHA256 => {
|
||||||
todo!()
|
// todo!()
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
self.cipher.replace(Some(cipher));
|
// self.cipher.replace(Some(cipher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.state.replace(TlsState::WAIT_EE);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Handle invalid TLS packet
|
// Handle invalid TLS packet
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is indeed a desirable ServerHello TLS repr
|
||||||
|
// Reprocess ServerHello into a slice
|
||||||
|
// Update SHA256 hasher with the slice
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -343,6 +376,23 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic inner send method for buffer IO, through TCP socket
|
||||||
|
fn send_tls_slice(&self, sockets: &mut SocketSet, slice: &[u8]) -> Result<()> {
|
||||||
|
let mut tcp_socket = sockets.get::<TcpSocket>(self.tcp_handle);
|
||||||
|
if !tcp_socket.can_send() {
|
||||||
|
return Err(Error::Illegal);
|
||||||
|
}
|
||||||
|
let buffer_size = slice.len();
|
||||||
|
tcp_socket.send_slice(slice)
|
||||||
|
.and_then(
|
||||||
|
|size| if size == buffer_size {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::Truncated)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Generic inner recv method, through TCP socket
|
// Generic inner recv method, through TCP socket
|
||||||
// A TCP packet can contain multiple TLS segments
|
// A TCP packet can contain multiple TLS segments
|
||||||
fn recv_tls_repr<'a>(&'a self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<Vec::<TlsRepr>> {
|
fn recv_tls_repr<'a>(&'a self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result<Vec::<TlsRepr>> {
|
||||||
|
|
|
@ -201,7 +201,7 @@ impl<'a> ClientHello<'a> {
|
||||||
random,
|
random,
|
||||||
session_id_length: 32,
|
session_id_length: 32,
|
||||||
session_id,
|
session_id,
|
||||||
cipher_suites_length: 6,
|
cipher_suites_length: 0,
|
||||||
cipher_suites: &[
|
cipher_suites: &[
|
||||||
CipherSuite::TLS_AES_128_GCM_SHA256,
|
CipherSuite::TLS_AES_128_GCM_SHA256,
|
||||||
CipherSuite::TLS_AES_256_GCM_SHA384,
|
CipherSuite::TLS_AES_256_GCM_SHA384,
|
||||||
|
@ -213,6 +213,7 @@ impl<'a> ClientHello<'a> {
|
||||||
extension_length: 0,
|
extension_length: 0,
|
||||||
extensions: Vec::new(),
|
extensions: Vec::new(),
|
||||||
};
|
};
|
||||||
|
client_hello.cipher_suites_length = u16::try_from(client_hello.cipher_suites.len() * 2).unwrap();
|
||||||
|
|
||||||
client_hello.add_ch_supported_versions()
|
client_hello.add_ch_supported_versions()
|
||||||
.add_sig_algs()
|
.add_sig_algs()
|
||||||
|
|
Loading…
Reference in New Issue