cipher: init

This commit is contained in:
occheung 2020-10-15 17:29:42 +08:00
parent f138b61ecd
commit 51410ac716
4 changed files with 149 additions and 20 deletions

View File

@ -6,7 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
aes-gcm = "0.7.0" aes-gcm = "0.7.0"
chacha20poly1305 = "0.6.0" ccm = "0.2.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 }
@ -23,21 +23,22 @@ version = "0.5.1"
default-features = false default-features = false
features = [] features = []
[dependencies.chacha20poly1305]
version = "0.6.0"
default-features = false
features = [ "alloc", "chacha20" ]
[dependencies.p256] [dependencies.p256]
version = "0.5.0" version = "0.5.0"
default-features = false default-features = false
features = [ "ecdh", "ecdsa", "arithmetic" ] features = [ "ecdh", "ecdsa", "arithmetic" ]
# Fetch from master, for "no_std" + "alloc" combination
[dependencies.rsa] [dependencies.rsa]
git = "https://github.com/RustCrypto/RSA.git" git = "https://github.com/RustCrypto/RSA.git"
default-features = false default-features = false
features = [ "alloc" ] features = [ "alloc" ]
[dependencies.heapless]
version = "0.5.6"
default-features = false
features = []
[dependencies.nom] [dependencies.nom]
version = "5.1.2" version = "5.1.2"
default-features = false default-features = false

View File

@ -11,5 +11,7 @@ pub mod parse;
// Details: Encapsulate smoltcp & nom errors // Details: Encapsulate smoltcp & nom errors
pub enum Error { pub enum Error {
PropagatedError(smoltcp::Error), PropagatedError(smoltcp::Error),
ParsingError() ParsingError,
EncryptionError,
CapacityError,
} }

View File

@ -23,9 +23,15 @@ use core::cell::RefCell;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret, ecdh::SharedSecret}; 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 alloc::vec::{ self, Vec }; use alloc::vec::{ self, Vec };
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;
@ -42,6 +48,7 @@ enum TlsState {
CONNECTED, CONNECTED,
} }
// TODO: Group up all session_specific parameters into a separate structure
pub struct TlsSocket<R: 'static + RngCore + CryptoRng> pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
{ {
state: RefCell<TlsState>, state: RefCell<TlsState>,
@ -49,10 +56,76 @@ pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
rng: R, rng: R,
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
cipher_suite: RefCell<Option<CipherSuite>>, received_change_cipher_spec: RefCell<Option<bool>>,
ecdhe_shared: RefCell<Option<SharedSecret>>, cipher: RefCell<Option<Cipher>>,
} }
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
);
impl<R: RngCore + CryptoRng> TlsSocket<R> { impl<R: RngCore + CryptoRng> TlsSocket<R> {
pub fn new<'a, 'b, 'c>( pub fn new<'a, 'b, 'c>(
sockets: &mut SocketSet<'a, 'b, 'c>, sockets: &mut SocketSet<'a, 'b, 'c>,
@ -71,8 +144,8 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
rng, rng,
secret: RefCell::new(None), secret: RefCell::new(None),
session_id: RefCell::new(None), session_id: RefCell::new(None),
cipher_suite: RefCell::new(None), received_change_cipher_spec: RefCell::new(None),
ecdhe_shared: RefCell::new(None), cipher: RefCell::new(None),
} }
} }
@ -131,6 +204,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
// 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));
self.session_id.replace(Some(session_id)); self.session_id.replace(Some(session_id));
self.received_change_cipher_spec.replace(Some(false));
// Update the TLS state // Update the TLS state
self.state.replace(TlsState::WAIT_SH); self.state.replace(TlsState::WAIT_SH);
@ -163,6 +237,18 @@ 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:
// 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`
if repr.is_change_cipher_spec() {
self.received_change_cipher_spec.replace(Some(true));
return Ok(())
}
match state { match state {
// During WAIT_SH for a TLS client, client should wait for ServerHello // During WAIT_SH for a TLS client, client should wait for ServerHello
TlsState::WAIT_SH => { TlsState::WAIT_SH => {
@ -200,7 +286,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
todo!() todo!()
} }
// Store the cipher suite // Store the cipher suite
self.cipher_suite.replace(Some(server_hello.cipher_suite)); let selected_cipher = server_hello.cipher_suite;
if server_hello.compression_method != 0 { if server_hello.compression_method != 0 {
// Abort communciation // Abort communciation
todo!() todo!()
@ -236,18 +322,27 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
todo!() todo!()
} }
// Store key // Store key
// It is surely from secp256r1 // It is surely from secp256r1, no other groups are permitted
// Convert untagged bytes into encoded point on p256 eliptic curve // Convert untagged bytes into encoded point on p256 eliptic curve
// Slice the first byte out of the bytes // Slice the first byte out of the bytes
let server_public = EncodedPoint::from_untagged_bytes( let server_public = EncodedPoint::from_untagged_bytes(
GenericArray::from_slice(&server_share.key_exchange[1..]) GenericArray::from_slice(&server_share.key_exchange[1..])
); );
// TODO: Handle improper shared key // TODO: Handle improper shared key
self.ecdhe_shared.replace(Some( // Right now is causes a panic, only socket abort is needed
self.secret.borrow().as_ref().unwrap() let secret = self.secret.replace(None);
let shared = secret.unwrap()
.diffie_hellman(&server_public) .diffie_hellman(&server_public)
.expect("Unsupported key") .expect("Unsupported key");
)); let cipher = match selected_cipher {
CipherSuite::TLS_AES_256_GCM_SHA384 => {
Cipher::TLS_AES_256_GCM_SHA384(
Aes256Gcm::new(shared.as_bytes())
)
}
_ => todo!()
};
self.cipher.replace(Some(cipher));
} }
} }
} }
@ -259,7 +354,14 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
} }
} }
} },
// Expect encrypted extensions after receiving SH
TlsState::WAIT_EE => {
// ExcepytedExtensions are disguised as ApplicationData
// Pull out the `payload` from TlsRepr, decrypt as EE
},
_ => {}, _ => {},
} }

