make progress

This commit is contained in:
nora 2024-06-29 15:53:00 +02:00
parent b4faf77116
commit eea717987d
11 changed files with 574 additions and 343 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

127
Cargo.lock generated
View file

@ -13,24 +13,21 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.14.0" version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -40,9 +37,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.9" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -59,9 +56,9 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "4.1.1" version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@ -75,9 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek-derive" name = "curve25519-dalek-derive"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -97,9 +94,9 @@ dependencies = [
[[package]] [[package]]
name = "fiat-crypto" name = "fiat-crypto"
version = "0.2.1" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]] [[package]]
name = "generic-array" name = "generic-array"
@ -113,9 +110,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@ -124,9 +121,9 @@ dependencies = [
[[package]] [[package]]
name = "hkdf" name = "hkdf"
version = "0.12.3" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [ dependencies = [
"hmac", "hmac",
] ]
@ -142,36 +139,36 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.64" version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.20" version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "platforms" name = "platforms"
version = "3.1.2" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
@ -181,18 +178,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.67" version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -253,30 +250,41 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.19" version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.188" version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.188" version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"
@ -291,9 +299,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.37" version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -308,6 +316,7 @@ dependencies = [
"hkdf", "hkdf",
"rand", "rand",
"ring", "ring",
"sha2",
"x25519-dalek", "x25519-dalek",
] ]
@ -343,9 +352,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.87" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -353,9 +362,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.87" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -368,9 +377,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.87" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -378,9 +387,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.87" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -391,15 +400,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.87" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.64" version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -429,9 +438,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "x25519-dalek" name = "x25519-dalek"
version = "2.0.0" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"rand_core", "rand_core",
@ -441,9 +450,9 @@ dependencies = [
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.6.0" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [ dependencies = [
"zeroize_derive", "zeroize_derive",
] ]

View file

@ -10,4 +10,5 @@ byteorder = "1.4.3"
hkdf = "0.12.3" hkdf = "0.12.3"
rand = "0.8.5" rand = "0.8.5"
ring = "0.16.20" ring = "0.16.20"
sha2 = "0.10.8"
x25519-dalek = "2.0.0" x25519-dalek = "2.0.0"

61
flake.lock generated
View file

@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1692799911,
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1693985761,
"narHash": "sha256-K5b+7j7Tt3+AqbWkcw+wMeqOAWyCD1MH26FPZyWXpdo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0bffda19b8af722f8069d09d8b6a24594c80b352",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,42 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
llvmPackages_16.clang
llvmPackages_16.bintools
llvmPackages_16.libllvm
llvmPackages_16.lldb
rustup
];
# https://github.com/rust-lang/rust-bindgen#environment-variables
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
shellHook = ''
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
'';
# Add glibc, clang, glib and other headers to bindgen search path
BINDGEN_EXTRA_CLANG_ARGS =
(builtins.map (a: ''-I"${a}/include"'') [
pkgs.glibc.dev
])
# Includes with special directory paths
++ [
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
''-I"${pkgs.glib.dev}/include/glib-2.0"''
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
];
packages = (with pkgs; [
wireshark
]);
};
});
}

9
shell.nix Normal file
View file

@ -0,0 +1,9 @@
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
buildInputs = with pkgs; [
];
RUST_BACKTRACE = 1;
packages = (with pkgs; [
wireshark
]);
}

View file

@ -1,11 +1,55 @@
//! Module for encrypting `TLSPlaintext` records. //! Module for encrypting `TLSPlaintext` records.
use crate::proto; use crate::proto::{
self,
ser_de::{proto_enum, proto_struct, Value},
};
use ring::aead; use ring::aead::{self, Nonce};
struct TlsCiphertext { pub struct TlsCiphertext {
encrypted_record: Vec<u8>, /// The encrypted [`TlsInnerPlaintext`] record.
pub encrypted_record: Vec<u8>,
}
impl From<Vec<u8>> for TlsCiphertext {
fn from(value: Vec<u8>) -> Self {
TlsCiphertext {
encrypted_record: value,
}
}
}
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>,
}
}
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(),
}
.write(&mut hkdf_label)
.unwrap();
// 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)
};
let derive_secret = |secret: &[u8], label: &[u8], messages: ()| {
hkdf_expand_label(secret, label, &[], 16) // todo: fix length
};
} }
pub struct AeadKey { pub struct AeadKey {
@ -16,12 +60,46 @@ impl AeadKey {
fn new(algorithm: proto::CipherSuite, key_bytes: &[u8]) -> Self { fn new(algorithm: proto::CipherSuite, key_bytes: &[u8]) -> Self {
Self { Self {
key: aead::LessSafeKey::new( key: aead::LessSafeKey::new(
aead::UnboundKey::new(proto_algo_to_ring(algorithm), key_bytes).expect("invalid key"), aead::UnboundKey::new(proto_algo_to_ring(algorithm), key_bytes)
.expect("invalid key"),
), ),
} }
} }
} }
mod seq {
use std::cell::Cell;
use ring::aead::{self, Nonce};
/// The sequence ID generator.
/// There is a separate one maintained for reading and writing.
pub struct SeqIdGen {
next: Cell<u64>,
}
impl SeqIdGen {
pub fn new() -> SeqIdGen {
SeqIdGen { next: Cell::new(0) }
}
pub fn next(&self) -> SeqId {
let next = self.next.get();
self.next.set(next.checked_add(1).unwrap());
SeqId(next)
}
}
// 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 {
let mut nonce = [0; aead::NONCE_LEN];
nonce[4..].copy_from_slice(&self.0.to_be_bytes());
Nonce::assume_unique_for_key(nonce)
}
}
}
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 {
proto::CipherSuite::TLS_AES_128_GCM_SHA256 => &aead::AES_128_GCM, proto::CipherSuite::TLS_AES_128_GCM_SHA256 => &aead::AES_128_GCM,
@ -41,7 +119,23 @@ fn encrypt(key: AeadKey, message: &[u8], seq: u8) -> Vec<u8> {
let nonce = aead::Nonce::assume_unique_for_key([0; aead::NONCE_LEN]); let nonce = aead::Nonce::assume_unique_for_key([0; aead::NONCE_LEN]);
// FIXME: fill out the AAD properly // FIXME: fill out the AAD properly
let aad = aead::Aad::from([0; 5]); let aad = aead::Aad::from([0; 5]);
key.key.seal_in_place_append_tag(nonce, aad, &mut ciphertext_payload).unwrap(); key.key
.seal_in_place_append_tag(nonce, aad, &mut ciphertext_payload)
.unwrap();
ciphertext_payload ciphertext_payload
} }
pub struct TlsInnerPlaintext {
/// The `TLSPlaintext.fragment`` value
pub content: Vec<u8>,
/// The `TLSPlaintext.type` value
pub content_type: u8,
pub padding_len: u16,
}
impl TlsCiphertext {
pub fn decrypt(&self, key: AeadKey, nonce: Nonce) -> TlsInnerPlaintext {
todo!()
}
}

View file

