parse: init ee

This commit is contained in:
occheung 2020-10-18 20:02:40 +08:00
parent a2ad8b3334
commit 38bf1d3c3b
9 changed files with 203 additions and 18 deletions

View File

@ -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"

View File

@ -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)?;

2
src/encrypted.rs Normal file

File diff suppressed because one or more lines are too long

View File

@ -16,5 +16,6 @@ pub enum Error {
PropagatedError(smoltcp::Error),
ParsingError,
EncryptionError,
DecryptionError,
CapacityError,
}

View File

@ -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);
}

View File

@ -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 => {

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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)]