View File

@ -5,7 +5,7 @@ use num_enum::TryFromPrimitive;
use rand_core::RngCore; use rand_core::RngCore;
use rand_core::CryptoRng; use rand_core::CryptoRng;
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret}; use p256::{EncodedPoint, AffinePoint, ecdh::{EphemeralSecret, SharedSecret}};
use core::convert::TryFrom; use core::convert::TryFrom;
use core::convert::TryInto; use core::convert::TryInto;
@ -90,6 +90,23 @@ impl<'a> TlsRepr<'a> {
} }
} }
} }
pub(crate) fn is_change_cipher_spec(&self) -> bool {
self.content_type == TlsContentType::ChangeCipherSpec &&
self.handshake.is_none() &&
self.payload.is_some() &&
{
if let Some(data) = self.payload {
[0x01] == data
} else {
false
}
}
}
pub(crate) fn decrypt_ee(&self, shared_secret: &SharedSecret) -> HandshakeRepr {
todo!()
}
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
@ -164,6 +181,7 @@ pub(crate) enum HandshakeData<'a> {
Uninitialized, Uninitialized,
ClientHello(ClientHello<'a>), ClientHello(ClientHello<'a>),
ServerHello(ServerHello<'a>), ServerHello(ServerHello<'a>),
ExcryptedExtensions(EncryptedExtensions),
} }
impl<'a> HandshakeData<'a> { impl<'a> HandshakeData<'a> {
@ -383,6 +401,12 @@ pub(crate) struct ServerHello<'a> {
pub(crate) extensions: Vec<Extension>, pub(crate) extensions: Vec<Extension>,
} }
#[derive(Debug, Clone)]
pub(crate) struct EncryptedExtensions {
length: u16,
extensions: Vec<Extension>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
#[repr(u16)] #[repr(u16)]
pub(crate) enum ExtensionType { pub(crate) enum ExtensionType {