@ -2,10 +2,14 @@ mod crypt;
pub mod proto; pub mod proto;
use std::{ use std::{
cell::RefCell,
fmt::Debug, fmt::Debug,
io::{self, Read, Write}, io::{self, Read, Write},
}; };
use crypt::{SeqId, SeqIdGen};
use proto::CipherSuite;
use crate::proto::TLSPlaintext; use crate::proto::TLSPlaintext;
type Result<T, E = Error> = std::result::Result<T, E>; type Result<T, E = Error> = std::result::Result<T, E>;
@ -16,24 +20,125 @@ pub struct ClientConnection<W> {
impl<W: Read + Write> ClientConnection<W> { impl<W: Read + Write> ClientConnection<W> {
pub fn establish(w: W, host: &str) -> Result<Self> { pub fn establish(w: W, host: &str) -> Result<Self> {
let _setup = ClientSetupConnection::establish(w, host)?; let mut setup = ClientSetupConnection {
stream: StreamState::new(w),
};
let _setup = setup.establish(host)?;
todo!() todo!()
} }
} }
struct ClientSetupConnection<W> { struct ClientSetupConnection<W> {
_w: W, stream: StreamState<W>,
} }
mod stream_state {
use std::io::{Read, Write};
use crate::crypt::{SeqId, SeqIdGen};
use crate::proto::{self, TLSPlaintext};
use crate::Result;
pub struct StreamState<W> {
stream: W,
read_seq_id: SeqIdGen,
write_seq_id: SeqIdGen,
}
impl<W: Write + Read> StreamState<W> {
pub fn new(stream: W) -> Self {
Self {
stream,
read_seq_id: SeqIdGen::new(),
write_seq_id: SeqIdGen::new(),
}
}
pub fn write_flush_record(&mut self, plaintext: TLSPlaintext) -> Result<()> {
self.write_record(plaintext)?;
self.stream.flush()?;
Ok(())
}
pub fn write_record(&mut self, plaintext: TLSPlaintext) -> Result<SeqId> {
plaintext.write(&mut self.stream)?;
self.stream.flush()?;
Ok(self.write_seq_id.next())
}
pub fn read_record(&mut self) -> Result<(TLSPlaintext, SeqId)> {
let seq_id = self.read_seq_id.next();
let frame = proto::TLSPlaintext::read(&mut self.stream)?;
Ok((frame, seq_id))
}
}
}
use stream_state::StreamState;
macro_rules! unexpected_message { macro_rules! unexpected_message {
($($tt:tt)*) => { ($($tt:tt)*) => {
Err(ErrorKind::UnexpectedMessage(format!($($tt)*)).into()) Err(ErrorKind::UnexpectedMessage(format!($($tt)*)).into())
}; };
} }
/**
https://datatracker.ietf.org/doc/html/rfc8446#appendix-A.1
```text
START <----+
Send ClientHello | | Recv HelloRetryRequest
[K_send = early data] | |
v |
/ WAIT_SH ----+
| | Recv ServerHello
| | K_recv = handshake
Can | V
send | WAIT_EE
early | | Recv EncryptedExtensions
data | +--------+--------+
| Using | | Using certificate
| PSK | v
| | WAIT_CERT_CR
| | Recv | | Recv CertificateRequest
| | Certificate | v
| | | WAIT_CERT
| | | | Recv Certificate
| | v v
| | WAIT_CV
| | | Recv CertificateVerify
| +> WAIT_FINISHED <+
| | Recv Finished
\ | [Send EndOfEarlyData]
| K_send = handshake
| [Send Certificate [+ CertificateVerify]]
Can send | Send Finished
app data --> | K_send = K_recv = application
after here v
CONNECTED
```
*/
enum ConnectState {
Start,
WaitServerHello {
legacy_session_id: [u8; 32],
secret: RefCell<Option<x25519_dalek::EphemeralSecret>>,
cipher_suites: Vec<CipherSuite>,
},
WaitEncryptedExtensions,
WaitCertificateRequest,
WaitCertificate,
WaitCertificateVerify,
WaitFinished,
Connected,
}
impl<W: Read + Write> ClientSetupConnection<W> { impl<W: Read + Write> ClientSetupConnection<W> {
fn establish(mut stream: W, host: &str) -> Result<Self> { fn establish(&mut self, host: &str) -> Result<Self> {
let mut state = ConnectState::Start;
loop {
let next_state = match &state {
ConnectState::Start => {
// https://datatracker.ietf.org/doc/html/rfc8446#section-2
let secret = x25519_dalek::EphemeralSecret::random_from_rng(rand::thread_rng()); let secret = x25519_dalek::EphemeralSecret::random_from_rng(rand::thread_rng());
let public = x25519_dalek::PublicKey::from(&secret); let public = x25519_dalek::PublicKey::from(&secret);
@ -89,12 +194,22 @@ impl<W: Read + Write> ClientSetupConnection<W> {
.into(), .into(),
}; };
let plaintext = proto::TLSPlaintext::Handshake { handshake }; let plaintext = proto::TLSPlaintext::Handshake { handshake };
plaintext.write(&mut stream)?; self.stream.write_flush_record(plaintext)?;
stream.flush()?; ConnectState::WaitServerHello {
legacy_session_id,
let out = proto::TLSPlaintext::read(&mut stream)?; secret: RefCell::new(Some(secret)),
dbg!(&out); cipher_suites,
}
}
ConnectState::WaitServerHello {
legacy_session_id,
secret,
cipher_suites,
} => {
let (frame, seq_id) = self.stream.read_record()?;
if frame.should_drop() {
continue;
}
let proto::TLSPlaintext::Handshake { let proto::TLSPlaintext::Handshake {
handshake: handshake:
proto::Handshake::ServerHello { proto::Handshake::ServerHello {
@ -105,11 +220,9 @@ impl<W: Read + Write> ClientSetupConnection<W> {
legacy_compression_method, legacy_compression_method,
extensions, extensions,
}, },
} = out } = frame
else { else {
return Err( return unexpected_message!("expected ServerHello, got {frame:?}");
ErrorKind::UnexpectedMessage(format!("expected ServerHello, got {out:?}")).into(),
);
}; };
if random.is_hello_retry_request() { if random.is_hello_retry_request() {
@ -148,41 +261,84 @@ impl<W: Read + Write> ClientSetupConnection<W> {
proto::ExtensionSH::PreSharedKey => todo!(), proto::ExtensionSH::PreSharedKey => todo!(),
proto::ExtensionSH::SupportedVersions { selected_version } => { proto::ExtensionSH::SupportedVersions { selected_version } => {
if *selected_version != proto::TLSV13 { if *selected_version != proto::TLSV13 {
return unexpected_message!("server returned non-TLS 1.3 version: {selected_version}"); return unexpected_message!(
"server returned non-TLS 1.3 version: {selected_version}"
);
} }
supported_versions = true; supported_versions = true;
}, }
proto::ExtensionSH::Cookie { .. } => todo!(), proto::ExtensionSH::Cookie { .. } => todo!(),
proto::ExtensionSH::KeyShare { key_share } => { proto::ExtensionSH::KeyShare { key_share } => {
let entry = key_share.unwrap_server_hello(); let entry = key_share.unwrap_server_hello();
match entry { match entry {
proto::KeyShareEntry::X25519 { len, key_exchange } => { proto::KeyShareEntry::X25519 { len, key_exchange } => {
if *len != 32 { if *len != 32 {
return unexpected_message!("key length for X25519 key share must be 32: {len}"); return unexpected_message!(
"key length for X25519 key share must be 32: {len}"
);
} }
server_key = Some(key_exchange); server_key = Some(key_exchange);
},
} }
}, }
}
} }
} }
if !supported_versions { if !supported_versions {
return unexpected_message!("server did not send supported_versions extension"); return unexpected_message!(
"server did not send supported_versions extension"
);
} }
let Some(server_key) = server_key else { let Some(server_key) = server_key else {
return unexpected_message!("server did not send its key"); return unexpected_message!("server did not send its key");
}; };
let server_key = x25519_dalek::PublicKey::from(*server_key); let server_key = x25519_dalek::PublicKey::from(*server_key);
let dh_shared_secret = secret.diffie_hellman(&server_key); let dh_shared_secret = secret
.borrow_mut()
.take()
.unwrap()
.diffie_hellman(&server_key);
println!("we have established a shared secret. dont leak it!! anywhere here is it: {:x?}", dh_shared_secret.as_bytes()); println!(
"we have established a shared secret. dont leak it!! anyways here is it: {:x?}",
dh_shared_secret.as_bytes()
);
dbg!(proto::TLSPlaintext::read(&mut stream))?; crypt::compute_keys(dh_shared_secret);
ConnectState::WaitEncryptedExtensions
}
ConnectState::WaitEncryptedExtensions => {
let (frame, seq_id) = self.stream.read_record()?;
if frame.should_drop() {
continue;
}
let proto::TLSPlaintext::ApplicationData { data } = frame else {
return unexpected_message!("expected ApplicationData, got {frame:?}");
};
//crypt::TlsCiphertext::from(data).decrypt(key, seq_id.to_nonce());
todo!() todo!()
} }
ConnectState::WaitCertificateRequest => todo!(),
ConnectState::WaitCertificate => todo!(),
ConnectState::WaitCertificateVerify => todo!(),
ConnectState::WaitFinished => todo!(),
ConnectState::Connected => todo!(),
};
state = next_state;
}
}
}
impl TLSPlaintext {
fn should_drop(&self) -> bool {
match self {
TLSPlaintext::ChangeCipherSpec { data: body } if body.as_ref() == &[0x01] => true,
_ => false,
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -201,16 +357,16 @@ pub enum ErrorKind {
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(value: io::Error) -> Self { fn from(value: io::Error) -> Self {
panic!("error: {value}"); panic!("error: {value}");
Self { //Self {
kind: ErrorKind::Io(value), // kind: ErrorKind::Io(value),
} //}
} }
} }
impl From<ErrorKind> for Error { impl From<ErrorKind> for Error {
fn from(value: ErrorKind) -> Self { fn from(value: ErrorKind) -> Self {
panic!("error:{value:?}"); panic!("error:{value:?}");
Self { kind: value } //Self { kind: value }
} }
} }

View file

@ -2,6 +2,6 @@ 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(("nilstrieb.dev", 443)).unwrap()); let conn = tls::LoggingWriter(TcpStream::connect(("vps1.nilstrieb.dev", 443)).unwrap());
tls::ClientConnection::establish(conn, "nilstrieb.dev").unwrap(); tls::ClientConnection::establish(conn, "vps1.nilstrieb.dev").unwrap();
} }

View file

@ -17,14 +17,24 @@ pub enum TLSPlaintext {
}, },
/// This only exists for compatibility and must be sent immediately before the second flight. /// This only exists for compatibility and must be sent immediately before the second flight.
/// If this is received with the single byte value `0x01`, then it should just be dropped. /// If this is received with the single byte value `0x01`, then it should just be dropped.
ChangeCipherSpec, ///
/// An implementation may receive an unencrypted record of type
/// change_cipher_spec consisting of the single byte value 0x01 at any
/// time after the first ClientHello message has been sent or received
/// and before the peer's Finished message has been received and MUST
/// simply drop it without further processing.
ChangeCipherSpec {
data: List<u8, u16>,
},
Alert { Alert {
alert: Alert, alert: Alert,
}, },
Handshake { Handshake {
handshake: Handshake, handshake: Handshake,
}, },
ApplicationData, ApplicationData {
data: Vec<u8>,
},
} }
impl TLSPlaintext { impl TLSPlaintext {
@ -36,9 +46,24 @@ impl TLSPlaintext {
pub fn write(&self, w: &mut impl Write) -> io::Result<()> { pub fn write(&self, w: &mut impl Write) -> io::Result<()> {
match self { match self {
TLSPlaintext::Invalid { .. } => todo!(), TLSPlaintext::Invalid { .. } => {
TLSPlaintext::ChangeCipherSpec => todo!(), Self::INVALID.write(w)?;
TLSPlaintext::Alert { .. } => todo!(), LEGACY_TLSV12.write(w)?;
todo!()
}
TLSPlaintext::ChangeCipherSpec { data: body } => {
Self::CHANGE_CIPHER_SPEC.write(w)?;
LEGACY_TLSV12.write(w)?;
body.write(w)?;
Ok(())
}
TLSPlaintext::Alert { .. } => {
Self::ALERT.write(w)?;
LEGACY_TLSV12.write(w)?;
todo!()
}
TLSPlaintext::Handshake { handshake } => { TLSPlaintext::Handshake { handshake } => {
Self::HANDSHAKE.write(w)?; Self::HANDSHAKE.write(w)?;
// MUST be set to 0x0303 for all records // MUST be set to 0x0303 for all records
@ -55,7 +80,14 @@ impl TLSPlaintext {
handshake.write(w)?; handshake.write(w)?;
Ok(()) Ok(())
} }
TLSPlaintext::ApplicationData => todo!(), TLSPlaintext::ApplicationData { data } => {
Self::APPLICATION_DATA.write(w)?;
LEGACY_TLSV12.write(w)?;
let len: u16 = data.len().try_into().unwrap();
len.write(w)?;
w.write_all(&data)?;
Ok(())
}
} }
} }
@ -63,10 +95,19 @@ impl TLSPlaintext {
let r = &mut FrameReader::new(r); let r = &mut FrameReader::new(r);
let discr = u8::read(r)?; let discr = u8::read(r)?;
let _legacy_version = ProtocolVersion::read(r)?; let _legacy_version = ProtocolVersion::read(r)?;
let _len = u16::read(r)?; let len = u16::read(r)?;
if len > 2_u16.pow(14) {
return Err(crate::ErrorKind::InvalidFrame(Box::new(format!(
"TLSPLaintext opaque length exceeds 2^14: {len}"
)))
.into());
}
match discr { match discr {
Self::INVALID => todo!(), Self::INVALID => todo!(),
Self::CHANGE_CIPHER_SPEC => todo!(), Self::CHANGE_CIPHER_SPEC => {
let data = List::read_for_byte_length(len.into(), r)?;
Ok(Self::ChangeCipherSpec { data })
}
Self::ALERT => { Self::ALERT => {
let alert = Alert::read(r)?; let alert = Alert::read(r)?;
Ok(Self::Alert { alert }) Ok(Self::Alert { alert })
@ -75,7 +116,12 @@ impl TLSPlaintext {
let handshake = Handshake::read(r)?; let handshake = Handshake::read(r)?;
Ok(TLSPlaintext::Handshake { handshake }) Ok(TLSPlaintext::Handshake { handshake })
} }
Self::APPLICATION_DATA => todo!(), Self::APPLICATION_DATA => {
let data = List::<u8, u16>::read_for_byte_length(len.into(), r)?;
Ok(Self::ApplicationData {
data: data.into_inner(),
})
}
_ => { _ => {
return Err(crate::ErrorKind::InvalidFrame(Box::new(format!( return Err(crate::ErrorKind::InvalidFrame(Box::new(format!(
"Invalid record discriminant: {discr}" "Invalid record discriminant: {discr}"
@ -86,6 +132,16 @@ impl TLSPlaintext {
} }
} }
// https://datatracker.ietf.org/doc/html/rfc8446#section-5.2
proto_struct! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TlsCiphertext {
pub opaque_type: u8,
pub legacy_record_version: u8,
pub encrypted_record: List<u8, u16>,
}
}
pub type ProtocolVersion = u16; pub type ProtocolVersion = u16;
pub type Random = [u8; 32]; pub type Random = [u8; 32];
@ -220,7 +276,6 @@ proto_enum! {
} }
} }
proto_enum! { proto_enum! {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ServerName: u8 { pub enum ServerName: u8 {
@ -385,7 +440,9 @@ pub enum ServerHelloKeyshare {
impl ServerHelloKeyshare { impl ServerHelloKeyshare {
pub fn unwrap_server_hello(&self) -> &KeyShareEntry { pub fn unwrap_server_hello(&self) -> &KeyShareEntry {
match self { match self {
Self::HelloRetryRequest(_) => panic!("unexpected hello retry request, expected server hello"), Self::HelloRetryRequest(_) => {
panic!("unexpected hello retry request, expected server hello")
}
Self::ServerHello(entry) => entry, Self::ServerHello(entry) => entry,
} }
} }

View file

@ -53,14 +53,14 @@ macro_rules! proto_struct {
impl crate::proto::ser_de::Value for $name { impl crate::proto::ser_de::Value for $name {
fn write<W: Write>(&self, mut w: &mut W) -> io::Result<()> { fn write<W: std::io::Write>(&self, mut w: &mut W) -> std::io::Result<()> {
$( $(
crate::proto::ser_de::Value::write(&self.$field_name, &mut w)?; crate::proto::ser_de::Value::write(&self.$field_name, &mut w)?;
)* )*
Ok(()) Ok(())
} }
fn read<R: Read>(r: &mut $crate::proto::ser_de::FrameReader<R>) -> crate::Result<Self> { fn read<R: std::io::Read>(r: &mut $crate::proto::ser_de::FrameReader<R>) -> crate::Result<Self> {
let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*); let ( $( $field_name ),* ) = ($( { crate::proto::ser_de::discard!($field_name); crate::proto::ser_de::Value::read(r)? } ),*);
Ok(Self { Ok(Self {
@ -248,17 +248,8 @@ impl<T: Debug, Len> Debug for List<T, Len> {
impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for List<T, Len> { impl<T: Value, Len: Value + Into<usize> + TryFrom<usize> + Default> Value for List<T, Len> {
fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> { fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self> {
eprintln!("reading a list"); let remaining_byte_size = Len::read(r)?.into();
let mut remaining_byte_size = Len::read(r)?.into(); Self::read_for_byte_length(remaining_byte_size, r)
let mut v = Vec::new();
eprintln!("list.. remaining bytes {remaining_byte_size}");
while remaining_byte_size > 0 {
eprintln!("things {remaining_byte_size} {v:?}");
let value = T::read(r)?;
remaining_byte_size -= value.byte_size();
v.push(value);
}
Ok(Self(v, PhantomData))
} }
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
let byte_size = self.0.iter().map(Value::byte_size).sum::<usize>(); let byte_size = self.0.iter().map(Value::byte_size).sum::<usize>();
@ -278,6 +269,22 @@ 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> {
let mut v = Vec::new();
while remaining_byte_size > 0 {
let value = T::read(r)?;
remaining_byte_size -= value.byte_size();
v.push(value);
}
Ok(Self(v, PhantomData))
}
pub fn into_inner(self) -> Vec<T> {
self.0
}
}
pub trait Value: Sized + std::fmt::Debug { pub trait Value: Sized + std::fmt::Debug {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()>; fn write<W: Write>(&self, w: &mut W) -> io::Result<()>;
fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self>; fn read<R: Read>(r: &mut FrameReader<R>) -> crate::Result<Self>;