From 0f93d225e7d6a5b2964b5f950d1869c4c1a15e9e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 20 Jul 2024 14:48:40 +0200 Subject: [PATCH] attempt to decrypt --- Cargo.lock | 228 ++++++++++++++++++--------------------------- Cargo.toml | 2 +- src/crypto/aead.rs | 103 +++++++++----------- src/crypto/keys.rs | 28 ++++-- src/crypto/mod.rs | 39 ++------ src/lib.rs | 16 ++-- src/proto.rs | 10 +- 7 files changed, 174 insertions(+), 252 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e47858..a1e1e47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -11,30 +46,28 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.0.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -51,9 +84,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "4.1.2" @@ -119,6 +162,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -138,12 +191,12 @@ dependencies = [ ] [[package]] -name = "js-sys" -version = "0.3.69" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "wasm-bindgen", + "generic-array", ] [[package]] @@ -153,16 +206,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] -name = "log" -version = "0.4.21" +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "platforms" @@ -170,6 +217,18 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -224,21 +283,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rustc_version" version = "0.4.0" @@ -285,12 +329,6 @@ dependencies = [ "digest", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "subtle" version = "2.5.0" @@ -312,10 +350,10 @@ dependencies = [ name = "tls" version = "0.1.0" dependencies = [ + "aes-gcm", "byteorder", "hkdf", "rand", - "ring", "sha2", "x25519-dalek", ] @@ -333,10 +371,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "untrusted" -version = "0.7.1" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] [[package]] name = "version_check" @@ -350,92 +392,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "x25519-dalek" version = "2.0.1" diff --git a/Cargo.toml b/Cargo.toml index 92d6626..35a03d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +aes-gcm = "0.10.3" byteorder = "1.4.3" hkdf = "0.12.3" rand = "0.8.5" -ring = "0.16.20" sha2 = "0.10.8" x25519-dalek = "2.0.0" diff --git a/src/crypto/aead.rs b/src/crypto/aead.rs index c215fb9..da1c603 100644 --- a/src/crypto/aead.rs +++ b/src/crypto/aead.rs @@ -1,70 +1,51 @@ -use ring::aead::Nonce; +//! Symmetric AEAD-based record encryption. +//! + +use aes_gcm::{ + aead::{Aead, Payload}, + Aes128Gcm, KeyInit, +}; use crate::proto; -use super::{keys, TlsCiphertext, TlsInnerPlaintext}; +use super::{keys, TlsInnerPlaintext}; +pub type AeadKey = aes_gcm::Key; +pub type Nonce = aes_gcm::Nonce<::NonceSize>; - -pub struct AeadKey { - key: ring::aead::LessSafeKey, +fn decrypt(key: &AeadKey, ciphertext: &[u8], nonce: &Nonce, additional_data: &[u8]) -> Vec { + let cipher = Aes128Gcm::new(key); + cipher + .decrypt( + nonce, + Payload { + msg: ciphertext, + aad: additional_data, + }, + ) + .unwrap() } -impl AeadKey { - fn new(algorithm: proto::CipherSuite, key_bytes: &[u8]) -> Self { - Self { - key: ring::aead::LessSafeKey::new( - ring::aead::UnboundKey::new(proto_algo_to_ring(algorithm), key_bytes) - .expect("invalid key"), - ), - } - } -} - - -fn proto_algo_to_ring(algo: proto::CipherSuite) -> &'static ring::aead::Algorithm { - match algo { - proto::CipherSuite::TLS_AES_128_GCM_SHA256 => &ring::aead::AES_128_GCM, - proto::CipherSuite::TLS_AES_256_GCM_SHA384 => &ring::aead::AES_256_GCM, - proto::CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => &ring::aead::CHACHA20_POLY1305, - proto::CipherSuite::TLS_AES_128_CCM_SHA256 => todo!("TLS_AES_128_CCM_SHA256"), - proto::CipherSuite::TLS_AES_128_CCM_8_SHA256 => todo!("TLS_AES_128_CCM_8_SHA256"), - } -} - -fn encrypt(key: AeadKey, message: &[u8], seq: u8, nonce: Nonce) -> Vec { - let total_len = message.len() + key.key.algorithm().tag_len(); - let mut ciphertext_payload = Vec::with_capacity(total_len); - ciphertext_payload.extend_from_slice(message); - - // FIXME: fill out the AAD properly - let aad = ring::aead::Aad::from([0; 5]); - key.key - .seal_in_place_append_tag(nonce, aad, &mut ciphertext_payload) - .unwrap(); - - ciphertext_payload -} - -fn decrypt(key: AeadKey, msg: &mut [u8], nonce: ring::aead::Nonce) { - // FIXME: fill out the AAD properly - let aad = ring::aead::Aad::from([0; 5]); - key.key.open_in_place(nonce, aad, msg); -} - -impl TlsCiphertext { - pub fn decrypt(mut self, secret: &[u8], nonce: Nonce) -> TlsInnerPlaintext { - let key = keys::hkdf_expand_label::(secret, b"key", &[]); - let iv = keys::hkdf_expand_label::(secret, b"iv", &[]); - - let key = AeadKey::new(proto::CipherSuite::TLS_AES_128_GCM_SHA256, secret); - - decrypt(key, &mut self.encrypted_record, nonce); - - TlsInnerPlaintext { - content: self.encrypted_record, - content_type: 0, - padding_len: 0, - } +pub fn decrypt_ciphertext( + encrypted_record: &[u8], + secret: &[u8], + nonce: Nonce, +) -> TlsInnerPlaintext { + // + let key = keys::hkdf_expand_label::(secret, b"key", &[], 128/8); + + let key = aes_gcm::Key::::from_slice(&key[0..(128 / 8)]); + + // 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); + + TlsInnerPlaintext { + content: result, + content_type: 0, + padding_len: 0, } } diff --git a/src/crypto/keys.rs b/src/crypto/keys.rs index b6fd71b..9455baa 100644 --- a/src/crypto/keys.rs +++ b/src/crypto/keys.rs @@ -1,4 +1,4 @@ -use sha2::{digest::OutputSizeUser, Digest}; +use sha2::Digest; use x25519_dalek::SharedSecret; use crate::proto::{self, ser_de::Value}; @@ -9,7 +9,12 @@ use super::{CryptoProvider, TlsHasher}; // 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(secret: &[u8], label: &[u8], context: &[u8]) -> Vec { +pub(super) fn hkdf_expand_label( + secret: &[u8], + label: &[u8], + context: &[u8], + length: usize, +) -> Vec { proto::ser_de::proto_struct! { #[derive(Debug)] pub struct HkdfLabel { @@ -21,7 +26,7 @@ pub(super) fn hkdf_expand_label(secret: &[u8], label: &[u8], conte let mut hkdf_label = Vec::new(); HkdfLabel { // Hash.length is its output length in bytes - length: ::output_size().try_into().unwrap(), + length: length.try_into().unwrap(), label: { let mut v = b"tls13 ".to_vec(); v.extend_from_slice(label); @@ -34,14 +39,18 @@ pub(super) fn hkdf_expand_label(secret: &[u8], label: &[u8], conte let mut okm = [0u8; 128]; H::expand(secret, &hkdf_label, &mut okm).unwrap(); - okm[..::output_size()].to_vec() + 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(secret: &[u8], label: &[u8], messages_hash: &[u8]) -> Vec { - hkdf_expand_label::(secret, label, messages_hash) +pub(super) fn derive_secret( + secret: &[u8], + label: &[u8], + messages_hash: &[u8], +) -> Vec { + hkdf_expand_label::(secret, label, messages_hash, H::output_size()) } pub struct TranscriptHash { @@ -117,7 +126,6 @@ impl TranscriptHash { pub struct KeysAfterServerHello { provider: CryptoProvider, - handhske_secret: Vec, pub client_handshake_traffic_secret: Vec, pub server_handshake_traffic_secret: Vec, master_secret: Vec, @@ -167,20 +175,20 @@ impl KeysAfterServerHello { Self { provider, - handhske_secret, client_handshake_traffic_secret, server_handshake_traffic_secret, master_secret, } } + #[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 = (self.provider.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 = (self.provider.derive_secret)( &self.master_secret, b"s ap traffic", &transcript.get_current(), diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 0ff52ec..bd0587c 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -3,36 +3,8 @@ pub mod aead; pub mod keys; -use crate::proto::{ - self, - ser_de::{proto_enum, proto_struct, Value}, - Handshake, -}; -use hkdf::{hmac::Hmac, HmacImpl}; -use ring::aead::Nonce; pub use seq::{SeqId, SeqIdGen}; -use sha2::{ - digest::{ - core_api::{self, CoreProxy}, - generic_array::ArrayLength, - typenum::Unsigned, - OutputSizeUser, - }, - Digest, -}; -use x25519_dalek::SharedSecret; - -pub struct TlsCiphertext { - /// The encrypted [`TlsInnerPlaintext`] record. - pub encrypted_record: Vec, -} -impl From> for TlsCiphertext { - fn from(value: Vec) -> Self { - TlsCiphertext { - encrypted_record: value, - } - } -} +use sha2::digest::{typenum::Unsigned, OutputSizeUser}; trait TlsHasher: OutputSizeUser { const ZEROED: &'static [u8]; @@ -47,7 +19,7 @@ macro_rules! impl_hkdf_hasher { hkdf::Hkdf::::new(None, ikm).expand(&label, okm) } fn extract(salt: &[u8], ikm: &[u8]) -> Vec { - hkdf::Hkdf::::extract(Some(&[]), &[]) + hkdf::Hkdf::::extract(Some(salt), ikm) .0 .as_slice() .to_vec() @@ -79,7 +51,7 @@ impl CryptoProvider { mod seq { use std::cell::Cell; - use ring::aead::{self, Nonce}; + use super::aead::Nonce; /// The sequence ID generator. /// There is a separate one maintained for reading and writing. @@ -100,13 +72,14 @@ mod seq { pub struct SeqId(u64); impl SeqId { pub fn to_nonce(self) -> Nonce { - let mut nonce = [0; aead::NONCE_LEN]; + let mut nonce = [0; 12]; nonce[4..].copy_from_slice(&self.0.to_be_bytes()); - Nonce::assume_unique_for_key(nonce) + Nonce::from(nonce) } } } +#[allow(dead_code)] pub struct TlsInnerPlaintext { /// The `TLSPlaintext.fragment`` value pub content: Vec, diff --git a/src/lib.rs b/src/lib.rs index a04ce4b..40f5905 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - mod crypto; pub mod proto; @@ -10,7 +8,10 @@ use std::{ }; use crypto::keys::{KeysAfterServerHello, TranscriptHash}; -use proto::{ser_de::Value, CipherSuite}; +use proto::{ + ser_de::{FrameReader, Value}, + CipherSuite, TlsCiphertext, +}; use crate::proto::TLSPlaintext; @@ -117,6 +118,7 @@ https://datatracker.ietf.org/doc/html/rfc8446#appendix-A.1 CONNECTED ``` */ +#[allow(dead_code)] enum ConnectState { Start, WaitServerHello { @@ -216,7 +218,7 @@ impl ClientSetupConnection { cipher_suites, transcript, } => { - let (frame, seq_id) = self.stream.read_record()?; + let (frame, _seq_id) = self.stream.read_record()?; if frame.should_drop() { continue; } @@ -332,11 +334,13 @@ impl ClientSetupConnection { if frame.should_drop() { continue; } - let proto::TLSPlaintext::ApplicationData { data } = frame else { + // Frame is a TLSCiphertext. + let proto::TLSPlaintext::ApplicationData { data: encrypted_record } = frame else { return unexpected_message!("expected ApplicationData, got {frame:?}"); }; // Encrypted with server_handshake_traffic_secret - crypto::TlsCiphertext::from(data).decrypt( + crypto::aead::decrypt_ciphertext( + &encrypted_record, &keys .borrow() .as_ref() diff --git a/src/proto.rs b/src/proto.rs index f1a2c46..53cd2d3 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -38,11 +38,11 @@ pub enum TLSPlaintext { } impl TLSPlaintext { - const INVALID: u8 = 0; - const CHANGE_CIPHER_SPEC: u8 = 20; - const ALERT: u8 = 21; - const HANDSHAKE: u8 = 22; - const APPLICATION_DATA: u8 = 23; + pub const INVALID: u8 = 0; + pub const CHANGE_CIPHER_SPEC: u8 = 20; + pub const ALERT: u8 = 21; + pub const HANDSHAKE: u8 = 22; + pub const APPLICATION_DATA: u8 = 23; pub fn write(&self, w: &mut impl Write) -> io::Result<()> { match self {