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,
|
self,
|
||||||
ser_de::{proto_enum, proto_struct, Value},
|
ser_de::{proto_enum, proto_struct, Value},
|
||||||
};
|
};
|
||||||
|
use hkdf::{hmac::Hmac, HmacImpl};
|
||||||
use ring::aead::{self, Nonce};
|
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 {
|
pub struct TlsCiphertext {
|
||||||
/// The encrypted [`TlsInnerPlaintext`] record.
|
/// The encrypted [`TlsInnerPlaintext`] record.
|
||||||
|
|
@ -19,37 +27,185 @@ impl From<Vec<u8>> for TlsCiphertext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_keys(shared_secret: SharedSecret) {
|
trait HkdfHasher: OutputSizeUser {
|
||||||
let hkdf_expand_label = |secret: &[u8], label: &[u8], context: &[u8], length| {
|
const ZEROED: &'static [u8];
|
||||||
proto_struct! {
|
fn expand(ikm: &[u8], label: &[u8], okm: &mut [u8]) -> Result<(), hkdf::InvalidLength>;
|
||||||
#[derive(Debug)]
|
fn extract(salt: &[u8], ikm: &[u8]) -> Vec<u8>;
|
||||||
pub struct HkdfLabel {
|
}
|
||||||
pub length: u16,
|
macro_rules! impl_hkdf_hasher {
|
||||||
pub label: proto::ser_de::List<u8,u8>,
|
() => {
|
||||||
pub context: proto::ser_de::List<u8,u8>,
|
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();
|
fn extract(salt: &[u8], ikm: &[u8]) -> Vec<u8> {
|
||||||
HkdfLabel {
|
hkdf::Hkdf::<Self>::extract(Some(&[]), &[])
|
||||||
length,
|
.0
|
||||||
label: {
|
.as_slice()
|
||||||
let mut v = b"tls13 ".to_vec();
|
.to_vec()
|
||||||
v.extend_from_slice(label);
|
|
||||||
v.into()
|
|
||||||
},
|
|
||||||
context: context.to_vec().into(),
|
|
||||||
}
|
}
|
||||||
.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!
|
// Key Schedule
|
||||||
let mut okm = [0u8; 42];
|
// https://datatracker.ietf.org/doc/html/rfc8446#section-7.1
|
||||||
hkdf::Hkdf::<sha2::Sha256>::new(None, secret).expand(&hkdf_label, &mut okm)
|
|
||||||
|
// 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: ()| {
|
let early_secret =
|
||||||
hkdf_expand_label(secret, label, &[], 16) // todo: fix length
|
(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 {
|
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 {
|
fn proto_algo_to_ring(algo: proto::CipherSuite) -> &'static aead::Algorithm {
|
||||||
match algo {
|
match algo {
|
||||||
|
|
|
||||||
13
src/lib.rs
13
src/lib.rs
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
mod crypt;
|
mod crypt;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
||||||
|
|
@ -7,7 +9,6 @@ use std::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crypt::{SeqId, SeqIdGen};
|
|
||||||
use proto::CipherSuite;
|
use proto::CipherSuite;
|
||||||
|
|
||||||
use crate::proto::TLSPlaintext;
|
use crate::proto::TLSPlaintext;
|
||||||
|
|
@ -61,7 +62,6 @@ mod stream_state {
|
||||||
|
|
||||||
pub fn write_record(&mut self, plaintext: TLSPlaintext) -> Result<SeqId> {
|
pub fn write_record(&mut self, plaintext: TLSPlaintext) -> Result<SeqId> {
|
||||||
plaintext.write(&mut self.stream)?;
|
plaintext.write(&mut self.stream)?;
|
||||||
self.stream.flush()?;
|
|
||||||
Ok(self.write_seq_id.next())
|
Ok(self.write_seq_id.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +305,7 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
||||||
dh_shared_secret.as_bytes()
|
dh_shared_secret.as_bytes()
|
||||||
);
|
);
|
||||||
|
|
||||||
crypt::compute_keys(dh_shared_secret);
|
crypt::compute_keys(dh_shared_secret, cipher_suite);
|
||||||
|
|
||||||
ConnectState::WaitEncryptedExtensions
|
ConnectState::WaitEncryptedExtensions
|
||||||
}
|
}
|
||||||
|
|
@ -396,3 +396,10 @@ impl<R: Read> io::Read for LoggingWriter<R> {
|
||||||
len
|
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.
|
// An example program that makes a shitty HTTP/1.1 request.
|
||||||
fn main() {
|
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();
|
tls::ClientConnection::establish(conn, "vps1.nilstrieb.dev").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ macro_rules! proto_enum {
|
||||||
impl crate::proto::ser_de::Value for $name {
|
impl crate::proto::ser_de::Value for $name {
|
||||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
w.flush()?;
|
w.flush()?;
|
||||||
eprintln!("{}", stringify!($name));
|
//eprintln!("{}", stringify!($name));
|
||||||
mod discr_consts {
|
mod discr_consts {
|
||||||
$(
|
$(
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
|
|
@ -116,7 +116,7 @@ macro_rules! proto_enum {
|
||||||
|
|
||||||
let write_len = |_w: &mut W, _len: usize| -> io::Result<()> {
|
let write_len = |_w: &mut W, _len: usize| -> io::Result<()> {
|
||||||
_w.flush()?;
|
_w.flush()?;
|
||||||
eprintln!("length");
|
//eprintln!("length");
|
||||||
$(
|
$(
|
||||||
<$len_ty>::try_from(_len).unwrap().write(_w)?;
|
<$len_ty>::try_from(_len).unwrap().write(_w)?;
|
||||||
)?
|
)?
|
||||||
|
|
@ -137,7 +137,7 @@ macro_rules! proto_enum {
|
||||||
|
|
||||||
$($(
|
$($(
|
||||||
w.flush()?;
|
w.flush()?;
|
||||||
eprintln!("{}", stringify!($field_name));
|
//eprintln!("{}", stringify!($field_name));
|
||||||
crate::proto::ser_de::Value::write($field_name, w)?;
|
crate::proto::ser_de::Value::write($field_name, w)?;
|
||||||
)*)?
|
)*)?
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue