parse: init ee
This commit is contained in:
parent
a2ad8b3334
commit
38bf1d3c3b
14
Cargo.toml
14
Cargo.toml
|
@ -5,8 +5,6 @@ authors = ["occheung"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.7.0"
|
||||
ccm = "0.2.0"
|
||||
hkdf = "0.9.0"
|
||||
sha2 = { version = "0.9.1", default-features = false }
|
||||
byteorder = { version = "1.3.4", default-features = false }
|
||||
|
@ -15,6 +13,16 @@ log = "0.4.11"
|
|||
generic-array = "0.14.4"
|
||||
heapless = "0.5.6"
|
||||
|
||||
[dependencies.aes-gcm]
|
||||
version = "0.8.0"
|
||||
default-features = true
|
||||
features = [ "heapless" ]
|
||||
|
||||
[dependencies.ccm]
|
||||
version = "0.3.0"
|
||||
default-features = true
|
||||
features = [ "heapless" ]
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.6.0"
|
||||
default-features = false
|
||||
|
@ -28,7 +36,7 @@ features = []
|
|||
[dependencies.chacha20poly1305]
|
||||
version = "0.6.0"
|
||||
default-features = false
|
||||
features = [ "alloc", "chacha20" ]
|
||||
features = [ "alloc", "chacha20", "heapless" ]
|
||||
|
||||
[dependencies.p256]
|
||||
version = "0.5.0"
|
||||
|
|
|
@ -86,7 +86,7 @@ impl<'a> TlsBuffer<'a> {
|
|||
self.write_u16(tls_repr.version.into())?;
|
||||
self.write_u16(tls_repr.length)?;
|
||||
if let Some(app_data) = tls_repr.payload {
|
||||
self.write(app_data)?;
|
||||
self.write(app_data.as_slice())?;
|
||||
} else if let Some(handshake_repr) = tls_repr.handshake {
|
||||
// Queue handshake_repr into buffer
|
||||
self.enqueue_handshake_repr(handshake_repr)?;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -16,5 +16,6 @@ pub enum Error {
|
|||
PropagatedError(smoltcp::Error),
|
||||
ParsingError,
|
||||
EncryptionError,
|
||||
DecryptionError,
|
||||
CapacityError,
|
||||
}
|
43
src/main.rs
43
src/main.rs
|
@ -22,6 +22,9 @@ use hkdf::Hkdf;
|
|||
use smoltcp_tls::key::*;
|
||||
use smoltcp_tls::buffer::TlsBuffer;
|
||||
|
||||
mod encrypted;
|
||||
use encrypted::ENCRYPTED_DATA;
|
||||
|
||||
struct CountingRng(u64);
|
||||
|
||||
impl RngCore for CountingRng {
|
||||
|
@ -120,7 +123,7 @@ fn main() {
|
|||
handshake_secret_hkdf.expand(info, &mut okm).unwrap();
|
||||
okm
|
||||
};
|
||||
let client_handshake_write_key = {
|
||||
let server_handshake_write_key = {
|
||||
let hkdf_label = HkdfLabel {
|
||||
length: 16,
|
||||
label_length: 9,
|
||||
|
@ -135,15 +138,49 @@ fn main() {
|
|||
|
||||
// Define output key material (OKM), dynamically sized by hash
|
||||
let mut okm: GenericArray<u8, U16> = GenericArray::default();
|
||||
Hkdf::<Sha256>::from_prk(&client_handshake_traffic_secret)
|
||||
Hkdf::<Sha256>::from_prk(&server_handshake_traffic_secret)
|
||||
.unwrap()
|
||||
.expand(info, &mut okm);
|
||||
okm
|
||||
};
|
||||
let server_handshake_write_iv = {
|
||||
let hkdf_label = HkdfLabel {
|
||||
length: 12,
|
||||
label_length: 8,
|
||||
label: b"tls13 iv",
|
||||
context_length: 0,
|
||||
context: b"",
|
||||
};
|
||||
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, U12> = GenericArray::default();
|
||||
Hkdf::<Sha256>::from_prk(&server_handshake_traffic_secret)
|
||||
.unwrap()
|
||||
.expand(info, &mut okm);
|
||||
okm
|
||||
};
|
||||
let cipher: Aes128Gcm = Aes128Gcm::new(&server_handshake_write_key);
|
||||
let decrypted_data = {
|
||||
let mut vec: Vec<u8, U2048> = Vec::from_slice(&ENCRYPTED_DATA).unwrap();
|
||||
cipher.decrypt_in_place(
|
||||
&server_handshake_write_iv,
|
||||
&[
|
||||
0x17, 0x03, 0x03, 0x04, 0x75
|
||||
],
|
||||
&mut vec
|
||||
).unwrap();
|
||||
vec
|
||||
};
|
||||
|
||||
println!("{:x?}", client_handshake_traffic_secret);
|
||||
println!("{:x?}", server_handshake_traffic_secret);
|
||||
println!("{:x?}", client_handshake_write_key);
|
||||
println!("{:x?}", server_handshake_write_key);
|
||||
println!("{:x?}", server_handshake_write_iv);
|
||||
println!("{:x?}", decrypted_data);
|
||||
|
||||
}
|
||||
|
||||
|
|
29
src/parse.rs
29
src/parse.rs
|
@ -43,7 +43,9 @@ pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], TlsRepr> {
|
|||
repr.handshake = Some(handshake);
|
||||
},
|
||||
ChangeCipherSpec | ApplicationData => {
|
||||
repr.payload = Some(bytes);
|
||||
let mut vec: Vec<u8> = Vec::new();
|
||||
vec.extend_from_slice(bytes);
|
||||
repr.payload = Some(vec);
|
||||
},
|
||||
_ => todo!()
|
||||
}
|
||||
|
@ -124,6 +126,30 @@ fn parse_server_hello(bytes: &[u8]) -> IResult<&[u8], HandshakeData> {
|
|||
Ok((rest, HandshakeData::ServerHello(server_hello)))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_encrypted_extensions(bytes: &[u8]) -> IResult<&[u8], EncryptedExtensions> {
|
||||
let (mut rest, extension_length) = take(2_usize)(bytes)?;
|
||||
let mut extension_length: i32 = NetworkEndian::read_u16(extension_length).into();
|
||||
let mut extension_vec: Vec<Extension> = Vec::new();
|
||||
while extension_length > 0 {
|
||||
let (rem, extension) = parse_extension(rest, HandshakeType::EncryptedExtensions)?;
|
||||
rest = rem;
|
||||
extension_length -= i32::try_from(extension.get_length()).unwrap();
|
||||
|
||||
// Todo:: Proper error
|
||||
if extension_length < 0 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
extension_vec.push(extension);
|
||||
}
|
||||
|
||||
let encrypted_extensions = EncryptedExtensions {
|
||||
length: u16::try_from(extension_length).unwrap(),
|
||||
extensions: extension_vec
|
||||
};
|
||||
Ok((rest, encrypted_extensions))
|
||||
}
|
||||
|
||||
fn parse_extension(bytes: &[u8], handshake_type: HandshakeType) -> IResult<&[u8], Extension> {
|
||||
let extension_type = take(2_usize);
|
||||
let length = take(2_usize);
|
||||
|
@ -139,6 +165,7 @@ fn parse_extension(bytes: &[u8], handshake_type: HandshakeType) -> IResult<&[u8]
|
|||
// Process extension data according to extension_type
|
||||
// TODO: Deal with HelloRetryRequest
|
||||
let (rest, extension_data) = {
|
||||
// TODO: Handle all mandatory extension types
|
||||
use ExtensionType::*;
|
||||
match extension_type {
|
||||
SupportedVersions => {
|
||||
|
|
|
@ -27,6 +27,7 @@ pub(crate) struct Session {
|
|||
changed_cipher_spec: bool,
|
||||
// Handshake secret, Master secret
|
||||
// Early secret is computed right before HS
|
||||
// TLS standard: Secrets should not be stored unnecessarily
|
||||
latest_secret: Option<Vec<u8, U64>>,
|
||||
// Hash functions needed
|
||||
hash: Hash,
|
||||
|
@ -412,6 +413,10 @@ impl Session {
|
|||
self.state = TlsState::WAIT_EE;
|
||||
}
|
||||
|
||||
pub(crate) fn client_update_for_ee(&mut self) {
|
||||
self.state = TlsState::WAIT_CERT_CR;
|
||||
}
|
||||
|
||||
pub(crate) fn verify_session_id_echo(&self, session_id_echo: &[u8]) -> bool {
|
||||
if let Some(session_id_inner) = self.session_id {
|
||||
session_id_inner == session_id_echo
|
||||
|
@ -431,6 +436,50 @@ impl Session {
|
|||
pub(crate) fn receive_change_cipher_spec(&mut self) {
|
||||
self.changed_cipher_spec = true;
|
||||
}
|
||||
|
||||
pub(crate) fn encrypt_in_place(
|
||||
&self,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer
|
||||
) -> Result<(), Error> {
|
||||
let (nonce, cipher): (&Vec<u8, U12>, &Cipher) = match self.role {
|
||||
TlsRole::Client => {(
|
||||
self.client_nonce.as_ref().unwrap(),
|
||||
self.client_cipher.as_ref().unwrap()
|
||||
)},
|
||||
TlsRole::Server => {(
|
||||
self.server_nonce.as_ref().unwrap(),
|
||||
self.server_cipher.as_ref().unwrap()
|
||||
)},
|
||||
};
|
||||
cipher.encrypt_in_place(
|
||||
&GenericArray::from_slice(nonce),
|
||||
associated_data,
|
||||
buffer
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn decrypt_in_place(
|
||||
&self,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer
|
||||
) -> Result<(), Error> {
|
||||
let (nonce, cipher): (&Vec<u8, U12>, &Cipher) = match self.role {
|
||||
TlsRole::Client => {(
|
||||
self.client_nonce.as_ref().unwrap(),
|
||||
self.client_cipher.as_ref().unwrap()
|
||||
)},
|
||||
TlsRole::Server => {(
|
||||
self.server_nonce.as_ref().unwrap(),
|
||||
self.server_cipher.as_ref().unwrap()
|
||||
)},
|
||||
};
|
||||
cipher.decrypt_in_place(
|
||||
&GenericArray::from_slice(nonce),
|
||||
associated_data,
|
||||
buffer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
|
@ -525,7 +574,7 @@ pub(crate) enum Cipher {
|
|||
|
||||
impl Cipher {
|
||||
pub(crate) fn encrypt_in_place(
|
||||
&mut self,
|
||||
&self,
|
||||
nonce: &GenericArray<u8, U12>,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer
|
||||
|
@ -545,4 +594,26 @@ impl Cipher {
|
|||
}
|
||||
}.map_err(|_| Error::EncryptionError)
|
||||
}
|
||||
|
||||
pub(crate) fn decrypt_in_place(
|
||||
&self,
|
||||
nonce: &GenericArray<u8, U12>,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer
|
||||
) -> Result<(), Error> {
|
||||
match self {
|
||||
Cipher::Aes128Gcm { aes128gcm } => {
|
||||
aes128gcm.decrypt_in_place(nonce, associated_data, buffer)
|
||||
},
|
||||
Cipher::Aes256Gcm { aes256gcm } => {
|
||||
aes256gcm.decrypt_in_place(nonce, associated_data, buffer)
|
||||
},
|
||||
Cipher::Chacha20poly1305 { chacha20poly1305 } => {
|
||||
chacha20poly1305.decrypt_in_place(nonce, associated_data, buffer)
|
||||
},
|
||||
Cipher::Ccm { ccm } => {
|
||||
ccm.decrypt_in_place(nonce, associated_data, buffer)
|
||||
}
|
||||
}.map_err(|_| Error::DecryptionError)
|
||||
}
|
||||
}
|
40
src/tls.rs
40
src/tls.rs
|
@ -34,7 +34,7 @@ use alloc::vec::{ self, Vec };
|
|||
|
||||
use crate::Error as TlsError;
|
||||
use crate::tls_packet::*;
|
||||
use crate::parse::parse_tls_repr;
|
||||
use crate::parse::{ parse_tls_repr, parse_encrypted_extensions };
|
||||
use crate::buffer::TlsBuffer;
|
||||
use crate::session::{Session, TlsRole};
|
||||
|
||||
|
@ -179,7 +179,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
}
|
||||
|
||||
// Process TLS ingress during handshake
|
||||
fn process(&self, repr: TlsRepr) -> Result<()> {
|
||||
fn process(&self, 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
|
||||
|
@ -316,8 +316,42 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
|
||||
// Expect encrypted extensions after receiving SH
|
||||
TlsState::WAIT_EE => {
|
||||
// Check that the packet is classified as application data
|
||||
if !repr.is_application_data() {
|
||||
// Abort communication, this affect IV calculation
|
||||
todo!()
|
||||
}
|
||||
|
||||
// ExcepytedExtensions are disguised as ApplicationData
|
||||
// Pull out the `payload` from TlsRepr, decrypt as EE
|
||||
let mut payload = repr.payload.take().unwrap();
|
||||
let mut array: [u8; 5] = [0; 5];
|
||||
let mut buffer = TlsBuffer::new(&mut array);
|
||||
buffer.write_u8(repr.content_type.into())?;
|
||||
buffer.write_u16(repr.version.into())?;
|
||||
buffer.write_u16(repr.length)?;
|
||||
let associated_data: &[u8] = buffer.into();
|
||||
self.session.borrow().encrypt_in_place(
|
||||
associated_data,
|
||||
&mut payload
|
||||
);
|
||||
|
||||
// TODO: Parse payload of EE
|
||||
let (_, encrypted_extensions) =
|
||||
parse_encrypted_extensions(&payload)
|
||||
.map_err(|_| Error::Unrecognized)?;
|
||||
|
||||
// TODO: Process payload
|
||||
// Practically, nothing will be done about cookies/server name
|
||||
// Extension processing is therefore skipped
|
||||
|
||||
self.session.borrow_mut().client_update_for_ee();
|
||||
},
|
||||
|
||||
// In this stage, wait for a certificate from server
|
||||
// Parse the certificate and check its content
|
||||
TlsState::WAIT_CERT_CR => {
|
||||
|
||||
},
|
||||
|
||||
_ => {},
|
||||
|
@ -370,10 +404,8 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
if !tcp_socket.can_recv() {
|
||||
return Ok((Vec::new()));
|
||||
}
|
||||
|
||||
let array_size = tcp_socket.recv_slice(byte_array)?;
|
||||
let mut vec: Vec<TlsRepr> = Vec::new();
|
||||
|
||||
let mut bytes: &[u8] = &byte_array[..array_size];
|
||||
loop {
|
||||
match parse_tls_repr(bytes) {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub(crate) struct TlsRepr<'a> {
|
|||
pub(crate) content_type: TlsContentType,
|
||||
pub(crate) version: TlsVersion,
|
||||
pub(crate) length: u16,
|
||||
pub(crate) payload: Option<&'a[u8]>,
|
||||
pub(crate) payload: Option<Vec<u8>>,
|
||||
pub(crate) handshake: Option<HandshakeRepr<'a>>
|
||||
}
|
||||
|
||||
|
@ -96,14 +96,21 @@ impl<'a> TlsRepr<'a> {
|
|||
self.handshake.is_none() &&
|
||||
self.payload.is_some() &&
|
||||
{
|
||||
if let Some(data) = self.payload {
|
||||
[0x01] == data
|
||||
if let Some(data) = &self.payload {
|
||||
data[0] == 0x01 &&
|
||||
data.len() == 1
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_application_data(&self) -> bool {
|
||||
self.content_type == TlsContentType::ApplicationData &&
|
||||
self.handshake.is_none() &&
|
||||
self.payload.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn decrypt_ee(&self, shared_secret: &SharedSecret) -> HandshakeRepr {
|
||||
todo!()
|
||||
}
|
||||
|
@ -405,8 +412,8 @@ pub(crate) struct ServerHello<'a> {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct EncryptedExtensions {
|
||||
length: u16,
|
||||
extensions: Vec<Extension>,
|
||||
pub(crate) length: u16,
|
||||
pub(crate) extensions: Vec<Extension>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||
|
|
Loading…
Reference in New Issue