mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-14 16:45:02 +01:00
secrets
This commit is contained in:
parent
eea717987d
commit
c767352e43
4 changed files with 200 additions and 36 deletions
212
src/crypt.rs
212
src/crypt.rs
|
|
@ -4,8 +4,16 @@ use crate::proto::{
|
|||
self,
|
||||
ser_de::{proto_enum, proto_struct, Value},
|
||||
};
|
||||
|
||||
use hkdf::{hmac::Hmac, HmacImpl};
|
||||
use ring::aead::{self, Nonce};
|
||||
pub use seq::{SeqId, SeqIdGen};
|
||||
use sha2::digest::{
|
||||
core_api::{self, CoreProxy},
|
||||
generic_array::ArrayLength,
|
||||
typenum::Unsigned,
|
||||
OutputSizeUser,
|
||||
};
|
||||
use x25519_dalek::SharedSecret;
|
||||
|
||||
pub struct TlsCiphertext {
|
||||
/// The encrypted [`TlsInnerPlaintext`] record.
|
||||
|
|
@ -19,37 +27,185 @@ impl From<Vec<u8>> for TlsCiphertext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn compute_keys(shared_secret: SharedSecret) {
|
||||
let hkdf_expand_label = |secret: &[u8], label: &[u8], context: &[u8], length| {
|
||||
proto_struct! {
|
||||
#[derive(Debug)]
|
||||
pub struct HkdfLabel {
|
||||
pub length: u16,
|
||||
pub label: proto::ser_de::List<u8,u8>,
|
||||
pub context: proto::ser_de::List<u8,u8>,
|
||||
}
|
||||
trait HkdfHasher: OutputSizeUser {
|
||||
const ZEROED: &'static [u8];
|
||||
fn expand(ikm: &[u8], label: &[u8], okm: &mut [u8]) -> Result<(), hkdf::InvalidLength>;
|
||||
fn extract(salt: &[u8], ikm: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
macro_rules! impl_hkdf_hasher {
|
||||
() => {
|
||||
const ZEROED: &'static [u8] =
|
||||
&[0; <<Self as OutputSizeUser>::OutputSize as Unsigned>::USIZE];
|
||||
fn expand(ikm: &[u8], label: &[u8], okm: &mut [u8]) -> Result<(), hkdf::InvalidLength> {
|
||||
hkdf::Hkdf::<Self>::new(None, ikm).expand(&label, okm)
|
||||
}
|
||||
let mut hkdf_label = Vec::new();
|
||||
HkdfLabel {
|
||||
length,
|
||||
label: {
|
||||
let mut v = b"tls13 ".to_vec();
|
||||
v.extend_from_slice(label);
|
||||
v.into()
|
||||
},
|
||||
context: context.to_vec().into(),
|
||||
fn extract(salt: &[u8], ikm: &[u8]) -> Vec<u8> {
|
||||
hkdf::Hkdf::<Self>::extract(Some(&[]), &[])
|
||||
.0
|
||||
.as_slice()
|
||||
.to_vec()
|
||||
}
|
||||
.write(&mut hkdf_label)
|
||||
.unwrap();
|
||||
};
|
||||
}
|
||||
impl HkdfHasher for sha2::Sha256 {
|
||||
impl_hkdf_hasher!();
|
||||
}
|
||||
impl HkdfHasher for sha2::Sha384 {
|
||||
impl_hkdf_hasher!();
|
||||
}
|
||||
|
||||
// TODO: use correct algo, the cipher suite hash algorithm!
|
||||
let mut okm = [0u8; 42];
|
||||
hkdf::Hkdf::<sha2::Sha256>::new(None, secret).expand(&hkdf_label, &mut okm)
|
||||
// Key Schedule
|
||||
// https://datatracker.ietf.org/doc/html/rfc8446#section-7.1
|
||||
|
||||
// The Hash function used by Transcript-Hash and HKDF is the cipher suite hash algorithm
|
||||
fn hkdf_expand_label<H: HkdfHasher>(secret: &[u8], label: &[u8], context: &[u8]) -> Vec<u8> {
|
||||
proto_struct! {
|
||||
#[derive(Debug)]
|
||||
pub struct HkdfLabel {
|
||||
pub length: u16,
|
||||
pub label: proto::ser_de::List<u8, u8>,
|
||||
pub context: proto::ser_de::List<u8, u8>,
|
||||
}
|
||||
}
|
||||
let mut hkdf_label = Vec::new();
|
||||
HkdfLabel {
|
||||
// Hash.length is its output length in bytes
|
||||
length: H::output_size().try_into().unwrap(),
|
||||
label: {
|
||||
let mut v = b"tls13 ".to_vec();
|
||||
v.extend_from_slice(label);
|
||||
v.into()
|
||||
},
|
||||
context: context.to_vec().into(),
|
||||
}
|
||||
.write(&mut hkdf_label)
|
||||
.unwrap();
|
||||
|
||||
let mut okm = [0u8; 128];
|
||||
H::expand(secret, &hkdf_label, &mut okm).unwrap();
|
||||
okm[..H::output_size()].to_vec()
|
||||
}
|
||||
|
||||
/// Messages is the concatenation of the indicated handshake messages,
|
||||
/// including the handshake message type and length fields, but not
|
||||
/// including record layer headers.
|
||||
fn derive_secret<H: HkdfHasher>(secret: &[u8], label: &[u8], messages: ()) -> Vec<u8> {
|
||||
hkdf_expand_label::<H>(secret, label, &[])
|
||||
}
|
||||
|
||||
pub struct CryptoProvider {
|
||||
zeroed_of_hash_size: &'static [u8],
|
||||
hkdf_extract: fn(salt: &[u8], ikm: &[u8]) -> Vec<u8>,
|
||||
derive_secret: fn(secret: &[u8], label: &[u8], messages: ()) -> Vec<u8>,
|
||||
}
|
||||
impl CryptoProvider {
|
||||
fn new<H: HkdfHasher>() -> Self {
|
||||
CryptoProvider {
|
||||
zeroed_of_hash_size: H::ZEROED,
|
||||
hkdf_extract: H::extract,
|
||||
derive_secret: derive_secret::<H>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
```text
|
||||
0
|
||||
|
|
||||
v
|
||||
PSK -> HKDF-Extract = Early Secret
|
||||
|
|
||||
+-----> Derive-Secret(., "ext binder" | "res binder", "")
|
||||
| = binder_key
|
||||
|
|
||||
+-----> Derive-Secret(., "c e traffic", ClientHello)
|
||||
| = client_early_traffic_secret
|
||||
|
|
||||
+-----> Derive-Secret(., "e exp master", ClientHello)
|
||||
| = early_exporter_master_secret
|
||||
v
|
||||
Derive-Secret(., "derived", "")
|
||||
|
|
||||
v
|
||||
(EC)DHE -> HKDF-Extract = Handshake Secret
|
||||
|
|
||||
+-----> Derive-Secret(., "c hs traffic",
|
||||
| ClientHello...ServerHello)
|
||||
| = client_handshake_traffic_secret
|
||||
|
|
||||
+-----> Derive-Secret(., "s hs traffic",
|
||||
| ClientHello...ServerHello)
|
||||
| = server_handshake_traffic_secret
|
||||
v
|
||||
Derive-Secret(., "derived", "")
|
||||
|
|
||||
v
|
||||
0 -> HKDF-Extract = Master Secret
|
||||
|
|
||||
+-----> Derive-Secret(., "c ap traffic",
|
||||
| ClientHello...server Finished)
|
||||
| = client_application_traffic_secret_0
|
||||
|
|
||||
+-----> Derive-Secret(., "s ap traffic",
|
||||
| ClientHello...server Finished)
|
||||
| = server_application_traffic_secret_0
|
||||
|
|
||||
+-----> Derive-Secret(., "exp master",
|
||||
| ClientHello...server Finished)
|
||||
| = exporter_master_secret
|
||||
|
|
||||
+-----> Derive-Secret(., "res master",
|
||||
ClientHello...client Finished)
|
||||
= resumption_master_secret
|
||||
```
|
||||
*/
|
||||
pub fn compute_keys(shared_secret: SharedSecret, algo: proto::CipherSuite) {
|
||||
let provider = match algo {
|
||||
proto::CipherSuite::TLS_AES_128_GCM_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||
proto::CipherSuite::TLS_AES_256_GCM_SHA384 => CryptoProvider::new::<sha2::Sha384>(),
|
||||
proto::CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||
proto::CipherSuite::TLS_AES_128_CCM_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||
proto::CipherSuite::TLS_AES_128_CCM_8_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||
};
|
||||
|
||||
let derive_secret = |secret: &[u8], label: &[u8], messages: ()| {
|
||||
hkdf_expand_label(secret, label, &[], 16) // todo: fix length
|
||||
};
|
||||
let early_secret =
|
||||
(provider.hkdf_extract)(&provider.zeroed_of_hash_size, &provider.zeroed_of_hash_size);
|
||||
|
||||
let handhske_secret = (provider.hkdf_extract)(
|
||||
&(provider.derive_secret)(&early_secret, b"derived", (/*empty*/)),
|
||||
shared_secret.as_bytes(),
|
||||
);
|
||||
|
||||
let client_handshake_traffic_secret = (provider.derive_secret)(
|
||||
&handhske_secret,
|
||||
b"c hs traffic",
|
||||
(/*clienthello..serverhello*/),
|
||||
);
|
||||
|
||||
let server_handshake_traffic_secret = (provider.derive_secret)(
|
||||
&handhske_secret,
|
||||
b"s hs traffic",
|
||||
(/*clienthello..serverhello*/),
|
||||
);
|
||||
|
||||
let master_secret = (provider.hkdf_extract)(
|
||||
&(provider.derive_secret)(&handhske_secret, b"derived", (/*empty*/)),
|
||||
&provider.zeroed_of_hash_size,
|
||||
);
|
||||
|
||||
let client_application_traffic_secret_0 = (provider.derive_secret)(
|
||||
&master_secret,
|
||||
b"c ap traffic",
|
||||
(/*clienthello..server finished*/),
|
||||
);
|
||||
let server_application_traffic_secret_0 = (provider.derive_secret)(
|
||||
&master_secret,
|
||||
b"s ap traffic",
|
||||
(/*clienthello..server finished*/),
|
||||
);
|
||||
|
||||
|
||||
dbg!("keys");
|
||||
}
|
||||
|
||||
pub struct AeadKey {
|
||||
|
|
@ -97,8 +253,6 @@ mod seq {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub use seq::{SeqId, SeqIdGen};
|
||||
use x25519_dalek::SharedSecret;
|
||||
|
||||
fn proto_algo_to_ring(algo: proto::CipherSuite) -> &'static aead::Algorithm {
|
||||
match algo {
|
||||
|
|
|
|||
13
src/lib.rs
13
src/lib.rs
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(unused)]
|
||||
|
||||
mod crypt;
|
||||
pub mod proto;
|
||||
|
||||
|
|
@ -7,7 +9,6 @@ use std::{
|
|||
io::{self, Read, Write},
|
||||
};
|
||||
|
||||
use crypt::{SeqId, SeqIdGen};
|
||||
use proto::CipherSuite;
|
||||
|
||||
use crate::proto::TLSPlaintext;
|
||||
|
|
@ -61,7 +62,6 @@ mod stream_state {
|
|||
|
||||
pub fn write_record(&mut self, plaintext: TLSPlaintext) -> Result<SeqId> {
|
||||
plaintext.write(&mut self.stream)?;
|
||||
self.stream.flush()?;
|
||||
Ok(self.write_seq_id.next())
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +305,7 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
|||
dh_shared_secret.as_bytes()
|
||||
);
|
||||
|
||||
crypt::compute_keys(dh_shared_secret);
|
||||
crypt::compute_keys(dh_shared_secret, cipher_suite);
|
||||
|
||||
ConnectState::WaitEncryptedExtensions
|
||||
}
|
||||
|
|
@ -396,3 +396,10 @@ impl<R: Read> io::Read for LoggingWriter<R> {
|
|||
len
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LoggingWriterExt: Sized {
|
||||
fn log(self) -> LoggingWriter<Self> {
|
||||
LoggingWriter(self)
|
||||
}
|
||||
}
|
||||
impl<W: io::Write> LoggingWriterExt for W {}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ use std::net::TcpStream;
|
|||
|
||||
// An example program that makes a shitty HTTP/1.1 request.
|
||||
fn main() {
|
||||
let conn = tls::LoggingWriter(TcpStream::connect(("vps1.nilstrieb.dev", 443)).unwrap());
|
||||
let conn = TcpStream::connect(("vps1.nilstrieb.dev", 443))
|
||||
.unwrap()
|
||||
//.log()
|
||||
;
|
||||
tls::ClientConnection::establish(conn, "vps1.nilstrieb.dev").unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ macro_rules! proto_enum {
|
|||
impl crate::proto::ser_de::Value for $name {
|
||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
w.flush()?;
|
||||
eprintln!("{}", stringify!($name));
|
||||
//eprintln!("{}", stringify!($name));
|
||||
mod discr_consts {
|
||||
$(
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
|
@ -116,7 +116,7 @@ macro_rules! proto_enum {
|
|||
|
||||
let write_len = |_w: &mut W, _len: usize| -> io::Result<()> {
|
||||
_w.flush()?;
|
||||
eprintln!("length");
|
||||
//eprintln!("length");
|
||||
$(
|
||||
<$len_ty>::try_from(_len).unwrap().write(_w)?;
|
||||
)?
|
||||
|
|
@ -137,7 +137,7 @@ macro_rules! proto_enum {
|
|||
|
||||
$($(
|
||||
w.flush()?;
|
||||
eprintln!("{}", stringify!($field_name));
|
||||
//eprintln!("{}", stringify!($field_name));
|
||||
crate::proto::ser_de::Value::write($field_name, w)?;
|
||||
)*)?
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue