mirror of
https://github.com/Noratrieb/tls.git
synced 2026-01-14 16:45:02 +01:00
fix several bugs
This commit is contained in:
parent
0f93d225e7
commit
c518c09937
6 changed files with 99 additions and 49 deletions
|
|
@ -13,11 +13,32 @@ use super::{keys, TlsInnerPlaintext};
|
||||||
pub type AeadKey = aes_gcm::Key<Aes128Gcm>;
|
pub type AeadKey = aes_gcm::Key<Aes128Gcm>;
|
||||||
pub type Nonce = aes_gcm::Nonce<<Aes128Gcm as aes_gcm::AeadCore>::NonceSize>;
|
pub type Nonce = aes_gcm::Nonce<<Aes128Gcm as aes_gcm::AeadCore>::NonceSize>;
|
||||||
|
|
||||||
fn decrypt(key: &AeadKey, ciphertext: &[u8], nonce: &Nonce, additional_data: &[u8]) -> Vec<u8> {
|
pub struct TlsNonce {
|
||||||
|
nonce: Nonce,
|
||||||
|
}
|
||||||
|
impl TlsNonce {
|
||||||
|
pub fn new(nonce: Nonce) -> Self {
|
||||||
|
Self { nonce }
|
||||||
|
}
|
||||||
|
fn get_aead_nonce(mut self, iv: &[u8]) -> Nonce {
|
||||||
|
// <https://datatracker.ietf.org/doc/html/rfc8446#section-5.3>
|
||||||
|
// 2. The padded sequence number is XORed with either the static
|
||||||
|
// client_write_iv or server_write_iv (depending on the role).
|
||||||
|
assert_eq!(self.nonce.len(), iv.len());
|
||||||
|
self.nonce
|
||||||
|
.as_mut_slice()
|
||||||
|
.iter_mut()
|
||||||
|
.zip(iv)
|
||||||
|
.for_each(|(lhs, rhs)| *lhs ^= rhs);
|
||||||
|
self.nonce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(key: &AeadKey, ciphertext: &[u8], nonce: Nonce, additional_data: &[u8]) -> Vec<u8> {
|
||||||
let cipher = Aes128Gcm::new(key);
|
let cipher = Aes128Gcm::new(key);
|
||||||
cipher
|
cipher
|
||||||
.decrypt(
|
.decrypt(
|
||||||
nonce,
|
&nonce,
|
||||||
Payload {
|
Payload {
|
||||||
msg: ciphertext,
|
msg: ciphertext,
|
||||||
aad: additional_data,
|
aad: additional_data,
|
||||||
|
|
@ -29,19 +50,27 @@ fn decrypt(key: &AeadKey, ciphertext: &[u8], nonce: &Nonce, additional_data: &[u
|
||||||
pub fn decrypt_ciphertext(
|
pub fn decrypt_ciphertext(
|
||||||
encrypted_record: &[u8],
|
encrypted_record: &[u8],
|
||||||
secret: &[u8],
|
secret: &[u8],
|
||||||
nonce: Nonce,
|
nonce: TlsNonce,
|
||||||
) -> TlsInnerPlaintext {
|
) -> TlsInnerPlaintext {
|
||||||
// <https://datatracker.ietf.org/doc/html/rfc8446#section-7.3>
|
// <https://datatracker.ietf.org/doc/html/rfc8446#section-7.3>
|
||||||
let key = keys::hkdf_expand_label::<sha2::Sha256>(secret, b"key", &[], 128/8);
|
let key = keys::hkdf_expand_label(secret, b"key", &[], 128 / 8);
|
||||||
|
|
||||||
let key = aes_gcm::Key::<Aes128Gcm>::from_slice(&key[0..(128 / 8)]);
|
let key = aes_gcm::Key::<Aes128Gcm>::from_slice(&key[0..(128 / 8)]);
|
||||||
|
|
||||||
|
let iv = keys::hkdf_expand_label(secret, b"iv", &[], 96 / 8);
|
||||||
|
|
||||||
|
// <https://datatracker.ietf.org/doc/html/rfc8446#section-5.2>
|
||||||
// TLS v1.2 0x03, 0x03
|
// TLS v1.2 0x03, 0x03
|
||||||
let mut additional_data = [proto::TLSPlaintext::APPLICATION_DATA, 0x03, 0x03, 0, 0];
|
let mut additional_data = [proto::TLSPlaintext::APPLICATION_DATA, 0x03, 0x03, 0, 0];
|
||||||
let ciphertext_len = encrypted_record.as_ref().len() as u16;
|
let ciphertext_len = encrypted_record.as_ref().len() as u16;
|
||||||
additional_data[3..].copy_from_slice(&ciphertext_len.to_be_bytes());
|
additional_data[3..].copy_from_slice(&ciphertext_len.to_be_bytes());
|
||||||
|
|
||||||
let result = decrypt(key, encrypted_record, &nonce, &additional_data);
|
// <https://datatracker.ietf.org/doc/html/rfc8446#section-5.3>
|
||||||
|
let result = decrypt(
|
||||||
|
key,
|
||||||
|
encrypted_record,
|
||||||
|
nonce.get_aead_nonce(&iv),
|
||||||
|
&additional_data,
|
||||||
|
);
|
||||||
|
|
||||||
TlsInnerPlaintext {
|
TlsInnerPlaintext {
|
||||||
content: result,
|
content: result,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
use sha2::Digest;
|
use hkdf::Hkdf;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use x25519_dalek::SharedSecret;
|
use x25519_dalek::SharedSecret;
|
||||||
|
|
||||||
use crate::proto::{self, ser_de::Value};
|
use crate::proto::{self, ser_de::Value};
|
||||||
|
|
||||||
use super::{CryptoProvider, TlsHasher};
|
use super::CryptoProvider;
|
||||||
|
|
||||||
// Key Schedule
|
// Key Schedule
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8446#section-7.1
|
// 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
|
// The Hash function used by Transcript-Hash and HKDF is the cipher suite hash algorithm
|
||||||
pub(super) fn hkdf_expand_label<H: TlsHasher>(
|
pub(super) fn hkdf_expand_label(
|
||||||
secret: &[u8],
|
secret: &[u8],
|
||||||
label: &[u8],
|
label: &[u8],
|
||||||
context: &[u8],
|
context: &[u8],
|
||||||
|
|
@ -38,19 +39,18 @@ pub(super) fn hkdf_expand_label<H: TlsHasher>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut okm = [0u8; 128];
|
let mut okm = [0u8; 128];
|
||||||
H::expand(secret, &hkdf_label, &mut okm).unwrap();
|
hkdf::Hkdf::<sha2::Sha256>::from_prk(secret)
|
||||||
|
.unwrap()
|
||||||
|
.expand(&hkdf_label, &mut okm[..length])
|
||||||
|
.unwrap();
|
||||||
okm[..length].to_vec()
|
okm[..length].to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages is the concatenation of the indicated handshake messages,
|
/// Messages is the concatenation of the indicated handshake messages,
|
||||||
/// including the handshake message type and length fields, but not
|
/// including the handshake message type and length fields, but not
|
||||||
/// including record layer headers.
|
/// including record layer headers.
|
||||||
pub(super) fn derive_secret<H: TlsHasher>(
|
pub(super) fn derive_secret(secret: &[u8], label: &[u8], messages_hash: &[u8]) -> Vec<u8> {
|
||||||
secret: &[u8],
|
hkdf_expand_label(secret, label, messages_hash, Sha256::output_size())
|
||||||
label: &[u8],
|
|
||||||
messages_hash: &[u8],
|
|
||||||
) -> Vec<u8> {
|
|
||||||
hkdf_expand_label::<H>(secret, label, messages_hash, H::output_size())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TranscriptHash {
|
pub struct TranscriptHash {
|
||||||
|
|
@ -150,26 +150,40 @@ impl KeysAfterServerHello {
|
||||||
proto::CipherSuite::TLS_AES_128_CCM_8_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
proto::CipherSuite::TLS_AES_128_CCM_8_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let early_secret =
|
let (early_secret, _hkdf_use_this_its_nice) = hkdf::Hkdf::<Sha256>::extract(
|
||||||
(provider.hkdf_extract)(&provider.zeroed_of_hash_size, &provider.zeroed_of_hash_size);
|
Some(provider.zeroed_of_hash_size),
|
||||||
|
provider.zeroed_of_hash_size,
|
||||||
let handhske_secret = (provider.hkdf_extract)(
|
|
||||||
&(provider.derive_secret)(&early_secret, b"derived", &transcript.get_current()),
|
|
||||||
shared_secret.as_bytes(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let client_handshake_traffic_secret =
|
let early_secret_derived =
|
||||||
(provider.derive_secret)(&handhske_secret, b"c hs traffic", &transcript.get_current());
|
derive_secret(&early_secret, b"derived", &sha2::Sha256::new().finalize());
|
||||||
|
println!("early_secret {:?}", early_secret);
|
||||||
|
|
||||||
let server_handshake_traffic_secret =
|
println!("early_secret_derived {:?}", early_secret_derived);
|
||||||
(provider.derive_secret)(&handhske_secret, b"s hs traffic", &transcript.get_current());
|
|
||||||
|
|
||||||
let master_secret = (provider.hkdf_extract)(
|
let (handshake_secret, _) =
|
||||||
&(provider.derive_secret)(
|
Hkdf::<Sha256>::extract(Some(&early_secret_derived), shared_secret.as_bytes());
|
||||||
&handhske_secret,
|
|
||||||
|
let client_handshake_traffic_secret = derive_secret(
|
||||||
|
&handshake_secret,
|
||||||
|
b"c hs traffic",
|
||||||
|
&transcript.get_current(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let server_handshake_traffic_secret = derive_secret(
|
||||||
|
&handshake_secret,
|
||||||
|
b"s hs traffic",
|
||||||
|
&transcript.get_current(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let handshake_secret_derived = derive_secret(
|
||||||
|
&handshake_secret,
|
||||||
b"derived",
|
b"derived",
|
||||||
&sha2::Sha256::new().finalize(),
|
&sha2::Sha256::new().finalize(),
|
||||||
),
|
);
|
||||||
|
|
||||||
|
let (master_secret, _) = Hkdf::<Sha256>::extract(
|
||||||
|
Some(&handshake_secret_derived),
|
||||||
&provider.zeroed_of_hash_size,
|
&provider.zeroed_of_hash_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -177,18 +191,18 @@ impl KeysAfterServerHello {
|
||||||
provider,
|
provider,
|
||||||
client_handshake_traffic_secret,
|
client_handshake_traffic_secret,
|
||||||
server_handshake_traffic_secret,
|
server_handshake_traffic_secret,
|
||||||
master_secret,
|
master_secret: master_secret.to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn after_handshake(self, transcript: &TranscriptHash) {
|
fn after_handshake(self, transcript: &TranscriptHash) {
|
||||||
let _client_application_traffic_secret_0 = (self.provider.derive_secret)(
|
let _client_application_traffic_secret_0 = derive_secret(
|
||||||
&self.master_secret,
|
&self.master_secret,
|
||||||
b"c ap traffic",
|
b"c ap traffic",
|
||||||
&transcript.get_current(),
|
&transcript.get_current(),
|
||||||
);
|
);
|
||||||
let _server_application_traffic_secret_0 = (self.provider.derive_secret)(
|
let _server_application_traffic_secret_0 = derive_secret(
|
||||||
&self.master_secret,
|
&self.master_secret,
|
||||||
b"s ap traffic",
|
b"s ap traffic",
|
||||||
&transcript.get_current(),
|
&transcript.get_current(),
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,12 @@ impl TlsHasher for sha2::Sha384 {
|
||||||
pub struct CryptoProvider {
|
pub struct CryptoProvider {
|
||||||
zeroed_of_hash_size: &'static [u8],
|
zeroed_of_hash_size: &'static [u8],
|
||||||
hkdf_extract: fn(salt: &[u8], ikm: &[u8]) -> Vec<u8>,
|
hkdf_extract: fn(salt: &[u8], ikm: &[u8]) -> Vec<u8>,
|
||||||
derive_secret: fn(secret: &[u8], label: &[u8], messages_hash: &[u8]) -> Vec<u8>,
|
|
||||||
}
|
}
|
||||||
impl CryptoProvider {
|
impl CryptoProvider {
|
||||||
fn new<H: TlsHasher>() -> Self {
|
fn new<H: TlsHasher>() -> Self {
|
||||||
CryptoProvider {
|
CryptoProvider {
|
||||||
zeroed_of_hash_size: H::ZEROED,
|
zeroed_of_hash_size: H::ZEROED,
|
||||||
hkdf_extract: H::extract,
|
hkdf_extract: H::extract,
|
||||||
derive_secret: keys::derive_secret::<H>,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +49,7 @@ impl CryptoProvider {
|
||||||
mod seq {
|
mod seq {
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
use super::aead::Nonce;
|
use super::aead::{Nonce, TlsNonce};
|
||||||
|
|
||||||
/// The sequence ID generator.
|
/// The sequence ID generator.
|
||||||
/// There is a separate one maintained for reading and writing.
|
/// There is a separate one maintained for reading and writing.
|
||||||
|
|
@ -71,10 +69,15 @@ mod seq {
|
||||||
// Don't implement `Clone` to ensure every seq id is only used once.
|
// Don't implement `Clone` to ensure every seq id is only used once.
|
||||||
pub struct SeqId(u64);
|
pub struct SeqId(u64);
|
||||||
impl SeqId {
|
impl SeqId {
|
||||||
pub fn to_nonce(self) -> Nonce {
|
pub fn to_nonce(self) -> TlsNonce {
|
||||||
|
// <https://datatracker.ietf.org/doc/html/rfc8446#section-5.3>
|
||||||
|
// 1. The 64-bit record sequence number is encoded in network byte
|
||||||
|
// order and padded to the left with zeros to iv_length.
|
||||||
let mut nonce = [0; 12];
|
let mut nonce = [0; 12];
|
||||||
nonce[4..].copy_from_slice(&self.0.to_be_bytes());
|
nonce[4..12].copy_from_slice(&self.0.to_be_bytes());
|
||||||
Nonce::from(nonce)
|
let nonce = Nonce::from(nonce);
|
||||||
|
|
||||||
|
TlsNonce::new(nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/lib.rs
11
src/lib.rs
|
|
@ -8,10 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crypto::keys::{KeysAfterServerHello, TranscriptHash};
|
use crypto::keys::{KeysAfterServerHello, TranscriptHash};
|
||||||
use proto::{
|
use proto::CipherSuite;
|
||||||
ser_de::{FrameReader, Value},
|
|
||||||
CipherSuite, TlsCiphertext,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::proto::TLSPlaintext;
|
use crate::proto::TLSPlaintext;
|
||||||
|
|
||||||
|
|
@ -149,6 +146,7 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
||||||
let public = x25519_dalek::PublicKey::from(&secret);
|
let public = x25519_dalek::PublicKey::from(&secret);
|
||||||
|
|
||||||
let legacy_session_id = rand::random::<[u8; 32]>();
|
let legacy_session_id = rand::random::<[u8; 32]>();
|
||||||
|
// Only AES-128 with SHA-256
|
||||||
let cipher_suites = vec![proto::CipherSuite::TLS_AES_128_GCM_SHA256];
|
let cipher_suites = vec![proto::CipherSuite::TLS_AES_128_GCM_SHA256];
|
||||||
|
|
||||||
let handshake = proto::Handshake::ClientHello {
|
let handshake = proto::Handshake::ClientHello {
|
||||||
|
|
@ -335,7 +333,10 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Frame is a TLSCiphertext.
|
// Frame is a TLSCiphertext.
|
||||||
let proto::TLSPlaintext::ApplicationData { data: encrypted_record } = frame else {
|
let proto::TLSPlaintext::ApplicationData {
|
||||||
|
data: encrypted_record,
|
||||||
|
} = frame
|
||||||
|
else {
|
||||||
return unexpected_message!("expected ApplicationData, got {frame:?}");
|
return unexpected_message!("expected ApplicationData, got {frame:?}");
|
||||||
};
|
};
|
||||||
// Encrypted with server_handshake_traffic_secret
|
// Encrypted with server_handshake_traffic_secret
|
||||||
|
|
|
||||||
|
|
@ -2,9 +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 = TcpStream::connect(("vps1.nilstrieb.dev", 443))
|
let conn = TcpStream::connect(("noratrieb.dev", 443))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
//.log()
|
//.log()
|
||||||
;
|
;
|
||||||
tls::ClientConnection::establish(conn, "vps1.nilstrieb.dev").unwrap();
|
tls::ClientConnection::establish(conn, "noratrieb.dev").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,10 @@ impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for Li
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> List<T, Len> {
|
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> List<T, Len> {
|
||||||
pub fn read_for_byte_length<R: Read>(mut remaining_byte_size: usize, r: &mut FrameReader<R>) -> crate::Result<Self> {
|
pub fn read_for_byte_length<R: Read>(
|
||||||
|
mut remaining_byte_size: usize,
|
||||||
|
r: &mut FrameReader<R>,
|
||||||
|
) -> crate::Result<Self> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
while remaining_byte_size > 0 {
|
while remaining_byte_size > 0 {
|
||||||
let value = T::read(r)?;
|
let value = T::read(r)?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue