From c9cef05b2967412012866f81ec7201a4dfa71883 Mon Sep 17 00:00:00 2001 From: Emil Engler Date: Mon, 20 Nov 2023 20:20:52 +0100 Subject: [PATCH 01/46] doc: Add bibliography to the manual page Fixes #153 --- doc/rosenpass.1 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/rosenpass.1 b/doc/rosenpass.1 index 0da3bf9..f9ec510 100644 --- a/doc/rosenpass.1 +++ b/doc/rosenpass.1 @@ -91,9 +91,18 @@ This makes it possible to add peers entirely from .Sh SEE ALSO .Xr rp 1 , .Xr wg 1 +.Rs +.%A Karolin Varner +.%A Benjamin Lipp +.%A Wanja Zaeske +.%A Lisa Schmidt +.%D 2023 +.%T Rosenpass +.%U https://rosenpass.eu/whitepaper.pdf +.Re .Sh STANDARDS -This tool is the reference implementation of the Rosenpass protocol, written -by Karolin Varner, Benjamin Lipp, Wanja Zaeske, and Lisa Schmidt. +This tool is the reference implementation of the Rosenpass protocol, as +specified within the whitepaper referenced above. .Sh AUTHORS Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske, Marei Peischl, Stephan Ajuvo, and Lisa Schmidt. From a220c11e67d93e4045798e5e571f44186a1dec37 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 16 Nov 2023 18:39:19 +0100 Subject: [PATCH 02/46] chore: Move xor_into, copying and base64 utils into own crates --- Cargo.lock | 14 ++++- Cargo.toml | 2 + rosenpass-constant-time/Cargo.toml | 14 +++++ rosenpass-constant-time/readme.md | 5 ++ rosenpass-constant-time/src/lib.rs | 18 +++++++ rosenpass-util/Cargo.toml | 15 ++++++ rosenpass-util/src/b64.rs | 20 ++++++++ rosenpass-util/src/lib.rs | 3 ++ rosenpass-util/src/mem.rs | 33 ++++++++++++ rosenpass-util/src/result.rs | 7 +++ rosenpass/Cargo.toml | 3 +- rosenpass/src/app_server.rs | 3 +- rosenpass/src/coloring.rs | 3 +- rosenpass/src/protocol.rs | 1 + rosenpass/src/sodium.rs | 3 +- rosenpass/src/util.rs | 82 +----------------------------- 16 files changed, 141 insertions(+), 85 deletions(-) create mode 100644 rosenpass-constant-time/Cargo.toml create mode 100644 rosenpass-constant-time/readme.md create mode 100644 rosenpass-constant-time/src/lib.rs create mode 100644 rosenpass-util/Cargo.toml create mode 100644 rosenpass-util/src/b64.rs create mode 100644 rosenpass-util/src/lib.rs create mode 100644 rosenpass-util/src/mem.rs create mode 100644 rosenpass-util/src/result.rs diff --git a/Cargo.lock b/Cargo.lock index 1f805c7..fcf1d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1014,7 +1014,6 @@ name = "rosenpass" version = "0.2.1" dependencies = [ "anyhow", - "base64", "clap 4.4.8", "criterion", "env_logger", @@ -1025,6 +1024,8 @@ dependencies = [ "mio", "oqs-sys", "paste", + "rosenpass-constant-time", + "rosenpass-util", "serde", "stacker", "static_assertions", @@ -1033,6 +1034,17 @@ dependencies = [ "toml", ] +[[package]] +name = "rosenpass-constant-time" +version = "0.1.0" + +[[package]] +name = "rosenpass-util" +version = "0.1.0" +dependencies = [ + "base64", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index 768d034..87e2a78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,8 @@ resolver = "2" members = [ "rosenpass", + "rosenpass-util", + "rosenpass-constant-time", ] [workspace.metadata.release] diff --git a/rosenpass-constant-time/Cargo.toml b/rosenpass-constant-time/Cargo.toml new file mode 100644 index 0000000..205b0af --- /dev/null +++ b/rosenpass-constant-time/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rosenpass-constant-time" +version = "0.1.0" +authors = ["Karolin Varner ", "wucke13 "] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal utilities for constant time crypto implementations" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/rosenpass-constant-time/readme.md b/rosenpass-constant-time/readme.md new file mode 100644 index 0000000..583faab --- /dev/null +++ b/rosenpass-constant-time/readme.md @@ -0,0 +1,5 @@ +# Rosenpass constant time library + +Rosenpass internal library providing basic constant-time operations. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/rosenpass-constant-time/src/lib.rs b/rosenpass-constant-time/src/lib.rs new file mode 100644 index 0000000..c721e43 --- /dev/null +++ b/rosenpass-constant-time/src/lib.rs @@ -0,0 +1,18 @@ +/// Xors a and b element-wise and writes the result into a. +/// +/// # 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"); +/// ``` +#[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; + } +} diff --git a/rosenpass-util/Cargo.toml b/rosenpass-util/Cargo.toml new file mode 100644 index 0000000..d4e66db --- /dev/null +++ b/rosenpass-util/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rosenpass-util" +version = "0.1.0" +authors = ["Karolin Varner ", "wucke13 "] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal utilities" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +base64 = "0.21.1" diff --git a/rosenpass-util/src/b64.rs b/rosenpass-util/src/b64.rs new file mode 100644 index 0000000..7b0d083 --- /dev/null +++ b/rosenpass-util/src/b64.rs @@ -0,0 +1,20 @@ +use base64::{ + display::Base64Display as B64Display, read::DecoderReader as B64Reader, + write::EncoderWriter as B64Writer, +}; +use std::io::{Read, Write}; + +use base64::engine::general_purpose::GeneralPurpose as Base64Engine; +const B64ENGINE: Base64Engine = base64::engine::general_purpose::STANDARD; + +pub fn fmt_b64<'a>(payload: &'a [u8]) -> B64Display<'a, 'static, Base64Engine> { + B64Display::<'a, 'static>::new(payload, &B64ENGINE) +} + +pub fn b64_writer(w: W) -> B64Writer<'static, Base64Engine, W> { + B64Writer::new(w, &B64ENGINE) +} + +pub fn b64_reader(r: R) -> B64Reader<'static, Base64Engine, R> { + B64Reader::new(r, &B64ENGINE) +} diff --git a/rosenpass-util/src/lib.rs b/rosenpass-util/src/lib.rs new file mode 100644 index 0000000..343a6d0 --- /dev/null +++ b/rosenpass-util/src/lib.rs @@ -0,0 +1,3 @@ +pub mod b64; +pub mod mem; +pub mod result; diff --git a/rosenpass-util/src/mem.rs b/rosenpass-util/src/mem.rs new file mode 100644 index 0000000..be3d0e8 --- /dev/null +++ b/rosenpass-util/src/mem.rs @@ -0,0 +1,33 @@ +use std::borrow::{Borrow, BorrowMut}; +use std::cmp::min; + +/// Concatenate two byte arrays +// TODO: Zeroize result? +#[macro_export] +macro_rules! cat { + ($len:expr; $($toks:expr),+) => {{ + let mut buf = [0u8; $len]; + let mut off = 0; + $({ + let tok = $toks; + let tr = ::std::borrow::Borrow::<[u8]>::borrow(tok); + (&mut buf[off..(off + tr.len())]).copy_from_slice(tr); + off += tr.len(); + })+ + assert!(off == buf.len(), "Size mismatch in cat!()"); + buf + }} +} + +// TODO: consistent inout ordering +pub fn cpy + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) { + dst.borrow_mut().copy_from_slice(src.borrow()); +} + +/// Copy from `src` to `dst`. If `src` and `dst` are not of equal length, copy as many bytes as possible. +pub fn cpy_min + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) { + let src = src.borrow(); + let dst = dst.borrow_mut(); + let len = min(src.len(), dst.len()); + dst[..len].copy_from_slice(&src[..len]); +} diff --git a/rosenpass-util/src/result.rs b/rosenpass-util/src/result.rs new file mode 100644 index 0000000..8c3b7c2 --- /dev/null +++ b/rosenpass-util/src/result.rs @@ -0,0 +1,7 @@ +/// Try block basically…returns a result and allows the use of the question mark operator inside +#[macro_export] +macro_rules! attempt { + ($block:expr) => { + (|| -> ::anyhow::Result<_> { $block })() + }; +} diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index a2bd3e0..bc641a1 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -14,8 +14,9 @@ name = "handshake" harness = false [dependencies] +rosenpass-util = { path = "../rosenpass-util" } +rosenpass-constant-time = { path = "../rosenpass-constant-time" } anyhow = { version = "1.0.71", features = ["backtrace"] } -base64 = "0.21.1" static_assertions = "1.1.0" memoffset = "0.9.0" libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] } diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 8b18a2a..30a04ef 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -26,8 +26,9 @@ use crate::util::fopen_w; use crate::{ config::Verbosity, protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing}, - util::{b64_writer, fmt_b64}, }; +use rosenpass_util::attempt; +use rosenpass_util::b64::{b64_writer, fmt_b64}; const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); const IPV6_ANY_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index d709c12..12b0262 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -8,10 +8,11 @@ use crate::{ sodium::{rng, zeroize}, - util::{cpy, mutating}, + util::mutating, }; use lazy_static::lazy_static; use libsodium_sys as libsodium; +use rosenpass_util::mem::cpy; use std::{ collections::HashMap, convert::TryInto, diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 44cf57d..49b4ee3 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -77,6 +77,7 @@ use crate::{ util::*, }; use anyhow::{bail, ensure, Context, Result}; +use rosenpass_util::{cat, mem::cpy_min}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, HashMap, diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index fd01880..84ed5f4 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -1,9 +1,10 @@ //! Bindings and helpers for accessing libsodium functions -use crate::util::*; use anyhow::{ensure, Result}; use libsodium_sys as libsodium; use log::trace; +use rosenpass_constant_time::xor_into; +use rosenpass_util::attempt; use static_assertions::const_assert_eq; use std::os::raw::{c_ulonglong, c_void}; use std::ptr::{null as nullptr, null_mut as nullptr_mut}; diff --git a/rosenpass/src/util.rs b/rosenpass/src/util.rs index 2f7c231..e0fd587 100644 --- a/rosenpass/src/util.rs +++ b/rosenpass/src/util.rs @@ -1,92 +1,14 @@ //! Helper functions and macros use anyhow::{ensure, Context, Result}; -use base64::{ - display::Base64Display as B64Display, read::DecoderReader as B64Reader, - write::EncoderWriter as B64Writer, -}; use std::{ - borrow::{Borrow, BorrowMut}, - cmp::min, fs::{File, OpenOptions}, - io::{Read, Write}, + io::Read, path::Path, time::{Duration, Instant}, }; use crate::coloring::{Public, Secret}; - -/// Xors a and b element-wise and writes the result into a. -/// -/// # Examples -/// -/// ``` -/// use rosenpass::util::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"); -/// ``` -#[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; - } -} - -/// Concatenate two byte arrays -// TODO: Zeroize result? -#[macro_export] -macro_rules! cat { - ($len:expr; $($toks:expr),+) => {{ - let mut buf = [0u8; $len]; - let mut off = 0; - $({ - let tok = $toks; - let tr = ::std::borrow::Borrow::<[u8]>::borrow(tok); - (&mut buf[off..(off + tr.len())]).copy_from_slice(tr); - off += tr.len(); - })+ - assert!(off == buf.len(), "Size mismatch in cat!()"); - buf - }} -} - -// TODO: consistent inout ordering -pub fn cpy + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) { - dst.borrow_mut().copy_from_slice(src.borrow()); -} - -/// Copy from `src` to `dst`. If `src` and `dst` are not of equal length, copy as many bytes as possible. -pub fn cpy_min + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) { - let src = src.borrow(); - let dst = dst.borrow_mut(); - let len = min(src.len(), dst.len()); - dst[..len].copy_from_slice(&src[..len]); -} - -/// Try block basically…returns a result and allows the use of the question mark operator inside -#[macro_export] -macro_rules! attempt { - ($block:expr) => { - (|| -> ::anyhow::Result<_> { $block })() - }; -} - -use base64::engine::general_purpose::GeneralPurpose as Base64Engine; -const B64ENGINE: Base64Engine = base64::engine::general_purpose::STANDARD; - -pub fn fmt_b64<'a>(payload: &'a [u8]) -> B64Display<'a, 'static, Base64Engine> { - B64Display::<'a, 'static>::new(payload, &B64ENGINE) -} - -pub fn b64_writer(w: W) -> B64Writer<'static, Base64Engine, W> { - B64Writer::new(w, &B64ENGINE) -} - -pub fn b64_reader(r: R) -> B64Reader<'static, Base64Engine, R> { - B64Reader::new(r, &B64ENGINE) -} +use rosenpass_util::b64::b64_reader; // TODO remove this once std::cmp::max becomes const pub const fn max_usize(a: usize, b: usize) -> usize { From 6cd42ebf5082ff025d4de0f155488d01ce966518 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 16 Nov 2023 19:04:54 +0100 Subject: [PATCH 03/46] chore: move max_usize into util crate --- rosenpass-util/src/lib.rs | 1 + rosenpass-util/src/ord.rs | 8 ++++++++ rosenpass/src/protocol.rs | 2 +- rosenpass/src/util.rs | 9 --------- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 rosenpass-util/src/ord.rs diff --git a/rosenpass-util/src/lib.rs b/rosenpass-util/src/lib.rs index 343a6d0..60eb187 100644 --- a/rosenpass-util/src/lib.rs +++ b/rosenpass-util/src/lib.rs @@ -1,3 +1,4 @@ pub mod b64; pub mod mem; +pub mod ord; pub mod result; diff --git a/rosenpass-util/src/ord.rs b/rosenpass-util/src/ord.rs new file mode 100644 index 0000000..9a38488 --- /dev/null +++ b/rosenpass-util/src/ord.rs @@ -0,0 +1,8 @@ +// TODO remove this once std::cmp::max becomes const +pub const fn max_usize(a: usize, b: usize) -> usize { + if a > b { + a + } else { + b + } +} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 49b4ee3..27c7450 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -77,7 +77,7 @@ use crate::{ util::*, }; use anyhow::{bail, ensure, Context, Result}; -use rosenpass_util::{cat, mem::cpy_min}; +use rosenpass_util::{cat, mem::cpy_min, ord::max_usize}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, HashMap, diff --git a/rosenpass/src/util.rs b/rosenpass/src/util.rs index e0fd587..6e1d59e 100644 --- a/rosenpass/src/util.rs +++ b/rosenpass/src/util.rs @@ -10,15 +10,6 @@ use std::{ use crate::coloring::{Public, Secret}; use rosenpass_util::b64::b64_reader; -// TODO remove this once std::cmp::max becomes const -pub const fn max_usize(a: usize, b: usize) -> usize { - if a > b { - a - } else { - b - } -} - #[derive(Clone, Debug)] pub struct Timebase(Instant); From 556dbd26007cdfcea7eb1e932265621892066fb3 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 16 Nov 2023 19:09:02 +0100 Subject: [PATCH 04/46] chore: move time utils into util crate --- rosenpass-util/src/lib.rs | 1 + rosenpass-util/src/time.rs | 20 ++++++++++++++++++++ rosenpass/src/protocol.rs | 3 +-- rosenpass/src/util.rs | 20 -------------------- 4 files changed, 22 insertions(+), 22 deletions(-) create mode 100644 rosenpass-util/src/time.rs diff --git a/rosenpass-util/src/lib.rs b/rosenpass-util/src/lib.rs index 60eb187..17b24d3 100644 --- a/rosenpass-util/src/lib.rs +++ b/rosenpass-util/src/lib.rs @@ -2,3 +2,4 @@ pub mod b64; pub mod mem; pub mod ord; pub mod result; +pub mod time; diff --git a/rosenpass-util/src/time.rs b/rosenpass-util/src/time.rs new file mode 100644 index 0000000..8d107b5 --- /dev/null +++ b/rosenpass-util/src/time.rs @@ -0,0 +1,20 @@ +use std::time::{Duration, Instant}; + +#[derive(Clone, Debug)] +pub struct Timebase(Instant); + +impl Default for Timebase { + fn default() -> Self { + Self(Instant::now()) + } +} + +impl Timebase { + pub fn now(&self) -> f64 { + self.0.elapsed().as_secs_f64() + } + + pub fn dur(&self, t: f64) -> Duration { + Duration::from_secs_f64(t) + } +} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 27c7450..2af37d8 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -74,10 +74,9 @@ use crate::{ pqkem::*, prftree::{SecretPrfTree, SecretPrfTreeBranch}, sodium::*, - util::*, }; use anyhow::{bail, ensure, Context, Result}; -use rosenpass_util::{cat, mem::cpy_min, ord::max_usize}; +use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, HashMap, diff --git a/rosenpass/src/util.rs b/rosenpass/src/util.rs index 6e1d59e..d255c77 100644 --- a/rosenpass/src/util.rs +++ b/rosenpass/src/util.rs @@ -4,31 +4,11 @@ use std::{ fs::{File, OpenOptions}, io::Read, path::Path, - time::{Duration, Instant}, }; use crate::coloring::{Public, Secret}; use rosenpass_util::b64::b64_reader; -#[derive(Clone, Debug)] -pub struct Timebase(Instant); - -impl Default for Timebase { - fn default() -> Self { - Self(Instant::now()) - } -} - -impl Timebase { - pub fn now(&self) -> f64 { - self.0.elapsed().as_secs_f64() - } - - pub fn dur(&self, t: f64) -> Duration { - Duration::from_secs_f64(t) - } -} - pub fn mutating(mut v: T, f: F) -> T where F: Fn(&mut T), From 3db9755580e00eb5ca50ddd19aec779e5e9f3091 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 16 Nov 2023 19:17:22 +0100 Subject: [PATCH 05/46] chore: move functional utils into utils library --- rosenpass-util/src/functional.rs | 15 +++++++++++++++ rosenpass-util/src/lib.rs | 1 + rosenpass/src/coloring.rs | 7 ++----- rosenpass/src/util.rs | 16 ---------------- 4 files changed, 18 insertions(+), 21 deletions(-) create mode 100644 rosenpass-util/src/functional.rs diff --git a/rosenpass-util/src/functional.rs b/rosenpass-util/src/functional.rs new file mode 100644 index 0000000..c138a2e --- /dev/null +++ b/rosenpass-util/src/functional.rs @@ -0,0 +1,15 @@ +pub fn mutating(mut v: T, f: F) -> T +where + F: Fn(&mut T), +{ + f(&mut v); + v +} + +pub fn sideeffect(v: T, f: F) -> T +where + F: Fn(&T), +{ + f(&v); + v +} diff --git a/rosenpass-util/src/lib.rs b/rosenpass-util/src/lib.rs index 17b24d3..e5d25c3 100644 --- a/rosenpass-util/src/lib.rs +++ b/rosenpass-util/src/lib.rs @@ -1,4 +1,5 @@ pub mod b64; +pub mod functional; pub mod mem; pub mod ord; pub mod result; diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index 12b0262..2b890b9 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -6,13 +6,10 @@ //! - guard pages before and after each allocation trap accidential sequential reads that creep towards our secrets //! - the memory is mlocked, e.g. it is never swapped -use crate::{ - sodium::{rng, zeroize}, - util::mutating, -}; +use crate::sodium::{rng, zeroize}; use lazy_static::lazy_static; use libsodium_sys as libsodium; -use rosenpass_util::mem::cpy; +use rosenpass_util::{functional::mutating, mem::cpy}; use std::{ collections::HashMap, convert::TryInto, diff --git a/rosenpass/src/util.rs b/rosenpass/src/util.rs index d255c77..8d77504 100644 --- a/rosenpass/src/util.rs +++ b/rosenpass/src/util.rs @@ -9,22 +9,6 @@ use std::{ use crate::coloring::{Public, Secret}; use rosenpass_util::b64::b64_reader; -pub fn mutating(mut v: T, f: F) -> T -where - F: Fn(&mut T), -{ - f(&mut v); - v -} - -pub fn sideeffect(v: T, f: F) -> T -where - F: Fn(&T), -{ - f(&v); - v -} - /// load'n store /// Open a file writable From e50542193fadeade5a0f075c1345c796f6f4a29f Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 16 Nov 2023 19:37:47 +0100 Subject: [PATCH 06/46] chore: Move file utils into coloring or the util crate --- Cargo.lock | 1 + rosenpass-util/Cargo.toml | 1 + rosenpass-util/src/file.rs | 63 +++++++++++++++++++ rosenpass-util/src/lib.rs | 1 + rosenpass/src/app_server.rs | 2 +- rosenpass/src/cli.rs | 2 +- rosenpass/src/coloring.rs | 87 +++++++++++++++++++++++++- rosenpass/src/config.rs | 3 +- rosenpass/src/lib.rs | 2 - rosenpass/src/util.rs | 121 ------------------------------------ 10 files changed, 155 insertions(+), 128 deletions(-) create mode 100644 rosenpass-util/src/file.rs delete mode 100644 rosenpass/src/util.rs diff --git a/Cargo.lock b/Cargo.lock index fcf1d73..de81d0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1042,6 +1042,7 @@ version = "0.1.0" name = "rosenpass-util" version = "0.1.0" dependencies = [ + "anyhow", "base64", ] diff --git a/rosenpass-util/Cargo.toml b/rosenpass-util/Cargo.toml index d4e66db..d5b7bfa 100644 --- a/rosenpass-util/Cargo.toml +++ b/rosenpass-util/Cargo.toml @@ -13,3 +13,4 @@ readme = "readme.md" [dependencies] base64 = "0.21.1" +anyhow = { version = "1.0.71", features = ["backtrace"] } diff --git a/rosenpass-util/src/file.rs b/rosenpass-util/src/file.rs new file mode 100644 index 0000000..4843382 --- /dev/null +++ b/rosenpass-util/src/file.rs @@ -0,0 +1,63 @@ +use anyhow::ensure; +use std::fs::File; +use std::io::Read; +use std::result::Result; +use std::{fs::OpenOptions, path::Path}; + +/// Open a file writable +pub fn fopen_w>(path: P) -> std::io::Result { + Ok(OpenOptions::new() + .read(false) + .write(true) + .create(true) + .truncate(true) + .open(path)?) +} +/// Open a file readable +pub fn fopen_r>(path: P) -> std::io::Result { + Ok(OpenOptions::new() + .read(true) + .write(false) + .create(false) + .truncate(false) + .open(path)?) +} + +pub trait ReadExactToEnd { + type Error; + + fn read_exact_to_end(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>; +} + +impl ReadExactToEnd for R { + type Error = anyhow::Error; + + fn read_exact_to_end(&mut self, buf: &mut [u8]) -> anyhow::Result<()> { + let mut dummy = [0u8; 8]; + self.read_exact(buf)?; + ensure!(self.read(&mut dummy)? == 0, "File too long!"); + Ok(()) + } +} + +pub trait LoadValue { + type Error; + + fn load>(path: P) -> Result + where + Self: Sized; +} + +pub trait LoadValueB64 { + type Error; + + fn load_b64>(path: P) -> Result + where + Self: Sized; +} + +pub trait StoreValue { + type Error; + + fn store>(&self, path: P) -> Result<(), Self::Error>; +} diff --git a/rosenpass-util/src/lib.rs b/rosenpass-util/src/lib.rs index e5d25c3..49ded9c 100644 --- a/rosenpass-util/src/lib.rs +++ b/rosenpass-util/src/lib.rs @@ -1,4 +1,5 @@ pub mod b64; +pub mod file; pub mod functional; pub mod mem; pub mod ord; diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 30a04ef..d047f9b 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -4,6 +4,7 @@ use anyhow::Result; use log::{debug, error, info, warn}; use mio::Interest; use mio::Token; +use rosenpass_util::file::fopen_w; use std::cell::Cell; use std::io::Write; @@ -22,7 +23,6 @@ use std::slice; use std::thread; use std::time::Duration; -use crate::util::fopen_w; use crate::{ config::Verbosity, protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing}, diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 448048e..2e888f5 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -1,10 +1,10 @@ use anyhow::{bail, ensure}; use clap::Parser; +use rosenpass_util::file::{LoadValue, LoadValueB64}; use std::path::{Path, PathBuf}; use crate::app_server; use crate::app_server::AppServer; -use crate::util::{LoadValue, LoadValueB64}; use crate::{ // app_server::{AppServer, LoadValue, LoadValueB64}, coloring::Secret, diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index 2b890b9..e9b6695 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -7,15 +7,23 @@ //! - the memory is mlocked, e.g. it is never swapped use crate::sodium::{rng, zeroize}; +use anyhow::Context; use lazy_static::lazy_static; use libsodium_sys as libsodium; -use rosenpass_util::{functional::mutating, mem::cpy}; +use rosenpass_util::{ + b64::b64_reader, + file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd, StoreValue}, + functional::mutating, + mem::cpy, +}; +use std::result::Result; use std::{ collections::HashMap, convert::TryInto, fmt, ops::{Deref, DerefMut}, os::raw::c_void, + path::Path, ptr::null_mut, sync::Mutex, }; @@ -357,3 +365,80 @@ mod test { assert_eq!(new_secret.secret(), &[0; N]); } } + +trait StoreSecret { + type Error; + + fn store_secret>(&self, path: P) -> Result<(), Self::Error>; +} + +impl StoreSecret for T { + type Error = ::Error; + + fn store_secret>(&self, path: P) -> Result<(), Self::Error> { + self.store(path) + } +} + +impl LoadValue for Secret { + type Error = anyhow::Error; + + fn load>(path: P) -> anyhow::Result { + let mut v = Self::random(); + let p = path.as_ref(); + fopen_r(p)? + .read_exact_to_end(v.secret_mut()) + .with_context(|| format!("Could not load file {p:?}"))?; + Ok(v) + } +} + +impl LoadValueB64 for Secret { + type Error = anyhow::Error; + + fn load_b64>(path: P) -> anyhow::Result { + use std::io::Read; + + let mut v = Self::random(); + let p = path.as_ref(); + // This might leave some fragments of the secret on the stack; + // in practice this is likely not a problem because the stack likely + // will be overwritten by something else soon but this is not exactly + // guaranteed. It would be possible to remedy this, but since the secret + // data will linger in the Linux page cache anyways with the current + // implementation, going to great length to erase the secret here is + // not worth it right now. + b64_reader(&mut fopen_r(p)?) + .read_exact(v.secret_mut()) + .with_context(|| format!("Could not load base64 file {p:?}"))?; + Ok(v) + } +} + +impl StoreSecret for Secret { + type Error = anyhow::Error; + + fn store_secret>(&self, path: P) -> anyhow::Result<()> { + std::fs::write(path, self.secret())?; + Ok(()) + } +} + +impl LoadValue for Public { + type Error = anyhow::Error; + + fn load>(path: P) -> anyhow::Result { + let mut v = Self::random(); + fopen_r(path)?.read_exact_to_end(&mut *v)?; + Ok(v) + } +} + +impl StoreValue for Public { + type Error = anyhow::Error; + + fn store>(&self, path: P) -> anyhow::Result<()> { + std::fs::write(path, **self)?; + Ok(()) + } +} diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 183aa57..7f54645 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -7,10 +7,9 @@ use std::{ }; use anyhow::{bail, ensure}; +use rosenpass_util::file::fopen_w; use serde::{Deserialize, Serialize}; -use crate::util::fopen_w; - #[derive(Debug, Serialize, Deserialize)] pub struct Rosenpass { pub public_key: PathBuf, diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index df02553..894f65c 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,6 +1,4 @@ #[macro_use] -pub mod util; -#[macro_use] pub mod sodium; pub mod coloring; #[rustfmt::skip] diff --git a/rosenpass/src/util.rs b/rosenpass/src/util.rs deleted file mode 100644 index 8d77504..0000000 --- a/rosenpass/src/util.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Helper functions and macros -use anyhow::{ensure, Context, Result}; -use std::{ - fs::{File, OpenOptions}, - io::Read, - path::Path, -}; - -use crate::coloring::{Public, Secret}; -use rosenpass_util::b64::b64_reader; - -/// load'n store - -/// Open a file writable -pub fn fopen_w>(path: P) -> Result { - Ok(OpenOptions::new() - .read(false) - .write(true) - .create(true) - .truncate(true) - .open(path)?) -} -/// Open a file readable -pub fn fopen_r>(path: P) -> Result { - Ok(OpenOptions::new() - .read(true) - .write(false) - .create(false) - .truncate(false) - .open(path)?) -} - -pub trait ReadExactToEnd { - fn read_exact_to_end(&mut self, buf: &mut [u8]) -> Result<()>; -} - -impl ReadExactToEnd for R { - fn read_exact_to_end(&mut self, buf: &mut [u8]) -> Result<()> { - let mut dummy = [0u8; 8]; - self.read_exact(buf)?; - ensure!(self.read(&mut dummy)? == 0, "File too long!"); - Ok(()) - } -} - -pub trait LoadValue { - fn load>(path: P) -> Result - where - Self: Sized; -} - -pub trait LoadValueB64 { - fn load_b64>(path: P) -> Result - where - Self: Sized; -} - -trait StoreValue { - fn store>(&self, path: P) -> Result<()>; -} - -trait StoreSecret { - fn store_secret>(&self, path: P) -> Result<()>; -} - -impl StoreSecret for T { - fn store_secret>(&self, path: P) -> Result<()> { - self.store(path) - } -} - -impl LoadValue for Secret { - fn load>(path: P) -> Result { - let mut v = Self::random(); - let p = path.as_ref(); - fopen_r(p)? - .read_exact_to_end(v.secret_mut()) - .with_context(|| format!("Could not load file {p:?}"))?; - Ok(v) - } -} - -impl LoadValueB64 for Secret { - fn load_b64>(path: P) -> Result { - let mut v = Self::random(); - let p = path.as_ref(); - // This might leave some fragments of the secret on the stack; - // in practice this is likely not a problem because the stack likely - // will be overwritten by something else soon but this is not exactly - // guaranteed. It would be possible to remedy this, but since the secret - // data will linger in the Linux page cache anyways with the current - // implementation, going to great length to erase the secret here is - // not worth it right now. - b64_reader(&mut fopen_r(p)?) - .read_exact(v.secret_mut()) - .with_context(|| format!("Could not load base64 file {p:?}"))?; - Ok(v) - } -} - -impl StoreSecret for Secret { - fn store_secret>(&self, path: P) -> Result<()> { - std::fs::write(path, self.secret())?; - Ok(()) - } -} - -impl LoadValue for Public { - fn load>(path: P) -> Result { - let mut v = Self::random(); - fopen_r(path)?.read_exact_to_end(&mut *v)?; - Ok(v) - } -} - -impl StoreValue for Public { - fn store>(&self, path: P) -> Result<()> { - std::fs::write(path, **self)?; - Ok(()) - } -} From 46156fcb291740ddc0e21d9413299684a68b8301 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 11:21:53 +0100 Subject: [PATCH 07/46] fix: Setup cargo fmt to check the entire workspace --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 06f7be0..8b994de 100644 --- a/flake.nix +++ b/flake.nix @@ -325,7 +325,7 @@ checks = { cargo-fmt = pkgs.runCommand "check-cargo-fmt" { inherit (self.devShells.${system}.default) nativeBuildInputs buildInputs; } '' - cargo fmt --manifest-path=${./.}/Cargo.toml --check && touch $out + cargo fmt --manifest-path=${./.}/Cargo.toml --check --all && touch $out ''; nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt" { nativeBuildInputs = [ pkgs.nixpkgs-fmt ]; } '' From 99634d9702497d3f64f42d1c18c5f034f6aa6a5a Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 16:47:25 +0100 Subject: [PATCH 08/46] chore: Move sodium init integration into rosenpass-sodium crate --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + rosenpass-sodium/Cargo.toml | 16 ++++++++++++++++ rosenpass-sodium/readme.md | 5 +++++ rosenpass-sodium/src/lib.rs | 16 ++++++++++++++++ rosenpass/Cargo.toml | 8 +++----- rosenpass/src/main.rs | 11 +++++++++-- rosenpass/src/protocol.rs | 6 +++--- rosenpass/src/sodium.rs | 7 ------- 9 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 rosenpass-sodium/Cargo.toml create mode 100644 rosenpass-sodium/readme.md create mode 100644 rosenpass-sodium/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index de81d0a..b97ac4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,6 +1025,7 @@ dependencies = [ "oqs-sys", "paste", "rosenpass-constant-time", + "rosenpass-sodium", "rosenpass-util", "serde", "stacker", @@ -1038,6 +1039,16 @@ dependencies = [ name = "rosenpass-constant-time" version = "0.1.0" +[[package]] +name = "rosenpass-sodium" +version = "0.1.0" +dependencies = [ + "anyhow", + "libsodium-sys-stable", + "log", + "rosenpass-util", +] + [[package]] name = "rosenpass-util" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 87e2a78..c939129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "rosenpass", "rosenpass-util", "rosenpass-constant-time", + "rosenpass-sodium", ] [workspace.metadata.release] diff --git a/rosenpass-sodium/Cargo.toml b/rosenpass-sodium/Cargo.toml new file mode 100644 index 0000000..91229da --- /dev/null +++ b/rosenpass-sodium/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rosenpass-sodium" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal bindings to libsodium" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] +rosenpass-util = { path = "../rosenpass-util" } +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/rosenpass-sodium/readme.md b/rosenpass-sodium/readme.md new file mode 100644 index 0000000..0ddad45 --- /dev/null +++ b/rosenpass-sodium/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal libsodium bindings + +Rosenpass internal library providing bindings to libsodium. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs new file mode 100644 index 0000000..8a95fd9 --- /dev/null +++ b/rosenpass-sodium/src/lib.rs @@ -0,0 +1,16 @@ +use libsodium_sys as libsodium; + +macro_rules! sodium_call { + ($name:ident, $($args:expr),*) => { ::rosenpass_util::attempt!({ + anyhow::ensure!(unsafe{libsodium::$name($($args),*)} > -1, + "Error in libsodium's {}.", stringify!($name)); + Ok(()) + })}; + ($name:ident) => { sodium_call!($name, ) }; +} + +#[inline] +pub fn init() -> anyhow::Result<()> { + log::trace!("initializing libsodium"); + sodium_call!(sodium_init) +} diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index bc641a1..0fc1b49 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -16,6 +16,7 @@ harness = false [dependencies] rosenpass-util = { path = "../rosenpass-util" } rosenpass-constant-time = { path = "../rosenpass-constant-time" } +rosenpass-sodium = { path = "../rosenpass-sodium" } anyhow = { version = "1.0.71", features = ["backtrace"] } static_assertions = "1.1.0" memoffset = "0.9.0" @@ -24,8 +25,8 @@ oqs-sys = { version = "0.8", default-features = false, features = ['classic_mcel lazy_static = "1.4.0" thiserror = "1.0.40" paste = "1.0.12" -log = { version = "0.4.17", optional = true } -env_logger = { version = "0.10.0", optional = true } +log = { version = "0.4.17" } +env_logger = { version = "0.10.0" } serde = { version = "1.0.163", features = ["derive"] } toml = "0.7.4" clap = { version = "4.3.0", features = ["derive"] } @@ -38,6 +39,3 @@ anyhow = "1.0.71" criterion = "0.4.0" test_bin = "0.4.0" stacker = "0.1.15" - -[features] -default = ["log", "env_logger"] diff --git a/rosenpass/src/main.rs b/rosenpass/src/main.rs index f4af8ce..c6c9d84 100644 --- a/rosenpass/src/main.rs +++ b/rosenpass/src/main.rs @@ -1,11 +1,18 @@ use log::error; -use rosenpass::{cli::Cli, sodium::sodium_init}; +use rosenpass::cli::Cli; +use rosenpass_util::attempt; use std::process::exit; /// Catches errors, prints them through the logger, then exits pub fn main() { env_logger::init(); - match sodium_init().and_then(|()| Cli::run()) { + + let res = attempt!({ + rosenpass_sodium::init()?; + Cli::run() + }); + + match res { Ok(_) => {} Err(e) => { error!("{e}"); diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 2af37d8..41bf5fc 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -25,8 +25,8 @@ //! }; //! # fn main() -> anyhow::Result<()> { //! -//! // always init libsodium before anything -//! rosenpass::sodium::sodium_init()?; +//! // always initialize libsodium before anything +//! rosenpass_sodium::init()?; //! //! // initialize secret and public key for peer a ... //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); @@ -1750,7 +1750,7 @@ mod test { /// Through all this, the handshake should still successfully terminate; /// i.e. an exchanged key must be produced in both servers. fn handles_incorrect_size_messages() { - crate::sodium::sodium_init().unwrap(); + rosenpass_sodium::init().unwrap(); stacker::grow(8 * 1024 * 1024, || { const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize; diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index 84ed5f4..e41d35e 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -2,7 +2,6 @@ use anyhow::{ensure, Result}; use libsodium_sys as libsodium; -use log::trace; use rosenpass_constant_time::xor_into; use rosenpass_util::attempt; use static_assertions::const_assert_eq; @@ -34,12 +33,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn sodium_init() -> Result<()> { - trace!("initializing libsodium"); - sodium_call!(sodium_init) -} - #[inline] pub fn sodium_memcmp(a: &[u8], b: &[u8]) -> bool { a.len() == b.len() From 5da0e4115e01403a337e4edb547a6a65518c0ac0 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 17:02:08 +0100 Subject: [PATCH 09/46] chore: Move memcmp into rosenpass-sodium crate --- rosenpass-sodium/src/lib.rs | 14 ++++++++++++++ rosenpass/src/protocol.rs | 5 ++++- rosenpass/src/sodium.rs | 13 ------------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 8a95fd9..0efffa5 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -1,4 +1,5 @@ use libsodium_sys as libsodium; +use std::os::raw::c_void; macro_rules! sodium_call { ($name:ident, $($args:expr),*) => { ::rosenpass_util::attempt!({ @@ -14,3 +15,16 @@ pub fn init() -> anyhow::Result<()> { log::trace!("initializing libsodium"); sodium_call!(sodium_init) } + +#[inline] +pub fn memcmp(a: &[u8], b: &[u8]) -> bool { + a.len() == b.len() + && unsafe { + let r = libsodium::sodium_memcmp( + a.as_ptr() as *const c_void, + b.as_ptr() as *const c_void, + a.len(), + ); + r == 0 + } +} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 41bf5fc..a2ff386 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1190,7 +1190,10 @@ where /// Check the message authentication code pub fn check_seal(&self, srv: &CryptoServer) -> Result { let expected = lprf::mac()?.mix(srv.spkm.secret())?.mix(self.until_mac())?; - Ok(sodium_memcmp(self.mac(), &expected.into_value()[..16])) + Ok(rosenpass_sodium::memcmp( + self.mac(), + &expected.into_value()[..16], + )) } } diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index e41d35e..9f63c4a 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -33,19 +33,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn sodium_memcmp(a: &[u8], b: &[u8]) -> bool { - a.len() == b.len() - && unsafe { - let r = libsodium::sodium_memcmp( - a.as_ptr() as *const c_void, - b.as_ptr() as *const c_void, - a.len(), - ); - r == 0 - } -} - #[inline] pub fn sodium_bigint_cmp(a: &[u8], b: &[u8]) -> i32 { assert!(a.len() == b.len()); From a3de526db8953fa1ba9a531de808cb4aed5442ee Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 17:22:23 +0100 Subject: [PATCH 10/46] chore: Move libsodium's compare into rosenpass-sodium crate --- rosenpass-sodium/src/lib.rs | 6 ++++++ rosenpass/src/protocol.rs | 4 ++-- rosenpass/src/sodium.rs | 6 ------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 0efffa5..2d8dfc6 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -28,3 +28,9 @@ pub fn memcmp(a: &[u8], b: &[u8]) -> bool { r == 0 } } + +#[inline] +pub fn compare(a: &[u8], b: &[u8]) -> i32 { + assert!(a.len() == b.len()); + unsafe { libsodium::sodium_compare(a.as_ptr(), b.as_ptr(), a.len()) } +} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index a2ff386..57485a4 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1360,7 +1360,7 @@ impl HandshakeState { // indicates retransmission // TODO: Handle retransmissions without involving the crypto code ensure!( - sodium_bigint_cmp(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0, + rosenpass_sodium::compare(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0, "Rejecting biscuit: Outdated biscuit number" ); @@ -1637,7 +1637,7 @@ impl CryptoServer { core.decrypt_and_mix(&mut [0u8; 0], ic.auth())?; // ICR5 - if sodium_bigint_cmp(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 { + if rosenpass_sodium::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 { // ICR6 peer.get_mut(self).biscuit_used = biscuit_no; diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index 9f63c4a..e87df97 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -33,12 +33,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn sodium_bigint_cmp(a: &[u8], b: &[u8]) -> i32 { - assert!(a.len() == b.len()); - unsafe { libsodium::sodium_compare(a.as_ptr(), b.as_ptr(), a.len()) } -} - #[inline] pub fn sodium_bigint_inc(v: &mut [u8]) { unsafe { From 655314163702e416fa6c8ab347ac6b4abc41a918 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 17:29:27 +0100 Subject: [PATCH 11/46] chore: Move libsodium's increment into rosenpass-sodium crate --- rosenpass-sodium/src/lib.rs | 7 +++++++ rosenpass/src/protocol.rs | 2 +- rosenpass/src/sodium.rs | 7 ------- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 2d8dfc6..8cb5c3b 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -34,3 +34,10 @@ pub fn compare(a: &[u8], b: &[u8]) -> i32 { assert!(a.len() == b.len()); unsafe { libsodium::sodium_compare(a.as_ptr(), b.as_ptr(), a.len()) } } + +#[inline] +pub fn increment(v: &mut [u8]) { + unsafe { + libsodium::sodium_increment(v.as_mut_ptr(), v.len()); + } +} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 57485a4..6ca94ce 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1297,7 +1297,7 @@ impl HandshakeState { .into_value(); // consume biscuit no - sodium_bigint_inc(&mut *srv.biscuit_ctr); + rosenpass_sodium::increment(&mut *srv.biscuit_ctr); // The first bit of the nonce indicates which biscuit key was used // TODO: This is premature optimization. Remove! diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index e87df97..9ef1592 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -33,13 +33,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn sodium_bigint_inc(v: &mut [u8]) { - unsafe { - libsodium::sodium_increment(v.as_mut_ptr(), v.len()); - } -} - #[inline] pub fn rng(buf: &mut [u8]) { unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; From c5c34523f31c149d82073cd845fcb3f71e4de434 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 17:38:23 +0100 Subject: [PATCH 12/46] chore: Move libsodium's memzero, randombytes fns into rosenpass-sodium --- rosenpass-sodium/src/lib.rs | 24 ++++++++++++++++++++++++ rosenpass/src/coloring.rs | 7 +++---- rosenpass/src/protocol.rs | 2 +- rosenpass/src/sodium.rs | 24 +----------------------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 8cb5c3b..34176a8 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -41,3 +41,27 @@ pub fn increment(v: &mut [u8]) { libsodium::sodium_increment(v.as_mut_ptr(), v.len()); } } + +#[inline] +pub fn randombytes_buf(buf: &mut [u8]) { + unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; +} + +#[inline] +pub fn memzero(buf: &mut [u8]) { + unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) }; +} + +// Choose a fully random u64 +// TODO: Replace with ::rand::random +pub fn rand_u64() -> u64 { + let mut buf = [0u8; 8]; + randombytes_buf(&mut buf); + u64::from_le_bytes(buf) +} + +// Choose a random f64 in [0; 1] inclusive; quick and dirty +// TODO: Replace with ::rand::random +pub fn rand_f64() -> f64 { + (rand_u64() as f64) / (u64::MAX as f64) +} diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index e9b6695..5ddd923 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -6,7 +6,6 @@ //! - guard pages before and after each allocation trap accidential sequential reads that creep towards our secrets //! - the memory is mlocked, e.g. it is never swapped -use crate::sodium::{rng, zeroize}; use anyhow::Context; use lazy_static::lazy_static; use libsodium_sys as libsodium; @@ -179,12 +178,12 @@ impl Secret { /// Sets all data of an existing secret to null bytes pub fn zeroize(&mut self) { - zeroize(self.secret_mut()); + rosenpass_sodium::memzero(self.secret_mut()); } /// Sets all data an existing secret to random bytes pub fn randomize(&mut self) { - rng(self.secret_mut()); + rosenpass_sodium::randombytes_buf(self.secret_mut()); } /// Borrows the data @@ -249,7 +248,7 @@ impl Public { /// Randomize all bytes in an existing [Public] pub fn randomize(&mut self) { - rng(&mut self.value); + rosenpass_sodium::randombytes_buf(&mut self.value); } } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 6ca94ce..3b5cf8a 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1154,7 +1154,7 @@ impl IniHsPtr { .min(ih.tx_count as f64), ) * RETRANSMIT_DELAY_JITTER - * (rand_f64() + 1.0); + * (rosenpass_sodium::rand_f64() + 1.0); // TODO: Relace with the rand crate ih.tx_count += 1; Ok(()) } diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index 9ef1592..34fe7f4 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -5,7 +5,7 @@ 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, c_void}; +use std::os::raw::c_ulonglong; use std::ptr::{null as nullptr, null_mut as nullptr_mut}; pub const AEAD_TAG_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_ABYTES as usize; @@ -33,16 +33,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn rng(buf: &mut [u8]) { - unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - -#[inline] -pub fn zeroize(buf: &mut [u8]) { - unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - #[inline] pub fn aead_enc_into( ciphertext: &mut [u8], @@ -239,15 +229,3 @@ pub fn hmac(key: &[u8], data: &[u8]) -> Result<[u8; KEY_SIZE]> { hmac_into(&mut r, key, data)?; Ok(r) } - -// Choose a fully random u64 -pub fn rand_u64() -> u64 { - let mut buf = [0u8; 8]; - rng(&mut buf); - u64::from_le_bytes(buf) -} - -// Choose a random f64 in [0; 1] inclusive; quick and dirty -pub fn rand_f64() -> f64 { - (rand_u64() as f64) / (u64::MAX as f64) -} From 4c51ead078055b81c4696403b885e27efcd07d5f Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 17:58:00 +0100 Subject: [PATCH 13/46] chore: Move libsodium's helper function into their own namespace --- rosenpass-sodium/src/lib.rs | 51 +------------------------------------ rosenpass/src/coloring.rs | 6 ++--- rosenpass/src/protocol.rs | 11 ++++---- 3 files changed, 10 insertions(+), 58 deletions(-) diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 34176a8..5264379 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -1,5 +1,4 @@ use libsodium_sys as libsodium; -use std::os::raw::c_void; macro_rules! sodium_call { ($name:ident, $($args:expr),*) => { ::rosenpass_util::attempt!({ @@ -16,52 +15,4 @@ pub fn init() -> anyhow::Result<()> { sodium_call!(sodium_init) } -#[inline] -pub fn memcmp(a: &[u8], b: &[u8]) -> bool { - a.len() == b.len() - && unsafe { - let r = libsodium::sodium_memcmp( - a.as_ptr() as *const c_void, - b.as_ptr() as *const c_void, - a.len(), - ); - r == 0 - } -} - -#[inline] -pub fn compare(a: &[u8], b: &[u8]) -> i32 { - assert!(a.len() == b.len()); - unsafe { libsodium::sodium_compare(a.as_ptr(), b.as_ptr(), a.len()) } -} - -#[inline] -pub fn increment(v: &mut [u8]) { - unsafe { - libsodium::sodium_increment(v.as_mut_ptr(), v.len()); - } -} - -#[inline] -pub fn randombytes_buf(buf: &mut [u8]) { - unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - -#[inline] -pub fn memzero(buf: &mut [u8]) { - unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - -// Choose a fully random u64 -// TODO: Replace with ::rand::random -pub fn rand_u64() -> u64 { - let mut buf = [0u8; 8]; - randombytes_buf(&mut buf); - u64::from_le_bytes(buf) -} - -// Choose a random f64 in [0; 1] inclusive; quick and dirty -// TODO: Replace with ::rand::random -pub fn rand_f64() -> f64 { - (rand_u64() as f64) / (u64::MAX as f64) -} +pub mod helpers; diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index 5ddd923..ecbe6fe 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -178,12 +178,12 @@ impl Secret { /// Sets all data of an existing secret to null bytes pub fn zeroize(&mut self) { - rosenpass_sodium::memzero(self.secret_mut()); + rosenpass_sodium::helpers::memzero(self.secret_mut()); } /// Sets all data an existing secret to random bytes pub fn randomize(&mut self) { - rosenpass_sodium::randombytes_buf(self.secret_mut()); + rosenpass_sodium::helpers::randombytes_buf(self.secret_mut()); } /// Borrows the data @@ -248,7 +248,7 @@ impl Public { /// Randomize all bytes in an existing [Public] pub fn randomize(&mut self) { - rosenpass_sodium::randombytes_buf(&mut self.value); + rosenpass_sodium::helpers::randombytes_buf(&mut self.value); } } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 3b5cf8a..da7020a 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1154,7 +1154,7 @@ impl IniHsPtr { .min(ih.tx_count as f64), ) * RETRANSMIT_DELAY_JITTER - * (rosenpass_sodium::rand_f64() + 1.0); // TODO: Relace with the rand crate + * (rosenpass_sodium::helpers::rand_f64() + 1.0); // TODO: Replace with the rand crate ih.tx_count += 1; Ok(()) } @@ -1190,7 +1190,7 @@ where /// Check the message authentication code pub fn check_seal(&self, srv: &CryptoServer) -> Result { let expected = lprf::mac()?.mix(srv.spkm.secret())?.mix(self.until_mac())?; - Ok(rosenpass_sodium::memcmp( + Ok(rosenpass_sodium::helpers::memcmp( self.mac(), &expected.into_value()[..16], )) @@ -1297,7 +1297,7 @@ impl HandshakeState { .into_value(); // consume biscuit no - rosenpass_sodium::increment(&mut *srv.biscuit_ctr); + rosenpass_sodium::helpers::increment(&mut *srv.biscuit_ctr); // The first bit of the nonce indicates which biscuit key was used // TODO: This is premature optimization. Remove! @@ -1360,7 +1360,8 @@ impl HandshakeState { // indicates retransmission // TODO: Handle retransmissions without involving the crypto code ensure!( - rosenpass_sodium::compare(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0, + rosenpass_sodium::helpers::compare(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) + >= 0, "Rejecting biscuit: Outdated biscuit number" ); @@ -1637,7 +1638,7 @@ impl CryptoServer { core.decrypt_and_mix(&mut [0u8; 0], ic.auth())?; // ICR5 - if rosenpass_sodium::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 { + if rosenpass_sodium::helpers::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 { // ICR6 peer.get_mut(self).biscuit_used = biscuit_no; From c64e721c2fcca9cee41755f5dce00f85e302af21 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 18:21:44 +0100 Subject: [PATCH 14/46] chore: Move chacha20 implementation out of rosenpass::sodium Introduces a new crate for selected ciphers which references a cipher implementation in the rosenpass-sodium crate. --- Cargo.lock | 8 +++ rosenpass-ciphers/Cargo.toml | 13 ++++ rosenpass-ciphers/readme.md | 5 ++ rosenpass-ciphers/src/lib.rs | 5 ++ .../src/aead/chacha20poly1305_ietf.rs | 63 +++++++++++++++++++ rosenpass-sodium/src/aead/mod.rs | 1 + rosenpass-sodium/src/helpers.rs | 52 +++++++++++++++ rosenpass-sodium/src/lib.rs | 1 + rosenpass/Cargo.toml | 1 + rosenpass/src/msgs.rs | 11 ++-- rosenpass/src/protocol.rs | 15 ++--- rosenpass/src/sodium.rs | 58 ----------------- 12 files changed, 163 insertions(+), 70 deletions(-) create mode 100644 rosenpass-ciphers/Cargo.toml create mode 100644 rosenpass-ciphers/readme.md create mode 100644 rosenpass-ciphers/src/lib.rs create mode 100644 rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs create mode 100644 rosenpass-sodium/src/aead/mod.rs create mode 100644 rosenpass-sodium/src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index b97ac4a..4fe895e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,6 +1024,7 @@ dependencies = [ "mio", "oqs-sys", "paste", + "rosenpass-ciphers", "rosenpass-constant-time", "rosenpass-sodium", "rosenpass-util", @@ -1035,6 +1036,13 @@ dependencies = [ "toml", ] +[[package]] +name = "rosenpass-ciphers" +version = "0.1.0" +dependencies = [ + "rosenpass-sodium", +] + [[package]] name = "rosenpass-constant-time" version = "0.1.0" diff --git a/rosenpass-ciphers/Cargo.toml b/rosenpass-ciphers/Cargo.toml new file mode 100644 index 0000000..d147974 --- /dev/null +++ b/rosenpass-ciphers/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rosenpass-ciphers" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal ciphers and other cryptographic primitives used by rosenpass." +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] +rosenpass-sodium = { path = "../rosenpass-sodium" } diff --git a/rosenpass-ciphers/readme.md b/rosenpass-ciphers/readme.md new file mode 100644 index 0000000..4009539 --- /dev/null +++ b/rosenpass-ciphers/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal cryptographic primitives + +Ciphers and other cryptographic primitives used by rosenpass. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/rosenpass-ciphers/src/lib.rs b/rosenpass-ciphers/src/lib.rs new file mode 100644 index 0000000..f9e6e00 --- /dev/null +++ b/rosenpass-ciphers/src/lib.rs @@ -0,0 +1,5 @@ +pub mod aead { + pub use rosenpass_sodium::aead::chacha20poly1305_ietf::{ + decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, + }; +} diff --git a/rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs b/rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs new file mode 100644 index 0000000..7f35f6c --- /dev/null +++ b/rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs @@ -0,0 +1,63 @@ +use libsodium_sys as libsodium; +use std::ffi::c_ulonglong; +use std::ptr::{null, null_mut}; + +pub const KEY_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_KEYBYTES as usize; +pub const TAG_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_ABYTES as usize; +pub const NONCE_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize; + +#[inline] +pub fn encrypt( + ciphertext: &mut [u8], + key: &[u8], + nonce: &[u8], + ad: &[u8], + plaintext: &[u8], +) -> anyhow::Result<()> { + assert!(ciphertext.len() == plaintext.len() + TAG_LEN); + assert!(key.len() == KEY_LEN); + assert!(nonce.len() == NONCE_LEN); + let mut clen: u64 = 0; + sodium_call!( + crypto_aead_chacha20poly1305_ietf_encrypt, + ciphertext.as_mut_ptr(), + &mut clen, + plaintext.as_ptr(), + plaintext.len() as c_ulonglong, + ad.as_ptr(), + ad.len() as c_ulonglong, + null(), // nsec is not used + nonce.as_ptr(), + key.as_ptr() + )?; + assert!(clen as usize == ciphertext.len()); + Ok(()) +} + +#[inline] +pub fn decrypt( + plaintext: &mut [u8], + key: &[u8], + nonce: &[u8], + ad: &[u8], + ciphertext: &[u8], +) -> anyhow::Result<()> { + assert!(ciphertext.len() == plaintext.len() + TAG_LEN); + assert!(key.len() == KEY_LEN); + assert!(nonce.len() == NONCE_LEN); + let mut mlen: u64 = 0; + sodium_call!( + crypto_aead_chacha20poly1305_ietf_decrypt, + plaintext.as_mut_ptr(), + &mut mlen as *mut c_ulonglong, + null_mut(), // nsec is not used + ciphertext.as_ptr(), + ciphertext.len() as c_ulonglong, + ad.as_ptr(), + ad.len() as c_ulonglong, + nonce.as_ptr(), + key.as_ptr() + )?; + assert!(mlen as usize == plaintext.len()); + Ok(()) +} diff --git a/rosenpass-sodium/src/aead/mod.rs b/rosenpass-sodium/src/aead/mod.rs new file mode 100644 index 0000000..c4e2b4e --- /dev/null +++ b/rosenpass-sodium/src/aead/mod.rs @@ -0,0 +1 @@ +pub mod chacha20poly1305_ietf; diff --git a/rosenpass-sodium/src/helpers.rs b/rosenpass-sodium/src/helpers.rs new file mode 100644 index 0000000..a83bc99 --- /dev/null +++ b/rosenpass-sodium/src/helpers.rs @@ -0,0 +1,52 @@ +use libsodium_sys as libsodium; +use std::os::raw::c_void; + +#[inline] +pub fn memcmp(a: &[u8], b: &[u8]) -> bool { + a.len() == b.len() + && unsafe { + let r = libsodium::sodium_memcmp( + a.as_ptr() as *const c_void, + b.as_ptr() as *const c_void, + a.len(), + ); + r == 0 + } +} + +#[inline] +pub fn compare(a: &[u8], b: &[u8]) -> i32 { + assert!(a.len() == b.len()); + unsafe { libsodium::sodium_compare(a.as_ptr(), b.as_ptr(), a.len()) } +} + +#[inline] +pub fn increment(v: &mut [u8]) { + unsafe { + libsodium::sodium_increment(v.as_mut_ptr(), v.len()); + } +} + +#[inline] +pub fn randombytes_buf(buf: &mut [u8]) { + unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; +} + +#[inline] +pub fn memzero(buf: &mut [u8]) { + unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) }; +} + +// Choose a fully random u64 +// TODO: Replace with ::rand::random +pub fn rand_u64() -> u64 { + let mut buf = [0u8; 8]; + randombytes_buf(&mut buf); + u64::from_le_bytes(buf) +} + +// Choose a random f64 in [0; 1] inclusive; quick and dirty +// TODO: Replace with ::rand::random +pub fn rand_f64() -> f64 { + (rand_u64() as f64) / (u64::MAX as f64) +} diff --git a/rosenpass-sodium/src/lib.rs b/rosenpass-sodium/src/lib.rs index 5264379..de43326 100644 --- a/rosenpass-sodium/src/lib.rs +++ b/rosenpass-sodium/src/lib.rs @@ -15,4 +15,5 @@ pub fn init() -> anyhow::Result<()> { sodium_call!(sodium_init) } +pub mod aead; pub mod helpers; diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 0fc1b49..197c439 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -17,6 +17,7 @@ harness = false rosenpass-util = { path = "../rosenpass-util" } rosenpass-constant-time = { path = "../rosenpass-constant-time" } rosenpass-sodium = { path = "../rosenpass-sodium" } +rosenpass-ciphers = { path = "../rosenpass-ciphers" } anyhow = { version = "1.0.71", features = ["backtrace"] } static_assertions = "1.1.0" memoffset = "0.9.0" diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 9108f1b..61f478d 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -45,6 +45,7 @@ use super::RosenpassError; use crate::{pqkem::*, sodium}; +use rosenpass_ciphers::aead; // Macro magic //////////////////////////////////////////////////////////////// @@ -277,9 +278,9 @@ data_lense! { InitHello := /// Classic McEliece Ciphertext sctr: StaticKEM::CT_LEN, /// Encryped: 16 byte hash of McEliece initiator static key - pidic: sodium::AEAD_TAG_LEN + 32, + pidic: aead::TAG_LEN + 32, /// Encrypted TAI64N Time Stamp (against replay attacks) - auth: sodium::AEAD_TAG_LEN + auth: aead::TAG_LEN } data_lense! { RespHello := @@ -292,7 +293,7 @@ data_lense! { RespHello := /// Classic McEliece Ciphertext scti: StaticKEM::CT_LEN, /// Empty encrypted message (just an auth tag) - auth: sodium::AEAD_TAG_LEN, + auth: aead::TAG_LEN, /// Responders handshake state in encrypted form biscuit: BISCUIT_CT_LEN } @@ -305,7 +306,7 @@ data_lense! { InitConf := /// Responders handshake state in encrypted form biscuit: BISCUIT_CT_LEN, /// Empty encrypted message (just an auth tag) - auth: sodium::AEAD_TAG_LEN + auth: aead::TAG_LEN } data_lense! { EmptyData := @@ -314,7 +315,7 @@ data_lense! { EmptyData := /// Nonce ctr: 8, /// Empty encrypted message (just an auth tag) - auth: sodium::AEAD_TAG_LEN + auth: aead::TAG_LEN } data_lense! { Biscuit := diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index da7020a..6272741 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -76,6 +76,7 @@ use crate::{ sodium::*, }; use anyhow::{bail, ensure, Context, Result}; +use rosenpass_ciphers::aead; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, @@ -1239,13 +1240,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_enc_into(ct, k.secret(), &NONCE0, &NOTHING, pt)?; + aead::encrypt(ct, k.secret(), &NONCE0, &NOTHING, 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_dec_into(pt, k.secret(), &NONCE0, &NOTHING, ct)?; + aead::decrypt(pt, k.secret(), &NONCE0, &NOTHING, ct)?; self.mix(ct) } @@ -1629,7 +1630,7 @@ impl CryptoServer { )?; // ICR2 - core.encrypt_and_mix(&mut [0u8; AEAD_TAG_LEN], &NOTHING)?; + core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &NOTHING)?; // ICR3 core.mix(ic.sidi())?.mix(ic.sidr())?; @@ -1683,9 +1684,9 @@ impl CryptoServer { rc.ctr_mut().copy_from_slice(&ses.txnm.to_le_bytes()); ses.txnm += 1; // Increment nonce before encryption, just in case an error is raised - let n = cat!(AEAD_NONCE_LEN; rc.ctr(), &[0u8; 4]); + let n = cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]); let k = ses.txkm.secret(); - aead_enc_into(rc.auth_mut(), k, &n, &NOTHING, &NOTHING)?; // ct, k, n, ad, pt + aead::encrypt(rc.auth_mut(), k, &n, &NOTHING, &NOTHING)?; // ct, k, n, ad, pt Ok(peer) } @@ -1717,11 +1718,11 @@ impl CryptoServer { let n = u64::from_le_bytes(rc.ctr().try_into().unwrap()); ensure!(n >= s.txnt, "Stale nonce"); s.txnt = n; - aead_dec_into( + aead::decrypt( // pt, k, n, ad, ct &mut [0u8; 0], s.txkt.secret(), - &cat!(AEAD_NONCE_LEN; rc.ctr(), &[0u8; 4]), + &cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]), &NOTHING, rc.auth(), )?; diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index 34fe7f4..b0945c5 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -8,8 +8,6 @@ use static_assertions::const_assert_eq; use std::os::raw::c_ulonglong; use std::ptr::{null as nullptr, null_mut as nullptr_mut}; -pub const AEAD_TAG_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_ABYTES as usize; -pub const AEAD_NONCE_LEN: usize = libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize; pub const XAEAD_TAG_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_ietf_ABYTES as usize; pub const XAEAD_NONCE_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_IETF_NPUBBYTES as usize; pub const NONCE0: [u8; libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize] = @@ -33,62 +31,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn aead_enc_into( - ciphertext: &mut [u8], - key: &[u8], - nonce: &[u8], - ad: &[u8], - plaintext: &[u8], -) -> Result<()> { - assert!(ciphertext.len() == plaintext.len() + AEAD_TAG_LEN); - assert!(key.len() == libsodium::crypto_aead_chacha20poly1305_IETF_KEYBYTES as usize); - assert!(nonce.len() == libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize); - let mut clen: u64 = 0; - sodium_call!( - crypto_aead_chacha20poly1305_ietf_encrypt, - ciphertext.as_mut_ptr(), - &mut clen, - plaintext.as_ptr(), - plaintext.len() as c_ulonglong, - ad.as_ptr(), - ad.len() as c_ulonglong, - nullptr(), // nsec is not used - nonce.as_ptr(), - key.as_ptr() - )?; - assert!(clen as usize == ciphertext.len()); - Ok(()) -} - -#[inline] -pub fn aead_dec_into( - plaintext: &mut [u8], - key: &[u8], - nonce: &[u8], - ad: &[u8], - ciphertext: &[u8], -) -> Result<()> { - assert!(ciphertext.len() == plaintext.len() + AEAD_TAG_LEN); - assert!(key.len() == libsodium::crypto_aead_chacha20poly1305_IETF_KEYBYTES as usize); - assert!(nonce.len() == libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize); - let mut mlen: u64 = 0; - sodium_call!( - crypto_aead_chacha20poly1305_ietf_decrypt, - plaintext.as_mut_ptr(), - &mut mlen as *mut c_ulonglong, - nullptr_mut(), // nsec is not used - ciphertext.as_ptr(), - ciphertext.len() as c_ulonglong, - ad.as_ptr(), - ad.len() as c_ulonglong, - nonce.as_ptr(), - key.as_ptr() - )?; - assert!(mlen as usize == plaintext.len()); - Ok(()) -} - #[inline] pub fn xaead_enc_into( ciphertext: &mut [u8], From 3ddf736b60eee06ab0554dcd37a6a0453b4ac323 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 17 Nov 2023 18:38:07 +0100 Subject: [PATCH 15/46] chore: Move xchacha20 implementation out of rosenpass::sodium --- Cargo.toml | 1 + rosenpass-ciphers/src/lib.rs | 6 ++ rosenpass-sodium/src/aead/mod.rs | 1 + .../src/aead/xchacha20poly1305_ietf.rs | 63 +++++++++++++++++++ rosenpass/src/msgs.rs | 7 ++- rosenpass/src/protocol.rs | 8 +-- rosenpass/src/sodium.rs | 60 +----------------- 7 files changed, 80 insertions(+), 66 deletions(-) create mode 100644 rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs diff --git a/Cargo.toml b/Cargo.toml index c939129..9bf207f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "rosenpass", + "rosenpass-ciphers", "rosenpass-util", "rosenpass-constant-time", "rosenpass-sodium", diff --git a/rosenpass-ciphers/src/lib.rs b/rosenpass-ciphers/src/lib.rs index f9e6e00..14fe2ac 100644 --- a/rosenpass-ciphers/src/lib.rs +++ b/rosenpass-ciphers/src/lib.rs @@ -3,3 +3,9 @@ pub mod aead { decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, }; } + +pub mod xaead { + pub use rosenpass_sodium::aead::xchacha20poly1305_ietf::{ + decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, + }; +} diff --git a/rosenpass-sodium/src/aead/mod.rs b/rosenpass-sodium/src/aead/mod.rs index c4e2b4e..0f10c84 100644 --- a/rosenpass-sodium/src/aead/mod.rs +++ b/rosenpass-sodium/src/aead/mod.rs @@ -1 +1,2 @@ pub mod chacha20poly1305_ietf; +pub mod xchacha20poly1305_ietf; diff --git a/rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs b/rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs new file mode 100644 index 0000000..a5d86c2 --- /dev/null +++ b/rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs @@ -0,0 +1,63 @@ +use libsodium_sys as libsodium; +use std::ffi::c_ulonglong; +use std::ptr::{null, null_mut}; + +pub const KEY_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_IETF_KEYBYTES as usize; +pub const TAG_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_ietf_ABYTES as usize; +pub const NONCE_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_IETF_NPUBBYTES as usize; + +#[inline] +pub fn encrypt( + ciphertext: &mut [u8], + key: &[u8], + nonce: &[u8], + ad: &[u8], + plaintext: &[u8], +) -> anyhow::Result<()> { + assert!(ciphertext.len() == plaintext.len() + NONCE_LEN + TAG_LEN); + assert!(key.len() == libsodium::crypto_aead_xchacha20poly1305_IETF_KEYBYTES as usize); + let (n, ct) = ciphertext.split_at_mut(NONCE_LEN); + n.copy_from_slice(nonce); + let mut clen: u64 = 0; + sodium_call!( + crypto_aead_xchacha20poly1305_ietf_encrypt, + ct.as_mut_ptr(), + &mut clen, + plaintext.as_ptr(), + plaintext.len() as c_ulonglong, + ad.as_ptr(), + ad.len() as c_ulonglong, + null(), // nsec is not used + nonce.as_ptr(), + key.as_ptr() + )?; + assert!(clen as usize == ct.len()); + Ok(()) +} + +#[inline] +pub fn decrypt( + plaintext: &mut [u8], + key: &[u8], + ad: &[u8], + ciphertext: &[u8], +) -> anyhow::Result<()> { + assert!(ciphertext.len() == plaintext.len() + NONCE_LEN + TAG_LEN); + assert!(key.len() == KEY_LEN); + let (n, ct) = ciphertext.split_at(NONCE_LEN); + let mut mlen: u64 = 0; + sodium_call!( + crypto_aead_xchacha20poly1305_ietf_decrypt, + plaintext.as_mut_ptr(), + &mut mlen as *mut c_ulonglong, + null_mut(), // nsec is not used + ct.as_ptr(), + ct.len() as c_ulonglong, + ad.as_ptr(), + ad.len() as c_ulonglong, + n.as_ptr(), + key.as_ptr() + )?; + assert!(mlen as usize == plaintext.len()); + Ok(()) +} diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 61f478d..b77af50 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -45,7 +45,7 @@ use super::RosenpassError; use crate::{pqkem::*, sodium}; -use rosenpass_ciphers::aead; +use rosenpass_ciphers::{aead, xaead}; // Macro magic //////////////////////////////////////////////////////////////// @@ -385,7 +385,7 @@ impl TryFrom for MsgType { pub const BISCUIT_PT_LEN: usize = Biscuit::<()>::LEN; /// Length in bytes of an encrypted Biscuit (cipher text) -pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + sodium::XAEAD_NONCE_LEN + sodium::XAEAD_TAG_LEN; +pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN; #[cfg(test)] mod test_constants { @@ -393,6 +393,7 @@ mod test_constants { msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN}, sodium, }; + use rosenpass_ciphers::xaead; #[test] fn sodium_keysize() { @@ -408,7 +409,7 @@ mod test_constants { fn biscuit_ct_len() { assert_eq!( BISCUIT_CT_LEN, - BISCUIT_PT_LEN + sodium::XAEAD_NONCE_LEN + sodium::XAEAD_TAG_LEN + BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN ); } } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 6272741..a68c3ed 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -76,7 +76,7 @@ use crate::{ sodium::*, }; use anyhow::{bail, ensure, Context, Result}; -use rosenpass_ciphers::aead; +use rosenpass_ciphers::{aead, xaead}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, @@ -152,7 +152,7 @@ pub type PeerId = Public; pub type SessionId = Public; pub type BiscuitId = Public; -pub type XAEADNonce = Public; +pub type XAEADNonce = Public<{ xaead::NONCE_LEN }>; pub type MsgBuf = Public; @@ -1309,7 +1309,7 @@ impl HandshakeState { let k = bk.get(srv).key.secret(); let pt = biscuit.all_bytes(); - xaead_enc_into(biscuit_ct, k, &*n, &ad, pt)?; + xaead::encrypt(biscuit_ct, k, &*n, &ad, pt)?; self.mix(biscuit_ct) } @@ -1335,7 +1335,7 @@ impl HandshakeState { // Allocate and decrypt the biscuit data let mut biscuit = Secret::::zero(); // pt buf let mut biscuit = (&mut biscuit.secret_mut()[..]).biscuit()?; // slice - xaead_dec_into( + xaead::decrypt( biscuit.all_bytes_mut(), bk.get(srv).key.secret(), &ad, diff --git a/rosenpass/src/sodium.rs b/rosenpass/src/sodium.rs index b0945c5..48e8fb9 100644 --- a/rosenpass/src/sodium.rs +++ b/rosenpass/src/sodium.rs @@ -6,10 +6,8 @@ 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, null_mut as nullptr_mut}; +use std::ptr::null as nullptr; -pub const XAEAD_TAG_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_ietf_ABYTES as usize; -pub const XAEAD_NONCE_LEN: usize = libsodium::crypto_aead_xchacha20poly1305_IETF_NPUBBYTES as usize; 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]; @@ -31,62 +29,6 @@ macro_rules! sodium_call { ($name:ident) => { sodium_call!($name, ) }; } -#[inline] -pub fn xaead_enc_into( - ciphertext: &mut [u8], - key: &[u8], - nonce: &[u8], - ad: &[u8], - plaintext: &[u8], -) -> Result<()> { - assert!(ciphertext.len() == plaintext.len() + XAEAD_NONCE_LEN + XAEAD_TAG_LEN); - assert!(key.len() == libsodium::crypto_aead_xchacha20poly1305_IETF_KEYBYTES as usize); - let (n, ct) = ciphertext.split_at_mut(XAEAD_NONCE_LEN); - n.copy_from_slice(nonce); - let mut clen: u64 = 0; - sodium_call!( - crypto_aead_xchacha20poly1305_ietf_encrypt, - ct.as_mut_ptr(), - &mut clen, - plaintext.as_ptr(), - plaintext.len() as c_ulonglong, - ad.as_ptr(), - ad.len() as c_ulonglong, - nullptr(), // nsec is not used - nonce.as_ptr(), - key.as_ptr() - )?; - assert!(clen as usize == ct.len()); - Ok(()) -} - -#[inline] -pub fn xaead_dec_into( - plaintext: &mut [u8], - key: &[u8], - ad: &[u8], - ciphertext: &[u8], -) -> Result<()> { - assert!(ciphertext.len() == plaintext.len() + XAEAD_NONCE_LEN + XAEAD_TAG_LEN); - assert!(key.len() == libsodium::crypto_aead_xchacha20poly1305_IETF_KEYBYTES as usize); - let (n, ct) = ciphertext.split_at(XAEAD_NONCE_LEN); - let mut mlen: u64 = 0; - sodium_call!( - crypto_aead_xchacha20poly1305_ietf_decrypt, - plaintext.as_mut_ptr(), - &mut mlen as *mut c_ulonglong, - nullptr_mut(), // nsec is not used - ct.as_ptr(), - ct.len() as c_ulonglong, - ad.as_ptr(), - ad.len() as c_ulonglong, - n.as_ptr(), - key.as_ptr() - )?; - assert!(mlen as usize == plaintext.len()); - Ok(()) -} - #[inline] fn blake2b_flexible(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> { const KEY_MIN: usize = libsodium::crypto_generichash_KEYBYTES_MIN as usize; From 86300ca9367adf9863e8af098dd0b8a4548e4b37 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Mon, 20 Nov 2023 10:44:26 +0100 Subject: [PATCH 16/46] chore: Use naming scheme without rosenpass- for crates --- Cargo.toml | 8 ++++---- {rosenpass-ciphers => ciphers}/Cargo.toml | 2 +- {rosenpass-ciphers => ciphers}/readme.md | 0 {rosenpass-ciphers => ciphers}/src/lib.rs | 0 {rosenpass-constant-time => constant-time}/Cargo.toml | 0 {rosenpass-constant-time => constant-time}/readme.md | 0 {rosenpass-constant-time => constant-time}/src/lib.rs | 0 rosenpass/Cargo.toml | 8 ++++---- {rosenpass-sodium => sodium}/Cargo.toml | 2 +- {rosenpass-sodium => sodium}/readme.md | 0 .../src/aead/chacha20poly1305_ietf.rs | 0 {rosenpass-sodium => sodium}/src/aead/mod.rs | 0 .../src/aead/xchacha20poly1305_ietf.rs | 0 {rosenpass-sodium => sodium}/src/helpers.rs | 0 {rosenpass-sodium => sodium}/src/lib.rs | 0 {rosenpass-util => util}/Cargo.toml | 0 {rosenpass-util => util}/src/b64.rs | 0 {rosenpass-util => util}/src/file.rs | 0 {rosenpass-util => util}/src/functional.rs | 0 {rosenpass-util => util}/src/lib.rs | 0 {rosenpass-util => util}/src/mem.rs | 0 {rosenpass-util => util}/src/ord.rs | 0 {rosenpass-util => util}/src/result.rs | 0 {rosenpass-util => util}/src/time.rs | 0 24 files changed, 10 insertions(+), 10 deletions(-) rename {rosenpass-ciphers => ciphers}/Cargo.toml (88%) rename {rosenpass-ciphers => ciphers}/readme.md (100%) rename {rosenpass-ciphers => ciphers}/src/lib.rs (100%) rename {rosenpass-constant-time => constant-time}/Cargo.toml (100%) rename {rosenpass-constant-time => constant-time}/readme.md (100%) rename {rosenpass-constant-time => constant-time}/src/lib.rs (100%) rename {rosenpass-sodium => sodium}/Cargo.toml (91%) rename {rosenpass-sodium => sodium}/readme.md (100%) rename {rosenpass-sodium => sodium}/src/aead/chacha20poly1305_ietf.rs (100%) rename {rosenpass-sodium => sodium}/src/aead/mod.rs (100%) rename {rosenpass-sodium => sodium}/src/aead/xchacha20poly1305_ietf.rs (100%) rename {rosenpass-sodium => sodium}/src/helpers.rs (100%) rename {rosenpass-sodium => sodium}/src/lib.rs (100%) rename {rosenpass-util => util}/Cargo.toml (100%) rename {rosenpass-util => util}/src/b64.rs (100%) rename {rosenpass-util => util}/src/file.rs (100%) rename {rosenpass-util => util}/src/functional.rs (100%) rename {rosenpass-util => util}/src/lib.rs (100%) rename {rosenpass-util => util}/src/mem.rs (100%) rename {rosenpass-util => util}/src/ord.rs (100%) rename {rosenpass-util => util}/src/result.rs (100%) rename {rosenpass-util => util}/src/time.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 9bf207f..579af31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,10 @@ resolver = "2" members = [ "rosenpass", - "rosenpass-ciphers", - "rosenpass-util", - "rosenpass-constant-time", - "rosenpass-sodium", + "ciphers", + "util", + "constant-time", + "sodium", ] [workspace.metadata.release] diff --git a/rosenpass-ciphers/Cargo.toml b/ciphers/Cargo.toml similarity index 88% rename from rosenpass-ciphers/Cargo.toml rename to ciphers/Cargo.toml index d147974..a84ba2f 100644 --- a/rosenpass-ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,4 +10,4 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] -rosenpass-sodium = { path = "../rosenpass-sodium" } +rosenpass-sodium = { path = "../sodium" } diff --git a/rosenpass-ciphers/readme.md b/ciphers/readme.md similarity index 100% rename from rosenpass-ciphers/readme.md rename to ciphers/readme.md diff --git a/rosenpass-ciphers/src/lib.rs b/ciphers/src/lib.rs similarity index 100% rename from rosenpass-ciphers/src/lib.rs rename to ciphers/src/lib.rs diff --git a/rosenpass-constant-time/Cargo.toml b/constant-time/Cargo.toml similarity index 100% rename from rosenpass-constant-time/Cargo.toml rename to constant-time/Cargo.toml diff --git a/rosenpass-constant-time/readme.md b/constant-time/readme.md similarity index 100% rename from rosenpass-constant-time/readme.md rename to constant-time/readme.md diff --git a/rosenpass-constant-time/src/lib.rs b/constant-time/src/lib.rs similarity index 100% rename from rosenpass-constant-time/src/lib.rs rename to constant-time/src/lib.rs diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 197c439..7fa2c5b 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -14,10 +14,10 @@ name = "handshake" harness = false [dependencies] -rosenpass-util = { path = "../rosenpass-util" } -rosenpass-constant-time = { path = "../rosenpass-constant-time" } -rosenpass-sodium = { path = "../rosenpass-sodium" } -rosenpass-ciphers = { path = "../rosenpass-ciphers" } +rosenpass-util = { path = "../util" } +rosenpass-constant-time = { path = "../constant-time" } +rosenpass-sodium = { path = "../sodium" } +rosenpass-ciphers = { path = "../ciphers" } anyhow = { version = "1.0.71", features = ["backtrace"] } static_assertions = "1.1.0" memoffset = "0.9.0" diff --git a/rosenpass-sodium/Cargo.toml b/sodium/Cargo.toml similarity index 91% rename from rosenpass-sodium/Cargo.toml rename to sodium/Cargo.toml index 91229da..1cd3bea 100644 --- a/rosenpass-sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] -rosenpass-util = { path = "../rosenpass-util" } +rosenpass-util = { path = "../util" } 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/rosenpass-sodium/readme.md b/sodium/readme.md similarity index 100% rename from rosenpass-sodium/readme.md rename to sodium/readme.md diff --git a/rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs b/sodium/src/aead/chacha20poly1305_ietf.rs similarity index 100% rename from rosenpass-sodium/src/aead/chacha20poly1305_ietf.rs rename to sodium/src/aead/chacha20poly1305_ietf.rs diff --git a/rosenpass-sodium/src/aead/mod.rs b/sodium/src/aead/mod.rs similarity index 100% rename from rosenpass-sodium/src/aead/mod.rs rename to sodium/src/aead/mod.rs diff --git a/rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs b/sodium/src/aead/xchacha20poly1305_ietf.rs similarity index 100% rename from rosenpass-sodium/src/aead/xchacha20poly1305_ietf.rs rename to sodium/src/aead/xchacha20poly1305_ietf.rs diff --git a/rosenpass-sodium/src/helpers.rs b/sodium/src/helpers.rs similarity index 100% rename from rosenpass-sodium/src/helpers.rs rename to sodium/src/helpers.rs diff --git a/rosenpass-sodium/src/lib.rs b/sodium/src/lib.rs similarity index 100% rename from rosenpass-sodium/src/lib.rs rename to sodium/src/lib.rs diff --git a/rosenpass-util/Cargo.toml b/util/Cargo.toml similarity index 100% rename from rosenpass-util/Cargo.toml rename to util/Cargo.toml diff --git a/rosenpass-util/src/b64.rs b/util/src/b64.rs similarity index 100% rename from rosenpass-util/src/b64.rs rename to util/src/b64.rs diff --git a/rosenpass-util/src/file.rs b/util/src/file.rs similarity index 100% rename from rosenpass-util/src/file.rs rename to util/src/file.rs diff --git a/rosenpass-util/src/functional.rs b/util/src/functional.rs similarity index 100% rename from rosenpass-util/src/functional.rs rename to util/src/functional.rs diff --git a/rosenpass-util/src/lib.rs b/util/src/lib.rs similarity index 100% rename from rosenpass-util/src/lib.rs rename to util/src/lib.rs diff --git a/rosenpass-util/src/mem.rs b/util/src/mem.rs similarity index 100% rename from rosenpass-util/src/mem.rs rename to util/src/mem.rs diff --git a/rosenpass-util/src/ord.rs b/util/src/ord.rs similarity index 100% rename from rosenpass-util/src/ord.rs rename to util/src/ord.rs diff --git a/rosenpass-util/src/result.rs b/util/src/result.rs similarity index 100% rename from rosenpass-util/src/result.rs rename to util/src/result.rs diff --git a/rosenpass-util/src/time.rs b/util/src/time.rs similarity index 100% rename from rosenpass-util/src/time.rs rename to util/src/time.rs From a49254a021bef0b8666b55097fe74b185fede4ae Mon Sep 17 00:00:00 2001 From: Morgan Hill Date: Sun, 19 Nov 2023 16:52:44 +0100 Subject: [PATCH 17/46] feat(fuzzing): Add initial set of fuzzing targets These targets can be used with rust nightly and cargo-fuzz to fuzz several bits of Rosenpass's API. Fuzzing is an automated way of exploring code paths that may not be hit in unit tests or normal operation. For example the `handle_msg` target exposed the DoS condition fixed in 0.2.1. The other targets focus on the FFI with libsodium and liboqs. Co-authored-by: Karolin Varner --- Cargo.lock | 53 + Cargo.toml | 1 + fuzzing/.gitignore | 4 + fuzzing/Cargo.lock | 1286 +++++++++++++++++++++++ fuzzing/Cargo.toml | 52 + fuzzing/fuzz_targets/aead_enc_into.rs | 32 + fuzzing/fuzz_targets/blake2b.rs | 22 + fuzzing/fuzz_targets/handle_msg.rs | 19 + fuzzing/fuzz_targets/kyber_encaps.rs | 19 + fuzzing/fuzz_targets/mceliece_encaps.rs | 13 + 10 files changed, 1501 insertions(+) create mode 100644 fuzzing/.gitignore create mode 100644 fuzzing/Cargo.lock create mode 100644 fuzzing/Cargo.toml create mode 100644 fuzzing/fuzz_targets/aead_enc_into.rs create mode 100644 fuzzing/fuzz_targets/blake2b.rs create mode 100644 fuzzing/fuzz_targets/handle_msg.rs create mode 100644 fuzzing/fuzz_targets/kyber_encaps.rs create mode 100644 fuzzing/fuzz_targets/mceliece_encaps.rs diff --git a/Cargo.lock b/Cargo.lock index 4fe895e..6b8a196 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,15 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "atty" version = "0.2.14" @@ -213,6 +222,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -438,6 +448,17 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.9.0" @@ -646,6 +667,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.65" @@ -697,6 +727,17 @@ dependencies = [ "rle-decode-fast", ] +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libloading" version = "0.7.4" @@ -1047,6 +1088,18 @@ dependencies = [ name = "rosenpass-constant-time" version = "0.1.0" +[[package]] +name = "rosenpass-fuzzing" +version = "0.0.1" +dependencies = [ + "arbitrary", + "libfuzzer-sys", + "rosenpass", + "rosenpass-ciphers", + "rosenpass-sodium", + "stacker", +] + [[package]] name = "rosenpass-sodium" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 579af31..9a442df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "util", "constant-time", "sodium", + "fuzzing", ] [workspace.metadata.release] diff --git a/fuzzing/.gitignore b/fuzzing/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzzing/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzzing/Cargo.lock b/fuzzing/Cargo.lock new file mode 100644 index 0000000..736749d --- /dev/null +++ b/fuzzing/Cargo.lock @@ -0,0 +1,1286 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "build-deps" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f14468960818ce4f3e3553c32d524446687884f8e7af5d3e252331d8a87e43" +dependencies = [ + "glob", +] + +[[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.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libflate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +dependencies = [ + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libsodium-sys-stable" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc31f983531631496f4e621110cd81468ab78b65dee0046cfddea83caa2c327" +dependencies = [ + "cc", + "libc", + "libflate", + "minisign-verify", + "pkg-config", + "tar", + "ureq", + "vcpkg", + "zip", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minisign-verify" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oqs-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa114149fb6e5362b9cf539de9305a6a3cf1556fbfa93b5053b4f70cf3adfb9" +dependencies = [ + "bindgen", + "build-deps", + "cmake", + "libc", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "rosenpass" +version = "0.2.1" +dependencies = [ + "anyhow", + "base64", + "clap", + "env_logger", + "lazy_static", + "libsodium-sys-stable", + "log", + "memoffset", + "mio", + "oqs-sys", + "paste", + "serde", + "static_assertions", + "thiserror", + "toml", +] + +[[package]] +name = "rosenpass-fuzz" +version = "0.0.0" +dependencies = [ + "arbitrary", + "libfuzzer-sys", + "rosenpass", + "stacker", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.21.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +dependencies = [ + "base64", + "log", + "once_cell", + "rustls", + "rustls-webpki", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[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-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[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 = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32" +dependencies = [ + "memchr", +] + +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] + +[[package]] +name = "zerocopy" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092cd76b01a033a9965b9097da258689d9e17c69ded5dcf41bca001dd20ebc6d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13a20a7c6a90e2034bcc65495799da92efcec6a8dd4f3fcb6f7a48988637ead" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] diff --git a/fuzzing/Cargo.toml b/fuzzing/Cargo.toml new file mode 100644 index 0000000..933f5ee --- /dev/null +++ b/fuzzing/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "rosenpass-fuzzing" +version = "0.0.1" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = { version = "1.3.2", features = ["derive"]} +libfuzzer-sys = "0.4" +stacker = "0.1.15" + +[dependencies.rosenpass] +path = "../rosenpass" + +[dependencies.rosenpass-sodium] +path = "../sodium" + +[dependencies.rosenpass-ciphers] +path = "../ciphers" + +[[bin]] +name = "fuzz_handle_msg" +path = "fuzz_targets/handle_msg.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_blake2b" +path = "fuzz_targets/blake2b.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_aead_enc_into" +path = "fuzz_targets/aead_enc_into.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_mceliece_encaps" +path = "fuzz_targets/mceliece_encaps.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_kyber_encaps" +path = "fuzz_targets/kyber_encaps.rs" +test = false +doc = false diff --git a/fuzzing/fuzz_targets/aead_enc_into.rs b/fuzzing/fuzz_targets/aead_enc_into.rs new file mode 100644 index 0000000..d90d52b --- /dev/null +++ b/fuzzing/fuzz_targets/aead_enc_into.rs @@ -0,0 +1,32 @@ +#![no_main] +extern crate arbitrary; +extern crate rosenpass; + +use libfuzzer_sys::fuzz_target; + +use rosenpass_ciphers::aead; +use rosenpass_sodium::init as sodium_init; + +#[derive(arbitrary::Arbitrary, Debug)] +pub struct Input { + pub key: [u8; 32], + pub nonce: [u8; 12], + pub ad: Box<[u8]>, + pub plaintext: Box<[u8]>, +} + +fuzz_target!(|input: Input| { + sodium_init().unwrap(); + + let mut ciphertext: Vec = Vec::with_capacity(input.plaintext.len() + 16); + ciphertext.resize(input.plaintext.len() + 16, 0); + + aead::encrypt( + ciphertext.as_mut_slice(), + &input.key, + &input.nonce, + &input.ad, + &input.plaintext, + ) + .unwrap(); +}); diff --git a/fuzzing/fuzz_targets/blake2b.rs b/fuzzing/fuzz_targets/blake2b.rs new file mode 100644 index 0000000..2d628b2 --- /dev/null +++ b/fuzzing/fuzz_targets/blake2b.rs @@ -0,0 +1,22 @@ +#![no_main] +extern crate arbitrary; +extern crate rosenpass; + +use libfuzzer_sys::fuzz_target; + +use rosenpass::sodium::mac_into; +use rosenpass_sodium::init as sodium_init; + +#[derive(arbitrary::Arbitrary, Debug)] +pub struct Blake2b { + pub key: [u8; 32], + pub data: Box<[u8]>, +} + +fuzz_target!(|input: Blake2b| { + sodium_init().unwrap(); + + let mut out = [0u8; 32]; + + mac_into(&mut out, &input.key, &input.data).unwrap(); +}); diff --git a/fuzzing/fuzz_targets/handle_msg.rs b/fuzzing/fuzz_targets/handle_msg.rs new file mode 100644 index 0000000..fad8dcc --- /dev/null +++ b/fuzzing/fuzz_targets/handle_msg.rs @@ -0,0 +1,19 @@ +#![no_main] +extern crate rosenpass; + +use libfuzzer_sys::fuzz_target; + +use rosenpass::coloring::Secret; +use rosenpass::protocol::CryptoServer; +use rosenpass_sodium::init as sodium_init; + +fuzz_target!(|rx_buf: &[u8]| { + sodium_init().unwrap(); + + let sk = Secret::from_slice(&[0; 13568]); + let pk = Secret::from_slice(&[0; 524160]); + + let mut cs = CryptoServer::new(sk, pk); + let mut tx_buf = [0; 10240]; + cs.handle_msg(rx_buf, &mut tx_buf).unwrap(); +}); diff --git a/fuzzing/fuzz_targets/kyber_encaps.rs b/fuzzing/fuzz_targets/kyber_encaps.rs new file mode 100644 index 0000000..dc61f06 --- /dev/null +++ b/fuzzing/fuzz_targets/kyber_encaps.rs @@ -0,0 +1,19 @@ +#![no_main] +extern crate arbitrary; +extern crate rosenpass; + +use libfuzzer_sys::fuzz_target; + +use rosenpass::pqkem::{EphemeralKEM, KEM}; + +#[derive(arbitrary::Arbitrary, Debug)] +pub struct Input { + pub pk: [u8; 800], +} + +fuzz_target!(|input: Input| { + let mut ciphertext = [0u8; 768]; + let mut shared_secret = [0u8; 32]; + + EphemeralKEM::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap(); +}); diff --git a/fuzzing/fuzz_targets/mceliece_encaps.rs b/fuzzing/fuzz_targets/mceliece_encaps.rs new file mode 100644 index 0000000..27800c4 --- /dev/null +++ b/fuzzing/fuzz_targets/mceliece_encaps.rs @@ -0,0 +1,13 @@ +#![no_main] +extern crate rosenpass; + +use libfuzzer_sys::fuzz_target; + +use rosenpass::pqkem::{StaticKEM, KEM}; + +fuzz_target!(|input: &[u8]| { + let mut ciphertext = [0u8; 188]; + let mut shared_secret = [0u8; 32]; + + StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input).unwrap(); +}); From d539be314252dd9a87aa432742b88dc347f556c3 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 19 Nov 2023 17:54:43 +0100 Subject: [PATCH 18/46] feat: Rosenpass-to for nicely handling destination parameters --- Cargo.lock | 13 + Cargo.toml | 1 + to/Cargo.toml | 13 + to/README.md | 477 ++++++++++++++++++++++++++++++++++ to/src/lib.rs | 14 + to/src/ops.rs | 80 ++++++ to/src/to/beside.rs | 45 ++++ to/src/to/condense.rs | 37 +++ to/src/to/dst_coercion.rs | 17 ++ to/src/to/mod.rs | 20 ++ to/src/to/to_function.rs | 14 + to/src/to/to_trait.rs | 96 +++++++ to/src/to/with_destination.rs | 35 +++ 13 files changed, 862 insertions(+) create mode 100644 to/Cargo.toml create mode 100644 to/README.md create mode 100644 to/src/lib.rs create mode 100644 to/src/ops.rs create mode 100644 to/src/to/beside.rs create mode 100644 to/src/to/condense.rs create mode 100644 to/src/to/dst_coercion.rs create mode 100644 to/src/to/mod.rs create mode 100644 to/src/to/to_function.rs create mode 100644 to/src/to/to_trait.rs create mode 100644 to/src/to/with_destination.rs diff --git a/Cargo.lock b/Cargo.lock index 6b8a196..aebadb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,6 +459,12 @@ dependencies = [ "syn", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.9.0" @@ -1110,6 +1116,13 @@ dependencies = [ "rosenpass-util", ] +[[package]] +name = "rosenpass-to" +version = "0.1.0" +dependencies = [ + "doc-comment", +] + [[package]] name = "rosenpass-util" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9a442df..e46d599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "util", "constant-time", "sodium", + "to", "fuzzing", ] diff --git a/to/Cargo.toml b/to/Cargo.toml new file mode 100644 index 0000000..09b49e6 --- /dev/null +++ b/to/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rosenpass-to" +version = "0.1.0" +authors = ["Karolin Varner ", "wucke13 "] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Flexible destination parameters" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dev-dependencies] +doc-comment = "0.3.3" diff --git a/to/README.md b/to/README.md new file mode 100644 index 0000000..b8e0146 --- /dev/null +++ b/to/README.md @@ -0,0 +1,477 @@ +# The To Crate – Patterns for dealing with destination parameters in rust functions + + + +![crates.io](https://img.shields.io/crates/v/rosenpass-to.svg) +![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/cargo/rosenpass-to) + +The To Crate provides a pattern for declaring and dealing with destination parameters in rust functions. It improves over stock rust by providing an interface that allows the caller to choose whether to place the destination parameter first – through a `to(dest, copy(source))` function – or last – through a chained function `copy(source).to(dest)`. + +The crate provides chained functions to simplify allocating the destination parameter on the fly and it provides well defined patterns for dealing with error handling and destination parameters. + +For now this crate is experimental; patch releases are guaranteed not to contain any breaking changes, but minor releases may. + +```rust +use std::ops::BitXorAssign; +use rosenpass_to::{To, to, with_destination}; +use rosenpass_to::ops::copy_array; + +// Destination functions return some value that implements the To trait. +// Unfortunately dealing with lifetimes is a bit more finicky than it would# +// be without destination parameters +fn xor_slice<'a, T>(src: &'a[T]) -> impl To<[T], ()> + 'a + where T: BitXorAssign + Clone { + // Custom implementations of the to trait can be created, but the easiest + with_destination(move |dst: &mut [T]| { + assert!(src.len() == dst.len()); + for (d, s) in dst.iter_mut().zip(src.iter()) { + *d ^= s.clone(); + } + }) +} + +let flip0 = b"\xff\x00\x00\x00"; +let flip1 = b"\x00\xff\x00\x00"; +let flip01 = b"\xff\xff\x00\x00"; + +// You can specify a destination by using the to method +let mut dst = [0u8; 4]; +xor_slice(flip0).to(&mut dst); +xor_slice(flip1).to(&mut dst); +assert_eq!(&dst[..], &flip01[..]); + +// Or using the to function +let mut dst = [0u8; 4]; +to(&mut dst, xor_slice(flip0)); +to(&mut dst, xor_slice(flip1)); +assert_eq!(&dst[..], &flip01[..]); + +// You can pass a function to generate the destination on the fly +let dst = xor_slice(flip1).to_this(|| flip0.to_vec()); +assert_eq!(&dst[..], &flip01[..]); + +// If xor_slice used a return value that could be created using Default::default(), +// you could just use `xor_slice(flip01).to_value()` to generate the destination +// on the fly. Since [u8] is unsized, it can only be used for references. +// +// You can however use collect to specify the storage value explicitly. +// This works for any type that implements Default::default() and BorrowMut<...> for +// the destination value. + +// Collect in an array with a fixed size +let dst = xor_slice(flip01).collect::<[u8; 4]>(); +assert_eq!(&dst[..], &flip01[..]); + +// The builtin function copy_array supports to_value() since its +// destination parameter is a fixed size array, which can be allocated +// using default() +let dst : [u8; 4] = copy_array(flip01).to_value(); +assert_eq!(&dst, flip01); +``` + +The to crate really starts to shine when error handling (through result) is combined with destination parameters. See the tutorial below for details. + +## Motivation + +Destination parameters are often used when simply returning the value is undesirable or impossible. + +Using stock rust features, functions can declare destination parameters by accepting mutable references as arguments. +This pattern introduces some shortcomings; developers have to make a call on whether to place destination parameters before or after source parameters and they have to enforce consistency across their codebase or accept inconsistencies, leading to hard-to-remember interfaces. + +Functions declared like this are more cumbersome to use when the destination parameter should be allocated on the fly. + +```rust +use std::ops::BitXorAssign; + +fn xor_slice(dst: &mut [T], src: &[T]) + where T: BitXorAssign + Clone { + assert!(src.len() == dst.len()); + for (d, s) in dst.iter_mut().zip(src.iter()) { + *d ^= s.clone(); + } +} + +let flip0 = b"\xff\x00\x00\x00"; +let flip1 = b"\x00\xff\x00\x00"; +let flip01 = b"\xff\xff\x00\x00"; + +// Copy a slice from src to dest; its unclear whether src or dest should come first +let mut dst = [0u8; 4]; +xor_slice(&mut dst, flip0); +xor_slice(&mut dst, flip1); +assert_eq!(&dst[..], &flip01[..]); + +// The other examples can not be translated to use the standard rust pattern, +// since using mutable references for destination parameters does not allow +// for specifying the destination parameter on the right side or allocating +// the destination parameter on the fly. +``` + +## Tutorial + +### Using a function with destination + +There are a couple of ways to use a function with destination: + +```rust +use rosenpass_to::{to, To}; +use rosenpass_to::ops::{copy_array, copy_slice_least}; + +let mut dst = b" ".to_vec(); + +// Using the to function to have data flowing from the right to the left, +// performing something akin to a variable assignment +to(&mut dst[..], copy_slice_least(b"Hello World")); +assert_eq!(&dst[..], b"Hello World"); + +// Using the to method to have information flowing from the left to the right +copy_slice_least(b"This is fin").to(&mut dst[..]); +assert_eq!(&dst[..], b"This is fin"); + +// You can allocate the destination variable on the fly using `.to_this(...)` +let tmp = copy_slice_least(b"This is new---").to_this(|| b"This will be overwritten".to_owned()); +assert_eq!(&tmp[..], b"This is new---verwritten"); + +// You can allocate the destination variable on the fly `.collect(..)` if it implements default +let tmp = copy_slice_least(b"This is ad-hoc").collect::<[u8; 16]>(); +assert_eq!(&tmp[..], b"This is ad-hoc\0\0"); + +// Finally, if the destination variable specified by the function implements default, +// you can simply use `.to_value()` to allocate it on the fly. +let tmp = copy_array(b"Fixed").to_value(); +assert_eq!(&tmp[..], b"Fixed"); +``` + +### Builtin functions with destination + +The to crate provides basic functions with destination for copying data between slices and arrays. + +```rust +use rosenpass_to::{to, To}; +use rosenpass_to::ops::{copy_array, copy_slice, copy_slice_least, copy_slice_least_src, try_copy_slice, try_copy_slice_least_src}; + +let mut dst = b" ".to_vec(); + +// Copy a slice, source and destination must match exactly +to(&mut dst[..], copy_slice(b"Hello World")); +assert_eq!(&dst[..], b"Hello World"); + +// Copy a slice, destination must be at least as long as the destination +to(&mut dst[4..], copy_slice_least_src(b"!!!")); +assert_eq!(&dst[..], b"Hell!!!orld"); + +// Copy a slice, copying as many bytes as possible +to(&mut dst[6..], copy_slice_least(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); +assert_eq!(&dst[..], b"Hell!!xxxxx"); + +// Copy a slice, will return None and abort if the sizes do not much +assert_eq!(Some(()), to(&mut dst[..], try_copy_slice(b"Hello World"))); +assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---"))); +assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---------------------"))); +assert_eq!(&dst[..], b"Hello World"); + +// Copy a slice, will return None and abort if source is longer than destination +assert_eq!(Some(()), to(&mut dst[4..], try_copy_slice_least_src(b"!!!"))); +assert_eq!(None, to(&mut dst[4..], try_copy_slice_least_src(b"-------------------------"))); +assert_eq!(&dst[..], b"Hell!!!orld"); + +// Copy fixed size arrays all at once +let mut dst = [0u8; 5]; +to(&mut dst, copy_array(b"Hello")); +assert_eq!(&dst, b"Hello"); +``` + +### Declaring a function with destination + +The easiest way to declare a function with destination is to use the with_destination function. + +```rust +use rosenpass_to::{To, to, with_destination}; +use rosenpass_to::ops::copy_array; + +/// Copy the given slice to the start of a vector, reusing its memory if possible +fn copy_to_vec<'a, T>(src: &'a [T]) -> impl To, ()> + 'a + where T: Clone { + with_destination(move |dst: &mut Vec| { + dst.clear(); + dst.extend_from_slice(src); + }) +} + +let mut buf = copy_to_vec(b"Hello World, this is a long text.").to_value(); +assert_eq!(&buf[..], b"Hello World, this is a long text."); + +to(&mut buf, copy_to_vec(b"Avoids allocation")); +assert_eq!(&buf[..], b"Avoids allocation"); +``` + +This example also shows of some of the advantages of using To: The function gains a very slight allocate over using `.to_vec()` by reusing memory: + +```rust +let mut buf = b"Hello World, this is a long text.".to_vec(); +buf = b"This allocates".to_vec(); // This uses memory allocation +``` + +The same pattern can be implemented without `to`, at the cost of being slightly more verbose + +```rust +/// Copy the given slice to the start of a vector, reusing its memory if possible +fn copy_to_vec(dst: &mut Vec, src: &[T]) + where T: Clone { + dst.clear(); + dst.extend_from_slice(src); +} + +let mut buf = Vec::default(); +copy_to_vec(&mut buf, b"Hello World, this is a long text."); +assert_eq!(&buf[..], b"Hello World, this is a long text."); + +copy_to_vec(&mut buf, b"Avoids allocation"); +assert_eq!(&buf[..], b"Avoids allocation"); +``` + +This usability enhancement might seem minor, but when many functions take destination parameters, manually allocating all of these can really become annoying. + +## Beside values: Functions with destination and return value + +Return values are supported, but `from_this()`, `to_value()`, and `collect()` cannot be used together with return values (unless they implement CondenseBeside – see the next section), since that would erase the return value. + +Alternative functions are returned, that return a `to::Beside` value, containing both the +destination variable and the return value. + +```rust +use std::cmp::{min, max}; +use rosenpass_to::{To, to, with_destination, Beside}; + +/// Copy an array of floats and calculate the average +pub fn copy_and_average<'a>(src: &'a[f64]) -> impl To<[f64], f64> + 'a { + with_destination(move |dst: &mut [f64]| { + assert!(src.len() == dst.len()); + let mut sum = 0f64; + for (d, s) in dst.iter_mut().zip(src.iter()) { + *d = *s; + sum = sum + *d; + } + sum / (src.len() as f64) + }) +} + +let src = [12f64, 13f64, 14f64]; + +// `.to()` and `to(...)` function as normal, but return the value now +let mut dst = [0f64; 3]; +let avg = copy_and_average(&src).to(&mut dst); +assert_eq!((&dst[..], avg), (&src[..], 13f64)); + +let mut dst = [0f64; 3]; +let avg = to(&mut dst, copy_and_average(&src)); +assert_eq!((&dst[..], avg), (&src[..], 13f64)); + +// Instead of .to_this, .to_value, or .collect variants returning a beside value have to be used + +let Beside(dst, avg) = copy_and_average(&src).to_this_beside(|| [0f64; 3]); +assert_eq!((&dst[..], avg), (&src[..], 13f64)); + +let Beside(dst, avg) = copy_and_average(&src).collect_beside::<[f64; 3]>(); +assert_eq!((&dst[..], avg), (&src[..], 13f64)); + +// Beside values are simple named tuples + +let b = copy_and_average(&src).collect_beside::<[f64; 3]>(); +assert_eq!(b, Beside(dst, avg)); + +// They can convert from and to tuples +let b_tup = (dst, avg); +assert_eq!(b, (dst, avg).into()); +assert_eq!(b, Beside::from(b_tup)); + +// Simple accessors for the value and returned value are provided +assert_eq!(&dst, b.dest()); +assert_eq!(&avg, b.ret()); + +let mut tmp = b; +*tmp.dest_mut() = [42f64; 3]; +*tmp.ret_mut() = 42f64; +assert_eq!(tmp, Beside([42f64; 3], 42f64)); +``` + +## Beside Condensation: Working with destinations and Optional or Result + +When Beside values contain a `()`, `Option<()>`, or `Result<(), Error>` return value, they expose a special method called `.condense()`; this method consumes the Beside value and condenses destination and return value into one value. + +```rust +use std::result::Result; +use rosenpass_to::{Beside}; + +assert_eq!((), Beside((), ()).condense()); + +assert_eq!(42, Beside(42, ()).condense()); +assert_eq!(None, Beside(42, None).condense()); + +let ok_unit = Result::<(), ()>::Ok(()); +assert_eq!(Ok(42), Beside(42, ok_unit).condense()); + +let err_unit = Result::<(), ()>::Err(()); +assert_eq!(Err(()), Beside(42, err_unit).condense()); +``` + +When condense is implemented for a type, `.to_this(|| ...)`, `.to_value()`, and `.collect::<...>()` on the `To` trait can be used even with a return value: + +```rust +use rosenpass_to::To; +use rosenpass_to::ops::try_copy_slice;; + +let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>(); +assert_eq!(tmp, Some(*b"Hello World")); + +let tmp = try_copy_slice(b"Hello World").collect::<[u8; 2]>(); +assert_eq!(tmp, None); + +let tmp = try_copy_slice(b"Hello World").to_this(|| [0u8; 11].to_vec()); +assert_eq!(tmp, Some(b"Hello World".to_vec())); + +let tmp = try_copy_slice(b"Hello World").to_this(|| [0u8; 2].to_vec()); +assert_eq!(tmp, None); +``` + +The same naturally also works for Results, but the example is a bit harder to motivate: + +```rust +use std::result::Result; +use rosenpass_to::{to, To, with_destination}; + +#[derive(PartialEq, Eq, Debug, Default)] +struct InvalidFloat; + +fn check_float(f: f64) -> Result<(), InvalidFloat> { + if f.is_normal() || f == 0.0 { + Ok(()) + } else { + Err(InvalidFloat) + } +} + +fn checked_add<'a>(src: f64) -> impl To> + 'a { + with_destination(move |dst: &mut f64| { + check_float(src)?; + check_float(*dst)?; + *dst += src; + Ok(()) + }) +} + +let mut tmp = 0.0; +checked_add(14.0).to(&mut tmp).unwrap(); +checked_add(12.0).to(&mut tmp).unwrap(); +assert_eq!(tmp, 26.0); + +assert_eq!(Ok(78.0), checked_add(14.0).to_this(|| 64.0)); +assert_eq!(Ok(14.0), checked_add(14.0).to_value()); +assert_eq!(Ok(14.0), checked_add(14.0).collect()); + +assert_eq!(Err(InvalidFloat), checked_add(f64::NAN).to_this(|| 64.0)); +assert_eq!(Err(InvalidFloat), checked_add(f64::INFINITY).to_value()); +``` + +## Custom condensation + +Condensation is implemented through a trait called CondenseBeside ([local](CondenseBeside) | [docs.rs](https://docs.rs/to/latest/rosenpass-to/trait.CondenseBeside.html)). You can implement it for your own types. + +If you can not implement this trait because its for an external type (see [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)), this crate welcomes contributions of new Condensation rules. + +```rust +use rosenpass_to::{To, with_destination, Beside, CondenseBeside}; +use rosenpass_to::ops::copy_slice; + +#[derive(PartialEq, Eq, Debug, Default)] +struct MyTuple(Left, Right); + +impl CondenseBeside for MyTuple<(), Right> { + type Condensed = MyTuple; + + fn condense(self, val: Val) -> MyTuple { + let MyTuple((), right) = self; + MyTuple(val, right) + } +} + +fn copy_slice_and_return_something<'a, T, U>(src: &'a [T], something: U) -> impl To<[T], U> + 'a + where T: Copy, U: 'a { + with_destination(move |dst: &mut [T]| { + copy_slice(src).to(dst); + something + }) +} + +let tmp = Beside(42, MyTuple((), 23)).condense(); +assert_eq!(tmp, MyTuple(42, 23)); + +let tmp = copy_slice_and_return_something(b"23", MyTuple((), 42)).collect::<[u8; 2]>(); +assert_eq!(tmp, MyTuple(*b"23", 42)); +``` + +## Manually implementing the To trait + +Using `with_destination(...)` is convenient, but since it uses closures it results in an type that can not be written down, which is why the `-> impl To<...>` pattern is used everywhere in this tutorial. + +Implementing the ToTrait manual is the right choice for library use cases. + +```rust +use rosenpass_to::{to, To, with_destination}; + +struct TryCopySliceSource<'a, T: Copy> { + src: &'a [T], +} + +impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> { + fn to(self, dst: &mut [T]) -> Option<()> { + (self.src.len() == dst.len()) + .then(|| dst.copy_from_slice(self.src)) + } +} + +fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T> + where T: Copy { + TryCopySliceSource { src } +} + +let mut dst = try_copy_slice(b"Hello World").collect::<[u8; 11]>().unwrap(); +assert_eq!(&dst[..], b"Hello World"); +assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---"))); +``` + +## Methods with destination + +Destinations can also be used with methods. This example demonstrates using destinations in an extension trait for everything that implements `Borrow<[T]>` for any `T` and a concrete `To` trait implementation. + +```rust +use std::borrow::Borrow; +use rosenpass_to::{to, To, with_destination}; + +struct TryCopySliceSource<'a, T: Copy> { + src: &'a [T], +} + +impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> { + fn to(self, dst: &mut [T]) -> Option<()> { + (self.src.len() == dst.len()) + .then(|| dst.copy_from_slice(self.src)) + } +} + +trait TryCopySliceExt<'a, T: Copy> { + fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T>; +} + +impl<'a, T: 'a + Copy, Ref: 'a + Borrow<[T]>> TryCopySliceExt<'a, T> for Ref { + fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> { + TryCopySliceSource { + src: self.borrow() + } + } +} + +let mut dst = b"Hello World".try_copy_slice().collect::<[u8; 11]>().unwrap(); +assert_eq!(&dst[..], b"Hello World"); +assert_eq!(None, to(&mut dst[..], b"---".try_copy_slice())); +``` diff --git a/to/src/lib.rs b/to/src/lib.rs new file mode 100644 index 0000000..5cd4ec5 --- /dev/null +++ b/to/src/lib.rs @@ -0,0 +1,14 @@ +#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] + +#[cfg(doctest)] +doc_comment::doctest!("../README.md"); + +// Core implementation +mod to; +pub use crate::to::{ + beside::Beside, condense::CondenseBeside, dst_coercion::DstCoercion, to_function::to, + to_trait::To, with_destination::with_destination, +}; + +// Example use cases +pub mod ops; diff --git a/to/src/ops.rs b/to/src/ops.rs new file mode 100644 index 0000000..cb4aad8 --- /dev/null +++ b/to/src/ops.rs @@ -0,0 +1,80 @@ +//! Functions with destination copying data between slices and arrays. + +use crate::{with_destination, To}; + +/// Function with destination that copies data from +/// origin into the destination. +/// +/// # Panics +/// +/// This function will panic if the two slices have different lengths. +pub fn copy_slice<'a, T>(origin: &'a [T]) -> impl To<[T], ()> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T]| out.copy_from_slice(origin)) +} + +/// Function with destination that copies all data from +/// origin into the destination. +/// +/// Destination may be longer than origin. +/// +/// # Panics +/// +/// This function will panic if destination is shorter than origin. +pub fn copy_slice_least_src<'a, T>(origin: &'a [T]) -> impl To<[T], ()> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T]| copy_slice(origin).to(&mut out[..origin.len()])) +} + +/// Function with destination that copies as much data as possible from origin to the +/// destination. +/// +/// Copies as much data as is present in the shorter slice. +pub fn copy_slice_least<'a, T>(origin: &'a [T]) -> impl To<[T], ()> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T]| { + let len = std::cmp::min(origin.len(), out.len()); + copy_slice(&origin[..len]).to(&mut out[..len]) + }) +} + +/// Function with destination that attempts to copy data from origin into the destination. +/// +/// Will return None if the slices are of different lengths. +pub fn try_copy_slice<'a, T>(origin: &'a [T]) -> impl To<[T], Option<()>> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T]| { + (origin.len() == out.len()).then(|| copy_slice(origin).to(out)) + }) +} + +/// Function with destination that tries to copy all data from +/// origin into the destination. +/// +/// Destination may be longer than origin. +/// +/// Will return None if the destination is shorter than origin. +pub fn try_copy_slice_least_src<'a, T>(origin: &'a [T]) -> impl To<[T], Option<()>> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T]| { + (origin.len() <= out.len()).then(|| copy_slice_least_src(origin).to(out)) + }) +} + +/// Function with destination that copies all data between two array references. +pub fn copy_array<'a, T, const N: usize>(origin: &'a [T; N]) -> impl To<[T; N], ()> + 'a +where + T: Copy, +{ + with_destination(|out: &mut [T; N]| out.copy_from_slice(origin)) +} diff --git a/to/src/to/beside.rs b/to/src/to/beside.rs new file mode 100644 index 0000000..1a6d5ed --- /dev/null +++ b/to/src/to/beside.rs @@ -0,0 +1,45 @@ +use crate::CondenseBeside; + +/// Named tuple holding the return value and the output from a function with destinations. +#[derive(Debug, PartialEq, Eq, Default, PartialOrd, Ord, Copy, Clone)] +pub struct Beside(pub Val, pub Ret); + +impl Beside { + pub fn dest(&self) -> &Val { + &self.0 + } + + pub fn ret(&self) -> &Ret { + &self.1 + } + + pub fn dest_mut(&mut self) -> &mut Val { + &mut self.0 + } + + pub fn ret_mut(&mut self) -> &mut Ret { + &mut self.1 + } + + /// Perform beside condensation. See [CondenseBeside] + pub fn condense(self) -> >::Condensed + where + Ret: CondenseBeside, + { + self.1.condense(self.0) + } +} + +impl From<(Val, Ret)> for Beside { + fn from(tuple: (Val, Ret)) -> Self { + let (val, ret) = tuple; + Self(val, ret) + } +} + +impl From> for (Val, Ret) { + fn from(beside: Beside) -> Self { + let Beside(val, ret) = beside; + (val, ret) + } +} diff --git a/to/src/to/condense.rs b/to/src/to/condense.rs new file mode 100644 index 0000000..3ac7b90 --- /dev/null +++ b/to/src/to/condense.rs @@ -0,0 +1,37 @@ +/// Beside condensation. +/// +/// This trait can be used to enable the use of [to_this(|| ...)](crate::To::to_this), +/// [to_value()](crate::To::to_value), and [collect::<...>()](crate::To::collect) with custom +/// types. +/// +/// The function [Beside::condense()](crate::Beside::condense) is a shorthand for using the +/// condense trait. +pub trait CondenseBeside { + type Condensed; + + fn condense(self, ret: Val) -> Self::Condensed; +} + +impl CondenseBeside for () { + type Condensed = Val; + + fn condense(self, ret: Val) -> Val { + ret + } +} + +impl CondenseBeside for Result<(), Error> { + type Condensed = Result; + + fn condense(self, ret: Val) -> Result { + self.map(|()| ret) + } +} + +impl CondenseBeside for Option<()> { + type Condensed = Option; + + fn condense(self, ret: Val) -> Option { + self.map(|()| ret) + } +} diff --git a/to/src/to/dst_coercion.rs b/to/src/to/dst_coercion.rs new file mode 100644 index 0000000..cb9aa3a --- /dev/null +++ b/to/src/to/dst_coercion.rs @@ -0,0 +1,17 @@ +/// Helper performing explicit unsized coercion. +/// Used by the [to](crate::to()) function. +pub trait DstCoercion { + fn coerce_dest(&mut self) -> &mut Dst; +} + +impl DstCoercion for T { + fn coerce_dest(&mut self) -> &mut T { + self + } +} + +impl DstCoercion<[T]> for [T; N] { + fn coerce_dest(&mut self) -> &mut [T] { + self + } +} diff --git a/to/src/to/mod.rs b/to/src/to/mod.rs new file mode 100644 index 0000000..33f54d9 --- /dev/null +++ b/to/src/to/mod.rs @@ -0,0 +1,20 @@ +//! Module implementing the core function with destination functionality. +//! +//! Parameter naming scheme +//! +//! - `Src: impl To` – The value of an instance of something implementing the `To` trait +//! - `Dst: ?Sized`; (e.g. [u8]) – The target to write to +//! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) – A reference to the target to write to +//! - `Coercable: ?Sized + DstCoercion`; (e.g. `[u8]`, `[u8; 16]`) – Some value that +//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or some sized variant of +//! `Dst` (e.g. `[u8; 64]`). +//! - `Ret: Sized`; (anything) – must be `CondenseBeside<_>` if condensing is to be applied. The ordinary return value of a function with an output +//! - `Val: Sized + BorrowMut`; (e.g. [u8; 16]) – Some owned storage that can be borrowed as `Dst` +//! - `Condensed: Sized = CondenseBeside::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) – The combiation of Val and Ret after condensing was applied (`Beside::condense()`/`Ret::condense(v)` for all `v : Val`). + +pub mod beside; +pub mod condense; +pub mod dst_coercion; +pub mod to_function; +pub mod to_trait; +pub mod with_destination; diff --git a/to/src/to/to_function.rs b/to/src/to/to_function.rs new file mode 100644 index 0000000..dfb01b5 --- /dev/null +++ b/to/src/to/to_function.rs @@ -0,0 +1,14 @@ +use crate::{DstCoercion, To}; + +/// Alias for [To::to] moving the destination to the left. +/// +/// This provides similar haptics to the let assignment syntax is rust, which also keeps +/// the variable to assign to on the left and the generating function on the right. +pub fn to(dst: &mut Coercable, src: Src) -> Ret +where + Coercable: ?Sized + DstCoercion, + Src: To, + Dst: ?Sized, +{ + src.to(dst.coerce_dest()) +} diff --git a/to/src/to/to_trait.rs b/to/src/to/to_trait.rs new file mode 100644 index 0000000..e8fc2bf --- /dev/null +++ b/to/src/to/to_trait.rs @@ -0,0 +1,96 @@ +use crate::{Beside, CondenseBeside}; +use std::borrow::BorrowMut; + +// The To trait is the core of the to crate; most functions with destinations will either return +// an object that is an instance of this trait or they will return `-> impl To: Sized { + fn to(self, out: &mut Dst) -> Ret; + + /// Generate a destination on the fly with a lambda. + /// + /// Calls the provided closure to create a value, + /// calls [crate::to()] to evaluate the function and finally + /// returns a [Beside] instance containing the generated destination value and the return + /// value. + fn to_this_beside(self, fun: Fun) -> Beside + where + Val: BorrowMut, + Fun: FnOnce() -> Val, + { + let mut val = fun(); + let ret = self.to(val.borrow_mut()); + Beside(val, ret) + } + + /// Generate a destination on the fly using default. + /// + /// Uses [Default] to create a value, + /// calls [crate::to()] to evaluate the function and finally + /// returns a [Beside] instance containing the generated destination value and the return + /// value. + fn to_value_beside(self) -> Beside + where + Dst: Sized + Default, + { + self.to_this_beside(|| Dst::default()) + } + + /// Generate a destination on the fly using default and a custom storage type. + /// + /// Uses [Default] to create a value of the given type, + /// calls [crate::to()] to evaluate the function and finally + /// returns a [Beside] instance containing the generated destination value and the return + /// value. + /// + /// Using collect_beside with an explicit type instead of [Self::to_value_beside] is mainly useful + /// when the Destination is unsized. + /// + /// This could be the case when the destination is an `[u8]` for instance. + fn collect_beside(self) -> Beside + where + Val: Default + BorrowMut, + { + self.to_this_beside(|| Val::default()) + } + + /// Generate a destination on the fly with a lambda, condensing the destination and the + /// return value into one. + /// + /// This is like using [Self::to_this_beside] followed by calling [Beside::condense]. + fn to_this(self, fun: Fun) -> >::Condensed + where + Ret: CondenseBeside, + Val: BorrowMut, + Fun: FnOnce() -> Val, + { + self.to_this_beside(fun).condense() + } + + /// Generate a destination on the fly using default, condensing the destination and the + /// return value into one. + /// + /// This is like using [Self::to_value_beside] followed by calling [Beside::condense]. + fn to_value(self) -> >::Condensed + where + Dst: Sized + Default, + Ret: CondenseBeside, + { + self.to_value_beside().condense() + } + + /// Generate a destination on the fly using default, condensing the destination and the + /// return value into one. + /// + /// This is like using [Self::collect_beside] followed by calling [Beside::condense]. + fn collect(self) -> >::Condensed + where + Val: Default + BorrowMut, + Ret: CondenseBeside, + { + self.collect_beside::().condense() + } +} diff --git a/to/src/to/with_destination.rs b/to/src/to/with_destination.rs new file mode 100644 index 0000000..a08cb5e --- /dev/null +++ b/to/src/to/with_destination.rs @@ -0,0 +1,35 @@ +use crate::To; +use std::marker::PhantomData; + +struct ToClosure +where + Dst: ?Sized, + Fun: FnOnce(&mut Dst) -> Ret, +{ + fun: Fun, + _val: PhantomData>, +} + +impl To for ToClosure +where + Dst: ?Sized, + Fun: FnOnce(&mut Dst) -> Ret, +{ + fn to(self, out: &mut Dst) -> Ret { + (self.fun)(out) + } +} + +/// Used to create a function with destination. +/// +/// See the tutorial in [readme.me].. +pub fn with_destination(fun: Fun) -> impl To +where + Dst: ?Sized, + Fun: FnOnce(&mut Dst) -> Ret, +{ + ToClosure { + fun, + _val: PhantomData, + } +} From d44793e07f577036a407116338877389e70938a2 Mon Sep 17 00:00:00 2001 From: Morgan Hill Date: Tue, 28 Nov 2023 23:51:14 +0100 Subject: [PATCH 19/46] 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 20/46] 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 21/46] 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 [ From cc8c13e12182712012982cf01f6e9efa0e3934cd Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 28 Nov 2023 19:14:00 +0100 Subject: [PATCH 22/46] chore: Remove lprf.rs (dead code) --- rosenpass/src/lprf.rs | 100 ------------------------------------------ 1 file changed, 100 deletions(-) delete mode 100644 rosenpass/src/lprf.rs diff --git a/rosenpass/src/lprf.rs b/rosenpass/src/lprf.rs deleted file mode 100644 index 765a49d..0000000 --- a/rosenpass/src/lprf.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! The rosenpass protocol relies on a special type -//! of hash function for most of its hashing or -//! message authentication needs: an incrementable -//! pseudo random function. -//! -//! This is a generalization of a PRF operating -//! on a sequence of inputs instead of a single input. -//! -//! Like a Dec function the Iprf features efficient -//! incrementability. -//! -//! You can also think of an Iprf as a Dec function with -//! a fixed size output. -//! -//! The idea behind a Iprf is that it can be efficiently -//! constructed from an Dec function as well as a PRF. -//! -//! TODO Base the construction on a proper Dec function - -use rosenpass_ciphers::{KEY_LEN, hash}; - -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_LEN]) - } - - fn dup(self) -> IprfBranch { - IprfBranch(self.0) - } - - // TODO: Protocol! Use domain separation to ensure that - fn mix(self, v: &[u8]) -> Self { - 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_LEN] { - self.0 - } - - fn extract(self, v: &[u8], dst: &mut [u8]) { - hash(&self.0, v).to(dst) - } -} - -impl IprfBranch { - fn mix(&self, v: &[u8]) -> Iprf { - Iprf(hash(self.0, v).collect<[u8; KEY_LEN]>()) - } - - fn mix_secret(&self, v: Secret) -> SecretIprf { - SecretIprf::prf_incov(self.0, v.secret()) - } -} - -impl SecretIprf { - fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf { - mutating(SecretIprf(Secret::zero()), |r| { - hash(k, d).to(r.secret_mut()) - }) - } - - fn from_key(k: Secret) -> SecretIprf { - Self(k) - } - - fn mix(self, v: &[u8]) -> SecretIprf { - Self::prf_invoc(self.0.secret(), v) - } - - fn mix_secret(self, v: Secret) -> SecretIprf { - Self::prf_invoc(self.0.secret(), v.secret()) - } - - fn into_secret(self) -> Secret { - self.0 - } - - fn into_secret_slice(self, v: &[u8], dst: &[u8]) { - hash(self.0.secret(), v).to(dst) - } -} - -impl SecretIprfBranch { - fn mix(&self, v: &[u8]) -> SecretIprf { - SecretIprf::prf_invoc(self.0.secret(), v) - } - - fn mix_secret(&self, v: Secret) -> SecretIprf { - SecretIprf::prf_invoc(self.0.secret(), v.secret()) - } -} From 181154b47002041f35756390286a4cad404da966 Mon Sep 17 00:00:00 2001 From: Jemilu Mohammed Date: Thu, 30 Nov 2023 14:18:19 +0000 Subject: [PATCH 23/46] move external dependencies to workspace level --- Cargo.toml | 22 ++++++++++++++++++++++ ciphers/Cargo.toml | 6 +++--- fuzzing/Cargo.toml | 6 +++--- rosenpass/Cargo.toml | 32 ++++++++++++++++---------------- sodium/Cargo.toml | 6 +++--- to/Cargo.toml | 2 +- util/Cargo.toml | 4 ++-- 7 files changed, 50 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e46d599..9747f99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,25 @@ members = [ [workspace.metadata.release] # ensure that adding `--package` as argument to `cargo release` still creates version tags in the form of `vx.y.z` tag-prefix = "" + +[workspace.dependencies] +arbitrary = "1.3.2" +libfuzzer-sys = "0.4" +stacker = "0.1.15" +doc-comment = "0.3.3" +base64 = "0.21.1" +zeroize = "1.7.0" +memoffset = "0.9.0" +lazy_static = "1.4.0" +thiserror = "1.0.40" +paste = "1.0.12" +env_logger = "0.10.0" +serde = "1.0.163" +toml = "0.7.4" +mio = "0.8.6" +clap = "4.3.0" +static_assertions = "1.1.0" +log = { version = "0.4.17" } +anyhow = { version = "1.0.71" } +libsodium-sys-stable= { version = "1.19.28" } +oqs-sys = {default-features = false, version = "0.8" } \ No newline at end of file diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index c190957..f23cc60 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,9 +10,9 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] -anyhow = "1.0.75" +anyhow = { workspace = true } rosenpass-sodium = { path = "../sodium" } rosenpass-to = { path = "../to" } rosenpass-constant-time = { path = "../constant-time" } -static_assertions = "1.1.0" -zeroize = "1.7.0" +static_assertions = { workspace = true } +zeroize = { workspace = true } diff --git a/fuzzing/Cargo.toml b/fuzzing/Cargo.toml index f9d3e0e..c786de0 100644 --- a/fuzzing/Cargo.toml +++ b/fuzzing/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" cargo-fuzz = true [dependencies] -arbitrary = { version = "1.3.2", features = ["derive"]} -libfuzzer-sys = "0.4" -stacker = "0.1.15" +arbitrary = { workspace = true, features = ["derive"]} +libfuzzer-sys = { workspace = true } +stacker = { workspace = true } [dependencies.rosenpass] path = "../rosenpass" diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 65d9c55..67195b9 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -19,25 +19,25 @@ 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" -libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] } -oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } -lazy_static = "1.4.0" -thiserror = "1.0.40" -paste = "1.0.12" -log = { version = "0.4.17" } -env_logger = { version = "0.10.0" } -serde = { version = "1.0.163", features = ["derive"] } -toml = "0.7.4" -clap = { version = "4.3.0", features = ["derive"] } -mio = { version = "0.8.6", features = ["net", "os-poll"] } +anyhow = { workspace = true, features = ["backtrace"] } +static_assertions = { workspace = true } +memoffset = { workspace = true } +libsodium-sys-stable = { workspace = true, features = ["use-pkg-config"] } +oqs-sys = { workspace = true, default-features = false, features = ['classic_mceliece', 'kyber'] } +lazy_static = { workspace = true } +thiserror = { workspace = true } +paste = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +serde = { workspace = true, features = ["derive"] } +toml = { workspace = true } +clap = { workspace = true, features = ["derive"] } +mio = { workspace = true, features = ["net", "os-poll"] } [build-dependencies] -anyhow = "1.0.71" +anyhow = { workspace = true } [dev-dependencies] criterion = "0.4.0" test_bin = "0.4.0" -stacker = "0.1.15" +stacker = { workspace = true } diff --git a/sodium/Cargo.toml b/sodium/Cargo.toml index 3108888..6d791e3 100644 --- a/sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -12,6 +12,6 @@ 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" } +anyhow = { workspace = true, features = ["backtrace"] } +libsodium-sys-stable = { workspace = true, features = ["use-pkg-config"] } +log = { workspace = true } diff --git a/to/Cargo.toml b/to/Cargo.toml index 09b49e6..bda13f3 100644 --- a/to/Cargo.toml +++ b/to/Cargo.toml @@ -10,4 +10,4 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dev-dependencies] -doc-comment = "0.3.3" +doc-comment = { workspace = true } diff --git a/util/Cargo.toml b/util/Cargo.toml index d5b7bfa..6435345 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -12,5 +12,5 @@ readme = "readme.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -base64 = "0.21.1" -anyhow = { version = "1.0.71", features = ["backtrace"] } +base64 = { workspace = true } +anyhow = { workspace = true, features = ["backtrace"] } From ca35e47d2a294ab336072155211a3ac7c9bebc28 Mon Sep 17 00:00:00 2001 From: Jemilu Mohammed Date: Thu, 30 Nov 2023 16:17:38 +0000 Subject: [PATCH 24/46] manage features in workspaces cargo.toml file --- Cargo.toml | 14 +++++++------- fuzzing/Cargo.toml | 2 +- rosenpass/Cargo.toml | 12 ++++++------ sodium/Cargo.toml | 4 ++-- util/Cargo.toml | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9747f99..f28fba8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ members = [ tag-prefix = "" [workspace.dependencies] -arbitrary = "1.3.2" libfuzzer-sys = "0.4" stacker = "0.1.15" doc-comment = "0.3.3" @@ -27,12 +26,13 @@ lazy_static = "1.4.0" thiserror = "1.0.40" paste = "1.0.12" env_logger = "0.10.0" -serde = "1.0.163" toml = "0.7.4" -mio = "0.8.6" -clap = "4.3.0" static_assertions = "1.1.0" log = { version = "0.4.17" } -anyhow = { version = "1.0.71" } -libsodium-sys-stable= { version = "1.19.28" } -oqs-sys = {default-features = false, version = "0.8" } \ No newline at end of file +clap = { version = "4.3.0", features = ["derive"] } +serde = { version = "1.0.163", features = ["derive"] } +arbitrary = { version = "1.3.2", features = ["derive"] } +anyhow = { version = "1.0.71", features = ["backtrace"] } +mio = { version = "0.8.6", features = ["net", "os-poll"] } +libsodium-sys-stable= { version = "1.19.28", features = ["use-pkg-config"] } +oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } \ No newline at end of file diff --git a/fuzzing/Cargo.toml b/fuzzing/Cargo.toml index c786de0..a410dbd 100644 --- a/fuzzing/Cargo.toml +++ b/fuzzing/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" cargo-fuzz = true [dependencies] -arbitrary = { workspace = true, features = ["derive"]} +arbitrary = { workspace = true } libfuzzer-sys = { workspace = true } stacker = { workspace = true } diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 67195b9..c30a606 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -19,20 +19,20 @@ rosenpass-constant-time = { path = "../constant-time" } rosenpass-sodium = { path = "../sodium" } rosenpass-ciphers = { path = "../ciphers" } rosenpass-to = { path = "../to" } -anyhow = { workspace = true, features = ["backtrace"] } +anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } -libsodium-sys-stable = { workspace = true, features = ["use-pkg-config"] } -oqs-sys = { workspace = true, default-features = false, features = ['classic_mceliece', 'kyber'] } +libsodium-sys-stable = { workspace = true } +oqs-sys = { workspace = true } lazy_static = { workspace = true } thiserror = { workspace = true } paste = { workspace = true } log = { workspace = true } env_logger = { workspace = true } -serde = { workspace = true, features = ["derive"] } +serde = { workspace = true } toml = { workspace = true } -clap = { workspace = true, features = ["derive"] } -mio = { workspace = true, features = ["net", "os-poll"] } +clap = { workspace = true } +mio = { workspace = true } [build-dependencies] anyhow = { workspace = true } diff --git a/sodium/Cargo.toml b/sodium/Cargo.toml index 6d791e3..d954126 100644 --- a/sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -12,6 +12,6 @@ readme = "readme.md" [dependencies] rosenpass-util = { path = "../util" } rosenpass-to = { path = "../to" } -anyhow = { workspace = true, features = ["backtrace"] } -libsodium-sys-stable = { workspace = true, features = ["use-pkg-config"] } +anyhow = { workspace = true } +libsodium-sys-stable = { workspace = true } log = { workspace = true } diff --git a/util/Cargo.toml b/util/Cargo.toml index 6435345..d1df0f1 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -13,4 +13,4 @@ readme = "readme.md" [dependencies] base64 = { workspace = true } -anyhow = { workspace = true, features = ["backtrace"] } +anyhow = { workspace = true } From ba224a220009299fb34a2fe8c9be23db5d4b63fd Mon Sep 17 00:00:00 2001 From: Jemilu Mohammed Date: Thu, 30 Nov 2023 17:04:27 +0000 Subject: [PATCH 25/46] add default member add shared dependencies to workspace dependencies all package level dependencies now rely on workspace --- Cargo.toml | 12 ++++++++++++ ciphers/Cargo.toml | 6 +++--- constant-time/Cargo.toml | 2 +- fuzzing/Cargo.toml | 16 ++++------------ rosenpass/Cargo.toml | 14 +++++++------- sodium/Cargo.toml | 4 ++-- 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f28fba8..232fe4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,23 @@ members = [ "fuzzing", ] +default-members = [ + "rosenpass" +] + [workspace.metadata.release] # ensure that adding `--package` as argument to `cargo release` still creates version tags in the form of `vx.y.z` tag-prefix = "" [workspace.dependencies] +rosenpass = { path = "rosenpass" } +rosenpass-util = { path = "util" } +rosenpass-constant-time = { path = "constant-time" } +rosenpass-sodium = { path = "sodium" } +rosenpass-ciphers = { path = "ciphers" } +rosenpass-to = { path = "to" } +criterion = "0.4.0" +test_bin = "0.4.0" libfuzzer-sys = "0.4" stacker = "0.1.15" doc-comment = "0.3.3" diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index f23cc60..2663cab 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -11,8 +11,8 @@ readme = "readme.md" [dependencies] anyhow = { workspace = true } -rosenpass-sodium = { path = "../sodium" } -rosenpass-to = { path = "../to" } -rosenpass-constant-time = { path = "../constant-time" } +rosenpass-sodium = { workspace = true } +rosenpass-to = { workspace = true } +rosenpass-constant-time = { workspace = true } static_assertions = { workspace = true } zeroize = { workspace = true } diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index 3e30b63..46de621 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -12,4 +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" } +rosenpass-to = { workspace = true } diff --git a/fuzzing/Cargo.toml b/fuzzing/Cargo.toml index a410dbd..9fb6f66 100644 --- a/fuzzing/Cargo.toml +++ b/fuzzing/Cargo.toml @@ -11,18 +11,10 @@ cargo-fuzz = true arbitrary = { workspace = true } libfuzzer-sys = { workspace = true } stacker = { workspace = true } - -[dependencies.rosenpass] -path = "../rosenpass" - -[dependencies.rosenpass-sodium] -path = "../sodium" - -[dependencies.rosenpass-ciphers] -path = "../ciphers" - -[dependencies.rosenpass-to] -path = "../to" +rosenpass-sodium = { workspace = true } +rosenpass-ciphers = { workspace = true } +rosenpass-to = { workspace = true } +rosenpass = { workspace = true } [[bin]] name = "fuzz_handle_msg" diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index c30a606..75295a5 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -14,11 +14,11 @@ name = "handshake" harness = false [dependencies] -rosenpass-util = { path = "../util" } -rosenpass-constant-time = { path = "../constant-time" } -rosenpass-sodium = { path = "../sodium" } -rosenpass-ciphers = { path = "../ciphers" } -rosenpass-to = { path = "../to" } +rosenpass-util = { workspace = true } +rosenpass-constant-time = { workspace = true } +rosenpass-sodium = { workspace = true } +rosenpass-ciphers = { workspace = true } +rosenpass-to = { workspace = true } anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } @@ -38,6 +38,6 @@ mio = { workspace = true } anyhow = { workspace = true } [dev-dependencies] -criterion = "0.4.0" -test_bin = "0.4.0" +criterion = { workspace = true } +test_bin = { workspace = true } stacker = { workspace = true } diff --git a/sodium/Cargo.toml b/sodium/Cargo.toml index d954126..b5ade3c 100644 --- a/sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -10,8 +10,8 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] -rosenpass-util = { path = "../util" } -rosenpass-to = { path = "../to" } +rosenpass-util = { workspace = true } +rosenpass-to = { workspace = true } anyhow = { workspace = true } libsodium-sys-stable = { workspace = true } log = { workspace = true } From 284ebb261f1b0c14e0dd120fba4e032a9e787eef Mon Sep 17 00:00:00 2001 From: Ezhil Shanmugham Date: Sun, 26 Nov 2023 17:59:31 +0530 Subject: [PATCH 26/46] fix: enabled fuzzing --- .github/workflows/qc.yaml | 29 +++++++++++++++++++ Cargo.toml | 2 +- {fuzzing => fuzz}/.gitignore | 0 {fuzzing => fuzz}/Cargo.lock | 0 {fuzzing => fuzz}/Cargo.toml | 0 .../fuzz_targets/aead_enc_into.rs | 0 {fuzzing => fuzz}/fuzz_targets/blake2b.rs | 0 {fuzzing => fuzz}/fuzz_targets/handle_msg.rs | 0 .../fuzz_targets/kyber_encaps.rs | 0 .../fuzz_targets/mceliece_encaps.rs | 0 10 files changed, 30 insertions(+), 1 deletion(-) rename {fuzzing => fuzz}/.gitignore (100%) rename {fuzzing => fuzz}/Cargo.lock (100%) rename {fuzzing => fuzz}/Cargo.toml (100%) rename {fuzzing => fuzz}/fuzz_targets/aead_enc_into.rs (100%) rename {fuzzing => fuzz}/fuzz_targets/blake2b.rs (100%) rename {fuzzing => fuzz}/fuzz_targets/handle_msg.rs (100%) rename {fuzzing => fuzz}/fuzz_targets/kyber_encaps.rs (100%) rename {fuzzing => fuzz}/fuzz_targets/mceliece_encaps.rs (100%) diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index ad38c26..5cbe8a3 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -117,3 +117,32 @@ jobs: name: rosenpass authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - run: nix develop --command cargo test + + cargo-fuzz: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Install libsodium + run: sudo apt-get install -y libsodium-dev + - name: Install nightly toolchain + run: | + rustup toolchain install nightly + rustup default nightly + - name: Install cargo-fuzz + run: cargo install cargo-fuzz + - name: Run fuzzing + run: | + cargo fuzz run fuzz_aead_enc_into -- -max_total_time=60 + cargo fuzz run fuzz_blake2b -- -max_total_time=60 + cargo fuzz run fuzz_handle_msg -- -max_total_time=60 + cargo fuzz run fuzz_kyber_encaps -- -max_total_time=60 + cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=60 diff --git a/Cargo.toml b/Cargo.toml index 232fe4f..0135514 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "constant-time", "sodium", "to", - "fuzzing", + "fuzz", ] default-members = [ diff --git a/fuzzing/.gitignore b/fuzz/.gitignore similarity index 100% rename from fuzzing/.gitignore rename to fuzz/.gitignore diff --git a/fuzzing/Cargo.lock b/fuzz/Cargo.lock similarity index 100% rename from fuzzing/Cargo.lock rename to fuzz/Cargo.lock diff --git a/fuzzing/Cargo.toml b/fuzz/Cargo.toml similarity index 100% rename from fuzzing/Cargo.toml rename to fuzz/Cargo.toml diff --git a/fuzzing/fuzz_targets/aead_enc_into.rs b/fuzz/fuzz_targets/aead_enc_into.rs similarity index 100% rename from fuzzing/fuzz_targets/aead_enc_into.rs rename to fuzz/fuzz_targets/aead_enc_into.rs diff --git a/fuzzing/fuzz_targets/blake2b.rs b/fuzz/fuzz_targets/blake2b.rs similarity index 100% rename from fuzzing/fuzz_targets/blake2b.rs rename to fuzz/fuzz_targets/blake2b.rs diff --git a/fuzzing/fuzz_targets/handle_msg.rs b/fuzz/fuzz_targets/handle_msg.rs similarity index 100% rename from fuzzing/fuzz_targets/handle_msg.rs rename to fuzz/fuzz_targets/handle_msg.rs diff --git a/fuzzing/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs similarity index 100% rename from fuzzing/fuzz_targets/kyber_encaps.rs rename to fuzz/fuzz_targets/kyber_encaps.rs diff --git a/fuzzing/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs similarity index 100% rename from fuzzing/fuzz_targets/mceliece_encaps.rs rename to fuzz/fuzz_targets/mceliece_encaps.rs From 7cece82119ab702e96d75121f1db1ede1cfd3330 Mon Sep 17 00:00:00 2001 From: Olaf Pichler Date: Wed, 29 Nov 2023 12:47:21 +0100 Subject: [PATCH 27/46] added WireGuard config example to gen-config --- rosenpass/src/config.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 7f54645..a2af6b0 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -361,7 +361,14 @@ impl Rosenpass { .collect(), key_out: Some("rp-key-out".into()), pre_shared_key: None, - wg: None, + wg: Some(WireGuard { + device: "wirgeguard device e.g. wg0".into(), + peer: "wireguard public key".into(), + extra_params: vec![ + "extra params".into(), + "passed to".into(), + "wg set".into() + ] }), }; Self { From d0140954696e2c132369935cfa5b215140b164f3 Mon Sep 17 00:00:00 2001 From: Olaf Pichler Date: Wed, 29 Nov 2023 12:53:29 +0100 Subject: [PATCH 28/46] Added indication that exchange_command is not used --- rosenpass/src/config.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index a2af6b0..afd8406 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -348,13 +348,9 @@ impl Rosenpass { public_key: "rp-peer-public-key".into(), endpoint: Some("my-peer.test:9999".into()), exchange_command: [ - "wg", - "set", - "wg0", - "peer", - "", - "preshared-key", - "/dev/stdin", + "currently", + "not", + "in use", ] .into_iter() .map(|x| x.to_string()) From 205c301012d7fba8ea8f5007d20524c7dc06595f Mon Sep 17 00:00:00 2001 From: Olaf Pichler Date: Wed, 29 Nov 2023 13:03:30 +0100 Subject: [PATCH 29/46] Added indications that file paths are used --- rosenpass/src/config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index afd8406..f01bda9 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -345,7 +345,7 @@ impl Rosenpass { /// Generate an example configuration pub fn example_config() -> Self { let peer = RosenpassPeer { - public_key: "rp-peer-public-key".into(), + public_key: "/path/to/rp-peer-public-key".into(), endpoint: Some("my-peer.test:9999".into()), exchange_command: [ "currently", @@ -355,7 +355,7 @@ impl Rosenpass { .into_iter() .map(|x| x.to_string()) .collect(), - key_out: Some("rp-key-out".into()), + key_out: Some("/path/to/rp-key-out.txt".into()), pre_shared_key: None, wg: Some(WireGuard { device: "wirgeguard device e.g. wg0".into(), @@ -368,8 +368,8 @@ impl Rosenpass { }; Self { - public_key: "rp-public-key".into(), - secret_key: "rp-secret-key".into(), + public_key: "/path/to/rp-public-key".into(), + secret_key: "/path/to/rp-secret-key".into(), peers: vec![peer], ..Self::new("", "") } From 31d43accd5f5671e4bd7b7ee2b6e7e1af964258a Mon Sep 17 00:00:00 2001 From: Olaf Pichler Date: Wed, 29 Nov 2023 16:59:49 +0100 Subject: [PATCH 30/46] #172 removed exchange_command --- rosenpass/src/config.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index f01bda9..ace5e58 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -41,10 +41,6 @@ pub struct RosenpassPeer { #[serde(default)] pub key_out: Option, - // TODO make sure failure does not crash but is logged - #[serde(default)] - pub exchange_command: Vec, - // TODO make this field only available on binary builds, not on library builds #[serde(flatten)] pub wg: Option, @@ -347,21 +343,12 @@ impl Rosenpass { let peer = RosenpassPeer { public_key: "/path/to/rp-peer-public-key".into(), endpoint: Some("my-peer.test:9999".into()), - exchange_command: [ - "currently", - "not", - "in use", - ] - .into_iter() - .map(|x| x.to_string()) - .collect(), key_out: Some("/path/to/rp-key-out.txt".into()), pre_shared_key: None, wg: Some(WireGuard { device: "wirgeguard device e.g. wg0".into(), peer: "wireguard public key".into(), extra_params: vec![ - "extra params".into(), "passed to".into(), "wg set".into() ] }), From 36089fd37f3a079c81dcaae81e95ddb5cc5ee6c7 Mon Sep 17 00:00:00 2001 From: Olaf Pichler Date: Thu, 30 Nov 2023 14:26:13 +0100 Subject: [PATCH 31/46] Added example for additional PSK --- rosenpass/src/config.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index ace5e58..8eee049 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -344,14 +344,12 @@ impl Rosenpass { public_key: "/path/to/rp-peer-public-key".into(), endpoint: Some("my-peer.test:9999".into()), key_out: Some("/path/to/rp-key-out.txt".into()), - pre_shared_key: None, + pre_shared_key: Some("additional pre shared key".into()), wg: Some(WireGuard { device: "wirgeguard device e.g. wg0".into(), peer: "wireguard public key".into(), - extra_params: vec![ - "passed to".into(), - "wg set".into() - ] }), + extra_params: vec!["passed to".into(), "wg set".into()], + }), }; Self { From 7bda010a9b0126c980488a12a02a077a0d89f71c Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 12:15:21 +0100 Subject: [PATCH 32/46] chore: Move Public and debug_crypto_array into secret-memory crate --- Cargo.lock | 11 ++++ Cargo.toml | 4 +- rosenpass/Cargo.toml | 1 + rosenpass/src/coloring.rs | 111 +----------------------------------- rosenpass/src/protocol.rs | 1 + secret-memory/Cargo.toml | 16 ++++++ secret-memory/readme.md | 5 ++ secret-memory/src/debug.rs | 20 +++++++ secret-memory/src/lib.rs | 4 ++ secret-memory/src/public.rs | 105 ++++++++++++++++++++++++++++++++++ 10 files changed, 167 insertions(+), 111 deletions(-) create mode 100644 secret-memory/Cargo.toml create mode 100644 secret-memory/readme.md create mode 100644 secret-memory/src/debug.rs create mode 100644 secret-memory/src/lib.rs create mode 100644 secret-memory/src/public.rs diff --git a/Cargo.lock b/Cargo.lock index f687edf..0afaded 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1073,6 +1073,7 @@ dependencies = [ "paste", "rosenpass-ciphers", "rosenpass-constant-time", + "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", "rosenpass-util", @@ -1116,6 +1117,16 @@ dependencies = [ "stacker", ] +[[package]] +name = "rosenpass-secret-memory" +version = "0.1.0" +dependencies = [ + "anyhow", + "rosenpass-sodium", + "rosenpass-to", + "rosenpass-util", +] + [[package]] name = "rosenpass-sodium" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0135514..5cd7b90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "sodium", "to", "fuzz", + "secret-memory", ] default-members = [ @@ -26,6 +27,7 @@ rosenpass-constant-time = { path = "constant-time" } rosenpass-sodium = { path = "sodium" } rosenpass-ciphers = { path = "ciphers" } rosenpass-to = { path = "to" } +rosenpass-secret-memory = { path = "secret-memory" } criterion = "0.4.0" test_bin = "0.4.0" libfuzzer-sys = "0.4" @@ -47,4 +49,4 @@ arbitrary = { version = "1.3.2", features = ["derive"] } anyhow = { version = "1.0.71", features = ["backtrace"] } mio = { version = "0.8.6", features = ["net", "os-poll"] } libsodium-sys-stable= { version = "1.19.28", features = ["use-pkg-config"] } -oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } \ No newline at end of file +oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 75295a5..49b7253 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -19,6 +19,7 @@ rosenpass-constant-time = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } rosenpass-to = { workspace = true } +rosenpass-secret-memory = { workspace = true } anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } diff --git a/rosenpass/src/coloring.rs b/rosenpass/src/coloring.rs index ecbe6fe..85cddad 100644 --- a/rosenpass/src/coloring.rs +++ b/rosenpass/src/coloring.rs @@ -1,11 +1,3 @@ -//! Types types for dealing with (secret-) values -//! -//! These types use type level coloring to make accidential leackage of secrets extra hard. Both [Secret] and [Public] own their data, but the memory backing -//! [Secret] is special: -//! - as it is heap allocated, we can actively zeroize the memory before freeing it. -//! - guard pages before and after each allocation trap accidential sequential reads that creep towards our secrets -//! - the memory is mlocked, e.g. it is never swapped - use anyhow::Context; use lazy_static::lazy_static; use libsodium_sys as libsodium; @@ -13,17 +5,10 @@ use rosenpass_util::{ b64::b64_reader, file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd, StoreValue}, functional::mutating, - mem::cpy, }; use std::result::Result; use std::{ - collections::HashMap, - convert::TryInto, - fmt, - ops::{Deref, DerefMut}, - os::raw::c_void, - path::Path, - ptr::null_mut, + collections::HashMap, convert::TryInto, fmt, os::raw::c_void, path::Path, ptr::null_mut, sync::Mutex, }; @@ -216,81 +201,6 @@ impl fmt::Debug for Secret { } } -/// Contains information in the form of a byte array that may be known to the -/// public -// TODO: We should get rid of the Public type; just use a normal value -#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct Public { - pub value: [u8; N], -} - -impl Public { - /// Create a new [Public] from a byte slice - pub fn from_slice(value: &[u8]) -> Self { - mutating(Self::zero(), |r| cpy(value, &mut r.value)) - } - - /// Create a new [Public] from a byte array - pub fn new(value: [u8; N]) -> Self { - Self { value } - } - - /// Create a zero initialized [Public] - pub fn zero() -> Self { - Self { value: [0u8; N] } - } - - /// Create a random initialized [Public] - pub fn random() -> Self { - mutating(Self::zero(), |r| r.randomize()) - } - - /// Randomize all bytes in an existing [Public] - pub fn randomize(&mut self) { - rosenpass_sodium::helpers::randombytes_buf(&mut self.value); - } -} - -/// Writes the contents of an `&[u8]` as hexadecimal symbols to a [std::fmt::Formatter] -pub fn debug_crypto_array(v: &[u8], fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str("[{}]=")?; - if v.len() > 64 { - for byte in &v[..32] { - std::fmt::LowerHex::fmt(byte, fmt)?; - } - fmt.write_str("…")?; - for byte in &v[v.len() - 32..] { - std::fmt::LowerHex::fmt(byte, fmt)?; - } - } else { - for byte in v { - std::fmt::LowerHex::fmt(byte, fmt)?; - } - } - Ok(()) -} - -impl fmt::Debug for Public { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - debug_crypto_array(&self.value, fmt) - } -} - -impl Deref for Public { - type Target = [u8; N]; - - fn deref(&self) -> &[u8; N] { - &self.value - } -} - -impl DerefMut for Public { - fn deref_mut(&mut self) -> &mut [u8; N] { - &mut self.value - } -} - #[cfg(test)] mod test { use super::*; @@ -422,22 +332,3 @@ impl StoreSecret for Secret { Ok(()) } } - -impl LoadValue for Public { - type Error = anyhow::Error; - - fn load>(path: P) -> anyhow::Result { - let mut v = Self::random(); - fopen_r(path)?.read_exact_to_end(&mut *v)?; - Ok(v) - } -} - -impl StoreValue for Public { - type Error = anyhow::Error; - - fn store>(&self, path: P) -> anyhow::Result<()> { - std::fs::write(path, **self)?; - Ok(()) - } -} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 7c7556f..29926a7 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -76,6 +76,7 @@ use crate::{ }; use anyhow::{bail, ensure, Context, Result}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +use rosenpass_secret_memory::Public; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml new file mode 100644 index 0000000..0220ffd --- /dev/null +++ b/secret-memory/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rosenpass-secret-memory" +version = "0.1.0" +authors = ["Karolin Varner ", "wucke13 "] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal utilities for storing secrets in memory" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] +anyhow = { workspace = true } +rosenpass-to = { workspace = true } +rosenpass-sodium = { workspace = true } +rosenpass-util = { workspace = true } diff --git a/secret-memory/readme.md b/secret-memory/readme.md new file mode 100644 index 0000000..b5cb16b --- /dev/null +++ b/secret-memory/readme.md @@ -0,0 +1,5 @@ +# Rosenpass secure memory library + +Rosenpass internal library providing utilities for securely storing secret data in memory. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/secret-memory/src/debug.rs b/secret-memory/src/debug.rs new file mode 100644 index 0000000..ae1b7dd --- /dev/null +++ b/secret-memory/src/debug.rs @@ -0,0 +1,20 @@ +use std::fmt; + +/// Writes the contents of an `&[u8]` as hexadecimal symbols to a [std::fmt::Formatter] +pub fn debug_crypto_array(v: &[u8], fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("[{}]=")?; + if v.len() > 64 { + for byte in &v[..32] { + std::fmt::LowerHex::fmt(byte, fmt)?; + } + fmt.write_str("…")?; + for byte in &v[v.len() - 32..] { + std::fmt::LowerHex::fmt(byte, fmt)?; + } + } else { + for byte in v { + std::fmt::LowerHex::fmt(byte, fmt)?; + } + } + Ok(()) +} diff --git a/secret-memory/src/lib.rs b/secret-memory/src/lib.rs new file mode 100644 index 0000000..fae4d3f --- /dev/null +++ b/secret-memory/src/lib.rs @@ -0,0 +1,4 @@ +mod public; +pub use crate::public::Public; + +pub mod debug; diff --git a/secret-memory/src/public.rs b/secret-memory/src/public.rs new file mode 100644 index 0000000..a27b4e2 --- /dev/null +++ b/secret-memory/src/public.rs @@ -0,0 +1,105 @@ +use crate::debug::debug_crypto_array; +use rosenpass_to::{ops::copy_slice, To}; +use rosenpass_util::file::{fopen_r, LoadValue, ReadExactToEnd, StoreValue}; +use rosenpass_util::functional::mutating; +use std::borrow::{Borrow, BorrowMut}; +use std::fmt; +use std::ops::{Deref, DerefMut}; +use std::path::Path; + +/// Contains information in the form of a byte array that may be known to the +/// public +// TODO: We should get rid of the Public type; just use a normal value +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct Public { + pub value: [u8; N], +} + +impl Public { + /// Create a new [Public] from a byte slice + pub fn from_slice(value: &[u8]) -> Self { + copy_slice(value).to_this(|| Self::zero()) + } + + /// Create a new [Public] from a byte array + pub fn new(value: [u8; N]) -> Self { + Self { value } + } + + /// Create a zero initialized [Public] + pub fn zero() -> Self { + Self { value: [0u8; N] } + } + + /// Create a random initialized [Public] + pub fn random() -> Self { + mutating(Self::zero(), |r| r.randomize()) + } + + /// Randomize all bytes in an existing [Public] + pub fn randomize(&mut self) { + rosenpass_sodium::helpers::randombytes_buf(&mut self.value); + } +} + +impl fmt::Debug for Public { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + debug_crypto_array(&self.value, fmt) + } +} + +impl Deref for Public { + type Target = [u8; N]; + + fn deref(&self) -> &[u8; N] { + &self.value + } +} + +impl DerefMut for Public { + fn deref_mut(&mut self) -> &mut [u8; N] { + &mut self.value + } +} + +impl Borrow<[u8; N]> for Public { + fn borrow(&self) -> &[u8; N] { + &self.value + } +} +impl BorrowMut<[u8; N]> for Public { + fn borrow_mut(&mut self) -> &mut [u8; N] { + &mut self.value + } +} + +impl Borrow<[u8]> for Public { + fn borrow(&self) -> &[u8] { + &self.value + } +} +impl BorrowMut<[u8]> for Public { + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.value + } +} + +impl LoadValue for Public { + type Error = anyhow::Error; + + fn load>(path: P) -> anyhow::Result { + let mut v = Self::random(); + fopen_r(path)?.read_exact_to_end(&mut *v)?; + Ok(v) + } +} + +impl StoreValue for Public { + type Error = anyhow::Error; + + fn store>(&self, path: P) -> anyhow::Result<()> { + std::fs::write(path, **self)?; + Ok(()) + } +} From cf132bca1154b434a3340c7c69c73eb9834dd993 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 12:46:13 +0100 Subject: [PATCH 33/46] chore: Move rest of coloring.rs into secret-memory crate Also removes the StoreSecret trait from cli.rs as it was redundant. --- Cargo.lock | 3 +- fuzz/Cargo.toml | 1 + fuzz/fuzz_targets/handle_msg.rs | 2 +- rosenpass/Cargo.toml | 1 - rosenpass/src/cli.rs | 16 +-- rosenpass/src/lib.rs | 1 - rosenpass/src/prftree.rs | 2 +- rosenpass/src/protocol.rs | 3 +- secret-memory/Cargo.toml | 2 + secret-memory/src/file.rs | 7 ++ secret-memory/src/lib.rs | 6 +- .../src/secret.rs | 111 ++++++++---------- 12 files changed, 72 insertions(+), 83 deletions(-) create mode 100644 secret-memory/src/file.rs rename rosenpass/src/coloring.rs => secret-memory/src/secret.rs (95%) diff --git a/Cargo.lock b/Cargo.lock index 0afaded..4e6870b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,7 +1064,6 @@ dependencies = [ "clap 4.4.8", "criterion", "env_logger", - "lazy_static", "libsodium-sys-stable", "log", "memoffset", @@ -1122,6 +1121,8 @@ name = "rosenpass-secret-memory" version = "0.1.0" dependencies = [ "anyhow", + "lazy_static", + "libsodium-sys-stable", "rosenpass-sodium", "rosenpass-to", "rosenpass-util", diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 9fb6f66..345f553 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true arbitrary = { workspace = true } libfuzzer-sys = { workspace = true } stacker = { workspace = true } +rosenpass-secret-memory = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } rosenpass-to = { workspace = true } diff --git a/fuzz/fuzz_targets/handle_msg.rs b/fuzz/fuzz_targets/handle_msg.rs index 2d202d9..83d3533 100644 --- a/fuzz/fuzz_targets/handle_msg.rs +++ b/fuzz/fuzz_targets/handle_msg.rs @@ -3,8 +3,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::coloring::Secret; use rosenpass::protocol::CryptoServer; +use rosenpass_secret_memory::Secret; use rosenpass_sodium::init as sodium_init; fuzz_target!(|rx_buf: &[u8]| { diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 49b7253..f8a0591 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -25,7 +25,6 @@ static_assertions = { workspace = true } memoffset = { workspace = true } libsodium-sys-stable = { workspace = true } oqs-sys = { workspace = true } -lazy_static = { workspace = true } thiserror = { workspace = true } paste = { workspace = true } log = { workspace = true } diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 2e888f5..ddb9ad3 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -1,13 +1,12 @@ use anyhow::{bail, ensure}; use clap::Parser; +use rosenpass_secret_memory::file::StoreSecret; use rosenpass_util::file::{LoadValue, LoadValueB64}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::app_server; use crate::app_server::AppServer; use crate::{ - // app_server::{AppServer, LoadValue, LoadValueB64}, - coloring::Secret, pqkem::{StaticKEM, KEM}, protocol::{SPk, SSk, SymKey}, }; @@ -248,14 +247,3 @@ impl Cli { srv.event_loop() } } - -trait StoreSecret { - fn store_secret>(&self, path: P) -> anyhow::Result<()>; -} - -impl StoreSecret for Secret { - fn store_secret>(&self, path: P) -> anyhow::Result<()> { - std::fs::write(path, self.secret())?; - Ok(()) - } -} diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index b223400..ee49bb6 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,4 +1,3 @@ -pub mod coloring; #[rustfmt::skip] pub mod labeled_prf; pub mod app_server; diff --git a/rosenpass/src/prftree.rs b/rosenpass/src/prftree.rs index f3956f1..289646c 100644 --- a/rosenpass/src/prftree.rs +++ b/rosenpass/src/prftree.rs @@ -1,5 +1,5 @@ //! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf) -use crate::coloring::Secret; +use rosenpass_secret_memory::Secret; use anyhow::Result; use rosenpass_ciphers::{hash, KEY_LEN}; diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 29926a7..0207f0a 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -68,7 +68,6 @@ //! ``` use crate::{ - coloring::*, labeled_prf as lprf, msgs::*, pqkem::*, @@ -76,7 +75,7 @@ use crate::{ }; use anyhow::{bail, ensure, Context, Result}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; -use rosenpass_secret_memory::Public; +use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ Entry::{Occupied, Vacant}, diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index 0220ffd..48d5ddb 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -14,3 +14,5 @@ anyhow = { workspace = true } rosenpass-to = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-util = { workspace = true } +libsodium-sys-stable = { workspace = true } +lazy_static = { workspace = true } diff --git a/secret-memory/src/file.rs b/secret-memory/src/file.rs new file mode 100644 index 0000000..f67bb52 --- /dev/null +++ b/secret-memory/src/file.rs @@ -0,0 +1,7 @@ +use std::path::Path; + +pub trait StoreSecret { + type Error; + + fn store_secret>(&self, path: P) -> Result<(), Self::Error>; +} diff --git a/secret-memory/src/lib.rs b/secret-memory/src/lib.rs index fae4d3f..3e57f54 100644 --- a/secret-memory/src/lib.rs +++ b/secret-memory/src/lib.rs @@ -1,4 +1,8 @@ +pub mod debug; +pub mod file; + mod public; pub use crate::public::Public; -pub mod debug; +mod secret; +pub use crate::secret::Secret; diff --git a/rosenpass/src/coloring.rs b/secret-memory/src/secret.rs similarity index 95% rename from rosenpass/src/coloring.rs rename to secret-memory/src/secret.rs index 85cddad..97983dc 100644 --- a/rosenpass/src/coloring.rs +++ b/secret-memory/src/secret.rs @@ -3,15 +3,17 @@ use lazy_static::lazy_static; use libsodium_sys as libsodium; use rosenpass_util::{ b64::b64_reader, - file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd, StoreValue}, + file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd}, functional::mutating, }; -use std::result::Result; + use std::{ collections::HashMap, convert::TryInto, fmt, os::raw::c_void, path::Path, ptr::null_mut, sync::Mutex, }; +use crate::file::StoreSecret; + // This might become a problem in library usage; it's effectively a memory // leak which probably isn't a problem right now because most memory will // be reused… @@ -28,7 +30,7 @@ lazy_static! { /// Further information about the protection in place can be found in in the /// [libsodium documentation](https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations) #[derive(Debug)] // TODO check on Debug derive, is that clever -pub struct SecretMemoryPool { +struct SecretMemoryPool { pool: HashMap>, } @@ -44,6 +46,7 @@ impl SecretMemoryPool { /// Return secrete back to the pool for future re-use /// /// This consumes the [Secret], but its memory is re-used. + #[allow(dead_code)] pub fn release(&mut self, mut s: Secret) { unsafe { self.release_by_ref(&mut s); @@ -201,6 +204,50 @@ impl fmt::Debug for Secret { } } +impl LoadValue for Secret { + type Error = anyhow::Error; + + fn load>(path: P) -> anyhow::Result { + let mut v = Self::random(); + let p = path.as_ref(); + fopen_r(p)? + .read_exact_to_end(v.secret_mut()) + .with_context(|| format!("Could not load file {p:?}"))?; + Ok(v) + } +} + +impl LoadValueB64 for Secret { + type Error = anyhow::Error; + + fn load_b64>(path: P) -> anyhow::Result { + use std::io::Read; + + let mut v = Self::random(); + let p = path.as_ref(); + // This might leave some fragments of the secret on the stack; + // in practice this is likely not a problem because the stack likely + // will be overwritten by something else soon but this is not exactly + // guaranteed. It would be possible to remedy this, but since the secret + // data will linger in the Linux page cache anyways with the current + // implementation, going to great length to erase the secret here is + // not worth it right now. + b64_reader(&mut fopen_r(p)?) + .read_exact(v.secret_mut()) + .with_context(|| format!("Could not load base64 file {p:?}"))?; + Ok(v) + } +} + +impl StoreSecret for Secret { + type Error = anyhow::Error; + + fn store_secret>(&self, path: P) -> anyhow::Result<()> { + std::fs::write(path, self.secret())?; + Ok(()) + } +} + #[cfg(test)] mod test { use super::*; @@ -274,61 +321,3 @@ mod test { assert_eq!(new_secret.secret(), &[0; N]); } } - -trait StoreSecret { - type Error; - - fn store_secret>(&self, path: P) -> Result<(), Self::Error>; -} - -impl StoreSecret for T { - type Error = ::Error; - - fn store_secret>(&self, path: P) -> Result<(), Self::Error> { - self.store(path) - } -} - -impl LoadValue for Secret { - type Error = anyhow::Error; - - fn load>(path: P) -> anyhow::Result { - let mut v = Self::random(); - let p = path.as_ref(); - fopen_r(p)? - .read_exact_to_end(v.secret_mut()) - .with_context(|| format!("Could not load file {p:?}"))?; - Ok(v) - } -} - -impl LoadValueB64 for Secret { - type Error = anyhow::Error; - - fn load_b64>(path: P) -> anyhow::Result { - use std::io::Read; - - let mut v = Self::random(); - let p = path.as_ref(); - // This might leave some fragments of the secret on the stack; - // in practice this is likely not a problem because the stack likely - // will be overwritten by something else soon but this is not exactly - // guaranteed. It would be possible to remedy this, but since the secret - // data will linger in the Linux page cache anyways with the current - // implementation, going to great length to erase the secret here is - // not worth it right now. - b64_reader(&mut fopen_r(p)?) - .read_exact(v.secret_mut()) - .with_context(|| format!("Could not load base64 file {p:?}"))?; - Ok(v) - } -} - -impl StoreSecret for Secret { - type Error = anyhow::Error; - - fn store_secret>(&self, path: P) -> anyhow::Result<()> { - std::fs::write(path, self.secret())?; - Ok(()) - } -} From 85a61808de0abd14f4a7cb243f74fd4e82e1b0c0 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 14:13:19 +0100 Subject: [PATCH 34/46] feat: Use the zeroize crate for zeroization --- Cargo.lock | 1 + secret-memory/Cargo.toml | 1 + secret-memory/src/secret.rs | 21 ++++++++++++--------- sodium/src/helpers.rs | 5 ----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e6870b..ba73b44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1126,6 +1126,7 @@ dependencies = [ "rosenpass-sodium", "rosenpass-to", "rosenpass-util", + "zeroize", ] [[package]] diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index 48d5ddb..c102599 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -16,3 +16,4 @@ rosenpass-sodium = { workspace = true } rosenpass-util = { workspace = true } libsodium-sys-stable = { workspace = true } lazy_static = { workspace = true } +zeroize = { workspace = true } diff --git a/secret-memory/src/secret.rs b/secret-memory/src/secret.rs index 97983dc..76d5b66 100644 --- a/secret-memory/src/secret.rs +++ b/secret-memory/src/secret.rs @@ -6,6 +6,7 @@ use rosenpass_util::{ file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd}, functional::mutating, }; +use zeroize::{Zeroize, ZeroizeOnDrop}; use std::{ collections::HashMap, convert::TryInto, fmt, os::raw::c_void, path::Path, ptr::null_mut, @@ -74,7 +75,7 @@ impl SecretMemoryPool { /// /// # Safety /// - /// This function contains an unsafe call to [libsodium::sodium_malloc]. + /// This function contains an unsafe call to [libsodium_sys::sodium_malloc]. /// This call has no known safety invariants, thus nothing can go wrong™. /// However, just like normal `malloc()` this can return a null ptr. Thus /// the returned pointer is checked for null; causing the program to panic @@ -100,8 +101,8 @@ impl Drop for SecretMemoryPool { /// # Safety /// /// The drop implementation frees the contained elements using - /// [libsodium::sodium_free]. This is safe as long as every `*mut c_void` - /// contained was initialized with a call to [libsodium::sodium_malloc] + /// [libsodium_sys::sodium_free]. This is safe as long as every `*mut c_void` + /// contained was initialized with a call to [libsodium_sys::sodium_malloc] fn drop(&mut self) { for ptr in self.pool.drain().flat_map(|(_, x)| x.into_iter()) { unsafe { @@ -119,7 +120,7 @@ unsafe impl Send for SecretMemoryPool {} /// Store for a secret /// -/// Uses memory allocated with [libsodium::sodium_malloc], +/// Uses memory allocated with [libsodium_sys::sodium_malloc], /// esentially can do the same things as `[u8; N].as_mut_ptr()`. pub struct Secret { ptr: *mut c_void, @@ -164,11 +165,6 @@ impl Secret { mutating(Self::zero(), |r| r.randomize()) } - /// Sets all data of an existing secret to null bytes - pub fn zeroize(&mut self) { - rosenpass_sodium::helpers::memzero(self.secret_mut()); - } - /// Sets all data an existing secret to random bytes pub fn randomize(&mut self) { rosenpass_sodium::helpers::randombytes_buf(self.secret_mut()); @@ -204,6 +200,13 @@ impl fmt::Debug for Secret { } } +impl ZeroizeOnDrop for Secret {} +impl Zeroize for Secret { + fn zeroize(&mut self) { + self.secret_mut().zeroize(); + } +} + impl LoadValue for Secret { type Error = anyhow::Error; diff --git a/sodium/src/helpers.rs b/sodium/src/helpers.rs index a83bc99..26e0bf5 100644 --- a/sodium/src/helpers.rs +++ b/sodium/src/helpers.rs @@ -32,11 +32,6 @@ pub fn randombytes_buf(buf: &mut [u8]) { unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; } -#[inline] -pub fn memzero(buf: &mut [u8]) { - unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - // Choose a fully random u64 // TODO: Replace with ::rand::random pub fn rand_u64() -> u64 { From 70fa9bd6d704a5c145b36bce91c6da3196bbbc6e Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 16:23:54 +0100 Subject: [PATCH 35/46] feat: Wrap sodium_malloc as a custom allocator This lets us get rid of quite a few unsafe blocks. --- Cargo.lock | 7 ++ Cargo.toml | 1 + secret-memory/src/secret.rs | 218 ++++++++++------------------------ sodium/Cargo.toml | 1 + sodium/src/alloc/allocator.rs | 95 +++++++++++++++ sodium/src/alloc/mod.rs | 10 ++ sodium/src/lib.rs | 1 + 7 files changed, 175 insertions(+), 158 deletions(-) create mode 100644 sodium/src/alloc/allocator.rs create mode 100644 sodium/src/alloc/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ba73b44..7c2f43c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anes" version = "0.1.6" @@ -1133,6 +1139,7 @@ dependencies = [ name = "rosenpass-sodium" version = "0.1.0" dependencies = [ + "allocator-api2", "anyhow", "libsodium-sys-stable", "log", diff --git a/Cargo.toml b/Cargo.toml index 5cd7b90..e336960 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ paste = "1.0.12" env_logger = "0.10.0" toml = "0.7.4" static_assertions = "1.1.0" +allocator-api2 = "0.2.16" log = { version = "0.4.17" } clap = { version = "4.3.0", features = ["derive"] } serde = { version = "1.0.163", features = ["derive"] } diff --git a/secret-memory/src/secret.rs b/secret-memory/src/secret.rs index 76d5b66..1bbeea5 100644 --- a/secret-memory/src/secret.rs +++ b/secret-memory/src/secret.rs @@ -1,6 +1,6 @@ use anyhow::Context; use lazy_static::lazy_static; -use libsodium_sys as libsodium; +use rosenpass_sodium::alloc::{Alloc as SodiumAlloc, Box as SodiumBox, Vec as SodiumVec}; use rosenpass_util::{ b64::b64_reader, file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd}, @@ -8,10 +8,7 @@ use rosenpass_util::{ }; use zeroize::{Zeroize, ZeroizeOnDrop}; -use std::{ - collections::HashMap, convert::TryInto, fmt, os::raw::c_void, path::Path, ptr::null_mut, - sync::Mutex, -}; +use std::{collections::HashMap, convert::TryInto, fmt, path::Path, sync::Mutex}; use crate::file::StoreSecret; @@ -32,116 +29,46 @@ lazy_static! { /// [libsodium documentation](https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations) #[derive(Debug)] // TODO check on Debug derive, is that clever struct SecretMemoryPool { - pool: HashMap>, + pool: HashMap>>, } impl SecretMemoryPool { /// Create a new [SecretMemoryPool] #[allow(clippy::new_without_default)] pub fn new() -> Self { - let pool = HashMap::new(); - - Self { pool } - } - - /// Return secrete back to the pool for future re-use - /// - /// This consumes the [Secret], but its memory is re-used. - #[allow(dead_code)] - pub fn release(&mut self, mut s: Secret) { - unsafe { - self.release_by_ref(&mut s); + Self { + pool: HashMap::new(), } - std::mem::forget(s); } - /// Return secret back to the pool for future re-use, by slice - /// - /// # Safety - /// - /// After calling this function on a [Secret], the secret must never be - /// used again for anything. - unsafe fn release_by_ref(&mut self, s: &mut Secret) { - s.zeroize(); - let Secret { ptr: secret } = s; - // don't call Secret::drop, that could cause a double free - self.pool.entry(N).or_default().push(*secret); + /// Return secret back to the pool for future re-use + pub fn release(&mut self, mut sec: SodiumBox<[u8; N]>) { + sec.zeroize(); + + // This conversion sequence is weird but at least it guarantees + // that the heap allocation is preserved according to the docs + let sec: SodiumVec = sec.into(); + let sec: SodiumBox<[u8]> = sec.into(); + + self.pool.entry(N).or_default().push(sec); } /// Take protected memory from the pool, allocating new one if no suitable /// chunk is found in the inventory. /// /// The secret is guaranteed to be full of nullbytes - /// - /// # Safety - /// - /// This function contains an unsafe call to [libsodium_sys::sodium_malloc]. - /// This call has no known safety invariants, thus nothing can go wrong™. - /// However, just like normal `malloc()` this can return a null ptr. Thus - /// the returned pointer is checked for null; causing the program to panic - /// if it is null. - pub fn take(&mut self) -> Secret { + pub fn take(&mut self) -> SodiumBox<[u8; N]> { let entry = self.pool.entry(N).or_default(); - let secret = entry.pop().unwrap_or_else(|| { - let ptr = unsafe { libsodium::sodium_malloc(N) }; - assert!( - !ptr.is_null(), - "libsodium::sodium_mallloc() returned a null ptr" - ); - ptr - }); - - let mut s = Secret { ptr: secret }; - s.zeroize(); - s - } -} - -impl Drop for SecretMemoryPool { - /// # Safety - /// - /// The drop implementation frees the contained elements using - /// [libsodium_sys::sodium_free]. This is safe as long as every `*mut c_void` - /// contained was initialized with a call to [libsodium_sys::sodium_malloc] - fn drop(&mut self) { - for ptr in self.pool.drain().flat_map(|(_, x)| x.into_iter()) { - unsafe { - libsodium::sodium_free(ptr); - } + match entry.pop() { + None => SodiumBox::new_in([0u8; N], SodiumAlloc::default()), + Some(sec) => sec.try_into().unwrap(), } } } -/// # Safety -/// -/// No safety implications are known, since the `*mut c_void` in -/// is essentially used like a `&mut u8` [SecretMemoryPool]. -unsafe impl Send for SecretMemoryPool {} - -/// Store for a secret -/// -/// Uses memory allocated with [libsodium_sys::sodium_malloc], -/// esentially can do the same things as `[u8; N].as_mut_ptr()`. +/// Storeage for a secret backed by [rosenpass_sodium::alloc::Alloc] pub struct Secret { - ptr: *mut c_void, -} - -impl Clone for Secret { - fn clone(&self) -> Self { - let mut new = Self::zero(); - new.secret_mut().clone_from_slice(self.secret()); - new - } -} - -impl Drop for Secret { - fn drop(&mut self) { - self.zeroize(); - // the invariant that the [Secret] is not used after the - // `release_by_ref` call is guaranteed, since this is a drop implementation - unsafe { SECRET_CACHE.lock().unwrap().release_by_ref(self) }; - self.ptr = null_mut(); - } + storage: Option>, } impl Secret { @@ -155,9 +82,9 @@ impl Secret { pub fn zero() -> Self { // Using [SecretMemoryPool] here because this operation is expensive, // yet it is used in hot loops - let s = SECRET_CACHE.lock().unwrap().take(); - assert_eq!(s.secret(), &[0u8; N]); - s + Self { + storage: Some(SECRET_CACHE.lock().unwrap().take()), + } } /// Returns a new [Secret] that is randomized @@ -172,23 +99,33 @@ impl Secret { /// Borrows the data pub fn secret(&self) -> &[u8; N] { - // - calling `from_raw_parts` is safe, because `ptr` is initalized with - // as `N` byte allocation from the creation of `Secret` onwards. `ptr` - // stays valid over the full lifetime of `Secret` - // - // - calling uwnrap is safe, because we can guarantee that the slice has - // exactly the required size `N` to create an array of `N` elements. - let ptr = self.ptr as *const u8; - let slice = unsafe { std::slice::from_raw_parts(ptr, N) }; - slice.try_into().unwrap() + self.storage.as_ref().unwrap() } /// Borrows the data mutably pub fn secret_mut(&mut self) -> &mut [u8; N] { - // the same safety argument as for `secret()` holds - let ptr = self.ptr as *mut u8; - let slice = unsafe { std::slice::from_raw_parts_mut(ptr, N) }; - slice.try_into().unwrap() + self.storage.as_mut().unwrap() + } +} + +impl ZeroizeOnDrop for Secret {} +impl Zeroize for Secret { + fn zeroize(&mut self) { + self.secret_mut().zeroize(); + } +} + +impl Drop for Secret { + fn drop(&mut self) { + self.storage + .take() + .map(|sec| SECRET_CACHE.lock().unwrap().release(sec)); + } +} + +impl Clone for Secret { + fn clone(&self) -> Self { + Self::from_slice(self.secret()) } } @@ -200,13 +137,6 @@ impl fmt::Debug for Secret { } } -impl ZeroizeOnDrop for Secret {} -impl Zeroize for Secret { - fn zeroize(&mut self) { - self.secret_mut().zeroize(); - } -} - impl LoadValue for Secret { type Error = anyhow::Error; @@ -255,72 +185,44 @@ impl StoreSecret for Secret { mod test { use super::*; - /// https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations - /// promises us that allocated memory is initialized with this magic byte - const SODIUM_MAGIC_BYTE: u8 = 0xdb; - - /// must be called before any interaction with libsodium - fn init() { - unsafe { libsodium_sys::sodium_init() }; - } - - /// checks that whe can malloc with libsodium - #[test] - fn sodium_malloc() { - init(); - const N: usize = 8; - let ptr = unsafe { libsodium_sys::sodium_malloc(N) }; - let mem = unsafe { std::slice::from_raw_parts(ptr as *mut u8, N) }; - assert_eq!(mem, &[SODIUM_MAGIC_BYTE; N]) - } - - /// checks that whe can free with libsodium - #[test] - fn sodium_free() { - init(); - const N: usize = 8; - let ptr = unsafe { libsodium_sys::sodium_malloc(N) }; - unsafe { libsodium_sys::sodium_free(ptr) } - } - /// check that we can alloc using the magic pool #[test] fn secret_memory_pool_take() { - init(); + rosenpass_sodium::init().unwrap(); const N: usize = 0x100; let mut pool = SecretMemoryPool::new(); - let secret: Secret = pool.take(); - assert_eq!(secret.secret(), &[0; N]); + let secret: SodiumBox<[u8; N]> = pool.take(); + assert_eq!(secret.as_ref(), &[0; N]); } /// check that a secrete lives, even if its [SecretMemoryPool] is deleted #[test] fn secret_memory_pool_drop() { - init(); + rosenpass_sodium::init().unwrap(); const N: usize = 0x100; let mut pool = SecretMemoryPool::new(); - let secret: Secret = pool.take(); + let secret: SodiumBox<[u8; N]> = pool.take(); std::mem::drop(pool); - assert_eq!(secret.secret(), &[0; N]); + assert_eq!(secret.as_ref(), &[0; N]); } /// check that a secrete can be reborn, freshly initialized with zero #[test] fn secret_memory_pool_release() { - init(); + rosenpass_sodium::init().unwrap(); const N: usize = 1; let mut pool = SecretMemoryPool::new(); - let mut secret: Secret = pool.take(); - let old_secret_ptr = secret.ptr; + let mut secret: SodiumBox<[u8; N]> = pool.take(); + let old_secret_ptr = secret.as_ref().as_ptr(); - secret.secret_mut()[0] = 0x13; + secret.as_mut()[0] = 0x13; pool.release(secret); // now check that we get the same ptr - let new_secret: Secret = pool.take(); - assert_eq!(old_secret_ptr, new_secret.ptr); + let new_secret: SodiumBox<[u8; N]> = pool.take(); + assert_eq!(old_secret_ptr, new_secret.as_ref().as_ptr()); // and that the secret was zeroized - assert_eq!(new_secret.secret(), &[0; N]); + assert_eq!(new_secret.as_ref(), &[0; N]); } } diff --git a/sodium/Cargo.toml b/sodium/Cargo.toml index b5ade3c..1476132 100644 --- a/sodium/Cargo.toml +++ b/sodium/Cargo.toml @@ -15,3 +15,4 @@ rosenpass-to = { workspace = true } anyhow = { workspace = true } libsodium-sys-stable = { workspace = true } log = { workspace = true } +allocator-api2 = { workspace = true } diff --git a/sodium/src/alloc/allocator.rs b/sodium/src/alloc/allocator.rs new file mode 100644 index 0000000..ad1314c --- /dev/null +++ b/sodium/src/alloc/allocator.rs @@ -0,0 +1,95 @@ +use allocator_api2::alloc::{AllocError, Allocator, Layout}; +use libsodium_sys as libsodium; +use std::fmt; +use std::os::raw::c_void; +use std::ptr::NonNull; + +#[derive(Clone, Default)] +struct AllocatorContents; + +/// Memory allocation using sodium_malloc/sodium_free +#[derive(Clone, Default)] +pub struct Alloc { + _dummy_private_data: AllocatorContents, +} + +impl Alloc { + pub fn new() -> Self { + Alloc { + _dummy_private_data: AllocatorContents, + } + } +} + +unsafe impl Allocator for Alloc { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + // Call sodium allocator + let ptr = unsafe { libsodium::sodium_malloc(layout.size()) }; + + // Ensure the right allocation is used + let off = ptr.align_offset(layout.align()); + if off != 0 { + log::error!("Allocation {layout:?} was requested but libsodium returned allocation \ + with offset {off} from the requested alignment. Libsodium always allocates values \ + at the end of a memory page for security reasons, custom alignments are not supported. \ + You could try allocating an oversized value."); + return Err(AllocError); + } + + // Convert to a pointer size + let ptr = core::ptr::slice_from_raw_parts_mut(ptr as *mut u8, layout.size()); + + // Conversion to a *const u8, then to a &[u8] + match NonNull::new(ptr) { + None => { + log::error!( + "Allocation {layout:?} was requested but libsodium returned a null pointer" + ); + Err(AllocError) + } + Some(ret) => Ok(ret), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { + unsafe { + libsodium::sodium_free(ptr.as_ptr() as *mut c_void); + } + } +} + +impl fmt::Debug for Alloc { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("") + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// checks that the can malloc with libsodium + #[test] + fn sodium_allocation() { + crate::init().unwrap(); + let alloc = Alloc::new(); + sodium_allocation_impl::<0>(&alloc); + sodium_allocation_impl::<7>(&alloc); + sodium_allocation_impl::<8>(&alloc); + sodium_allocation_impl::<64>(&alloc); + sodium_allocation_impl::<999>(&alloc); + } + + fn sodium_allocation_impl(alloc: &Alloc) { + crate::init().unwrap(); + let layout = Layout::new::<[u8; N]>(); + let mem = alloc.allocate(layout).unwrap(); + + // https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations + // promises us that allocated memory is initialized with the magic byte 0xDB + assert_eq!(unsafe { mem.as_ref() }, &[0xDBu8; N]); + + let mem = NonNull::new(mem.as_ptr() as *mut u8).unwrap(); + unsafe { alloc.deallocate(mem, layout) }; + } +} diff --git a/sodium/src/alloc/mod.rs b/sodium/src/alloc/mod.rs new file mode 100644 index 0000000..0dceb01 --- /dev/null +++ b/sodium/src/alloc/mod.rs @@ -0,0 +1,10 @@ +//! Access to sodium_malloc/sodium_free + +mod allocator; +pub use allocator::Alloc; + +/// A box backed by sodium_malloc +pub type Box = allocator_api2::boxed::Box; + +/// A vector backed by sodium_malloc +pub type Vec = allocator_api2::vec::Vec; diff --git a/sodium/src/lib.rs b/sodium/src/lib.rs index 606bbd0..752e1a7 100644 --- a/sodium/src/lib.rs +++ b/sodium/src/lib.rs @@ -16,5 +16,6 @@ pub fn init() -> anyhow::Result<()> { } pub mod aead; +pub mod alloc; pub mod hash; pub mod helpers; From 0f89ab7976c32568d11a4d2780bd9d2f50b5ea0c Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 1 Dec 2023 18:08:59 +0100 Subject: [PATCH 36/46] chore: Shorten fuzzing runtime to make sure the CI finishes quickly --- .github/workflows/qc.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 5cbe8a3..c2b7197 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -141,8 +141,8 @@ jobs: run: cargo install cargo-fuzz - name: Run fuzzing run: | - cargo fuzz run fuzz_aead_enc_into -- -max_total_time=60 - cargo fuzz run fuzz_blake2b -- -max_total_time=60 - cargo fuzz run fuzz_handle_msg -- -max_total_time=60 - cargo fuzz run fuzz_kyber_encaps -- -max_total_time=60 - cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=60 + cargo fuzz run fuzz_aead_enc_into -- -max_total_time=5 + cargo fuzz run fuzz_blake2b -- -max_total_time=5 + cargo fuzz run fuzz_handle_msg -- -max_total_time=5 + cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 + cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5 From 77cd8a9fd107f6fa11bb945a3111d0bd08bb7e85 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 17:52:41 +0100 Subject: [PATCH 37/46] feat: Move prftree into ciphers crate - Use a new nomenclature for these functions based on the idea of a hash domain (as in domain separation); this makes much more sence - Remove the ciphers::hash export; we did not even export a hash function in the purest sence of the word. This gets us around the difficulty of figuring out what we should call the underlying primitive --- Cargo.lock | 1 + ciphers/Cargo.toml | 1 + .../prftree.rs => ciphers/src/hash_domain.rs | 71 ++++++++++--------- ciphers/src/lib.rs | 8 +-- rosenpass/src/hash_domains.rs | 46 ++++++++++++ rosenpass/src/labeled_prf.rs | 48 ------------- rosenpass/src/lib.rs | 4 +- rosenpass/src/protocol.rs | 48 ++++++------- 8 files changed, 111 insertions(+), 116 deletions(-) rename rosenpass/src/prftree.rs => ciphers/src/hash_domain.rs (50%) create mode 100644 rosenpass/src/hash_domains.rs delete mode 100644 rosenpass/src/labeled_prf.rs diff --git a/Cargo.lock b/Cargo.lock index 7c2f43c..9b3cab1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1096,6 +1096,7 @@ version = "0.1.0" dependencies = [ "anyhow", "rosenpass-constant-time", + "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", "static_assertions", diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 2663cab..4d739d4 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -14,5 +14,6 @@ anyhow = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-to = { workspace = true } rosenpass-constant-time = { workspace = true } +rosenpass-secret-memory = { workspace = true } static_assertions = { workspace = true } zeroize = { workspace = true } diff --git a/rosenpass/src/prftree.rs b/ciphers/src/hash_domain.rs similarity index 50% rename from rosenpass/src/prftree.rs rename to ciphers/src/hash_domain.rs index 289646c..856e7bd 100644 --- a/rosenpass/src/prftree.rs +++ b/ciphers/src/hash_domain.rs @@ -1,31 +1,32 @@ -//! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf) -use rosenpass_secret_memory::Secret; - use anyhow::Result; -use rosenpass_ciphers::{hash, KEY_LEN}; +use rosenpass_secret_memory::Secret; use rosenpass_to::To; +use crate::subtle::incorrect_hmac_blake2b as hash; + +pub use hash::KEY_LEN; + // TODO Use a proper Dec interface #[derive(Clone, Debug)] -pub struct PrfTree([u8; KEY_LEN]); +pub struct HashDomain([u8; KEY_LEN]); #[derive(Clone, Debug)] -pub struct PrfTreeBranch([u8; KEY_LEN]); +pub struct HashDomainNamespace([u8; KEY_LEN]); #[derive(Clone, Debug)] -pub struct SecretPrfTree(Secret); +pub struct SecretHashDomain(Secret); #[derive(Clone, Debug)] -pub struct SecretPrfTreeBranch(Secret); +pub struct SecretHashDomainNamespace(Secret); -impl PrfTree { +impl HashDomain { pub fn zero() -> Self { Self([0u8; KEY_LEN]) } - pub fn dup(self) -> PrfTreeBranch { - PrfTreeBranch(self.0) + pub fn dup(self) -> HashDomainNamespace { + HashDomainNamespace(self.0) } - pub fn into_secret_prf_tree(self) -> SecretPrfTree { - SecretPrfTree(Secret::from_slice(&self.0)) + pub fn turn_secret(self) -> SecretHashDomain { + SecretHashDomain(Secret::from_slice(&self.0)) } // TODO: Protocol! Use domain separation to ensure that @@ -33,8 +34,8 @@ impl PrfTree { 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 mix_secret(self, v: Secret) -> Result { + SecretHashDomain::invoke_primitive(&self.0, v.secret()) } pub fn into_value(self) -> [u8; KEY_LEN] { @@ -42,19 +43,21 @@ impl PrfTree { } } -impl PrfTreeBranch { - pub fn mix(&self, v: &[u8]) -> Result { - Ok(PrfTree(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?)) +impl HashDomainNamespace { + pub fn mix(&self, v: &[u8]) -> Result { + Ok(HashDomain( + 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 mix_secret(&self, v: Secret) -> Result { + SecretHashDomain::invoke_primitive(&self.0, v.secret()) } } -impl SecretPrfTree { - pub fn prf_invoc(k: &[u8], d: &[u8]) -> Result { - let mut r = SecretPrfTree(Secret::zero()); +impl SecretHashDomain { + pub fn invoke_primitive(k: &[u8], d: &[u8]) -> Result { + let mut r = SecretHashDomain(Secret::zero()); hash::hash(k, d).to(r.0.secret_mut())?; Ok(r) } @@ -63,20 +66,20 @@ impl SecretPrfTree { Self(Secret::zero()) } - pub fn dup(self) -> SecretPrfTreeBranch { - SecretPrfTreeBranch(self.0) + pub fn dup(self) -> SecretHashDomainNamespace { + SecretHashDomainNamespace(self.0) } pub fn danger_from_secret(k: Secret) -> Self { Self(k) } - pub fn mix(self, v: &[u8]) -> Result { - Self::prf_invoc(self.0.secret(), v) + pub fn mix(self, v: &[u8]) -> Result { + Self::invoke_primitive(self.0.secret(), v) } - pub fn mix_secret(self, v: Secret) -> Result { - Self::prf_invoc(self.0.secret(), v.secret()) + pub fn mix_secret(self, v: Secret) -> Result { + Self::invoke_primitive(self.0.secret(), v.secret()) } pub fn into_secret(self) -> Secret { @@ -88,13 +91,13 @@ impl SecretPrfTree { } } -impl SecretPrfTreeBranch { - pub fn mix(&self, v: &[u8]) -> Result { - SecretPrfTree::prf_invoc(self.0.secret(), v) +impl SecretHashDomainNamespace { + pub fn mix(&self, v: &[u8]) -> Result { + SecretHashDomain::invoke_primitive(self.0.secret(), v) } - pub fn mix_secret(&self, v: Secret) -> Result { - SecretPrfTree::prf_invoc(self.0.secret(), v.secret()) + pub fn mix_secret(&self, v: Secret) -> Result { + SecretHashDomain::invoke_primitive(self.0.secret(), v.secret()) } // TODO: This entire API is not very nice; we need this for biscuits, but diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index b791db2..f0040ea 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -5,7 +5,7 @@ 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); +const_assert!(KEY_LEN == hash_domain::KEY_LEN); /// Authenticated encryption with associated data pub mod aead { @@ -21,8 +21,4 @@ pub mod xaead { }; } -pub mod hash { - pub use crate::subtle::incorrect_hmac_blake2b::{ - hash, KEY_LEN, KEY_MAX, KEY_MIN, OUT_MAX, OUT_MIN, - }; -} +pub mod hash_domain; diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs new file mode 100644 index 0000000..988dc21 --- /dev/null +++ b/rosenpass/src/hash_domains.rs @@ -0,0 +1,46 @@ +//! Pseudo Random Functions (PRFs) with a tree-like label scheme which +//! ensures their uniqueness + +use anyhow::Result; +use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN}; + +// TODO Use labels that can serve as identifiers +macro_rules! hash_domain_ns { + ($base:ident, $name:ident, $($lbl:expr),* ) => { + pub fn $name() -> Result { + let t = $base()?; + $( let t = t.mix($lbl.as_bytes())?; )* + Ok(t) + } + } +} + +macro_rules! hash_domain { + ($base:ident, $name:ident, $($lbl:expr),* ) => { + pub fn $name() -> Result<[u8; KEY_LEN]> { + let t = $base()?; + $( let t = t.mix($lbl.as_bytes())?; )* + Ok(t.into_value()) + } + } +} + +pub fn protocol() -> Result { + HashDomain::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes()) +} + +hash_domain_ns!(protocol, mac, "mac"); +hash_domain_ns!(protocol, cookie, "cookie"); +hash_domain_ns!(protocol, peerid, "peer id"); +hash_domain_ns!(protocol, biscuit_ad, "biscuit additional data"); +hash_domain_ns!(protocol, ckinit, "chaining key init"); +hash_domain_ns!(protocol, _ckextract, "chaining key extract"); + +hash_domain!(_ckextract, mix, "mix"); +hash_domain!(_ckextract, hs_enc, "handshake encryption"); +hash_domain!(_ckextract, ini_enc, "initiator handshake encryption"); +hash_domain!(_ckextract, res_enc, "responder handshake encryption"); + +hash_domain_ns!(_ckextract, _user, "user"); +hash_domain_ns!(_user, _rp, "rosenpass.eu"); +hash_domain!(_rp, osk, "wireguard psk"); diff --git a/rosenpass/src/labeled_prf.rs b/rosenpass/src/labeled_prf.rs deleted file mode 100644 index 87090f4..0000000 --- a/rosenpass/src/labeled_prf.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Pseudo Random Functions (PRFs) with a tree-like label scheme which -//! ensures their uniqueness - - -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()) -} - -// TODO Use labels that can serve as identifiers -macro_rules! prflabel { - ($base:ident, $name:ident, $($lbl:expr),* ) => { - pub fn $name() -> Result { - let t = $base()?; - $( let t = t.mix($lbl.as_bytes())?; )* - Ok(t) - } - } -} - -prflabel!(protocol, mac, "mac"); -prflabel!(protocol, cookie, "cookie"); -prflabel!(protocol, peerid, "peer id"); -prflabel!(protocol, biscuit_ad, "biscuit additional data"); -prflabel!(protocol, ckinit, "chaining key init"); -prflabel!(protocol, _ckextract, "chaining key extract"); - -macro_rules! prflabel_leaf { - ($base:ident, $name:ident, $($lbl:expr),* ) => { - pub fn $name() -> Result<[u8; KEY_LEN]> { - let t = $base()?; - $( let t = t.mix($lbl.as_bytes())?; )* - Ok(t.into_value()) - } - } -} - -prflabel_leaf!(_ckextract, mix, "mix"); -prflabel_leaf!(_ckextract, hs_enc, "handshake encryption"); -prflabel_leaf!(_ckextract, ini_enc, "initiator handshake encryption"); -prflabel_leaf!(_ckextract, res_enc, "responder handshake encryption"); - -prflabel!(_ckextract, _user, "user"); -prflabel!(_user, _rp, "rosenpass.eu"); -prflabel_leaf!(_rp, osk, "wireguard psk"); diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index ee49bb6..a6cf82c 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,11 +1,9 @@ -#[rustfmt::skip] -pub mod labeled_prf; pub mod app_server; pub mod cli; pub mod config; +pub mod hash_domains; pub mod msgs; pub mod pqkem; -pub mod prftree; pub mod protocol; #[derive(thiserror::Error, Debug)] diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 0207f0a..5f50b01 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -67,13 +67,9 @@ //! # } //! ``` -use crate::{ - labeled_prf as lprf, - msgs::*, - pqkem::*, - prftree::{SecretPrfTree, SecretPrfTreeBranch}, -}; +use crate::{hash_domains, msgs::*, pqkem::*}; use anyhow::{bail, ensure, Context, Result}; +use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; @@ -233,7 +229,7 @@ pub struct HandshakeState { /// Session ID of Responder pub sidr: SessionId, /// Chaining Key - pub ck: SecretPrfTreeBranch, + pub ck: SecretHashDomainNamespace, // TODO: We should probably add an abstr } #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] @@ -285,7 +281,7 @@ pub struct Session { pub sidt: SessionId, pub handshake_role: HandshakeRole, // Crypto - pub ck: SecretPrfTreeBranch, + pub ck: SecretHashDomainNamespace, /// Key for Transmission ("transmission key mine") pub txkm: SymKey, /// Key for Reception ("transmission key theirs") @@ -460,7 +456,7 @@ impl CryptoServer { #[rustfmt::skip] pub fn pidm(&self) -> Result { Ok(Public::new( - lprf::peerid()? + hash_domains::peerid()? .mix(self.spkm.secret())? .into_value())) } @@ -590,7 +586,7 @@ impl Peer { #[rustfmt::skip] pub fn pidt(&self) -> Result { Ok(Public::new( - lprf::peerid()? + hash_domains::peerid()? .mix(self.spkt.secret())? .into_value())) } @@ -603,7 +599,7 @@ impl Session { sidm: SessionId::zero(), sidt: SessionId::zero(), handshake_role: HandshakeRole::Initiator, - ck: SecretPrfTree::zero().dup(), + ck: SecretHashDomain::zero().dup(), txkm: SymKey::zero(), txkt: SymKey::zero(), txnm: 0, @@ -1174,7 +1170,7 @@ where { /// Calculate the message authentication code (`mac`) pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { - let mac = lprf::mac()? + let mac = hash_domains::mac()? .mix(peer.get(srv).spkt.secret())? .mix(self.until_mac())?; self.mac_mut() @@ -1189,7 +1185,9 @@ where { /// Check the message authentication code pub fn check_seal(&self, srv: &CryptoServer) -> Result { - let expected = lprf::mac()?.mix(srv.spkm.secret())?.mix(self.until_mac())?; + let expected = hash_domains::mac()? + .mix(srv.spkm.secret())? + .mix(self.until_mac())?; Ok(rosenpass_sodium::helpers::memcmp( self.mac(), &expected.into_value()[..16], @@ -1219,32 +1217,32 @@ impl HandshakeState { Self { sidi: SessionId::zero(), sidr: SessionId::zero(), - ck: SecretPrfTree::zero().dup(), + ck: SecretHashDomain::zero().dup(), } } pub fn erase(&mut self) { - self.ck = SecretPrfTree::zero().dup(); + self.ck = SecretHashDomain::zero().dup(); } pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> { - self.ck = lprf::ckinit()?.mix(spkr)?.into_secret_prf_tree().dup(); + self.ck = hash_domains::ckinit()?.turn_secret().mix(spkr)?.dup(); Ok(self) } pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> { - self.ck = self.ck.mix(&lprf::mix()?)?.mix(a)?.dup(); + self.ck = self.ck.mix(&hash_domains::mix()?)?.mix(a)?.dup(); Ok(self) } 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(); + let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret(); 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(); + let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret(); aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?; self.mix(ct) } @@ -1290,7 +1288,7 @@ impl HandshakeState { .copy_from_slice(self.ck.clone().danger_into_secret().secret()); // calculate ad contents - let ad = lprf::biscuit_ad()? + let ad = hash_domains::biscuit_ad()? .mix(srv.spkm.secret())? .mix(self.sidi.as_slice())? .mix(self.sidr.as_slice())? @@ -1325,7 +1323,7 @@ impl HandshakeState { let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize); // Calculate additional data fields - let ad = lprf::biscuit_ad()? + let ad = hash_domains::biscuit_ad()? .mix(srv.spkm.secret())? .mix(sidi.as_slice())? .mix(sidr.as_slice())? @@ -1343,7 +1341,7 @@ impl HandshakeState { // Reconstruct the biscuit fields let no = BiscuitId::from_slice(biscuit.biscuit_no()); - let ck = SecretPrfTree::danger_from_secret(Secret::from_slice(biscuit.ck())).dup(); + let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(biscuit.ck())).dup(); let pid = PeerId::from_slice(biscuit.pidi()); // Reconstruct the handshake state @@ -1370,8 +1368,8 @@ impl HandshakeState { pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole) -> Result { let HandshakeState { ck, sidi, sidr } = self; - let tki = ck.mix(&lprf::ini_enc()?)?.into_secret(); - let tkr = ck.mix(&lprf::res_enc()?)?.into_secret(); + let tki = ck.mix(&hash_domains::ini_enc()?)?.into_secret(); + let tkr = ck.mix(&hash_domains::res_enc()?)?.into_secret(); let created_at = srv.timebase.now(); let (ntx, nrx) = (0, 0); let (mysid, peersid, ktx, krx) = match role { @@ -1402,7 +1400,7 @@ impl CryptoServer { .get(self) .as_ref() .with_context(|| format!("No current session for peer {:?}", peer))?; - Ok(session.ck.mix(&lprf::osk()?)?.into_secret()) + Ok(session.ck.mix(&hash_domains::osk()?)?.into_secret()) } } From 5448cdc565f5f5f0fc964dbfc2696e3184323c82 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 30 Nov 2023 18:23:59 +0100 Subject: [PATCH 38/46] feat: Use the rand crate for random values instead of sodium --- Cargo.lock | 38 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + rosenpass/Cargo.toml | 1 + rosenpass/src/protocol.rs | 2 +- secret-memory/Cargo.toml | 1 + secret-memory/src/lib.rs | 1 + secret-memory/src/public.rs | 9 ++++++++- secret-memory/src/rand.rs | 5 +++++ secret-memory/src/secret.rs | 19 ++++++++++++++----- sodium/src/helpers.rs | 19 ------------------- 10 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 secret-memory/src/rand.rs diff --git a/Cargo.lock b/Cargo.lock index 9b3cab1..cc5f63d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -947,6 +947,12 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.2.15" @@ -984,6 +990,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rayon" version = "1.8.0" @@ -1076,6 +1112,7 @@ dependencies = [ "mio", "oqs-sys", "paste", + "rand", "rosenpass-ciphers", "rosenpass-constant-time", "rosenpass-secret-memory", @@ -1130,6 +1167,7 @@ dependencies = [ "anyhow", "lazy_static", "libsodium-sys-stable", + "rand", "rosenpass-sodium", "rosenpass-to", "rosenpass-util", diff --git a/Cargo.toml b/Cargo.toml index e336960..485cbbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ env_logger = "0.10.0" toml = "0.7.4" static_assertions = "1.1.0" allocator-api2 = "0.2.16" +rand = "0.8.5" log = { version = "0.4.17" } clap = { version = "4.3.0", features = ["derive"] } serde = { version = "1.0.163", features = ["derive"] } diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index f8a0591..65f43c5 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -33,6 +33,7 @@ serde = { workspace = true } toml = { workspace = true } clap = { workspace = true } mio = { workspace = true } +rand = { workspace = true } [build-dependencies] anyhow = { workspace = true } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 5f50b01..ce86b1c 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -1150,7 +1150,7 @@ impl IniHsPtr { .min(ih.tx_count as f64), ) * RETRANSMIT_DELAY_JITTER - * (rosenpass_sodium::helpers::rand_f64() + 1.0); // TODO: Replace with the rand crate + * (rand::random::() + 1.0); // TODO: Replace with the rand crate ih.tx_count += 1; Ok(()) } diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index c102599..45b5169 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -17,3 +17,4 @@ rosenpass-util = { workspace = true } libsodium-sys-stable = { workspace = true } lazy_static = { workspace = true } zeroize = { workspace = true } +rand = { workspace = true } diff --git a/secret-memory/src/lib.rs b/secret-memory/src/lib.rs index 3e57f54..75a6e72 100644 --- a/secret-memory/src/lib.rs +++ b/secret-memory/src/lib.rs @@ -1,5 +1,6 @@ pub mod debug; pub mod file; +pub mod rand; mod public; pub use crate::public::Public; diff --git a/secret-memory/src/public.rs b/secret-memory/src/public.rs index a27b4e2..39a43b8 100644 --- a/secret-memory/src/public.rs +++ b/secret-memory/src/public.rs @@ -1,4 +1,5 @@ use crate::debug::debug_crypto_array; +use rand::{Fill as Randomize, Rng}; use rosenpass_to::{ops::copy_slice, To}; use rosenpass_util::file::{fopen_r, LoadValue, ReadExactToEnd, StoreValue}; use rosenpass_util::functional::mutating; @@ -39,7 +40,13 @@ impl Public { /// Randomize all bytes in an existing [Public] pub fn randomize(&mut self) { - rosenpass_sodium::helpers::randombytes_buf(&mut self.value); + self.try_fill(&mut crate::rand::rng()).unwrap() + } +} + +impl Randomize for Public { + fn try_fill(&mut self, rng: &mut R) -> Result<(), rand::Error> { + self.value.try_fill(rng) } } diff --git a/secret-memory/src/rand.rs b/secret-memory/src/rand.rs new file mode 100644 index 0000000..a6daa6d --- /dev/null +++ b/secret-memory/src/rand.rs @@ -0,0 +1,5 @@ +pub type Rng = rand::rngs::ThreadRng; + +pub fn rng() -> Rng { + rand::thread_rng() +} diff --git a/secret-memory/src/secret.rs b/secret-memory/src/secret.rs index 1bbeea5..54cc5a4 100644 --- a/secret-memory/src/secret.rs +++ b/secret-memory/src/secret.rs @@ -1,16 +1,15 @@ +use crate::file::StoreSecret; use anyhow::Context; use lazy_static::lazy_static; +use rand::{Fill as Randomize, Rng}; use rosenpass_sodium::alloc::{Alloc as SodiumAlloc, Box as SodiumBox, Vec as SodiumVec}; use rosenpass_util::{ b64::b64_reader, file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd}, functional::mutating, }; -use zeroize::{Zeroize, ZeroizeOnDrop}; - use std::{collections::HashMap, convert::TryInto, fmt, path::Path, sync::Mutex}; - -use crate::file::StoreSecret; +use zeroize::{Zeroize, ZeroizeOnDrop}; // This might become a problem in library usage; it's effectively a memory // leak which probably isn't a problem right now because most memory will @@ -94,7 +93,7 @@ impl Secret { /// Sets all data an existing secret to random bytes pub fn randomize(&mut self) { - rosenpass_sodium::helpers::randombytes_buf(self.secret_mut()); + self.try_fill(&mut crate::rand::rng()).unwrap() } /// Borrows the data @@ -115,6 +114,16 @@ impl Zeroize for Secret { } } +impl Randomize for Secret { + fn try_fill(&mut self, rng: &mut R) -> Result<(), rand::Error> { + // Zeroize self first just to make sure the barriers from the zeroize create take + // effect to prevent the compiler from optimizing this away. + // We should at some point replace this with our own barriers. + self.zeroize(); + self.secret_mut().try_fill(rng) + } +} + impl Drop for Secret { fn drop(&mut self) { self.storage diff --git a/sodium/src/helpers.rs b/sodium/src/helpers.rs index 26e0bf5..a64f95b 100644 --- a/sodium/src/helpers.rs +++ b/sodium/src/helpers.rs @@ -26,22 +26,3 @@ pub fn increment(v: &mut [u8]) { libsodium::sodium_increment(v.as_mut_ptr(), v.len()); } } - -#[inline] -pub fn randombytes_buf(buf: &mut [u8]) { - unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) }; -} - -// Choose a fully random u64 -// TODO: Replace with ::rand::random -pub fn rand_u64() -> u64 { - let mut buf = [0u8; 8]; - randombytes_buf(&mut buf); - u64::from_le_bytes(buf) -} - -// Choose a random f64 in [0; 1] inclusive; quick and dirty -// TODO: Replace with ::rand::random -pub fn rand_f64() -> f64 { - (rand_u64() as f64) / (u64::MAX as f64) -} From 838f700a74d5adf985651dfd4bd2251ffaff65a9 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 1 Dec 2023 18:07:11 +0100 Subject: [PATCH 39/46] chore: Upgrade dependencies --- Cargo.lock | 181 ++++++++++++++++++++++++++++++++++++----------------- Cargo.toml | 22 +++---- 2 files changed, 135 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc5f63d..fd64bb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -101,7 +101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.8" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -498,12 +498,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -515,7 +515,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -530,9 +530,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -583,9 +583,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -614,7 +614,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -625,9 +625,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -650,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -661,7 +661,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -762,9 +762,9 @@ dependencies = [ [[package]] name = "libsodium-sys-stable" -version = "1.20.3" +version = "1.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc31f983531631496f4e621110cd81468ab78b65dee0046cfddea83caa2c327" +checksum = "d1d164bc6f9139c5f95efb4f0be931b2bd5a9edf7e4e3c945d26b95ab8fa669b" dependencies = [ "cc", "libc", @@ -779,9 +779,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" @@ -834,7 +834,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -909,9 +909,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pkg-config" @@ -965,9 +965,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -1080,16 +1080,16 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ring" -version = "0.17.5" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" dependencies = [ "cc", "getrandom", "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1103,7 +1103,7 @@ name = "rosenpass" version = "0.2.1" dependencies = [ "anyhow", - "clap 4.4.8", + "clap 4.4.10", "criterion", "env_logger", "libsodium-sys-stable", @@ -1155,6 +1155,7 @@ dependencies = [ "libfuzzer-sys", "rosenpass", "rosenpass-ciphers", + "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", "stacker", @@ -1215,22 +1216,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring", @@ -1281,18 +1282,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -1380,9 +1381,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] @@ -1507,9 +1508,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ "base64", "log", @@ -1522,9 +1523,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -1631,9 +1632,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "which" @@ -1684,7 +1685,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -1693,13 +1703,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -1708,42 +1733,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.19" diff --git a/Cargo.toml b/Cargo.toml index 485cbbf..d6800d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,22 +33,22 @@ test_bin = "0.4.0" libfuzzer-sys = "0.4" stacker = "0.1.15" doc-comment = "0.3.3" -base64 = "0.21.1" +base64 = "0.21.5" zeroize = "1.7.0" memoffset = "0.9.0" lazy_static = "1.4.0" -thiserror = "1.0.40" -paste = "1.0.12" -env_logger = "0.10.0" -toml = "0.7.4" +thiserror = "1.0.50" +paste = "1.0.14" +env_logger = "0.10.1" +toml = "0.7.8" static_assertions = "1.1.0" allocator-api2 = "0.2.16" rand = "0.8.5" -log = { version = "0.4.17" } -clap = { version = "4.3.0", features = ["derive"] } -serde = { version = "1.0.163", features = ["derive"] } +log = { version = "0.4.20" } +clap = { version = "4.4.10", features = ["derive"] } +serde = { version = "1.0.193", features = ["derive"] } arbitrary = { version = "1.3.2", features = ["derive"] } -anyhow = { version = "1.0.71", features = ["backtrace"] } -mio = { version = "0.8.6", features = ["net", "os-poll"] } -libsodium-sys-stable= { version = "1.19.28", features = ["use-pkg-config"] } +anyhow = { version = "1.0.75", features = ["backtrace"] } +mio = { version = "0.8.9", features = ["net", "os-poll"] } +libsodium-sys-stable= { version = "1.20.4", features = ["use-pkg-config"] } oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } From 955d57ea4988cf3ca92b81324762589562768bdc Mon Sep 17 00:00:00 2001 From: "Marei (peiTeX)" Date: Fri, 1 Dec 2023 19:12:30 +0100 Subject: [PATCH 40/46] fix output of authorlist to support unlimited authors --- papers/tex/template-rosenpass.tex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/papers/tex/template-rosenpass.tex b/papers/tex/template-rosenpass.tex index 55c76ca..d753594 100644 --- a/papers/tex/template-rosenpass.tex +++ b/papers/tex/template-rosenpass.tex @@ -177,7 +177,11 @@ version={4.0}, \titlehead{\centerline{\includegraphics[width=4cm]{RosenPass-Logo}}} \title{\inserttitle} } - \author{\csname insertauthor\endcsname} + \ifx\csname insertauthor\endcsname\relax + \author{} + \else + \author{\parbox{\linewidth}{\centering\insertauthor}} + \fi \subject{\csname insertsubject\endcsname} \date{\vspace{-1cm}} } From 101c9bf4b34e43f997d831d165dee8783ecdbf76 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 1 Dec 2023 19:07:24 +0100 Subject: [PATCH 41/46] feat: Add an internal library for guaranteed results This is helpful for functions that have to return a result to implement some interface but that do not actually need to return a result value. --- util/src/result.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/util/src/result.rs b/util/src/result.rs index 8c3b7c2..57f756d 100644 --- a/util/src/result.rs +++ b/util/src/result.rs @@ -1,3 +1,6 @@ +use std::convert::Infallible; +use std::result::Result; + /// Try block basically…returns a result and allows the use of the question mark operator inside #[macro_export] macro_rules! attempt { @@ -5,3 +8,86 @@ macro_rules! attempt { (|| -> ::anyhow::Result<_> { $block })() }; } + +/// Trait for container types that guarantee successful unwrapping. +/// +/// The `.guaranteed()` function can be used over unwrap to show that +/// the function will not panic. +/// +/// Implementations must not panic. +pub trait GuaranteedValue { + type Value; + + /// Extract the contained value while being panic-safe, like .unwrap() + /// + /// # Panic Safety + /// + /// Implementations of guaranteed() must not panic. + fn guaranteed(self) -> Self::Value; +} + +/// A result type that never contains an error. +/// +/// This is mostly useful in generic contexts. +/// +/// # Examples +/// +/// ``` +/// use std::num::Wrapping; +/// use std::result::Result; +/// use std::convert::Infallible +/// +/// trait FailableAddition { +/// type Error; +/// fn failable_addition(&self, other: &Self) -> Result; +/// } +/// +/// struct OverflowError; +/// +/// impl FailableAddition for Wrapping { +/// type Error = Infallible; +/// fn failable_addition(&self, other: &Self) -> Guaranteed { +/// self + other +/// } +/// } +/// +/// impl FailableAddition for u32 { +/// type Error = Infallible; +/// fn failable_addition(&self, other: &Self) -> Guaranteed { +/// match self.checked_add(*other) { +/// Some(v) => Ok(v), +/// None => Err(OverflowError), +/// } +/// } +/// } +/// +/// fn failable_multiply(a: &T, b: u32) +/// -> Result { +/// where +/// T: FailableAddition { +/// let mut accu = a.failable_addition(a)?; +/// for _ in ..(b-1) { +/// accu.failable_addition(a)?; +/// } +/// Ok(accu) +/// } +/// +/// // We can use .guaranteed() with Wrapping, since the operation uses +/// // the Infallible error type. +/// // We can also use unwrap which just happens to not raise an error. +/// assert_eq!(failable_multiply(&Wrapping::new(42u32), 3).guaranteed(), 126); +/// assert_eq!(failable_multiply(&Wrapping::new(42u32), 3).unwrap(), 126); +/// +/// // We can not use .guaranteed() with u32, since there can be an error. +/// // We can however use unwrap(), which may panic +/// assert_eq!(failable_multiply(&42u32, 3).guaranteed(), 126); // COMPILER ERROR +/// assert_eq!(failable_multiply(&42u32, 3).unwrap(), 126); +/// ``` +pub type Guaranteed = Result; + +impl GuaranteedValue for Guaranteed { + type Value = T; + fn guaranteed(self) -> Self::Value { + self.unwrap() + } +} From 972e82b35f1765e488696e9caf441771d2ef9956 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 1 Dec 2023 19:45:10 +0100 Subject: [PATCH 42/46] chore: Move kems out of rosenpass crate --- .github/workflows/qc.yaml | 2 +- Cargo.lock | 18 ++- Cargo.toml | 6 +- cipher-traits/Cargo.toml | 12 ++ cipher-traits/readme.md | 5 + cipher-traits/src/kem.rs | 47 ++++++++ cipher-traits/src/lib.rs | 2 + ciphers/Cargo.toml | 1 + ciphers/src/lib.rs | 5 + fuzz/Cargo.toml | 1 + fuzz/fuzz_targets/kyber_encaps.rs | 5 +- fuzz/fuzz_targets/mceliece_encaps.rs | 7 +- oqs/Cargo.toml | 16 +++ oqs/readme.md | 5 + oqs/src/kem_macro.rs | 80 +++++++++++++ oqs/src/lib.rs | 21 ++++ rosenpass/Cargo.toml | 2 +- rosenpass/src/cli.rs | 9 +- rosenpass/src/lib.rs | 36 ------ rosenpass/src/msgs.rs | 13 ++- rosenpass/src/pqkem.rs | 168 --------------------------- rosenpass/src/protocol.rs | 40 ++++--- 22 files changed, 259 insertions(+), 242 deletions(-) create mode 100644 cipher-traits/Cargo.toml create mode 100644 cipher-traits/readme.md create mode 100644 cipher-traits/src/kem.rs create mode 100644 cipher-traits/src/lib.rs create mode 100644 oqs/Cargo.toml create mode 100644 oqs/readme.md create mode 100644 oqs/src/kem_macro.rs create mode 100644 oqs/src/lib.rs delete mode 100644 rosenpass/src/pqkem.rs diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index c2b7197..7b69137 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -144,5 +144,5 @@ jobs: cargo fuzz run fuzz_aead_enc_into -- -max_total_time=5 cargo fuzz run fuzz_blake2b -- -max_total_time=5 cargo fuzz run fuzz_handle_msg -- -max_total_time=5 - cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 + ulimit -s 8192000 && RUST_MIN_STACK=33554432000 && cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5 diff --git a/Cargo.lock b/Cargo.lock index fd64bb2..4b46a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1110,9 +1110,9 @@ dependencies = [ "log", "memoffset", "mio", - "oqs-sys", "paste", "rand", + "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-constant-time", "rosenpass-secret-memory", @@ -1127,12 +1127,17 @@ dependencies = [ "toml", ] +[[package]] +name = "rosenpass-cipher-traits" +version = "0.1.0" + [[package]] name = "rosenpass-ciphers" version = "0.1.0" dependencies = [ "anyhow", "rosenpass-constant-time", + "rosenpass-oqs", "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", @@ -1154,6 +1159,7 @@ dependencies = [ "arbitrary", "libfuzzer-sys", "rosenpass", + "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-secret-memory", "rosenpass-sodium", @@ -1161,6 +1167,16 @@ dependencies = [ "stacker", ] +[[package]] +name = "rosenpass-oqs" +version = "0.1.0" +dependencies = [ + "oqs-sys", + "paste", + "rosenpass-cipher-traits", + "rosenpass-util", +] + [[package]] name = "rosenpass-secret-memory" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d6800d1..b903cf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,12 @@ resolver = "2" members = [ "rosenpass", + "cipher-traits", "ciphers", "util", "constant-time", "sodium", + "oqs", "to", "fuzz", "secret-memory", @@ -25,9 +27,11 @@ rosenpass = { path = "rosenpass" } rosenpass-util = { path = "util" } rosenpass-constant-time = { path = "constant-time" } rosenpass-sodium = { path = "sodium" } +rosenpass-cipher-traits = { path = "cipher-traits" } rosenpass-ciphers = { path = "ciphers" } rosenpass-to = { path = "to" } rosenpass-secret-memory = { path = "secret-memory" } +rosenpass-oqs = { path = "oqs" } criterion = "0.4.0" test_bin = "0.4.0" libfuzzer-sys = "0.4" @@ -48,7 +52,7 @@ log = { version = "0.4.20" } clap = { version = "4.4.10", features = ["derive"] } serde = { version = "1.0.193", features = ["derive"] } arbitrary = { version = "1.3.2", features = ["derive"] } -anyhow = { version = "1.0.75", features = ["backtrace"] } +anyhow = { version = "1.0.75", features = ["backtrace", "std"] } mio = { version = "0.8.9", features = ["net", "os-poll"] } libsodium-sys-stable= { version = "1.20.4", features = ["use-pkg-config"] } oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml new file mode 100644 index 0000000..b5c08cc --- /dev/null +++ b/cipher-traits/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rosenpass-cipher-traits" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal traits for cryptographic primitives" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] diff --git a/cipher-traits/readme.md b/cipher-traits/readme.md new file mode 100644 index 0000000..97ca5ac --- /dev/null +++ b/cipher-traits/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal libsodium bindings + +Rosenpass internal library providing traits for cryptographic primitives. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/cipher-traits/src/kem.rs b/cipher-traits/src/kem.rs new file mode 100644 index 0000000..f03f5f7 --- /dev/null +++ b/cipher-traits/src/kem.rs @@ -0,0 +1,47 @@ +//! Traits and implementations for Key Encapsulation Mechanisms (KEMs) +//! +//! KEMs are the interface provided by almost all post-quantum +//! secure key exchange mechanisms. +//! +//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting +//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during +//! +//! encapsulation. +//! The [KEM] Trait describes the basic API offered by a Key Encapsulation +//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM]. + +use std::result::Result; + +/// Key Encapsulation Mechanism +/// +/// The KEM interface defines three operations: Key generation, key encapsulation and key +/// decapsulation. +pub trait Kem { + type Error; + + /// Secrete Key length + const SK_LEN: usize; + /// Public Key length + const PK_LEN: usize; + /// Ciphertext length + const CT_LEN: usize; + /// Shared Secret length + const SHK_LEN: usize; + + /// Generate a keypair consisting of secret key (`sk`) and public key (`pk`) + /// + /// `keygen() -> sk, pk` + fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error>; + + /// From a public key (`pk`), generate a shared key (`shk`, for local use) + /// and a cipher text (`ct`, to be sent to the owner of the `pk`). + /// + /// `encaps(pk) -> shk, ct` + fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error>; + + /// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key + /// (`shk`) + /// + /// `decaps(sk, ct) -> shk` + fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error>; +} diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs new file mode 100644 index 0000000..99369d9 --- /dev/null +++ b/cipher-traits/src/lib.rs @@ -0,0 +1,2 @@ +mod kem; +pub use kem::Kem; diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 4d739d4..a226239 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -15,5 +15,6 @@ rosenpass-sodium = { workspace = true } rosenpass-to = { workspace = true } rosenpass-constant-time = { workspace = true } rosenpass-secret-memory = { workspace = true } +rosenpass-oqs = { workspace = true } static_assertions = { workspace = true } zeroize = { workspace = true } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index f0040ea..994431b 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -22,3 +22,8 @@ pub mod xaead { } pub mod hash_domain; + +pub mod kem { + pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem; + pub use rosenpass_oqs::Kyber512 as EphemeralKem; +} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 345f553..47c1344 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -14,6 +14,7 @@ stacker = { workspace = true } rosenpass-secret-memory = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } +rosenpass-cipher-traits = { workspace = true } rosenpass-to = { workspace = true } rosenpass = { workspace = true } diff --git a/fuzz/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs index dc61f06..9bdfe64 100644 --- a/fuzz/fuzz_targets/kyber_encaps.rs +++ b/fuzz/fuzz_targets/kyber_encaps.rs @@ -4,7 +4,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::pqkem::{EphemeralKEM, KEM}; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::EphemeralKem; #[derive(arbitrary::Arbitrary, Debug)] pub struct Input { @@ -15,5 +16,5 @@ fuzz_target!(|input: Input| { let mut ciphertext = [0u8; 768]; let mut shared_secret = [0u8; 32]; - EphemeralKEM::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap(); + EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap(); }); diff --git a/fuzz/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs index 00aa05e..25e36ae 100644 --- a/fuzz/fuzz_targets/mceliece_encaps.rs +++ b/fuzz/fuzz_targets/mceliece_encaps.rs @@ -3,12 +3,13 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass::pqkem::{StaticKEM, KEM}; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::StaticKem; -fuzz_target!(|input: &[u8]| { +fuzz_target!(|input: [u8; StaticKem::PK_LEN]| { let mut ciphertext = [0u8; 188]; let mut shared_secret = [0u8; 32]; // We expect errors while fuzzing therefore we do not check the result. - let _ = StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input); + let _ = StaticKem::encaps(&mut shared_secret, &mut ciphertext, &input); }); diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml new file mode 100644 index 0000000..ed93c74 --- /dev/null +++ b/oqs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rosenpass-oqs" +authors = ["Karolin Varner ", "wucke13 "] +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal bindings to liboqs" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +[dependencies] +rosenpass-cipher-traits = { workspace = true } +rosenpass-util = { workspace = true } +oqs-sys = { workspace = true } +paste = { workspace = true } diff --git a/oqs/readme.md b/oqs/readme.md new file mode 100644 index 0000000..f6602ad --- /dev/null +++ b/oqs/readme.md @@ -0,0 +1,5 @@ +# Rosenpass internal liboqs bindings + +Rosenpass internal library providing bindings to liboqs. + +This is an internal library; not guarantee is made about its API at this point in time. diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs new file mode 100644 index 0000000..d26a53b --- /dev/null +++ b/oqs/src/kem_macro.rs @@ -0,0 +1,80 @@ +macro_rules! oqs_kem { + ($name:ident) => { ::paste::paste!{ + mod [< $name:snake >] { + use rosenpass_cipher_traits::Kem; + use rosenpass_util::result::Guaranteed; + + pub enum [< $name:camel >] {} + + /// # Panic & Safety + /// + /// This Trait impl calls unsafe [oqs_sys] functions, that write to byte + /// slices only identified using raw pointers. It must be ensured that the raw + /// pointers point into byte slices of sufficient length, to avoid UB through + /// overwriting of arbitrary data. This is ensured through assertions in the + /// implementation. + /// + /// __Note__: This requirement is stricter than necessary, it would suffice + /// to only check that the buffers are big enough, allowing them to be even + /// bigger. However, from a correctness point of view it does not make sense to + /// allow bigger buffers. + impl Kem for [< $name:camel >] { + type Error = ::std::convert::Infallible; + + const SK_LEN: usize = ::oqs_sys::kem::[] as usize; + const PK_LEN: usize = ::oqs_sys::kem::[] as usize; + const CT_LEN: usize = ::oqs_sys::kem::[] as usize; + const SHK_LEN: usize = ::oqs_sys::kem::[] as usize; + + fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Guaranteed<()> { + assert_eq!(sk.len(), Self::SK_LEN); + assert_eq!(pk.len(), Self::PK_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >], + pk.as_mut_ptr(), + sk.as_mut_ptr() + ); + } + + Ok(()) + } + + fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Guaranteed<()> { + assert_eq!(shk.len(), Self::SHK_LEN); + assert_eq!(ct.len(), Self::CT_LEN); + assert_eq!(pk.len(), Self::PK_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >], + ct.as_mut_ptr(), + shk.as_mut_ptr(), + pk.as_ptr() + ); + } + + Ok(()) + } + + fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Guaranteed<()> { + assert_eq!(shk.len(), Self::SHK_LEN); + assert_eq!(sk.len(), Self::SK_LEN); + assert_eq!(ct.len(), Self::CT_LEN); + unsafe { + oqs_call!( + ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >], + shk.as_mut_ptr(), + ct.as_ptr(), + sk.as_ptr() + ); + } + + Ok(()) + } + } + + } + + pub use [< $name:snake >] :: [< $name:camel >]; + }} +} diff --git a/oqs/src/lib.rs b/oqs/src/lib.rs new file mode 100644 index 0000000..f8f4506 --- /dev/null +++ b/oqs/src/lib.rs @@ -0,0 +1,21 @@ +macro_rules! oqs_call { + ($name:path, $($args:expr),*) => {{ + use oqs_sys::common::OQS_STATUS::*; + + match $name($($args),*) { + OQS_SUCCESS => {}, // nop + OQS_EXTERNAL_LIB_ERROR_OPENSSL => { + panic!("OpenSSL error in liboqs' {}.", stringify!($name)); + }, + OQS_ERROR => { + panic!("Unknown error in liboqs' {}.", stringify!($name)); + } + } + }}; + ($name:ident) => { oqs_call!($name, ) }; +} + +#[macro_use] +mod kem_macro; +oqs_kem!(kyber_512); +oqs_kem!(classic_mceliece_460896); diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 65f43c5..f8feb6f 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -18,13 +18,13 @@ rosenpass-util = { workspace = true } rosenpass-constant-time = { workspace = true } rosenpass-sodium = { workspace = true } rosenpass-ciphers = { workspace = true } +rosenpass-cipher-traits = { workspace = true } rosenpass-to = { workspace = true } rosenpass-secret-memory = { workspace = true } anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } libsodium-sys-stable = { workspace = true } -oqs-sys = { workspace = true } thiserror = { workspace = true } paste = { workspace = true } log = { workspace = true } diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index ddb9ad3..dc6b596 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -1,15 +1,14 @@ use anyhow::{bail, ensure}; use clap::Parser; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::file::StoreSecret; use rosenpass_util::file::{LoadValue, LoadValueB64}; use std::path::PathBuf; use crate::app_server; use crate::app_server::AppServer; -use crate::{ - pqkem::{StaticKEM, KEM}, - protocol::{SPk, SSk, SymKey}, -}; +use crate::protocol::{SPk, SSk, SymKey}; use super::config; @@ -163,7 +162,7 @@ impl Cli { // generate the keys and store them in files let mut ssk = crate::protocol::SSk::random(); let mut spk = crate::protocol::SPk::random(); - StaticKEM::keygen(ssk.secret_mut(), spk.secret_mut())?; + StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?; ssk.store_secret(skf)?; spk.store_secret(pkf)?; diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index a6cf82c..1f37c25 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -3,15 +3,10 @@ pub mod cli; pub mod config; pub mod hash_domains; pub mod msgs; -pub mod pqkem; pub mod protocol; #[derive(thiserror::Error, Debug)] pub enum RosenpassError { - #[error("error in OQS")] - Oqs, - #[error("error from external library while calling OQS")] - OqsExternalLib, #[error("buffer size mismatch, required {required_size} but found {actual_size}")] BufferSizeMismatch { required_size: usize, @@ -20,34 +15,3 @@ pub enum RosenpassError { #[error("invalid message type")] InvalidMessageType(u8), } - -impl RosenpassError { - /// Helper function to check a buffer size - fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<(), Self> { - if required_size != actual_size { - Err(Self::BufferSizeMismatch { - required_size, - actual_size, - }) - } else { - Ok(()) - } - } -} - -/// Extension trait to attach function calls to foreign types. -trait RosenpassMaybeError { - /// Checks whether something is an error or not - fn to_rg_error(&self) -> Result<(), RosenpassError>; -} - -impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS { - fn to_rg_error(&self) -> Result<(), RosenpassError> { - use oqs_sys::common::OQS_STATUS; - match self { - OQS_STATUS::OQS_SUCCESS => Ok(()), - OQS_STATUS::OQS_ERROR => Err(RosenpassError::Oqs), - OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(RosenpassError::OqsExternalLib), - } - } -} diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index eac0369..1aefba5 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -44,7 +44,8 @@ //! ``` use super::RosenpassError; -use crate::pqkem::*; +use rosenpass_cipher_traits::Kem; +use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; // Macro magic //////////////////////////////////////////////////////////////// @@ -108,7 +109,7 @@ macro_rules! data_lense( (maybe_docstring_link $x:expr) => (stringify!([$x])); // struct name < optional generics > := optional doc string field name : field length, ... - ($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ +($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ #[allow(rustdoc::broken_intra_doc_links)] /// A data lense to manipulate byte slices. @@ -274,9 +275,9 @@ data_lense! { InitHello := /// Randomly generated connection id sidi: 4, /// Kyber 512 Ephemeral Public Key - epki: EphemeralKEM::PK_LEN, + epki: EphemeralKem::PK_LEN, /// Classic McEliece Ciphertext - sctr: StaticKEM::CT_LEN, + sctr: StaticKem::CT_LEN, /// Encryped: 16 byte hash of McEliece initiator static key pidic: aead::TAG_LEN + 32, /// Encrypted TAI64N Time Stamp (against replay attacks) @@ -289,9 +290,9 @@ data_lense! { RespHello := /// Copied from InitHello sidi: 4, /// Kyber 512 Ephemeral Ciphertext - ecti: EphemeralKEM::CT_LEN, + ecti: EphemeralKem::CT_LEN, /// Classic McEliece Ciphertext - scti: StaticKEM::CT_LEN, + scti: StaticKem::CT_LEN, /// Empty encrypted message (just an auth tag) auth: aead::TAG_LEN, /// Responders handshake state in encrypted form diff --git a/rosenpass/src/pqkem.rs b/rosenpass/src/pqkem.rs deleted file mode 100644 index 36084da..0000000 --- a/rosenpass/src/pqkem.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Traits and implementations for Key Encapsulation Mechanisms (KEMs) -//! -//! KEMs are the interface provided by almost all post-quantum -//! secure key exchange mechanisms. -//! -//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting -//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during -//! -//! encapsulation. -//! The [KEM] Trait describes the basic API offered by a Key Encapsulation -//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM]. - -use crate::{RosenpassError, RosenpassMaybeError}; - -/// Key Encapsulation Mechanism -/// -/// The KEM interface defines three operations: Key generation, key encapsulation and key -/// decapsulation. -pub trait KEM { - /// Secrete Key length - const SK_LEN: usize; - /// Public Key length - const PK_LEN: usize; - /// Ciphertext length - const CT_LEN: usize; - /// Shared Secret length - const SHK_LEN: usize; - - /// Generate a keypair consisting of secret key (`sk`) and public key (`pk`) - /// - /// `keygen() -> sk, pk` - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError>; - - /// From a public key (`pk`), generate a shared key (`shk`, for local use) - /// and a cipher text (`ct`, to be sent to the owner of the `pk`). - /// - /// `encaps(pk) -> shk, ct` - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError>; - - /// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key - /// (`shk`) - /// - /// `decaps(sk, ct) -> shk` - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError>; -} - -/// A KEM that is secure against Chosen Ciphertext Attacks (CCA). -/// In the context of rosenpass this is used for static keys. -/// Uses [Classic McEliece](https://classic.mceliece.org/) 460896 from liboqs. -/// -/// Classic McEliece is chosen because of its high security margin and its small -/// ciphertexts. The public keys are humongous, but (being static keys) the are never transmitted over -/// the wire so this is not a big problem. -pub struct StaticKEM; - -/// # Safety -/// -/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte -/// slices only identified using raw pointers. It must be ensured that the raw -/// pointers point into byte slices of sufficient length, to avoid UB through -/// overwriting of arbitrary data. This is checked in the following code before -/// the unsafe calls, and an early return with an Err occurs if the byte slice -/// size does not match the required size. -/// -/// __Note__: This requirement is stricter than necessary, it would suffice -/// to only check that the buffers are big enough, allowing them to be even -/// bigger. However, from a correctness point of view it does not make sense to -/// allow bigger buffers. -impl KEM for StaticKEM { - const SK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_secret_key as usize; - const PK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_public_key as usize; - const CT_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_ciphertext as usize; - const SHK_LEN: usize = - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_shared_secret as usize; - - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) - .to_rg_error() - } - } - - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_encaps( - ct.as_mut_ptr(), - shk.as_mut_ptr(), - pk.as_ptr(), - ) - .to_rg_error() - } - } - - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_classic_mceliece_460896_decaps( - shk.as_mut_ptr(), - ct.as_ptr(), - sk.as_ptr(), - ) - .to_rg_error() - } - } -} - -/// Implements a KEM that is secure against Chosen Plaintext Attacks (CPA). -/// In the context of rosenpass this is used for ephemeral keys. -/// Currently the implementation uses -/// [Kyber 512](https://openquantumsafe.org/liboqs/algorithms/kem/kyber) from liboqs. -/// -/// This is being used for ephemeral keys; since these are use-once the first post quantum -/// wireguard paper claimed that CPA security would be sufficient. Nonetheless we choose kyber -/// which provides CCA security since there are no publicly vetted KEMs out there which provide -/// only CPA security. -pub struct EphemeralKEM; - -/// # Safety -/// -/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte -/// slices only identified using raw pointers. It must be ensured that the raw -/// pointers point into byte slices of sufficient length, to avoid UB through -/// overwriting of arbitrary data. This is checked in the following code before -/// the unsafe calls, and an early return with an Err occurs if the byte slice -/// size does not match the required size. -/// -/// __Note__: This requirement is stricter than necessary, it would suffice -/// to only check that the buffers are big enough, allowing them to be even -/// bigger. However, from a correctness point of view it does not make sense to -/// allow bigger buffers. -impl KEM for EphemeralKEM { - const SK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_secret_key as usize; - const PK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_public_key as usize; - const CT_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_ciphertext as usize; - const SHK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_shared_secret as usize; - fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()).to_rg_error() - } - } - fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_encaps(ct.as_mut_ptr(), shk.as_mut_ptr(), pk.as_ptr()) - .to_rg_error() - } - } - fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> { - RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?; - RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?; - RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?; - unsafe { - oqs_sys::kem::OQS_KEM_kyber_512_decaps(shk.as_mut_ptr(), ct.as_ptr(), sk.as_ptr()) - .to_rg_error() - } - } -} diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index ce86b1c..c719609 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -19,8 +19,9 @@ //! [CryptoServer]. //! //! ``` +//! use rosenpass_cipher_traits::Kem; +//! use rosenpass_ciphers::kem::StaticKem; //! use rosenpass::{ -//! pqkem::{StaticKEM, KEM}, //! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey}, //! }; //! # fn main() -> anyhow::Result<()> { @@ -30,11 +31,11 @@ //! //! // initialize secret and public key for peer a ... //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); -//! StaticKEM::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?; +//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?; //! //! // ... and for peer b //! let (mut peer_b_sk, mut peer_b_pk) = (SSk::zero(), SPk::zero()); -//! StaticKEM::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?; +//! StaticKem::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?; //! //! // initialize server and a pre-shared key //! let psk = SymKey::random(); @@ -67,9 +68,11 @@ //! # } //! ``` -use crate::{hash_domains, msgs::*, pqkem::*}; +use crate::{hash_domains, msgs::*}; use anyhow::{bail, ensure, Context, Result}; +use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; +use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; @@ -77,6 +80,7 @@ use std::collections::hash_map::{ Entry::{Occupied, Vacant}, HashMap, }; +use std::convert::Infallible; // CONSTANTS & SETTINGS ////////////////////////// @@ -135,10 +139,10 @@ pub fn has_happened(ev: Timing, now: Timing) -> bool { // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// -pub type SPk = Secret<{ StaticKEM::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap -pub type SSk = Secret<{ StaticKEM::SK_LEN }>; -pub type EPk = Public<{ EphemeralKEM::PK_LEN }>; -pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>; +pub type SPk = Secret<{ StaticKem::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap +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; @@ -1248,7 +1252,7 @@ impl HandshakeState { } // I loathe "error: constant expression depends on a generic parameter" - pub fn encaps_and_mix( + pub fn encaps_and_mix, const SHK_LEN: usize>( &mut self, ct: &mut [u8], pk: &[u8], @@ -1258,7 +1262,7 @@ impl HandshakeState { self.mix(pk)?.mix(shk.secret())?.mix(ct) } - pub fn decaps_and_mix( + pub fn decaps_and_mix, const SHK_LEN: usize>( &mut self, sk: &[u8], pk: &[u8], @@ -1422,7 +1426,7 @@ impl CryptoServer { ih.sidi_mut().copy_from_slice(&hs.core.sidi.value); // IHI3 - EphemeralKEM::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; + EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; ih.epki_mut().copy_from_slice(&hs.epki.value); // IHI4 @@ -1430,7 +1434,7 @@ impl CryptoServer { // IHI5 hs.core - .encaps_and_mix::( + .encaps_and_mix::( ih.sctr_mut(), peer.get(self).spkt.secret(), )?; @@ -1469,7 +1473,7 @@ impl CryptoServer { core.mix(ih.sidi())?.mix(ih.epki())?; // IHR5 - core.decaps_and_mix::( + core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), ih.sctr(), @@ -1499,10 +1503,10 @@ impl CryptoServer { core.mix(rh.sidr())?.mix(rh.sidi())?; // RHR4 - core.encaps_and_mix::(rh.ecti_mut(), ih.epki())?; + core.encaps_and_mix::(rh.ecti_mut(), ih.epki())?; // RHR5 - core.encaps_and_mix::( + core.encaps_and_mix::( rh.scti_mut(), peer.get(self).spkt.secret(), )?; @@ -1567,14 +1571,14 @@ impl CryptoServer { core.mix(rh.sidr())?.mix(rh.sidi())?; // RHI4 - core.decaps_and_mix::( + core.decaps_and_mix::( hs!().eski.secret(), &*hs!().epki, rh.ecti(), )?; // RHI5 - core.decaps_and_mix::( + core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), rh.scti(), @@ -1810,7 +1814,7 @@ mod test { fn keygen() -> Result<(SSk, SPk)> { // TODO: Copied from the benchmark; deduplicate let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); - StaticKEM::keygen(sk.secret_mut(), pk.secret_mut())?; + StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?; Ok((sk, pk)) } From d45e24e9b6e1bad5c02a97a9101572ece5baef35 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sat, 2 Dec 2023 10:56:18 +0100 Subject: [PATCH 43/46] feat: Move lenses into library --- Cargo.lock | 9 ++ Cargo.toml | 2 + lenses/Cargo.toml | 16 +++ lenses/readme.md | 3 + lenses/src/lib.rs | 206 +++++++++++++++++++++++++++++++++ rosenpass/Cargo.toml | 1 + rosenpass/src/lib.rs | 17 ++- rosenpass/src/msgs.rs | 231 +++----------------------------------- rosenpass/src/protocol.rs | 1 + 9 files changed, 263 insertions(+), 223 deletions(-) create mode 100644 lenses/Cargo.toml create mode 100644 lenses/readme.md create mode 100644 lenses/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4b46a70..b4f59a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1115,6 +1115,7 @@ dependencies = [ "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-constant-time", + "rosenpass-lenses", "rosenpass-secret-memory", "rosenpass-sodium", "rosenpass-to", @@ -1167,6 +1168,14 @@ dependencies = [ "stacker", ] +[[package]] +name = "rosenpass-lenses" +version = "0.1.0" +dependencies = [ + "paste", + "thiserror", +] + [[package]] name = "rosenpass-oqs" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b903cf9..3ab316f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "to", "fuzz", "secret-memory", + "lenses", ] default-members = [ @@ -32,6 +33,7 @@ rosenpass-ciphers = { path = "ciphers" } rosenpass-to = { path = "to" } rosenpass-secret-memory = { path = "secret-memory" } rosenpass-oqs = { path = "oqs" } +rosenpass-lenses = { path = "lenses" } criterion = "0.4.0" test_bin = "0.4.0" libfuzzer-sys = "0.4" diff --git a/lenses/Cargo.toml b/lenses/Cargo.toml new file mode 100644 index 0000000..9304dee --- /dev/null +++ b/lenses/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rosenpass-lenses" +version = "0.1.0" +authors = ["Karolin Varner ", "wucke13 "] +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Rosenpass internal library for parsing binary data securely" +homepage = "https://rosenpass.eu/" +repository = "https://github.com/rosenpass/rosenpass" +readme = "readme.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +paste = { workspace = true } +thiserror = { workspace = true } diff --git a/lenses/readme.md b/lenses/readme.md new file mode 100644 index 0000000..c709695 --- /dev/null +++ b/lenses/readme.md @@ -0,0 +1,3 @@ +# Rosenpass internal binary parsing library + +This is an internal library; no guarantee is made about its API at this point in time. diff --git a/lenses/src/lib.rs b/lenses/src/lib.rs new file mode 100644 index 0000000..f699c8a --- /dev/null +++ b/lenses/src/lib.rs @@ -0,0 +1,206 @@ +use std::result::Result; + +/// Common trait shared by all Lenses +pub trait LenseView { + const LEN: usize; +} + +/// Error during lense creation +#[derive(thiserror::Error, Debug, Eq, PartialEq, Clone)] +pub enum LenseError { + #[error("buffer size mismatch")] + BufferSizeMismatch, +} + +pub type LenseResult = Result; + +impl LenseError { + pub fn ensure_exact_buffer_size(len: usize, required: usize) -> LenseResult<()> { + (len == required) + .then_some(()) + .ok_or(LenseError::BufferSizeMismatch) + } + + pub fn ensure_sufficient_buffer_size(len: usize, required: usize) -> LenseResult<()> { + (len >= required) + .then_some(()) + .ok_or(LenseError::BufferSizeMismatch) + } +} + +/// A macro to create data lenses. +#[macro_export] +macro_rules! lense( + // prefix @ offset ; optional meta ; field name : field length, ... + (token_muncher_ref @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => { + ::paste::paste!{ + + #[allow(rustdoc::broken_intra_doc_links)] + $( #[ $attr ] )* + /// + #[doc = lense!(maybe_docstring_link $len)] + /// bytes long + pub fn $field(&self) -> &__ContainerType::Output { + &self.0[$offset .. $offset + $len] + } + + /// The bytes until the + #[doc = lense!(maybe_docstring_link Self::$field)] + /// field + pub fn [< until_ $field >](&self) -> &__ContainerType::Output { + &self.0[0 .. $offset] + } + + // if the tail exits, consume it as well + $( + lense!{token_muncher_ref @ $offset + $len ; $( $tail )+ } + )? + } + }; + + // prefix @ offset ; optional meta ; field name : field length, ... + (token_muncher_mut @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => { + ::paste::paste!{ + + #[allow(rustdoc::broken_intra_doc_links)] + $( #[ $attr ] )* + /// + #[doc = lense!(maybe_docstring_link $len)] + /// bytes long + pub fn [< $field _mut >](&mut self) -> &mut __ContainerType::Output { + &mut self.0[$offset .. $offset + $len] + } + + // if the tail exits, consume it as well + $( + lense!{token_muncher_mut @ $offset + $len ; $( $tail )+ } + )? + } + }; + + // switch that yields literals unchanged, but creates docstring links to + // constants + // TODO the doc string link doesn't work if $x is taken from a generic, + (maybe_docstring_link $x:literal) => (stringify!($x)); + (maybe_docstring_link $x:expr) => (stringify!([$x])); + + // struct name < optional generics > := optional doc string field name : field length, ... +($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ + + #[allow(rustdoc::broken_intra_doc_links)] + /// A data lense to manipulate byte slices. + /// + //// # Fields + /// + $( + /// - ` + #[doc = stringify!($field)] + /// `: + #[doc = lense!(maybe_docstring_link $len)] + /// bytes + )+ + pub struct $type<__ContainerType $(, $( $generic ),+ )? > ( + __ContainerType, + // The phantom data is required, since all generics declared on a + // type need to be used on the type. + // https://doc.rust-lang.org/stable/error_codes/E0392.html + $( $( ::core::marker::PhantomData<$generic> ),+ )? + ); + + impl<__ContainerType $(, $( $generic: LenseView ),+ )? > $type<__ContainerType $(, $( $generic ),+ )? >{ + $( + /// Size in bytes of the field ` + #[doc = !($field)] + /// ` + pub const fn [< $field _len >]() -> usize{ + $len + } + )+ + + /// Verify that `len` exactly holds [Self] + pub fn check_size(len: usize) -> ::rosenpass_lenses::LenseResult<()> { + ::rosenpass_lenses::LenseError::ensure_exact_buffer_size(len, $( $len + )+ 0) + } + } + + // read-only accessor functions + impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a __ContainerType $(, $( $generic ),+ )?> + where + __ContainerType: std::ops::Index> + ?Sized, + { + lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ } + + /// View into all bytes belonging to this Lense + pub fn all_bytes(&self) -> &__ContainerType::Output { + &self.0[0..Self::LEN] + } + } + + // mutable accessor functions + impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a mut __ContainerType $(, $( $generic ),+ )?> + where + __ContainerType: std::ops::IndexMut> + ?Sized, + { + lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ } + lense!{token_muncher_mut @ 0 ; $( $( $attr )* ; $field : $len ),+ } + + /// View into all bytes belonging to this Lense + pub fn all_bytes(&self) -> &__ContainerType::Output { + &self.0[0..Self::LEN] + } + + /// View into all bytes belonging to this Lense + pub fn all_bytes_mut(&mut self) -> &mut __ContainerType::Output { + &mut self.0[0..Self::LEN] + } + } + + // lense trait, allowing us to know the implementing lenses size + impl<__ContainerType $(, $( $generic: LenseView ),+ )? > LenseView for $type<__ContainerType $(, $( $generic ),+ )? >{ + /// Number of bytes required to store this type in binary format + const LEN: usize = $( $len + )+ 0; + } + + /// Extension trait to allow checked creation of a lense over + /// some byte slice that contains a + #[doc = lense!(maybe_docstring_link $type)] + pub trait [< $type Ext >] { + type __ContainerType; + + /// Create a lense to the byte slice + fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type>; + + /// Create a lense to the byte slice, automatically truncating oversized buffers + fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type>; + } + + impl<'a> [< $type Ext >] for &'a [u8] { + type __ContainerType = &'a [u8]; + + fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type> { + $type::::check_size(self.len())?; + Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? )) + } + + fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type> { + let required_size = $( $len + )+ 0; + ::rosenpass_lenses::LenseError::ensure_sufficient_buffer_size(self.len(), required_size)?; + [< $type Ext >]::[< $type:snake >](&self[..required_size]) + } + } + + impl<'a> [< $type Ext >] for &'a mut [u8] { + type __ContainerType = &'a mut [u8]; + fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type> { + $type::::check_size(self.len())?; + Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? )) + } + + fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type> { + let required_size = $( $len + )+ 0; + ::rosenpass_lenses::LenseError::ensure_sufficient_buffer_size(self.len(), required_size)?; + [< $type Ext >]::[< $type:snake >](&mut self[..required_size]) + } + } + }); +); diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index f8feb6f..2cad94d 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -21,6 +21,7 @@ rosenpass-ciphers = { workspace = true } rosenpass-cipher-traits = { workspace = true } rosenpass-to = { workspace = true } rosenpass-secret-memory = { workspace = true } +rosenpass-lenses = { workspace = true } anyhow = { workspace = true } static_assertions = { workspace = true } memoffset = { workspace = true } diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index 1f37c25..729e6bb 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,3 +1,5 @@ +use rosenpass_lenses::LenseError; + pub mod app_server; pub mod cli; pub mod config; @@ -7,11 +9,16 @@ pub mod protocol; #[derive(thiserror::Error, Debug)] pub enum RosenpassError { - #[error("buffer size mismatch, required {required_size} but found {actual_size}")] - BufferSizeMismatch { - required_size: usize, - actual_size: usize, - }, + #[error("buffer size mismatch")] + BufferSizeMismatch, #[error("invalid message type")] InvalidMessageType(u8), } + +impl From for RosenpassError { + fn from(value: LenseError) -> Self { + match value { + LenseError::BufferSizeMismatch => RosenpassError::BufferSizeMismatch, + } + } +} diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 1aefba5..ed03214 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -9,14 +9,15 @@ //! //! # Example //! -//! The following example uses the [`data_lense` macro](crate::data_lense) to create a lense that +//! The following example uses the [`lense` macro](rosenpass_lenses::lense) to create a lense that //! might be useful when dealing with UDP headers. //! //! ``` -//! use rosenpass::{data_lense, RosenpassError, msgs::LenseView}; +//! use rosenpass_lenses::{lense, LenseView}; +//! use rosenpass::RosenpassError; //! # fn main() -> Result<(), RosenpassError> { //! -//! data_lense! {UdpDatagramHeader := +//! lense! {UdpDatagramHeader := //! source_port: 2, //! dest_port: 2, //! length: 2, @@ -47,217 +48,11 @@ use super::RosenpassError; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +use rosenpass_lenses::{lense, LenseView}; // Macro magic //////////////////////////////////////////////////////////////// -/// A macro to create data lenses. Refer to the [`msgs` mod](crate::msgs) for -/// an example and further elaboration -// TODO implement TryFrom<[u8]> and From<[u8; Self::len()]> -#[macro_export] -macro_rules! data_lense( - // prefix @ offset ; optional meta ; field name : field length, ... - (token_muncher_ref @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => { - ::paste::paste!{ - - #[allow(rustdoc::broken_intra_doc_links)] - $( #[ $attr ] )* - /// - #[doc = data_lense!(maybe_docstring_link $len)] - /// bytes long - pub fn $field(&self) -> &__ContainerType::Output { - &self.0[$offset .. $offset + $len] - } - - /// The bytes until the - #[doc = data_lense!(maybe_docstring_link Self::$field)] - /// field - pub fn [< until_ $field >](&self) -> &__ContainerType::Output { - &self.0[0 .. $offset] - } - - // if the tail exits, consume it as well - $( - data_lense!{token_muncher_ref @ $offset + $len ; $( $tail )+ } - )? - } - }; - - // prefix @ offset ; optional meta ; field name : field length, ... - (token_muncher_mut @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => { - ::paste::paste!{ - - #[allow(rustdoc::broken_intra_doc_links)] - $( #[ $attr ] )* - /// - #[doc = data_lense!(maybe_docstring_link $len)] - /// bytes long - pub fn [< $field _mut >](&mut self) -> &mut __ContainerType::Output { - &mut self.0[$offset .. $offset + $len] - } - - // if the tail exits, consume it as well - $( - data_lense!{token_muncher_mut @ $offset + $len ; $( $tail )+ } - )? - } - }; - - // switch that yields literals unchanged, but creates docstring links to - // constants - // TODO the doc string link doesn't work if $x is taken from a generic, - (maybe_docstring_link $x:literal) => (stringify!($x)); - (maybe_docstring_link $x:expr) => (stringify!([$x])); - - // struct name < optional generics > := optional doc string field name : field length, ... -($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{ - - #[allow(rustdoc::broken_intra_doc_links)] - /// A data lense to manipulate byte slices. - /// - //// # Fields - /// - $( - /// - ` - #[doc = stringify!($field)] - /// `: - #[doc = data_lense!(maybe_docstring_link $len)] - /// bytes - )+ - pub struct $type<__ContainerType $(, $( $generic ),+ )? > ( - __ContainerType, - // The phantom data is required, since all generics declared on a - // type need to be used on the type. - // https://doc.rust-lang.org/stable/error_codes/E0392.html - $( $( ::core::marker::PhantomData<$generic> ),+ )? - ); - - impl<__ContainerType $(, $( $generic: LenseView ),+ )? > $type<__ContainerType $(, $( $generic ),+ )? >{ - $( - /// Size in bytes of the field ` - #[doc = !($field)] - /// ` - pub const fn [< $field _len >]() -> usize{ - $len - } - )+ - - /// Verify that `len` is sufficiently long to hold [Self] - pub fn check_size(len: usize) -> Result<(), RosenpassError>{ - let required_size = $( $len + )+ 0; - let actual_size = len; - if required_size != actual_size { - Err(RosenpassError::BufferSizeMismatch { - required_size, - actual_size, - }) - }else{ - Ok(()) - } - } - } - - // read-only accessor functions - impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a __ContainerType $(, $( $generic ),+ )?> - where - __ContainerType: std::ops::Index> + ?Sized, - { - data_lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ } - - /// View into all bytes belonging to this Lense - pub fn all_bytes(&self) -> &__ContainerType::Output { - &self.0[0..Self::LEN] - } - } - - // mutable accessor functions - impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a mut __ContainerType $(, $( $generic ),+ )?> - where - __ContainerType: std::ops::IndexMut> + ?Sized, - { - data_lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ } - data_lense!{token_muncher_mut @ 0 ; $( $( $attr )* ; $field : $len ),+ } - - /// View into all bytes belonging to this Lense - pub fn all_bytes(&self) -> &__ContainerType::Output { - &self.0[0..Self::LEN] - } - - /// View into all bytes belonging to this Lense - pub fn all_bytes_mut(&mut self) -> &mut __ContainerType::Output { - &mut self.0[0..Self::LEN] - } - } - - // lense trait, allowing us to know the implementing lenses size - impl<__ContainerType $(, $( $generic: LenseView ),+ )? > LenseView for $type<__ContainerType $(, $( $generic ),+ )? >{ - /// Number of bytes required to store this type in binary format - const LEN: usize = $( $len + )+ 0; - } - - /// Extension trait to allow checked creation of a lense over - /// some byte slice that contains a - #[doc = data_lense!(maybe_docstring_link $type)] - pub trait [< $type Ext >] { - type __ContainerType; - - /// Create a lense to the byte slice - fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError>; - - /// Create a lense to the byte slice, automatically truncating oversized buffers - fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError>; - } - - impl<'a> [< $type Ext >] for &'a [u8] { - type __ContainerType = &'a [u8]; - - fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError> { - $type::::check_size(self.len())?; - Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? )) - } - - fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError> { - let required_size = $( $len + )+ 0; - let actual_size = self.len(); - if actual_size < required_size { - return Err(RosenpassError::BufferSizeMismatch { - required_size, - actual_size, - }); - } - - [< $type Ext >]::[< $type:snake >](&self[..required_size]) - } - } - - impl<'a> [< $type Ext >] for &'a mut [u8] { - type __ContainerType = &'a mut [u8]; - fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError> { - $type::::check_size(self.len())?; - Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? )) - } - - fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type, RosenpassError> { - let required_size = $( $len + )+ 0; - let actual_size = self.len(); - if actual_size < required_size { - return Err(RosenpassError::BufferSizeMismatch { - required_size, - actual_size, - }); - } - - [< $type Ext >]::[< $type:snake >](&mut self[..required_size]) - } - } - }); -); - -/// Common trait shared by all Lenses -pub trait LenseView { - const LEN: usize; -} - -data_lense! { Envelope := +lense! { Envelope := /// [MsgType] of this message msg_type: 1, /// Reserved for future use @@ -271,7 +66,7 @@ data_lense! { Envelope := cookie: 16 } -data_lense! { InitHello := +lense! { InitHello := /// Randomly generated connection id sidi: 4, /// Kyber 512 Ephemeral Public Key @@ -284,7 +79,7 @@ data_lense! { InitHello := auth: aead::TAG_LEN } -data_lense! { RespHello := +lense! { RespHello := /// Randomly generated connection id sidr: 4, /// Copied from InitHello @@ -299,7 +94,7 @@ data_lense! { RespHello := biscuit: BISCUIT_CT_LEN } -data_lense! { InitConf := +lense! { InitConf := /// Copied from InitHello sidi: 4, /// Copied from RespHello @@ -310,7 +105,7 @@ data_lense! { InitConf := auth: aead::TAG_LEN } -data_lense! { EmptyData := +lense! { EmptyData := /// Copied from RespHello sid: 4, /// Nonce @@ -319,7 +114,7 @@ data_lense! { EmptyData := auth: aead::TAG_LEN } -data_lense! { Biscuit := +lense! { Biscuit := /// H(spki) – Ident ifies the initiator pidi: KEY_LEN, /// The biscuit number (replay protection) @@ -328,11 +123,11 @@ data_lense! { Biscuit := ck: KEY_LEN } -data_lense! { DataMsg := +lense! { DataMsg := dummy: 4 } -data_lense! { CookieReply := +lense! { CookieReply := dummy: 4 } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index c719609..c541d60 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -74,6 +74,7 @@ use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +use rosenpass_lenses::LenseView; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use std::collections::hash_map::{ From 1eefb5f2631eaf1cffd616a0e527502aef614003 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sat, 2 Dec 2023 12:15:33 +0100 Subject: [PATCH 44/46] fix: Guaranteed results typo --- util/src/result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/result.rs b/util/src/result.rs index 57f756d..47d1a5c 100644 --- a/util/src/result.rs +++ b/util/src/result.rs @@ -62,7 +62,7 @@ pub trait GuaranteedValue { /// } /// /// fn failable_multiply(a: &T, b: u32) -/// -> Result { +/// -> Result /// where /// T: FailableAddition { /// let mut accu = a.failable_addition(a)?; From 3a0ebd2cbc9f873f2f33624dbb108650eaa5095c Mon Sep 17 00:00:00 2001 From: Morgan Hill Date: Sat, 2 Dec 2023 10:43:48 +0100 Subject: [PATCH 45/46] feat: Add fuzzing for libsodium allocator --- .github/workflows/qc.yaml | 2 ++ fuzz/Cargo.toml | 12 ++++++++++++ fuzz/fuzz_targets/box_sodium_alloc.rs | 12 ++++++++++++ fuzz/fuzz_targets/vec_sodium_alloc.rs | 13 +++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 fuzz/fuzz_targets/box_sodium_alloc.rs create mode 100644 fuzz/fuzz_targets/vec_sodium_alloc.rs diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 7b69137..173c0d5 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -146,3 +146,5 @@ jobs: cargo fuzz run fuzz_handle_msg -- -max_total_time=5 ulimit -s 8192000 && RUST_MIN_STACK=33554432000 && cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5 cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5 + cargo fuzz run fuzz_box_sodium_alloc -- -max_total_time=5 + cargo fuzz run fuzz_vec_sodium_alloc -- -max_total_time=5 diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 47c1344..89000db 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -47,3 +47,15 @@ name = "fuzz_kyber_encaps" path = "fuzz_targets/kyber_encaps.rs" test = false doc = false + +[[bin]] +name = "fuzz_box_sodium_alloc" +path = "fuzz_targets/box_sodium_alloc.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_vec_sodium_alloc" +path = "fuzz_targets/vec_sodium_alloc.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/box_sodium_alloc.rs b/fuzz/fuzz_targets/box_sodium_alloc.rs new file mode 100644 index 0000000..e1b1799 --- /dev/null +++ b/fuzz/fuzz_targets/box_sodium_alloc.rs @@ -0,0 +1,12 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use rosenpass_sodium::{ + alloc::{Alloc as SodiumAlloc, Box as SodiumBox}, + init, +}; + +fuzz_target!(|data: &[u8]| { + let _ = init(); + let _ = SodiumBox::new_in(data, SodiumAlloc::new()); +}); diff --git a/fuzz/fuzz_targets/vec_sodium_alloc.rs b/fuzz/fuzz_targets/vec_sodium_alloc.rs new file mode 100644 index 0000000..a37368d --- /dev/null +++ b/fuzz/fuzz_targets/vec_sodium_alloc.rs @@ -0,0 +1,13 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use rosenpass_sodium::{ + alloc::{Alloc as SodiumAlloc, Vec as SodiumVec}, + init, +}; + +fuzz_target!(|data: &[u8]| { + let _ = init(); + let mut vec = SodiumVec::new_in(SodiumAlloc::new()); + vec.extend_from_slice(data); +}); From 9819148b6f7473b60ccff3e338299cfb1e0b135c Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 3 Dec 2023 13:27:47 +0100 Subject: [PATCH 46/46] fix: remove OSFONTDIR var from whitepaper build Fixes #65. I checked with `pdffonts` that the whitepaper still has all fonts embedded. --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1f09b93..5d0a559 100644 --- a/flake.nix +++ b/flake.nix @@ -291,7 +291,6 @@ ]; buildPhase = '' export HOME=$(mktemp -d) - export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}" latexmk -r tex/CI.rc ''; installPhase = ''