diff --git a/Cargo.lock b/Cargo.lock index 79d5115..0bbe943 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,16 +17,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aead" -version = "0.6.0-rc.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" -dependencies = [ - "crypto-common 0.2.1", - "inout", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -72,12 +62,6 @@ dependencies = [ "backtrace", ] -[[package]] -name = "base16ct" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" - [[package]] name = "base64ct" version = "1.8.3" @@ -105,15 +89,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" -dependencies = [ - "hybrid-array", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -151,46 +126,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.3.0", -] - -[[package]] -name = "chacha20poly1305" -version = "0.11.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9ed179664f12fd6f155f6dd632edf5f3806d48c228c67ff78366f2a0eb6b5e" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", -] - -[[package]] -name = "cipher" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" -dependencies = [ - "block-buffer 0.12.0", - "crypto-common 0.2.1", - "inout", -] - -[[package]] -name = "cmov" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" - [[package]] name = "core-foundation" version = "0.10.1" @@ -207,12 +142,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpubits" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -222,36 +151,12 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" -[[package]] -name = "crypto-bigint" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" -dependencies = [ - "cpubits", - "ctutils", - "hybrid-array", - "num-traits", - "rand_core", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -262,53 +167,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" -dependencies = [ - "getrandom", - "hybrid-array", - "rand_core", -] - -[[package]] -name = "ctutils" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" -dependencies = [ - "cmov", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "5.0.0-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "darling" version = "0.20.11" @@ -416,37 +274,14 @@ dependencies = [ "syn", ] -[[package]] -name = "dhkem" -version = "0.1.0-rc.0" -source = "git+https://github.com/RustCrypto/KEMs?rev=5b84cfb1c94ef4eeecd77d3281d18833d095978b#5b84cfb1c94ef4eeecd77d3281d18833d095978b" -dependencies = [ - "hkdf", - "kem", - "rand_core", - "x25519-dalek", - "zeroize", -] - [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", -] - -[[package]] -name = "digest" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" -dependencies = [ - "block-buffer 0.12.0", - "crypto-common 0.2.1", - "ctutils", + "block-buffer", + "crypto-common", ] [[package]] @@ -464,25 +299,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "elliptic-curve" -version = "0.14.0-rc.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84043d573efd4ac9d2d125817979a379204bf7e328b25a4a30487e8d100e618" -dependencies = [ - "base16ct", - "crypto-bigint", - "crypto-common 0.2.1", - "digest 0.11.2", - "hkdf", - "hybrid-array", - "rand_core", - "rustcrypto-ff", - "rustcrypto-group", - "subtle", - "zeroize", -] - [[package]] name = "embassy-net" version = "0.9.0" @@ -650,12 +466,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fiat-crypto" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" - [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -711,7 +521,6 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "rand_core", "wasip2", "wasip3", ] @@ -790,24 +599,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hkdf" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" -dependencies = [ - "digest 0.11.2", -] - [[package]] name = "home" version = "0.5.12" @@ -817,17 +608,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "hybrid-array" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" -dependencies = [ - "subtle", - "typenum", - "zeroize", -] - [[package]] name = "i2cdev" version = "0.6.2" @@ -864,15 +644,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" -dependencies = [ - "hybrid-array", -] - [[package]] name = "io-kit-sys" version = "0.4.1" @@ -926,16 +697,6 @@ dependencies = [ "syn", ] -[[package]] -name = "kem" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" -dependencies = [ - "crypto-common 0.2.1", - "rand_core", -] - [[package]] name = "leb128fmt" version = "0.1.0" @@ -1228,7 +989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -1248,16 +1009,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "poly1305" -version = "0.9.0-rc.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19feddcbdf17fad33f40041c7f9e768faf19455f32a6d52ba1b8b65ffc7b1cae" -dependencies = [ - "cpufeatures 0.3.0", - "universal-hash", -] - [[package]] name = "portable-atomic" version = "1.13.1" @@ -1416,12 +1167,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - [[package]] name = "regex" version = "1.12.3" @@ -1457,36 +1202,6 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustcrypto-ff" -version = "0.14.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "rustcrypto-group" -version = "0.14.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed" -dependencies = [ - "rand_core", - "rustcrypto-ff", - "subtle", -] - [[package]] name = "rustix" version = "0.38.44" @@ -1535,17 +1250,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "sachy-crypto" -version = "0.1.0" -dependencies = [ - "chacha20poly1305", - "dhkem", - "elliptic-curve", - "sha2 0.11.0-rc.5", - "x25519-dalek", -] - [[package]] name = "sachy-esphome" version = "0.1.0" @@ -1690,19 +1394,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.0-rc.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.11.2", + "cpufeatures", + "digest", ] [[package]] @@ -1747,12 +1440,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "supports-color" version = "3.0.2" @@ -1955,16 +1642,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" -dependencies = [ - "crypto-common 0.2.1", - "ctutils", -] - [[package]] name = "version_check" version = "0.9.5" @@ -2241,23 +1918,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "x25519-dalek" -version = "3.0.0-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d5d6ff67acd3945b933e592bfa7143db4fcbb2f871754b6b9fbd7847fc5aea" -dependencies = [ - "curve25519-dalek", - "rand_core", - "zeroize", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index bf76c7c..bb0bacc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "sachy-battery", "sachy-bthome", "sachy-config", - "sachy-crypto", "sachy-esphome", "sachy-fmt", "sachy-fnv", @@ -27,6 +26,3 @@ embassy-time = { version = "0.5" } embassy-sync = { version = "0.8" } embassy-net = { version = "0.9" } defmt = { version = "1" } - -[patch.crates-io] -dhkem = { git = "https://github.com/RustCrypto/KEMs", rev = "5b84cfb1c94ef4eeecd77d3281d18833d095978b" } diff --git a/sachy-crypto/Cargo.toml b/sachy-crypto/Cargo.toml deleted file mode 100644 index 6cbb9fa..0000000 --- a/sachy-crypto/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "sachy-crypto" -authors.workspace = true -edition.workspace = true -repository.workspace = true -license.workspace = true -version.workspace = true -rust-version.workspace = true - -[features] -alloc = ["chacha20poly1305/alloc"] - -[dependencies] -chacha20poly1305 = { version = "=0.11.0-rc.3", default-features = false, features = ["getrandom"] } -x25519-dalek = { version = "=3.0.0-pre.6", default-features = false, features = ["zeroize"] } -sha2 = { version = "=0.11.0-rc.5", default-features = false, features = [] } -dhkem = { version = "0.1.0-rc.0", features = ["getrandom", "x25519"] } -elliptic-curve = { version = "0.14.0-rc.28", default-features = false, features = ["ecdh"] } - -[dev-dependencies] -chacha20poly1305 = { version = "=0.11.0-rc.3", default-features = false, features = [ - "getrandom", - "alloc", -] } diff --git a/sachy-crypto/README.md b/sachy-crypto/README.md deleted file mode 100644 index f5bc735..0000000 --- a/sachy-crypto/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Sachy's Crypto - -A custom rolled encryption scheme that more or less implements HPKE. - -☢️ **WARNING: DO NOT USE IN PRODUCTION. THIS CRATE IS FOR LEARNING/PERSONAL USAGE. AAAAAAAAAA** ☢️ diff --git a/sachy-crypto/src/lib.rs b/sachy-crypto/src/lib.rs deleted file mode 100644 index 9981068..0000000 --- a/sachy-crypto/src/lib.rs +++ /dev/null @@ -1,442 +0,0 @@ -#![no_std] - -use core::ops::BitXor; - -use chacha20poly1305::{AeadInOut, ChaCha20Poly1305, KeyInit, aead}; -use dhkem::{ - Encapsulate, Expander, Kem, X25519DecapsulationKey, X25519EncapsulationKey, X25519Kem, - kem::{Ciphertext, Decapsulate, Key, KeyExport, SharedKey, TryKeyInit}, -}; -use elliptic_curve::subtle::ConstantTimeEq; - -/// Error type. -/// -/// This type is deliberately opaque as to avoid potential side-channel -/// leakage (e.g. padding oracle). -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ProtoError; - -impl core::fmt::Display for ProtoError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("ProtoError") - } -} - -impl core::error::Error for ProtoError {} - -impl From for ProtoError { - fn from(_value: chacha20poly1305::Error) -> Self { - Self - } -} - -pub struct ClientHandshake(X25519DecapsulationKey); - -pub struct EncapsulatedPublicKey(X25519EncapsulationKey); - -/// The role of the participant, whether sending/receiving during handshake, -/// and then whether sending/receiving during communication. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Role { - /// Participant SENDS data - Sender, - /// Participant RECEIVES data - Receiver, -} - -impl From for u8 { - fn from(value: Role) -> Self { - match value { - Role::Sender => 0, - Role::Receiver => 1, - } - } -} - -impl BitXor for Role { - type Output = u8; - - fn bitxor(self, rhs: Self) -> u8 { - u8::from(self) ^ u8::from(rhs) - } -} - -impl EncapsulatedPublicKey { - pub fn serialize(&self) -> Key { - self.0.to_bytes() - } - - pub fn deserialize(buf: &[u8]) -> Result { - Ok(Self( - X25519EncapsulationKey::new_from_slice(buf).map_err(|_| ProtoError)?, - )) - } - - pub fn encapsulate(&self) -> (Ciphertext, SharedKey) { - self.0.encapsulate() - } -} - -impl ClientHandshake { - pub fn send() -> (EncapsulatedPublicKey, Self) { - let (decap, encap) = X25519Kem::generate_keypair(); - - (EncapsulatedPublicKey(encap), Self(decap)) - } - - pub fn finish(self, ciphertext: &[u8], psk: &[u8; 32]) -> Result { - let shared = self - .0 - .decapsulate_slice(ciphertext) - .map_err(|_| ProtoError)?; - - TransportState::init(psk, shared, Role::Sender) - } -} - -pub struct ServerHandshake(SharedKey); - -impl ServerHandshake { - pub fn receive(buf: &[u8]) -> Result<(Ciphertext, Self), ProtoError> { - let encap = EncapsulatedPublicKey::deserialize(buf)?; - - let (ciphertext, sk) = encap.encapsulate(); - - Ok((ciphertext, Self(sk))) - } - - pub fn finish(self, psk: &[u8; 32]) -> Result { - TransportState::init(psk, self.0, Role::Receiver) - } -} - -pub struct SendingState<'a> { - transport: &'a TransportState, - counter: u64, -} - -impl SendingState<'_> { - pub fn encrypt( - &mut self, - msg: &mut dyn aead::Buffer, - associated_data: &[u8], - ) -> Result<(), ProtoError> { - if self.counter.ct_eq(&u64::MAX).into() { - return Err(ProtoError); - } - - self.transport.aead.encrypt_in_place( - &self - .transport - .mix_nonce(&self.counter.to_be_bytes(), Role::Sender), - associated_data, - msg, - )?; - - self.counter = self.counter.wrapping_add(1); - - Ok(()) - } -} - -pub struct ReceivingState<'a> { - transport: &'a TransportState, - counter: u64, -} - -impl ReceivingState<'_> { - pub fn decrypt( - &mut self, - msg: &mut dyn aead::Buffer, - associated_data: &[u8], - ) -> Result<(), ProtoError> { - if self.counter.ct_eq(&u64::MAX).into() { - return Err(ProtoError); - } - - self.transport.aead.decrypt_in_place( - &self - .transport - .mix_nonce(&self.counter.to_be_bytes(), Role::Receiver), - associated_data, - msg, - )?; - - self.counter = self.counter.wrapping_add(1); - - Ok(()) - } -} - -#[repr(align(4))] -pub struct TransportState { - aead: ChaCha20Poly1305, - client: aead::Nonce, - server: aead::Nonce, - role: Role, -} - -impl TransportState { - pub fn init( - psk: &[u8; 32], - shared: SharedKey, - role: Role, - ) -> Result { - let kdf = Expander::::new_labeled_hpke(psk, b"Sachy-Crypto", &shared) - .map_err(|_| ProtoError)?; - - let mut key = [0u8; 32]; - let mut client = aead::Nonce::::default(); - let mut server = aead::Nonce::::default(); - - kdf.expand(b"SecretKey012", &mut key) - .map_err(|_| ProtoError)?; - kdf.expand(b"NonceClient*", &mut client) - .map_err(|_| ProtoError)?; - kdf.expand(b"NonceServer#", &mut server) - .map_err(|_| ProtoError)?; - - Ok(Self { - aead: ChaCha20Poly1305::new(&key.into()), - client, - server, - role, - }) - } - - pub fn split(&self) -> (SendingState<'_>, ReceivingState<'_>) { - ( - SendingState { - transport: self, - counter: 0, - }, - ReceivingState { - transport: self, - counter: 0, - }, - ) - } - - /// Selects which nonce to use for encrypting/decrypting, which matters for - /// ensuring the same nonce is used only for one direction of communication. - fn select_nonce_context(&self, send: Role) -> &aead::Nonce { - let context_select = self.role ^ send; - - // Handshake ROLE XOR Transport ROLE selects either one or other nonce context, - // (0) for first context, (1) for second context - // Sending: Client ^ Sender = 0 (select first/client context) - // Receiving: Server ^ Receiver = 0 (select first/client context) - // Sending: Server ^ Sender = 1 (select second/server context) - // Receiving: Client ^ Receiver = 1 (select second/server context) - if context_select.ct_eq(&0).into() { - &self.client - } else { - &self.server - } - } - - fn mix_nonce(&self, position: &[u8; 8], send: Role) -> aead::Nonce { - let mut trump = aead::Nonce::::default(); - - let epstein = self.select_nonce_context(send); - - let index = trump.len() - position.len(); - - // Copy position bytes into BE format onto derived nonce - trump[index..].copy_from_slice(position); - - // XOR the base nonce onto the derived nonce bytes - trump - .iter_mut() - .zip(epstein) - .for_each(|(trump, epstein)| *trump ^= *epstein); - - trump - } -} - -#[derive(Debug)] -pub struct BufferSlice<'a> { - slice: &'a mut [u8], - end: usize, -} - -impl<'a> BufferSlice<'a> { - pub fn new(slice: &'a mut [u8]) -> Self { - Self { - end: slice.len(), - slice, - } - } - - pub fn reset(&mut self) { - self.end = self.slice.len(); - } -} - -impl AsRef<[u8]> for BufferSlice<'_> { - fn as_ref(&self) -> &[u8] { - &self.slice[..self.end] - } -} - -impl AsMut<[u8]> for BufferSlice<'_> { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.slice[..self.end] - } -} - -impl aead::Buffer for BufferSlice<'_> { - fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> { - let index = self.end + other.len(); - - if index > self.slice.len() { - return Err(aead::Error); - } - - self.slice[self.end..index].copy_from_slice(other); - - self.end = index; - - Ok(()) - } - - fn truncate(&mut self, len: usize) { - self.end = len; - } -} - -#[cfg(test)] -mod tests { - use alloc::vec; - use chacha20poly1305::aead::Buffer; - use dhkem::Generate; - use elliptic_curve::array::Array; - - extern crate alloc; - - use super::*; - - #[test] - fn buffer_slice_works() { - let mut buf = vec![0u8; 128]; - - let mut buf_slice = BufferSlice::new(&mut buf); - - assert_eq!(buf_slice.len(), 128); - assert_eq!(buf_slice.extend_from_slice(&[0, 0, 0]), Err(aead::Error)); - - buf_slice.truncate(64); - - assert_eq!(buf_slice.extend_from_slice(&[0, 0, 0, 0, 0, 0]), Ok(())); - assert_eq!(buf_slice.len(), 70); - - buf_slice.reset(); - - assert_eq!(buf_slice.len(), 128); - } - - #[test] - fn handshake_protocol_works() -> Result<(), ProtoError> { - let psk: [u8; 32] = [ - 31, 48, 29, 177, 88, 236, 186, 84, 65, 51, 214, 243, 174, 24, 45, 101, 229, 129, 62, - 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, - ]; - - let (ek, client) = ClientHandshake::send(); - - // Pretend to send ek across the webz: client -> server - let (ciphertext, server) = ServerHandshake::receive(&ek.serialize())?; - - // Pretend to send ciphertext across the webz: server -> client - let alice = client.finish(&ciphertext, &psk)?; - let bob = server.finish(&psk)?; - - let nonce = aead::Nonce::::generate(); - - let mut buffer1 = vec![0u8; 64]; - let mut buffer2 = vec![0u8; 64]; - - // Using the same nonce to check that the internal AEAD states match. Normally, client/server - // would work with unique derived nonces, because nonce reuse is BAD - alice.aead.encrypt_in_place(&nonce, &[], &mut buffer1)?; - bob.aead.encrypt_in_place(&nonce, &[], &mut buffer2)?; - - // If the nonces match, then we can assume the rest of the internal state is the same too - // so the outputs should match each other - assert_eq!(&buffer1, &buffer2); - - // Both Transports have derived base nonces for each context. - // Client context nonces will not match Server context nonces. - assert_eq!(alice.client, bob.client); - assert_eq!(alice.server, bob.server); - assert_ne!(alice.client, alice.server); - assert_ne!(bob.client, bob.server); - - Ok(()) - } - - #[test] - fn two_way_transport_sync_works() -> Result<(), ProtoError> { - let shared_secret = [ - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, - 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, - 0x9c, 0x9d, 0x9e, 0x9f, - ]; - - let psk: [u8; 32] = [ - 31, 48, 29, 177, 88, 236, 186, 84, 65, 51, 214, 243, 174, 24, 45, 101, 229, 129, 62, - 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, - ]; - - let alice = TransportState::init(&psk, Array(shared_secret), Role::Sender)?; - let bob = TransportState::init(&psk, Array(shared_secret), Role::Receiver)?; - - let (mut alice_send, mut alice_recv) = alice.split(); - let (mut bob_send, mut bob_recv) = bob.split(); - - let orig = b"Test Message, Please ignore."; - - let ad = b"random"; - - let mut msg = orig.to_vec(); - - // a -> b - alice_send.encrypt(&mut msg, ad)?; - - assert_ne!(orig.as_slice(), msg.as_slice()); - let ct1 = msg.clone(); - - bob_recv.decrypt(&mut msg, ad)?; - - // a -> b - alice_send.encrypt(&mut msg, b"")?; - - assert_ne!(msg.as_slice(), ct1.as_slice()); - let ct2 = msg.clone(); - - bob_recv.decrypt(&mut msg, b"")?; - - // b -> a - bob_send.encrypt(&mut msg, ad)?; - - // None of the ciphertexts should match each other - assert_ne!(msg.as_slice(), ct1.as_slice()); - assert_ne!(msg.as_slice(), ct2.as_slice()); - assert_ne!(ct1.as_slice(), ct2.as_slice()); - - alice_recv.decrypt(&mut msg, ad)?; - - assert_eq!(orig.as_slice(), msg.as_slice()); - - // Counters are tracked from sender to receiver - assert_eq!(alice_send.counter, bob_recv.counter); - assert_eq!(bob_send.counter, alice_recv.counter); - - // Counters are not linked on the same side - assert_ne!(alice_send.counter, alice_recv.counter); - assert_ne!(bob_send.counter, bob_recv.counter); - - Ok(()) - } -}