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 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);
|
||||
cipher
|
||||
.decrypt(
|
||||
nonce,
|
||||
&nonce,
|
||||
Payload {
|
||||
msg: ciphertext,
|
||||
aad: additional_data,
|
||||
|
|
@ -29,19 +50,27 @@ fn decrypt(key: &AeadKey, ciphertext: &[u8], nonce: &Nonce, additional_data: &[u
|
|||
pub fn decrypt_ciphertext(
|
||||
encrypted_record: &[u8],
|
||||
secret: &[u8],
|
||||
nonce: Nonce,
|
||||
nonce: TlsNonce,
|
||||
) -> TlsInnerPlaintext {
|
||||
// <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 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
|
||||
let mut additional_data = [proto::TLSPlaintext::APPLICATION_DATA, 0x03, 0x03, 0, 0];
|
||||
let ciphertext_len = encrypted_record.as_ref().len() as u16;
|
||||
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 {
|
||||
content: result,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
use sha2::Digest;
|
||||
use hkdf::Hkdf;
|
||||
use sha2::{Digest, Sha256};
|
||||
use x25519_dalek::SharedSecret;
|
||||
|
||||
use crate::proto::{self, ser_de::Value};
|
||||
|
||||
use super::{CryptoProvider, TlsHasher};
|
||||
use super::CryptoProvider;
|
||||
|
||||
// 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
|
||||
pub(super) fn hkdf_expand_label<H: TlsHasher>(
|
||||
pub(super) fn hkdf_expand_label(
|
||||
secret: &[u8],
|
||||
label: &[u8],
|
||||
context: &[u8],
|
||||
|
|
@ -38,19 +39,18 @@ pub(super) fn hkdf_expand_label<H: TlsHasher>(
|
|||
.unwrap();
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
/// Messages is the concatenation of the indicated handshake messages,
|
||||
/// including the handshake message type and length fields, but not
|
||||
/// including record layer headers.
|
||||
pub(super) fn derive_secret<H: TlsHasher>(
|
||||
secret: &[u8],
|
||||
label: &[u8],
|
||||
messages_hash: &[u8],
|
||||
) -> Vec<u8> {
|
||||
hkdf_expand_label::<H>(secret, label, messages_hash, H::output_size())
|
||||
pub(super) fn derive_secret(secret: &[u8], label: &[u8], messages_hash: &[u8]) -> Vec<u8> {
|
||||
hkdf_expand_label(secret, label, messages_hash, Sha256::output_size())
|
||||
}
|
||||
|
||||
pub struct TranscriptHash {
|
||||
|
|
@ -150,26 +150,40 @@ impl KeysAfterServerHello {
|
|||
proto::CipherSuite::TLS_AES_128_CCM_8_SHA256 => CryptoProvider::new::<sha2::Sha256>(),
|
||||
};
|
||||
|
||||
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", &transcript.get_current()),
|
||||
shared_secret.as_bytes(),
|
||||
let (early_secret, _hkdf_use_this_its_nice) = hkdf::Hkdf::<Sha256>::extract(
|
||||
Some(provider.zeroed_of_hash_size),
|
||||
provider.zeroed_of_hash_size,
|
||||
);
|
||||
|
||||
let client_handshake_traffic_secret =
|
||||
(provider.derive_secret)(&handhske_secret, b"c hs traffic", &transcript.get_current());
|
||||
let early_secret_derived =
|
||||
derive_secret(&early_secret, b"derived", &sha2::Sha256::new().finalize());
|
||||
println!("early_secret {:?}", early_secret);
|
||||
|
||||
let server_handshake_traffic_secret =
|
||||
(provider.derive_secret)(&handhske_secret, b"s hs traffic", &transcript.get_current());
|
||||
println!("early_secret_derived {:?}", early_secret_derived);
|
||||
|
||||
let master_secret = (provider.hkdf_extract)(
|
||||
&(provider.derive_secret)(
|
||||
&handhske_secret,
|
||||
let (handshake_secret, _) =
|
||||
Hkdf::<Sha256>::extract(Some(&early_secret_derived), shared_secret.as_bytes());
|
||||
|
||||
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",
|
||||
&sha2::Sha256::new().finalize(),
|
||||
),
|
||||
);
|
||||
|
||||
let (master_secret, _) = Hkdf::<Sha256>::extract(
|
||||
Some(&handshake_secret_derived),
|
||||
&provider.zeroed_of_hash_size,
|
||||
);
|
||||
|
||||
|
|
@ -177,18 +191,18 @@ impl KeysAfterServerHello {
|
|||
provider,
|
||||
client_handshake_traffic_secret,
|
||||
server_handshake_traffic_secret,
|
||||
master_secret,
|
||||
master_secret: master_secret.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
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,
|
||||
b"c ap traffic",
|
||||
&transcript.get_current(),
|
||||
);
|
||||
let _server_application_traffic_secret_0 = (self.provider.derive_secret)(
|
||||
let _server_application_traffic_secret_0 = derive_secret(
|
||||
&self.master_secret,
|
||||
b"s ap traffic",
|
||||
&transcript.get_current(),
|
||||
|
|
|
|||
|
|
@ -36,14 +36,12 @@ impl TlsHasher for sha2::Sha384 {
|
|||
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_hash: &[u8]) -> Vec<u8>,
|
||||
}
|
||||
impl CryptoProvider {
|
||||
fn new<H: TlsHasher>() -> Self {
|
||||
CryptoProvider {
|
||||
zeroed_of_hash_size: H::ZEROED,
|
||||
hkdf_extract: H::extract,
|
||||
derive_secret: keys::derive_secret::<H>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +49,7 @@ impl CryptoProvider {
|
|||
mod seq {
|
||||
use std::cell::Cell;
|
||||
|
||||
use super::aead::Nonce;
|
||||
use super::aead::{Nonce, TlsNonce};
|
||||
|
||||
/// The sequence ID generator.
|
||||
/// 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.
|
||||
pub struct SeqId(u64);
|
||||
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];
|
||||
nonce[4..].copy_from_slice(&self.0.to_be_bytes());
|
||||
Nonce::from(nonce)
|
||||
nonce[4..12].copy_from_slice(&self.0.to_be_bytes());
|
||||
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 proto::{
|
||||
ser_de::{FrameReader, Value},
|
||||
CipherSuite, TlsCiphertext,
|
||||
};
|
||||
use proto::CipherSuite;
|
||||
|
||||
use crate::proto::TLSPlaintext;
|
||||
|
||||
|
|
@ -149,6 +146,7 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
|||
let public = x25519_dalek::PublicKey::from(&secret);
|
||||
|
||||
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 handshake = proto::Handshake::ClientHello {
|
||||
|
|
@ -335,7 +333,10 @@ impl<W: Read + Write> ClientSetupConnection<W> {
|
|||
continue;
|
||||
}
|
||||
// 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:?}");
|
||||
};
|
||||
// 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.
|
||||
fn main() {
|
||||
let conn = TcpStream::connect(("vps1.nilstrieb.dev", 443))
|
||||
let conn = TcpStream::connect(("noratrieb.dev", 443))
|
||||
.unwrap()
|
||||
//.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> {
|
||||
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();
|
||||
while remaining_byte_size > 0 {
|
||||
let value = T::read(r)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue