cipher: init
This commit is contained in:
parent
f138b61ecd
commit
51410ac716
13
Cargo.toml
13
Cargo.toml
|
@ -6,7 +6,7 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
aes-gcm = "0.7.0"
|
||||
chacha20poly1305 = "0.6.0"
|
||||
ccm = "0.2.0"
|
||||
sha2 = { version = "0.9.1", default-features = false }
|
||||
byteorder = { version = "1.3.4", default-features = false }
|
||||
num_enum = { version = "0.5.1", default-features = false }
|
||||
|
@ -23,21 +23,22 @@ version = "0.5.1"
|
|||
default-features = false
|
||||
features = []
|
||||
|
||||
[dependencies.chacha20poly1305]
|
||||
version = "0.6.0"
|
||||
default-features = false
|
||||
features = [ "alloc", "chacha20" ]
|
||||
|
||||
[dependencies.p256]
|
||||
version = "0.5.0"
|
||||
default-features = false
|
||||
features = [ "ecdh", "ecdsa", "arithmetic" ]
|
||||
|
||||
# Fetch from master, for "no_std" + "alloc" combination
|
||||
[dependencies.rsa]
|
||||
git = "https://github.com/RustCrypto/RSA.git"
|
||||
default-features = false
|
||||
features = [ "alloc" ]
|
||||
|
||||
[dependencies.heapless]
|
||||
version = "0.5.6"
|
||||
default-features = false
|
||||
features = []
|
||||
|
||||
[dependencies.nom]
|
||||
version = "5.1.2"
|
||||
default-features = false
|
||||
|
|
|
@ -11,5 +11,7 @@ pub mod parse;
|
|||
// Details: Encapsulate smoltcp & nom errors
|
||||
pub enum Error {
|
||||
PropagatedError(smoltcp::Error),
|
||||
ParsingError()
|
||||
ParsingError,
|
||||
EncryptionError,
|
||||
CapacityError,
|
||||
}
|
126
src/tls.rs
126
src/tls.rs
|
@ -23,9 +23,15 @@ use core::cell::RefCell;
|
|||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
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 crate::Error as TlsError;
|
||||
use crate::tls_packet::*;
|
||||
use crate::parse::parse_tls_repr;
|
||||
|
||||
|
@ -42,6 +48,7 @@ enum TlsState {
|
|||
CONNECTED,
|
||||
}
|
||||
|
||||
// TODO: Group up all session_specific parameters into a separate structure
|
||||
pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
|
||||
{
|
||||
state: RefCell<TlsState>,
|
||||
|
@ -49,10 +56,76 @@ pub struct TlsSocket<R: 'static + RngCore + CryptoRng>
|
|||
rng: R,
|
||||
secret: RefCell<Option<EphemeralSecret>>, // Used enum Option to allow later init
|
||||
session_id: RefCell<Option<[u8; 32]>>, // init session specific field later
|
||||
cipher_suite: RefCell<Option<CipherSuite>>,
|
||||
ecdhe_shared: RefCell<Option<SharedSecret>>,
|
||||
received_change_cipher_spec: RefCell<Option<bool>>,
|
||||
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> {
|
||||
pub fn new<'a, 'b, 'c>(
|
||||
sockets: &mut SocketSet<'a, 'b, 'c>,
|
||||
|
@ -71,8 +144,8 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
rng,
|
||||
secret: RefCell::new(None),
|
||||
session_id: RefCell::new(None),
|
||||
cipher_suite: RefCell::new(None),
|
||||
ecdhe_shared: RefCell::new(None),
|
||||
received_change_cipher_spec: 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
|
||||
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);
|
||||
|
@ -163,6 +237,18 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
// 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
|
||||
//
|
||||
// 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 {
|
||||
// During WAIT_SH for a TLS client, client should wait for ServerHello
|
||||
TlsState::WAIT_SH => {
|
||||
|
@ -200,7 +286,7 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
todo!()
|
||||
}
|
||||
// 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 {
|
||||
// Abort communciation
|
||||
todo!()
|
||||
|
@ -236,18 +322,27 @@ impl<R: RngCore + CryptoRng> TlsSocket<R> {
|
|||
todo!()
|
||||
}
|
||||
// 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
|
||||
// Slice the first byte out of the bytes
|
||||
let server_public = EncodedPoint::from_untagged_bytes(
|
||||
GenericArray::from_slice(&server_share.key_exchange[1..])
|
||||
);
|
||||
// TODO: Handle improper shared key
|
||||
self.ecdhe_shared.replace(Some(
|
||||
self.secret.borrow().as_ref().unwrap()
|
||||
.diffie_hellman(&server_public)
|
||||
.expect("Unsupported 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_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
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use num_enum::TryFromPrimitive;
|
|||
use rand_core::RngCore;
|
||||
use rand_core::CryptoRng;
|
||||
|
||||
use p256::{EncodedPoint, AffinePoint, ecdh::EphemeralSecret};
|
||||
use p256::{EncodedPoint, AffinePoint, ecdh::{EphemeralSecret, SharedSecret}};
|
||||
|
||||
use core::convert::TryFrom;
|
||||
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)]
|
||||
|
@ -164,6 +181,7 @@ pub(crate) enum HandshakeData<'a> {
|
|||
Uninitialized,
|
||||
ClientHello(ClientHello<'a>),
|
||||
ServerHello(ServerHello<'a>),
|
||||
ExcryptedExtensions(EncryptedExtensions),
|
||||
}
|
||||
|
||||
impl<'a> HandshakeData<'a> {
|
||||
|
@ -383,6 +401,12 @@ pub(crate) struct ServerHello<'a> {
|
|||
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)]
|
||||
#[repr(u16)]
|
||||
pub(crate) enum ExtensionType {
|
||||
|
|
Loading…
Reference in New Issue