From d44793e07f577036a407116338877389e70938a2 Mon Sep 17 00:00:00 2001 From: Morgan Hill Date: Tue, 28 Nov 2023 23:51:14 +0100 Subject: [PATCH 1/3] Remove unwrap from fuzz targets that return errors When fuzzing we are interested in what happens inside the target function not necessarily what it returns. Functions returning errors with bogus input in generally desired behaviour. --- fuzzing/fuzz_targets/handle_msg.rs | 4 +++- fuzzing/fuzz_targets/mceliece_encaps.rs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fuzzing/fuzz_targets/handle_msg.rs b/fuzzing/fuzz_targets/handle_msg.rs index fad8dcc..2d202d9 100644 --- a/fuzzing/fuzz_targets/handle_msg.rs +++ b/fuzzing/fuzz_targets/handle_msg.rs @@ -15,5 +15,7 @@ fuzz_target!(|rx_buf: &[u8]| { let mut cs = CryptoServer::new(sk, pk); let mut tx_buf = [0; 10240]; - cs.handle_msg(rx_buf, &mut tx_buf).unwrap(); + + // We expect errors while fuzzing therefore we do not check the result. + let _ = cs.handle_msg(rx_buf, &mut tx_buf); }); diff --git a/fuzzing/fuzz_targets/mceliece_encaps.rs b/fuzzing/fuzz_targets/mceliece_encaps.rs index 27800c4..00aa05e 100644 --- a/fuzzing/fuzz_targets/mceliece_encaps.rs +++ b/fuzzing/fuzz_targets/mceliece_encaps.rs @@ -9,5 +9,6 @@ fuzz_target!(|input: &[u8]| { let mut ciphertext = [0u8; 188]; let mut shared_secret = [0u8; 32]; - StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input).unwrap(); + // We expect errors while fuzzing therefore we do not check the result. + let _ = StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input); }); From 09aa0e027ed5b0b3f95e62253dd6acd0b9de9f2e Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 28 Nov 2023 15:06:27 +0100 Subject: [PATCH 2/3] chore: Move hashing functions into sodium/ciphers crate This finishes the last step of removing sodium.rs from the rosenpass crate itself and also removes the NOTHING and NONCE0 constants. Hashing functions now use destination parameters; rosenpass_constant_time::xor now does too. --- Cargo.lock | 17 +++ ciphers/Cargo.toml | 5 + ciphers/src/lib.rs | 17 +++ ciphers/src/subtle/incorrect_hmac_blake2b.rs | 44 +++++++ ciphers/src/subtle/mod.rs | 1 + constant-time/Cargo.toml | 1 + constant-time/src/lib.rs | 30 +++-- fuzzing/Cargo.toml | 3 + fuzzing/fuzz_targets/blake2b.rs | 6 +- rosenpass/Cargo.toml | 1 + rosenpass/src/labeled_prf.rs | 10 +- rosenpass/src/lib.rs | 2 - rosenpass/src/lprf.rs | 33 +++--- rosenpass/src/msgs.rs | 23 ++-- rosenpass/src/prftree.rs | 38 +++--- rosenpass/src/protocol.rs | 25 ++-- rosenpass/src/sodium.rs | 115 ------------------- sodium/Cargo.toml | 1 + sodium/src/hash/blake2b.rs | 31 +++++ sodium/src/hash/mod.rs | 1 + sodium/src/lib.rs | 1 + 21 files changed, 203 insertions(+), 202 deletions(-) create mode 100644 ciphers/src/subtle/incorrect_hmac_blake2b.rs create mode 100644 ciphers/src/subtle/mod.rs delete mode 100644 rosenpass/src/sodium.rs create mode 100644 sodium/src/hash/blake2b.rs create mode 100644 sodium/src/hash/mod.rs diff --git a/Cargo.lock b/Cargo.lock index aebadb3..f687edf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1074,6 +1074,7 @@ dependencies = [ "rosenpass-ciphers", "rosenpass-constant-time", "rosenpass-sodium", + "rosenpass-to", "rosenpass-util", "serde", "stacker", @@ -1087,12 +1088,20 @@ dependencies = [ name = "rosenpass-ciphers" version = "0.1.0" dependencies = [ + "anyhow", + "rosenpass-constant-time", "rosenpass-sodium", + "rosenpass-to", + "static_assertions", + "zeroize", ] [[package]] name = "rosenpass-constant-time" version = "0.1.0" +dependencies = [ + "rosenpass-to", +] [[package]] name = "rosenpass-fuzzing" @@ -1103,6 +1112,7 @@ dependencies = [ "rosenpass", "rosenpass-ciphers", "rosenpass-sodium", + "rosenpass-to", "stacker", ] @@ -1113,6 +1123,7 @@ dependencies = [ "anyhow", "libsodium-sys-stable", "log", + "rosenpass-to", "rosenpass-util", ] @@ -1712,6 +1723,12 @@ dependencies = [ "syn", ] +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + [[package]] name = "zip" version = "0.6.6" diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index a84ba2f..c190957 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,4 +10,9 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] +anyhow = "1.0.75" rosenpass-sodium = { path = "../sodium" } +rosenpass-to = { path = "../to" } +rosenpass-constant-time = { path = "../constant-time" } +static_assertions = "1.1.0" +zeroize = "1.7.0" diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index 14fe2ac..b791db2 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -1,11 +1,28 @@ +use static_assertions::const_assert; + +pub mod subtle; + +pub const KEY_LEN: usize = 32; +const_assert!(KEY_LEN == aead::KEY_LEN); +const_assert!(KEY_LEN == xaead::KEY_LEN); +const_assert!(KEY_LEN == hash::KEY_LEN); + +/// Authenticated encryption with associated data pub mod aead { pub use rosenpass_sodium::aead::chacha20poly1305_ietf::{ decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, }; } +/// Authenticated encryption with associated data with a constant nonce pub mod xaead { pub use rosenpass_sodium::aead::xchacha20poly1305_ietf::{ decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, }; } + +pub mod hash { + pub use crate::subtle::incorrect_hmac_blake2b::{ + hash, KEY_LEN, KEY_MAX, KEY_MIN, OUT_MAX, OUT_MIN, + }; +} diff --git a/ciphers/src/subtle/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/incorrect_hmac_blake2b.rs new file mode 100644 index 0000000..802859a --- /dev/null +++ b/ciphers/src/subtle/incorrect_hmac_blake2b.rs @@ -0,0 +1,44 @@ +use anyhow::ensure; +use rosenpass_constant_time::xor; +use rosenpass_sodium::hash::blake2b; +use rosenpass_to::{ops::copy_slice, with_destination, To}; +use zeroize::Zeroizing; + +pub const KEY_LEN: usize = 32; +pub const KEY_MIN: usize = KEY_LEN; +pub const KEY_MAX: usize = KEY_LEN; +pub const OUT_MIN: usize = blake2b::OUT_MIN; +pub const OUT_MAX: usize = blake2b::OUT_MAX; + +/// This is a woefully incorrect implementation of hmac_blake2b. +/// See +/// +/// It accepts 32 byte keys, exclusively. +/// +/// This will be replaced, likely by Kekkac at some point soon. +/// +#[inline] +pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a { + const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN]; + const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN]; + + with_destination(|out: &mut [u8]| { + // Not bothering with padding; the implementation + // uses appropriately sized keys. + ensure!(key.len() == KEY_LEN); + + type Key = Zeroizing<[u8; KEY_LEN]>; + let mut tmp_key = Key::default(); + + copy_slice(key).to(tmp_key.as_mut()); + xor(&IPAD).to(tmp_key.as_mut()); + let mut outer_data = Key::default(); + blake2b::hash(tmp_key.as_ref(), data).to(outer_data.as_mut())?; + + copy_slice(key).to(tmp_key.as_mut()); + xor(&OPAD).to(tmp_key.as_mut()); + blake2b::hash(tmp_key.as_ref(), outer_data.as_ref()).to(out)?; + + Ok(()) + }) +} diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs new file mode 100644 index 0000000..b4f4456 --- /dev/null +++ b/ciphers/src/subtle/mod.rs @@ -0,0 +1 @@ +pub mod incorrect_hmac_blake2b; diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index 205b0af..3e30b63 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -12,3 +12,4 @@ readme = "readme.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rosenpass-to = { path = "../to" } diff --git a/constant-time/src/lib.rs b/constant-time/src/lib.rs index c721e43..f1a8c54 100644 --- a/constant-time/src/lib.rs +++ b/constant-time/src/lib.rs @@ -1,18 +1,26 @@ -/// Xors a and b element-wise and writes the result into a. +use rosenpass_to::{with_destination, To}; + +/// Xors the source into the destination /// /// # Examples /// /// ``` -/// use rosenpass_constant_time::xor_into; -/// let mut a = String::from("hello").into_bytes(); -/// let b = b"world"; -/// xor_into(&mut a, b); -/// assert_eq!(&a, b"\x1f\n\x1e\x00\x0b"); +/// use rosenpass_constant_time::xor; +/// use rosenpass_to::To; +/// assert_eq!( +/// xor(b"world").to_this(|| b"hello".to_vec()), +/// b"\x1f\n\x1e\x00\x0b"); /// ``` +/// +/// # Panics +/// +/// If source and destination are of different sizes. #[inline] -pub fn xor_into(a: &mut [u8], b: &[u8]) { - assert!(a.len() == b.len()); - for (av, bv) in a.iter_mut().zip(b.iter()) { - *av ^= *bv; - } +pub fn xor<'a>(src: &'a [u8]) -> impl To<[u8], ()> + 'a { + with_destination(|dst: &mut [u8]| { + assert!(src.len() == dst.len()); + for (dv, sv) in dst.iter_mut().zip(src.iter()) { + *dv ^= *sv; + } + }) } diff --git a/fuzzing/Cargo.toml b/fuzzing/Cargo.toml index 933f5ee..f9d3e0e 100644 --- a/fuzzing/Cargo.toml +++ b/fuzzing/Cargo.toml @@ -21,6 +21,9 @@ path = "../sodium" [dependencies.rosenpass-ciphers] path = "../ciphers" +[dependencies.rosenpass-to] +path = "../to" + [[bin]] name = "fuzz_handle_msg" path = "fuzz_targets/handle_msg.rs" diff --git a/fuzzing/fuzz_targets/blake2b.rs b/fuzzing/fuzz_targets/blake2b.rs index 2d628b2..807c1a6 100644 --- a/fuzzing/fuzz_targets/blake2b.rs +++ b/fuzzing/fuzz_targets/blake2b.rs @@ -4,8 +4,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::sodium::mac_into; -use rosenpass_sodium::init as sodium_init; +use rosenpass_sodium::{hash::blake2b, init as sodium_init}; +use rosenpass_to::To; #[derive(arbitrary::Arbitrary, Debug)] pub struct Blake2b { @@ -18,5 +18,5 @@ fuzz_target!(|input: Blake2b| { let mut out = [0u8; 32]; - mac_into(&mut out, &input.key, &input.data).unwrap(); + blake2b::hash(&input.key, &input.data).to(&mut out).unwrap(); }); diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 7fa2c5b..65d9c55 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -18,6 +18,7 @@ rosenpass-util = { path = "../util" } rosenpass-constant-time = { path = "../constant-time" } rosenpass-sodium = { path = "../sodium" } rosenpass-ciphers = { path = "../ciphers" } +rosenpass-to = { path = "../to" } anyhow = { version = "1.0.71", features = ["backtrace"] } static_assertions = "1.1.0" memoffset = "0.9.0" diff --git a/rosenpass/src/labeled_prf.rs b/rosenpass/src/labeled_prf.rs index bbc88bc..87090f4 100644 --- a/rosenpass/src/labeled_prf.rs +++ b/rosenpass/src/labeled_prf.rs @@ -1,10 +1,10 @@ //! Pseudo Random Functions (PRFs) with a tree-like label scheme which //! ensures their uniqueness -use { - crate::{prftree::PrfTree, sodium::KEY_SIZE}, - anyhow::Result, -}; + +use crate::prftree::PrfTree; +use anyhow::Result; +use rosenpass_ciphers::KEY_LEN; pub fn protocol() -> Result { PrfTree::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes()) @@ -30,7 +30,7 @@ prflabel!(protocol, _ckextract, "chaining key extract"); macro_rules! prflabel_leaf { ($base:ident, $name:ident, $($lbl:expr),* ) => { - pub fn $name() -> Result<[u8; KEY_SIZE]> { + pub fn $name() -> Result<[u8; KEY_LEN]> { let t = $base()?; $( let t = t.mix($lbl.as_bytes())?; )* Ok(t.into_value()) diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index 894f65c..b223400 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,5 +1,3 @@ -#[macro_use] -pub mod sodium; pub mod coloring; #[rustfmt::skip] pub mod labeled_prf; diff --git a/rosenpass/src/lprf.rs b/rosenpass/src/lprf.rs index 6dff7a0..765a49d 100644 --- a/rosenpass/src/lprf.rs +++ b/rosenpass/src/lprf.rs @@ -17,23 +17,16 @@ //! //! TODO Base the construction on a proper Dec function -pub struct Iprf([u8; KEY_SIZE]); -pub struct IprfBranch([u8; KEY_SIZE]); -pub struct SecretIprf(Secret); -pub struct SecretIprfBranch(Secret); +use rosenpass_ciphers::{KEY_LEN, hash}; -pub fn prf_into(out: &mut [u8], key: &[u8], data: &[u8]) { - // TODO: The error handling with sodium is a scurge - hmac_into(out, key, data).unwrap() -} - -pub fn prf(key: &[u8], data: &[u8]) -> [u8; KEY_SIZE] { - mutating([0u8; KEY_SIZE], |r| prf_into(r, key, data)) -} +pub struct Iprf([u8; KEY_LEN]); +pub struct IprfBranch([u8; KEY_LEN]); +pub struct SecretIprf(Secret); +pub struct SecretIprfBranch(Secret); impl Iprf { fn zero() -> Self { - Self([0u8; KEY_SIZE]) + Self([0u8; KEY_LEN]) } fn dup(self) -> IprfBranch { @@ -42,25 +35,25 @@ impl Iprf { // TODO: Protocol! Use domain separation to ensure that fn mix(self, v: &[u8]) -> Self { - Self(prf(&self.0, v)) + Self(hash(&self.0, v).collect<[u8; KEY_LEN]>()) } fn mix_secret(self, v: Secret) -> SecretIprf { SecretIprf::prf_invoc(&self.0, v.secret()) } - fn into_value(self) -> [u8; KEY_SIZE] { + fn into_value(self) -> [u8; KEY_LEN] { self.0 } fn extract(self, v: &[u8], dst: &mut [u8]) { - prf_into(&self.0, v, dst) + hash(&self.0, v).to(dst) } } impl IprfBranch { fn mix(&self, v: &[u8]) -> Iprf { - Iprf(prf(self.0, v)) + Iprf(hash(self.0, v).collect<[u8; KEY_LEN]>()) } fn mix_secret(&self, v: Secret) -> SecretIprf { @@ -71,7 +64,7 @@ impl IprfBranch { impl SecretIprf { fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf { mutating(SecretIprf(Secret::zero()), |r| { - prf_into(k, d, r.secret_mut()) + hash(k, d).to(r.secret_mut()) }) } @@ -87,12 +80,12 @@ impl SecretIprf { Self::prf_invoc(self.0.secret(), v.secret()) } - fn into_secret(self) -> Secret { + fn into_secret(self) -> Secret { self.0 } fn into_secret_slice(self, v: &[u8], dst: &[u8]) { - prf_into(self.0.secret(), v, dst) + hash(self.0.secret(), v).to(dst) } } diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index b77af50..eac0369 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -44,8 +44,8 @@ //! ``` use super::RosenpassError; -use crate::{pqkem::*, sodium}; -use rosenpass_ciphers::{aead, xaead}; +use crate::pqkem::*; +use rosenpass_ciphers::{aead, xaead, KEY_LEN}; // Macro magic //////////////////////////////////////////////////////////////// @@ -265,9 +265,9 @@ data_lense! { Envelope := payload: M::LEN, /// Message Authentication Code (mac) over all bytes until (exclusive) /// `mac` itself - mac: sodium::MAC_SIZE, + mac: 16, /// Currently unused, TODO: do something with this - cookie: sodium::MAC_SIZE + cookie: 16 } data_lense! { InitHello := @@ -320,11 +320,11 @@ data_lense! { EmptyData := data_lense! { Biscuit := /// H(spki) – Ident ifies the initiator - pidi: sodium::KEY_SIZE, + pidi: KEY_LEN, /// The biscuit number (replay protection) biscuit_no: 12, /// Chaining key - ck: sodium::KEY_SIZE + ck: KEY_LEN } data_lense! { DataMsg := @@ -389,20 +389,17 @@ pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG #[cfg(test)] mod test_constants { - use crate::{ - msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN}, - sodium, - }; - use rosenpass_ciphers::xaead; + use crate::msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN}; + use rosenpass_ciphers::{xaead, KEY_LEN}; #[test] fn sodium_keysize() { - assert_eq!(sodium::KEY_SIZE, 32); + assert_eq!(KEY_LEN, 32); } #[test] fn biscuit_pt_len() { - assert_eq!(BISCUIT_PT_LEN, 2 * sodium::KEY_SIZE + 12); + assert_eq!(BISCUIT_PT_LEN, 2 * KEY_LEN + 12); } #[test] diff --git a/rosenpass/src/prftree.rs b/rosenpass/src/prftree.rs index 8064416..f3956f1 100644 --- a/rosenpass/src/prftree.rs +++ b/rosenpass/src/prftree.rs @@ -1,25 +1,23 @@ //! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf) -use { - crate::{ - coloring::Secret, - sodium::{hmac, hmac_into, KEY_SIZE}, - }, - anyhow::Result, -}; +use crate::coloring::Secret; + +use anyhow::Result; +use rosenpass_ciphers::{hash, KEY_LEN}; +use rosenpass_to::To; // TODO Use a proper Dec interface #[derive(Clone, Debug)] -pub struct PrfTree([u8; KEY_SIZE]); +pub struct PrfTree([u8; KEY_LEN]); #[derive(Clone, Debug)] -pub struct PrfTreeBranch([u8; KEY_SIZE]); +pub struct PrfTreeBranch([u8; KEY_LEN]); #[derive(Clone, Debug)] -pub struct SecretPrfTree(Secret); +pub struct SecretPrfTree(Secret); #[derive(Clone, Debug)] -pub struct SecretPrfTreeBranch(Secret); +pub struct SecretPrfTreeBranch(Secret); impl PrfTree { pub fn zero() -> Self { - Self([0u8; KEY_SIZE]) + Self([0u8; KEY_LEN]) } pub fn dup(self) -> PrfTreeBranch { @@ -32,21 +30,21 @@ impl PrfTree { // TODO: Protocol! Use domain separation to ensure that pub fn mix(self, v: &[u8]) -> Result { - Ok(Self(hmac(&self.0, v)?)) + Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?)) } pub fn mix_secret(self, v: Secret) -> Result { SecretPrfTree::prf_invoc(&self.0, v.secret()) } - pub fn into_value(self) -> [u8; KEY_SIZE] { + pub fn into_value(self) -> [u8; KEY_LEN] { self.0 } } impl PrfTreeBranch { pub fn mix(&self, v: &[u8]) -> Result { - Ok(PrfTree(hmac(&self.0, v)?)) + Ok(PrfTree(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?)) } pub fn mix_secret(&self, v: Secret) -> Result { @@ -57,7 +55,7 @@ impl PrfTreeBranch { impl SecretPrfTree { pub fn prf_invoc(k: &[u8], d: &[u8]) -> Result { let mut r = SecretPrfTree(Secret::zero()); - hmac_into(r.0.secret_mut(), k, d)?; + hash::hash(k, d).to(r.0.secret_mut())?; Ok(r) } @@ -69,7 +67,7 @@ impl SecretPrfTree { SecretPrfTreeBranch(self.0) } - pub fn danger_from_secret(k: Secret) -> Self { + pub fn danger_from_secret(k: Secret) -> Self { Self(k) } @@ -81,12 +79,12 @@ impl SecretPrfTree { Self::prf_invoc(self.0.secret(), v.secret()) } - pub fn into_secret(self) -> Secret { + pub fn into_secret(self) -> Secret { self.0 } pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> { - hmac_into(self.0.secret_mut(), v, dst) + hash::hash(v, dst).to(self.0.secret_mut()) } } @@ -102,7 +100,7 @@ impl SecretPrfTreeBranch { // TODO: This entire API is not very nice; we need this for biscuits, but // it might be better to extract a special "biscuit" // labeled subkey and reinitialize the chain with this - pub fn danger_into_secret(self) -> Secret { + pub fn danger_into_secret(self) -> Secret { self.0 } } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index a68c3ed..7c7556f 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -73,10 +73,9 @@ use crate::{ msgs::*, pqkem::*, prftree::{SecretPrfTree, SecretPrfTreeBranch}, - sodium::*, }; use anyhow::{bail, ensure, Context, Result}; -use rosenpass_ciphers::{aead, xaead}; +use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, @@ -145,10 +144,10 @@ pub type SSk = Secret<{ StaticKEM::SK_LEN }>; pub type EPk = Public<{ EphemeralKEM::PK_LEN }>; pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>; -pub type SymKey = Secret; -pub type SymHash = Public; +pub type SymKey = Secret; +pub type SymHash = Public; -pub type PeerId = Public; +pub type PeerId = Public; pub type SessionId = Public; pub type BiscuitId = Public; @@ -1240,13 +1239,13 @@ impl HandshakeState { pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> { let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret(); - aead::encrypt(ct, k.secret(), &NONCE0, &NOTHING, pt)?; + aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?; self.mix(ct) } pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> { let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret(); - aead::decrypt(pt, k.secret(), &NONCE0, &NOTHING, ct)?; + aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?; self.mix(ct) } @@ -1448,7 +1447,7 @@ impl CryptoServer { .mix(peer.get(self).psk.secret())?; // IHI8 - hs.core.encrypt_and_mix(ih.auth_mut(), &NOTHING)?; + hs.core.encrypt_and_mix(ih.auth_mut(), &[])?; // Update the handshake hash last (not changing any state on prior error peer.hs().insert(self, hs)?; @@ -1514,7 +1513,7 @@ impl CryptoServer { core.store_biscuit(self, peer, rh.biscuit_mut())?; // RHR7 - core.encrypt_and_mix(rh.auth_mut(), &NOTHING)?; + core.encrypt_and_mix(rh.auth_mut(), &[])?; Ok(peer) } @@ -1600,7 +1599,7 @@ impl CryptoServer { ic.biscuit_mut().copy_from_slice(rh.biscuit()); // ICI4 - core.encrypt_and_mix(ic.auth_mut(), &NOTHING)?; + core.encrypt_and_mix(ic.auth_mut(), &[])?; // Split() – We move the secrets into the session; we do not // delete the InitiatorHandshake, just clear it's secrets because @@ -1630,7 +1629,7 @@ impl CryptoServer { )?; // ICR2 - core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &NOTHING)?; + core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?; // ICR3 core.mix(ic.sidi())?.mix(ic.sidr())?; @@ -1686,7 +1685,7 @@ impl CryptoServer { let n = cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]); let k = ses.txkm.secret(); - aead::encrypt(rc.auth_mut(), k, &n, &NOTHING, &NOTHING)?; // ct, k, n, ad, pt + aead::encrypt(rc.auth_mut(), k, &n, &[], &[])?; // ct, k, n, ad, pt Ok(peer) } @@ -1723,7 +1722,7 @@ impl CryptoServer { &mut [0u8; 0], s.txkt.secret(), &cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]), - &NOTHING, + &[], rc.auth(), )?; } diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs deleted file mode 100644 index 48e8fb9..0000000 --- a/rosenpass/src/sodium.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Bindings and helpers for accessing libsodium functions - -use anyhow::{ensure, Result}; -use libsodium_sys as libsodium; -use rosenpass_constant_time::xor_into; -use rosenpass_util::attempt; -use static_assertions::const_assert_eq; -use std::os::raw::c_ulonglong; -use std::ptr::null as nullptr; - -pub const NONCE0: [u8; libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize] = - [0u8; libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize]; -pub const NOTHING: [u8; 0] = [0u8; 0]; -pub const KEY_SIZE: usize = 32; -pub const MAC_SIZE: usize = 16; - -const_assert_eq!( - KEY_SIZE, - libsodium::crypto_aead_chacha20poly1305_IETF_KEYBYTES as usize -); -const_assert_eq!(KEY_SIZE, libsodium::crypto_generichash_BYTES as usize); - -macro_rules! sodium_call { - ($name:ident, $($args:expr),*) => { attempt!({ - ensure!(unsafe{libsodium::$name($($args),*)} > -1, - "Error in libsodium's {}.", stringify!($name)); - Ok(()) - })}; - ($name:ident) => { sodium_call!($name, ) }; -} - -#[inline] -fn blake2b_flexible(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> { - const KEY_MIN: usize = libsodium::crypto_generichash_KEYBYTES_MIN as usize; - const KEY_MAX: usize = libsodium::crypto_generichash_KEYBYTES_MAX as usize; - const OUT_MIN: usize = libsodium::crypto_generichash_BYTES_MIN as usize; - const OUT_MAX: usize = libsodium::crypto_generichash_BYTES_MAX as usize; - assert!(key.is_empty() || (KEY_MIN <= key.len() && key.len() <= KEY_MAX)); - assert!(OUT_MIN <= out.len() && out.len() <= OUT_MAX); - let kptr = match key.len() { - // NULL key - 0 => nullptr(), - _ => key.as_ptr(), - }; - sodium_call!( - crypto_generichash_blake2b, - out.as_mut_ptr(), - out.len(), - data.as_ptr(), - data.len() as c_ulonglong, - kptr, - key.len() - ) -} - -// TODO: Use proper streaming hash; for mix_hash too. -#[inline] -pub fn hash_into(out: &mut [u8], data: &[u8]) -> Result<()> { - assert!(out.len() == KEY_SIZE); - blake2b_flexible(out, &NOTHING, data) -} - -#[inline] -pub fn hash(data: &[u8]) -> Result<[u8; KEY_SIZE]> { - let mut r = [0u8; KEY_SIZE]; - hash_into(&mut r, data)?; - Ok(r) -} - -#[inline] -pub fn mac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> { - assert!(out.len() == KEY_SIZE); - assert!(key.len() == KEY_SIZE); - blake2b_flexible(out, key, data) -} - -#[inline] -pub fn mac(key: &[u8], data: &[u8]) -> Result<[u8; KEY_SIZE]> { - let mut r = [0u8; KEY_SIZE]; - mac_into(&mut r, key, data)?; - Ok(r) -} - -#[inline] -pub fn mac16(key: &[u8], data: &[u8]) -> Result<[u8; 16]> { - assert!(key.len() == KEY_SIZE); - let mut out = [0u8; 16]; - blake2b_flexible(&mut out, key, data)?; - Ok(out) -} - -#[inline] -pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> { - // Not bothering with padding; the implementation - // uses appropriately sized keys. - ensure!(key.len() == KEY_SIZE); - - const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE]; - let mut temp_key = [0u8; KEY_SIZE]; - temp_key.copy_from_slice(key); - xor_into(&mut temp_key, &IPAD); - let outer_data = mac(&temp_key, data)?; - - const OPAD: [u8; KEY_SIZE] = [0x5Cu8; KEY_SIZE]; - temp_key.copy_from_slice(key); - xor_into(&mut temp_key, &OPAD); - mac_into(out, &temp_key, &outer_data) -} - -#[inline] -pub fn hmac(key: &[u8], data: &[u8]) -> Result<[u8; KEY_SIZE]> { - let mut r = [0u8; KEY_SIZE]; - hmac_into(&mut r, key, data)?; - Ok(r) -} diff --git a/sodium/Cargo.toml b/sodium/Cargo.toml index 1cd3bea..3108888 100644 --- a/sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -11,6 +11,7 @@ readme = "readme.md" [dependencies] rosenpass-util = { path = "../util" } +rosenpass-to = { path = "../to" } anyhow = { version = "1.0.71", features = ["backtrace"] } libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] } log = { version = "0.4.17" } diff --git a/sodium/src/hash/blake2b.rs b/sodium/src/hash/blake2b.rs new file mode 100644 index 0000000..e2f2fe3 --- /dev/null +++ b/sodium/src/hash/blake2b.rs @@ -0,0 +1,31 @@ +use libsodium_sys as libsodium; +use rosenpass_to::{with_destination, To}; +use std::ffi::c_ulonglong; +use std::ptr::null; + +pub const KEY_MIN: usize = libsodium::crypto_generichash_blake2b_KEYBYTES_MIN as usize; +pub const KEY_MAX: usize = libsodium::crypto_generichash_blake2b_KEYBYTES_MAX as usize; +pub const OUT_MIN: usize = libsodium::crypto_generichash_blake2b_BYTES_MIN as usize; +pub const OUT_MAX: usize = libsodium::crypto_generichash_blake2b_BYTES_MAX as usize; + +#[inline] +pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a { + with_destination(|out: &mut [u8]| { + assert!(key.is_empty() || (KEY_MIN <= key.len() && key.len() <= KEY_MAX)); + assert!(OUT_MIN <= out.len() && out.len() <= OUT_MAX); + let kptr = match key.len() { + // NULL key + 0 => null(), + _ => key.as_ptr(), + }; + sodium_call!( + crypto_generichash_blake2b, + out.as_mut_ptr(), + out.len(), + data.as_ptr(), + data.len() as c_ulonglong, + kptr, + key.len() + ) + }) +} diff --git a/sodium/src/hash/mod.rs b/sodium/src/hash/mod.rs new file mode 100644 index 0000000..52d9d26 --- /dev/null +++ b/sodium/src/hash/mod.rs @@ -0,0 +1 @@ +pub mod blake2b; diff --git a/sodium/src/lib.rs b/sodium/src/lib.rs index de43326..606bbd0 100644 --- a/sodium/src/lib.rs +++ b/sodium/src/lib.rs @@ -16,4 +16,5 @@ pub fn init() -> anyhow::Result<()> { } pub mod aead; +pub mod hash; pub mod helpers; From 40861cc2ea8922894c712989527aeeb59a29e6e7 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 28 Nov 2023 16:07:14 +0100 Subject: [PATCH 3/3] fix: Nix flake failing due to rosenpass-to README.md was missing; added it to the list of source files --- flake.nix | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index 8b994de..1f09b93 100644 --- a/flake.nix +++ b/flake.nix @@ -29,6 +29,7 @@ ] (system: let + scoped = (scope: scope.result); lib = nixpkgs.lib; # normal nixpkgs @@ -58,11 +59,35 @@ cargoToml = builtins.fromTOML (builtins.readFile ./rosenpass/Cargo.toml); # source files relevant for rust - src = pkgs.lib.sources.sourceFilesBySuffices ./. [ - ".lock" - ".rs" - ".toml" - ]; + src = scoped rec { + # File suffices to include + extensions = [ + "lock" + "rs" + "toml" + ]; + # Files to explicitly include + files = [ + "to/README.md" + ]; + + src = ./.; + filter = (path: type: scoped rec { + inherit (lib) any id removePrefix hasSuffix; + anyof = (any id); + + basename = baseNameOf (toString path); + relative = removePrefix (toString src + "/") (toString path); + + result = anyof [ + (type == "directory") + (any (ext: hasSuffix ".${ext}" basename) extensions) + (any (file: file == relative) files) + ]; + }); + + result = pkgs.lib.sources.cleanSourceWith { inherit src filter; }; + }; # builds a bin path for all dependencies for the `rp` shellscript rpBinPath = p: with p; lib.makeBinPath [