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 {