From a996b082796308a4ee7c2124621156a32989aa0a Mon Sep 17 00:00:00 2001 From: Aaron Kaiser Date: Tue, 6 Feb 2024 15:33:59 +0100 Subject: [PATCH 01/19] refactor: replace lenses library with the zerocopy crate --- Cargo.lock | 31 ++++-- Cargo.toml | 3 +- flake.nix | 6 +- lenses/Cargo.toml | 16 --- lenses/readme.md | 3 - lenses/src/lib.rs | 206 ------------------------------------- rosenpass/Cargo.toml | 2 +- rosenpass/src/lib.rs | 9 +- rosenpass/src/msgs.rs | 134 +++++++++++------------- rosenpass/src/protocol.rs | 210 ++++++++++++++++++++------------------ 10 files changed, 194 insertions(+), 426 deletions(-) delete mode 100644 lenses/Cargo.toml delete mode 100644 lenses/readme.md delete mode 100644 lenses/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f53b0c2..657d11d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1131,7 +1131,6 @@ dependencies = [ "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-constant-time", - "rosenpass-lenses", "rosenpass-secret-memory", "rosenpass-to", "rosenpass-util", @@ -1141,6 +1140,7 @@ dependencies = [ "test_bin", "thiserror", "toml", + "zerocopy", ] [[package]] @@ -1185,14 +1185,6 @@ dependencies = [ "stacker", ] -[[package]] -name = "rosenpass-lenses" -version = "0.1.0" -dependencies = [ - "paste", - "thiserror", -] - [[package]] name = "rosenpass-oqs" version = "0.1.0" @@ -1848,6 +1840,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 35d7386..3647d92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "to", "fuzz", "secret-memory", - "lenses", ] default-members = [ @@ -31,7 +30,6 @@ 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" @@ -59,3 +57,4 @@ mio = { version = "0.8.9", features = ["net", "os-poll"] } oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } blake2 = "0.10.6" chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] } +zerocopy = { version = "0.7.32", features = ["derive"] } diff --git a/flake.nix b/flake.nix index 5d0a559..f166df5 100644 --- a/flake.nix +++ b/flake.nix @@ -112,9 +112,9 @@ # suitable Rust toolchain toolchain = with inputs.fenix.packages.${system}; combine [ - stable.cargo - stable.rustc - targets.${target}.stable.rust-std + latest.cargo + latest.rustc + targets.${target}.latest.rust-std ]; # naersk with a custom toolchain diff --git a/lenses/Cargo.toml b/lenses/Cargo.toml deleted file mode 100644 index 9304dee..0000000 --- a/lenses/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[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 deleted file mode 100644 index c709695..0000000 --- a/lenses/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# 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 deleted file mode 100644 index f699c8a..0000000 --- a/lenses/src/lib.rs +++ /dev/null @@ -1,206 +0,0 @@ -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 e906433..83d1680 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -20,7 +20,6 @@ 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 } @@ -33,6 +32,7 @@ toml = { workspace = true } clap = { workspace = true } mio = { workspace = true } rand = { workspace = true } +zerocopy = { workspace = true } [build-dependencies] anyhow = { workspace = true } diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index 729e6bb..6791cd8 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,4 +1,4 @@ -use rosenpass_lenses::LenseError; +#![feature(offset_of)] pub mod app_server; pub mod cli; @@ -15,10 +15,3 @@ pub enum RosenpassError { 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 ed03214..30721ee 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -6,129 +6,111 @@ //! always serialized instance of the data in question. This is closely related //! to the concept of lenses in function programming; more on that here: //! [https://sinusoid.es/misc/lager/lenses.pdf](https://sinusoid.es/misc/lager/lenses.pdf) -//! -//! # Example -//! -//! 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_lenses::{lense, LenseView}; -//! use rosenpass::RosenpassError; -//! # fn main() -> Result<(), RosenpassError> { -//! -//! lense! {UdpDatagramHeader := -//! source_port: 2, -//! dest_port: 2, -//! length: 2, -//! checksum: 2 -//! } -//! -//! let mut buf = [0u8; 8]; -//! -//! // read-only lense, no check of size: -//! let lense = UdpDatagramHeader(&buf); -//! assert_eq!(lense.checksum(), &[0, 0]); -//! -//! // mutable lense, runtime check of size -//! let mut lense = buf.as_mut().udp_datagram_header()?; -//! lense.source_port_mut().copy_from_slice(&53u16.to_be_bytes()); // some DNS, anyone? -//! -//! // the original buffer is still available -//! assert_eq!(buf, [0, 53, 0, 0, 0, 0, 0, 0]); -//! -//! // read-only lense, runtime check of size -//! let lense = buf.as_ref().udp_datagram_header()?; -//! assert_eq!(lense.source_port(), &[0, 53]); -//! # Ok(()) -//! # } -//! ``` +//! To achieve this we utilize the zerocopy library. use super::RosenpassError; +use std::mem::size_of; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; -use rosenpass_lenses::{lense, LenseView}; +use zerocopy::{AsBytes, FromBytes, FromZeroes}; // Macro magic //////////////////////////////////////////////////////////////// -lense! { Envelope := + +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct Envelope { /// [MsgType] of this message - msg_type: 1, + pub msg_type: u8, /// Reserved for future use - reserved: 3, + pub reserved: [u8; 3], /// The actual Paylod - payload: M::LEN, + pub payload: M, /// Message Authentication Code (mac) over all bytes until (exclusive) /// `mac` itself - mac: 16, - /// Currently unused, TODO: do something with this - cookie: 16 + pub mac: [u8; 16], + /// Currently unused, TODO: do something with this + pub cookie: [u8; 16] } -lense! { InitHello := +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct InitHello { /// Randomly generated connection id - sidi: 4, + pub sidi: [u8; 4], /// Kyber 512 Ephemeral Public Key - epki: EphemeralKem::PK_LEN, + pub epki: [u8; EphemeralKem::PK_LEN], /// Classic McEliece Ciphertext - sctr: StaticKem::CT_LEN, + pub sctr: [u8; StaticKem::CT_LEN], /// Encryped: 16 byte hash of McEliece initiator static key - pidic: aead::TAG_LEN + 32, + pub pidic: [u8; aead::TAG_LEN + 32], /// Encrypted TAI64N Time Stamp (against replay attacks) - auth: aead::TAG_LEN + pub auth: [u8; aead::TAG_LEN], } -lense! { RespHello := +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct RespHello { /// Randomly generated connection id - sidr: 4, + pub sidr: [u8; 4], /// Copied from InitHello - sidi: 4, + pub sidi: [u8; 4], /// Kyber 512 Ephemeral Ciphertext - ecti: EphemeralKem::CT_LEN, + pub ecti: [u8; EphemeralKem::CT_LEN], /// Classic McEliece Ciphertext - scti: StaticKem::CT_LEN, + pub scti: [u8; StaticKem::CT_LEN], /// Empty encrypted message (just an auth tag) - auth: aead::TAG_LEN, + pub auth: [u8; aead::TAG_LEN], /// Responders handshake state in encrypted form - biscuit: BISCUIT_CT_LEN + pub biscuit: [u8; BISCUIT_CT_LEN], } -lense! { InitConf := +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct InitConf { /// Copied from InitHello - sidi: 4, + pub sidi: [u8; 4], /// Copied from RespHello - sidr: 4, + pub sidr: [u8; 4], /// Responders handshake state in encrypted form - biscuit: BISCUIT_CT_LEN, + pub biscuit: [u8; BISCUIT_CT_LEN], /// Empty encrypted message (just an auth tag) - auth: aead::TAG_LEN + pub auth: [u8; aead::TAG_LEN] } -lense! { EmptyData := +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct EmptyData { /// Copied from RespHello - sid: 4, + pub sid: [u8; 4], /// Nonce - ctr: 8, + pub ctr: [u8; 8], /// Empty encrypted message (just an auth tag) - auth: aead::TAG_LEN + pub auth: [u8; aead::TAG_LEN] } -lense! { Biscuit := +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct Biscuit { /// H(spki) – Ident ifies the initiator - pidi: KEY_LEN, + pub pidi: [u8; KEY_LEN], /// The biscuit number (replay protection) - biscuit_no: 12, + pub biscuit_no: [u8; 12], /// Chaining key - ck: KEY_LEN + pub ck: [u8; KEY_LEN] } -lense! { DataMsg := - dummy: 4 +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct DataMsg { + pub dummy: [u8; 4] } -lense! { CookieReply := - dummy: 4 +#[repr(packed)] +#[derive(AsBytes, FromBytes, FromZeroes)] +pub struct CookieReply { + pub dummy: [u8; 4] } // Traits ///////////////////////////////////////////////////////////////////// @@ -178,7 +160,7 @@ impl TryFrom for MsgType { } /// length in bytes of an unencrypted Biscuit (plain text) -pub const BISCUIT_PT_LEN: usize = Biscuit::<()>::LEN; +pub const BISCUIT_PT_LEN: usize = size_of::(); /// Length in bytes of an encrypted Biscuit (cipher text) pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN; diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 63af5fe..1562816 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -70,6 +70,7 @@ use std::collections::hash_map::{ HashMap, }; use std::convert::Infallible; +use std::mem::{size_of,offset_of}; use anyhow::{bail, ensure, Context, Result}; @@ -78,18 +79,18 @@ use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_constant_time as constant_time; -use rosenpass_lenses::LenseView; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; +use zerocopy::{AsBytes, FromBytes, Ref}; -use crate::{hash_domains, msgs::*}; +use crate::{hash_domains, msgs::*, RosenpassError}; // CONSTANTS & SETTINGS ////////////////////////// /// Size required to fit any message in binary form pub const RTX_BUFFER_SIZE: usize = max_usize( - > as LenseView>::LEN, - > as LenseView>::LEN, + size_of::>(), + size_of::>() ); /// A type for time, e.g. for backoff before re-tries @@ -739,11 +740,12 @@ impl CryptoServer { // TODO remove unnecessary copying between global tx_buf and per-peer buf // TODO move retransmission storage to io server pub fn initiate_handshake(&mut self, peer: PeerPtr, tx_buf: &mut [u8]) -> Result { - let mut msg = tx_buf.envelope_truncating::>()?; // Envelope::::default(); // TODO - self.handle_initiation(peer, msg.payload_mut().init_hello()?)?; - let len = self.seal_and_commit_msg(peer, MsgType::InitHello, msg)?; + // Envelope::::default(); // TODO + let mut msg = truncating_cast_into::>(tx_buf)?; + self.handle_initiation(peer, &mut msg.payload)?; + let len = self.seal_and_commit_msg(peer, MsgType::InitHello, &mut msg)?; peer.hs() - .store_msg_for_retransmission(self, &tx_buf[..len])?; + .store_msg_for_retransmission(self, &msg.as_bytes()[..len])?; Ok(len) } } @@ -793,50 +795,50 @@ impl CryptoServer { let peer = match rx_buf[0].try_into() { Ok(MsgType::InitHello) => { - let msg_in = rx_buf.envelope::>()?; + let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); - let mut msg_out = tx_buf.envelope_truncating::>()?; + let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_init_hello( - msg_in.payload().init_hello()?, - msg_out.payload_mut().resp_hello()?, + &msg_in.payload, + &mut msg_out.payload, )?; - len = self.seal_and_commit_msg(peer, MsgType::RespHello, msg_out)?; + len = self.seal_and_commit_msg(peer, MsgType::RespHello, &mut msg_out)?; peer } Ok(MsgType::RespHello) => { - let msg_in = rx_buf.envelope::>()?; + let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); - let mut msg_out = tx_buf.envelope_truncating::>()?; + let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_resp_hello( - msg_in.payload().resp_hello()?, - msg_out.payload_mut().init_conf()?, + &msg_in.payload, + &mut msg_out.payload, )?; - len = self.seal_and_commit_msg(peer, MsgType::InitConf, msg_out)?; + len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?; peer.hs() - .store_msg_for_retransmission(self, &tx_buf[..len])?; + .store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?; exchanged = true; peer } Ok(MsgType::InitConf) => { - let msg_in = rx_buf.envelope::>()?; + let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); - let mut msg_out = tx_buf.envelope_truncating::>()?; + let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_init_conf( - msg_in.payload().init_conf()?, - msg_out.payload_mut().empty_data()?, + &msg_in.payload, + &mut msg_out.payload, )?; - len = self.seal_and_commit_msg(peer, MsgType::EmptyData, msg_out)?; + len = self.seal_and_commit_msg(peer, MsgType::EmptyData, &mut msg_out)?; exchanged = true; peer } Ok(MsgType::EmptyData) => { - let msg_in = rx_buf.envelope::>()?; + let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); - self.handle_resp_conf(msg_in.payload().empty_data()?)? + self.handle_resp_conf(&msg_in.payload)? } Ok(MsgType::DataMsg) => bail!("DataMsg handling not implemented!"), Ok(MsgType::CookieReply) => bail!("CookieReply handling not implemented!"), @@ -856,15 +858,15 @@ impl CryptoServer { /// /// The message type is explicitly required here because it is very easy to /// forget setting that, which creates subtle but far ranging errors. - pub fn seal_and_commit_msg( + pub fn seal_and_commit_msg( &mut self, peer: PeerPtr, msg_type: MsgType, - mut msg: Envelope<&mut [u8], M>, + msg: &mut Ref<&mut [u8], Envelope>, ) -> Result { - msg.msg_type_mut()[0] = msg_type as u8; + msg.msg_type = msg_type as u8; msg.seal(peer, self)?; - Ok( as LenseView>::LEN) + Ok(size_of::>()) } } @@ -1170,32 +1172,32 @@ impl IniHsPtr { // CRYPTO/HANDSHAKE HANDLING ///////////////////// -impl Envelope<&mut [u8], M> +impl Envelope where - M: LenseView, + M: AsBytes + FromBytes, { /// Calculate the message authentication code (`mac`) pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { let mac = hash_domains::mac()? .mix(peer.get(srv).spkt.secret())? - .mix(self.until_mac())?; - self.mac_mut() + .mix(&self.as_bytes()[..offset_of!(Self, mac)])?; + self.mac .copy_from_slice(mac.into_value()[..16].as_ref()); Ok(()) } } -impl Envelope<&[u8], M> +impl Envelope where - M: LenseView, + M: AsBytes + FromBytes, { /// Check the message authentication code pub fn check_seal(&self, srv: &CryptoServer) -> Result { let expected = hash_domains::mac()? .mix(srv.spkm.secret())? - .mix(self.until_mac())?; + .mix(&self.as_bytes()[..offset_of!(Self, mac)])?; Ok(constant_time::memcmp( - self.mac(), + &self.mac, &expected.into_value()[..16], )) } @@ -1282,15 +1284,15 @@ impl HandshakeState { biscuit_ct: &mut [u8], ) -> Result<&mut Self> { let mut biscuit = Secret::::zero(); // pt buffer - let mut biscuit = (&mut biscuit.secret_mut()[..]).biscuit()?; // lens view + let mut biscuit: Ref<&mut [u8], Biscuit> = Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); // calculate pt contents biscuit - .pidi_mut() + .pidi .copy_from_slice(peer.get(srv).pidt()?.as_slice()); - biscuit.biscuit_no_mut().copy_from_slice(&*srv.biscuit_ctr); + biscuit.biscuit_no.copy_from_slice(&*srv.biscuit_ctr); biscuit - .ck_mut() + .ck .copy_from_slice(self.ck.clone().danger_into_secret().secret()); // calculate ad contents @@ -1311,7 +1313,7 @@ impl HandshakeState { n[0] |= (bk.0 as u8 & 0x1) << 7; let k = bk.get(srv).key.secret(); - let pt = biscuit.all_bytes(); + let pt = biscuit.as_bytes(); xaead::encrypt(biscuit_ct, k, &*n, &ad, pt)?; self.mix(biscuit_ct) @@ -1337,18 +1339,18 @@ impl HandshakeState { // Allocate and decrypt the biscuit data let mut biscuit = Secret::::zero(); // pt buf - let mut biscuit = (&mut biscuit.secret_mut()[..]).biscuit()?; // slice + let mut biscuit: Ref<&mut [u8], Biscuit> = Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); xaead::decrypt( - biscuit.all_bytes_mut(), + biscuit.as_bytes_mut(), bk.get(srv).key.secret(), &ad, biscuit_ct, )?; // Reconstruct the biscuit fields - let no = BiscuitId::from_slice(biscuit.biscuit_no()); - let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(biscuit.ck())).dup(); - let pid = PeerId::from_slice(biscuit.pidi()); + let no = BiscuitId::from_slice(&biscuit.biscuit_no); + let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck)).dup(); + let pid = PeerId::from_slice(&biscuit.pidi); // Reconstruct the handshake state let mut hs = Self { sidi, sidr, ck }; @@ -1364,7 +1366,7 @@ impl HandshakeState { // indicates retransmission // TODO: Handle retransmissions without involving the crypto code ensure!( - constant_time::compare(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0, + constant_time::compare(&biscuit.biscuit_no, &*peer.get(srv).biscuit_used) >= 0, "Rejecting biscuit: Outdated biscuit number" ); @@ -1415,7 +1417,7 @@ impl CryptoServer { pub fn handle_initiation( &mut self, peer: PeerPtr, - mut ih: InitHello<&mut [u8]>, + ih: &mut InitHello, ) -> Result { let mut hs = InitiatorHandshake::zero_with_timestamp(self); @@ -1424,25 +1426,25 @@ impl CryptoServer { // IHI2 hs.core.sidi.randomize(); - ih.sidi_mut().copy_from_slice(&hs.core.sidi.value); + ih.sidi.copy_from_slice(&hs.core.sidi.value); // IHI3 EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; - ih.epki_mut().copy_from_slice(&hs.epki.value); + ih.epki.copy_from_slice(&hs.epki.value); // IHI4 - hs.core.mix(ih.sidi())?.mix(ih.epki())?; + hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?; // IHI5 hs.core .encaps_and_mix::( - ih.sctr_mut(), + ih.sctr.as_mut_slice(), peer.get(self).spkt.secret(), )?; // IHI6 hs.core - .encrypt_and_mix(ih.pidic_mut(), self.pidm()?.as_ref())?; + .encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm()?.as_ref())?; // IHI7 hs.core @@ -1450,7 +1452,7 @@ impl CryptoServer { .mix(peer.get(self).psk.secret())?; // IHI8 - hs.core.encrypt_and_mix(ih.auth_mut(), &[])?; + hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?; // Update the handshake hash last (not changing any state on prior error peer.hs().insert(self, hs)?; @@ -1460,30 +1462,30 @@ impl CryptoServer { pub fn handle_init_hello( &mut self, - ih: InitHello<&[u8]>, - mut rh: RespHello<&mut [u8]>, + ih: &InitHello, + rh: &mut RespHello, ) -> Result { let mut core = HandshakeState::zero(); - core.sidi = SessionId::from_slice(ih.sidi()); + core.sidi = SessionId::from_slice(&ih.sidi); // IHR1 core.init(self.spkm.secret())?; // IHR4 - core.mix(ih.sidi())?.mix(ih.epki())?; + core.mix(&ih.sidi)?.mix(&ih.epki)?; // IHR5 core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), - ih.sctr(), + &ih.sctr, )?; // IHR6 let peer = { let mut peerid = PeerId::zero(); - core.decrypt_and_mix(&mut *peerid, ih.pidic())?; + core.decrypt_and_mix(&mut *peerid, &ih.pidic)?; self.find_peer(peerid) .with_context(|| format!("No such peer {peerid:?}."))? }; @@ -1493,46 +1495,46 @@ impl CryptoServer { .mix(peer.get(self).psk.secret())?; // IHR8 - core.decrypt_and_mix(&mut [0u8; 0], ih.auth())?; + core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?; // RHR1 core.sidr.randomize(); - rh.sidi_mut().copy_from_slice(core.sidi.as_ref()); - rh.sidr_mut().copy_from_slice(core.sidr.as_ref()); + rh.sidi.copy_from_slice(core.sidi.as_ref()); + rh.sidr.copy_from_slice(core.sidr.as_ref()); // RHR3 - core.mix(rh.sidr())?.mix(rh.sidi())?; + core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHR4 - core.encaps_and_mix::(rh.ecti_mut(), ih.epki())?; + core.encaps_and_mix::(&mut rh.ecti, &ih.epki)?; // RHR5 core.encaps_and_mix::( - rh.scti_mut(), + &mut rh.scti, peer.get(self).spkt.secret(), )?; // RHR6 - core.store_biscuit(self, peer, rh.biscuit_mut())?; + core.store_biscuit(self, peer, &mut rh.biscuit)?; // RHR7 - core.encrypt_and_mix(rh.auth_mut(), &[])?; + core.encrypt_and_mix(&mut rh.auth, &[])?; Ok(peer) } pub fn handle_resp_hello( &mut self, - rh: RespHello<&[u8]>, - mut ic: InitConf<&mut [u8]>, + rh: &RespHello, + ic: &mut InitConf, ) -> Result { // RHI2 let peer = self - .lookup_handshake(SessionId::from_slice(rh.sidi())) + .lookup_handshake(SessionId::from_slice(&rh.sidi)) .with_context(|| { format!( "Got RespHello packet for non-existent session {:?}", - rh.sidi() + rh.sidi ) })? .peer(); @@ -1557,52 +1559,52 @@ impl CryptoServer { ensure!( exp == got, "Unexpected package in session {:?}. Expected {:?}, got {:?}.", - SessionId::from_slice(rh.sidi()), + SessionId::from_slice(&rh.sidi), exp, got ); let mut core = hs!().core.clone(); - core.sidr.copy_from_slice(rh.sidr()); + core.sidr.copy_from_slice(&rh.sidr); // TODO: decaps_and_mix should take Secret<> directly // to save us from the repetitive secret unwrapping // RHI3 - core.mix(rh.sidr())?.mix(rh.sidi())?; + core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHI4 core.decaps_and_mix::( hs!().eski.secret(), &*hs!().epki, - rh.ecti(), + &rh.ecti, )?; // RHI5 core.decaps_and_mix::( self.sskm.secret(), self.spkm.secret(), - rh.scti(), + &rh.scti, )?; // RHI6 - core.mix(rh.biscuit())?; + core.mix(&rh.biscuit)?; // RHI7 - core.decrypt_and_mix(&mut [0u8; 0], rh.auth())?; + core.decrypt_and_mix(&mut [0u8; 0], &rh.auth)?; // TODO: We should just authenticate the entire network package up to the auth // tag as a pattern instead of mixing in fields separately - ic.sidi_mut().copy_from_slice(rh.sidi()); - ic.sidr_mut().copy_from_slice(rh.sidr()); + ic.sidi.copy_from_slice(&rh.sidi); + ic.sidr.copy_from_slice(&rh.sidr); // ICI3 - core.mix(ic.sidi())?.mix(ic.sidr())?; - ic.biscuit_mut().copy_from_slice(rh.biscuit()); + core.mix(&ic.sidi)?.mix(&ic.sidr)?; + ic.biscuit.copy_from_slice(&rh.biscuit); // ICI4 - core.encrypt_and_mix(ic.auth_mut(), &[])?; + core.encrypt_and_mix(&mut ic.auth, &[])?; // Split() – We move the secrets into the session; we do not // delete the InitiatorHandshake, just clear it's secrets because @@ -1619,26 +1621,26 @@ impl CryptoServer { pub fn handle_init_conf( &mut self, - ic: InitConf<&[u8]>, - mut rc: EmptyData<&mut [u8]>, + ic: &InitConf, + rc: &mut EmptyData, ) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit( self, - ic.biscuit(), - SessionId::from_slice(ic.sidi()), - SessionId::from_slice(ic.sidr()), + &ic.biscuit, + SessionId::from_slice(&ic.sidi), + SessionId::from_slice(&ic.sidr), )?; // ICR2 core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?; // ICR3 - core.mix(ic.sidi())?.mix(ic.sidr())?; + core.mix(&ic.sidi)?.mix(&ic.sidr)?; // ICR4 - core.decrypt_and_mix(&mut [0u8; 0], ic.auth())?; + core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?; // ICR5 if constant_time::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 { @@ -1682,19 +1684,19 @@ impl CryptoServer { .get_mut(self) .as_mut() .context("Cannot send acknowledgement. No session.")?; - rc.sid_mut().copy_from_slice(&ses.sidt.value); - rc.ctr_mut().copy_from_slice(&ses.txnm.to_le_bytes()); + rc.sid.copy_from_slice(&ses.sidt.value); + rc.ctr.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::encrypt(rc.auth_mut(), k, &n, &[], &[])?; // ct, k, n, ad, pt + aead::encrypt(&mut rc.auth, k, &n, &[], &[])?; // ct, k, n, ad, pt Ok(peer) } - pub fn handle_resp_conf(&mut self, rc: EmptyData<&[u8]>) -> Result { - let sid = SessionId::from_slice(rc.sid()); + pub fn handle_resp_conf(&mut self, rc: &EmptyData) -> Result { + let sid = SessionId::from_slice(&rc.sid); let hs = self .lookup_handshake(sid) .with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?; @@ -1717,16 +1719,16 @@ impl CryptoServer { })?; // the unwrap can not fail, because the slice returned by ctr() is // guaranteed to have the correct size - let n = u64::from_le_bytes(rc.ctr().try_into().unwrap()); + let n = u64::from_le_bytes(rc.ctr.try_into().unwrap()); ensure!(n >= s.txnt, "Stale nonce"); s.txnt = n; 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]), &[], - rc.auth(), + &rc.auth, )?; } @@ -1737,6 +1739,10 @@ impl CryptoServer { } } +fn truncating_cast_into(buf: &mut [u8]) -> Result, RosenpassError> { + Ok(Ref::new(&mut buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch)?) +} + #[cfg(test)] mod test { use super::*; From fd8f2e4424b20b78e523ad0af5b0cedb825345be Mon Sep 17 00:00:00 2001 From: Aaron Kaiser Date: Tue, 6 Feb 2024 15:51:18 +0100 Subject: [PATCH 02/19] style: apply rustfmt --- rosenpass/src/lib.rs | 1 - rosenpass/src/msgs.rs | 17 +++++------ rosenpass/src/protocol.rs | 64 ++++++++++++++------------------------- 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index 6791cd8..29e7b49 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -14,4 +14,3 @@ pub enum RosenpassError { #[error("invalid message type")] InvalidMessageType(u8), } - diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 30721ee..a400891 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -9,15 +9,14 @@ //! To achieve this we utilize the zerocopy library. use super::RosenpassError; -use std::mem::size_of; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +use std::mem::size_of; use zerocopy::{AsBytes, FromBytes, FromZeroes}; // Macro magic //////////////////////////////////////////////////////////////// - #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct Envelope { @@ -30,8 +29,8 @@ pub struct Envelope { /// Message Authentication Code (mac) over all bytes until (exclusive) /// `mac` itself pub mac: [u8; 16], - /// Currently unused, TODO: do something with this - pub cookie: [u8; 16] + /// Currently unused, TODO: do something with this + pub cookie: [u8; 16], } #[repr(packed)] @@ -76,7 +75,7 @@ pub struct InitConf { /// Responders handshake state in encrypted form pub biscuit: [u8; BISCUIT_CT_LEN], /// Empty encrypted message (just an auth tag) - pub auth: [u8; aead::TAG_LEN] + pub auth: [u8; aead::TAG_LEN], } #[repr(packed)] @@ -87,7 +86,7 @@ pub struct EmptyData { /// Nonce pub ctr: [u8; 8], /// Empty encrypted message (just an auth tag) - pub auth: [u8; aead::TAG_LEN] + pub auth: [u8; aead::TAG_LEN], } #[repr(packed)] @@ -98,19 +97,19 @@ pub struct Biscuit { /// The biscuit number (replay protection) pub biscuit_no: [u8; 12], /// Chaining key - pub ck: [u8; KEY_LEN] + pub ck: [u8; KEY_LEN], } #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct DataMsg { - pub dummy: [u8; 4] + pub dummy: [u8; 4], } #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct CookieReply { - pub dummy: [u8; 4] + pub dummy: [u8; 4], } // Traits ///////////////////////////////////////////////////////////////////// diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 1562816..6a15a3b 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -70,7 +70,7 @@ use std::collections::hash_map::{ HashMap, }; use std::convert::Infallible; -use std::mem::{size_of,offset_of}; +use std::mem::{offset_of, size_of}; use anyhow::{bail, ensure, Context, Result}; @@ -90,7 +90,7 @@ use crate::{hash_domains, msgs::*, RosenpassError}; /// Size required to fit any message in binary form pub const RTX_BUFFER_SIZE: usize = max_usize( size_of::>(), - size_of::>() + size_of::>(), ); /// A type for time, e.g. for backoff before re-tries @@ -795,26 +795,22 @@ impl CryptoServer { let peer = match rx_buf[0].try_into() { Ok(MsgType::InitHello) => { - let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; + let msg_in: Ref<&[u8], Envelope> = + Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); let mut msg_out = truncating_cast_into::>(tx_buf)?; - let peer = self.handle_init_hello( - &msg_in.payload, - &mut msg_out.payload, - )?; + let peer = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload)?; len = self.seal_and_commit_msg(peer, MsgType::RespHello, &mut msg_out)?; peer } Ok(MsgType::RespHello) => { - let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; + let msg_in: Ref<&[u8], Envelope> = + Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); let mut msg_out = truncating_cast_into::>(tx_buf)?; - let peer = self.handle_resp_hello( - &msg_in.payload, - &mut msg_out.payload, - )?; + let peer = self.handle_resp_hello(&msg_in.payload, &mut msg_out.payload)?; len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?; peer.hs() .store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?; @@ -822,20 +818,19 @@ impl CryptoServer { peer } Ok(MsgType::InitConf) => { - let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; + let msg_in: Ref<&[u8], Envelope> = + Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); let mut msg_out = truncating_cast_into::>(tx_buf)?; - let peer = self.handle_init_conf( - &msg_in.payload, - &mut msg_out.payload, - )?; + let peer = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload)?; len = self.seal_and_commit_msg(peer, MsgType::EmptyData, &mut msg_out)?; exchanged = true; peer } Ok(MsgType::EmptyData) => { - let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; + let msg_in: Ref<&[u8], Envelope> = + Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; ensure!(msg_in.check_seal(self)?, seal_broken); self.handle_resp_conf(&msg_in.payload)? @@ -1181,8 +1176,7 @@ where let mac = hash_domains::mac()? .mix(peer.get(srv).spkt.secret())? .mix(&self.as_bytes()[..offset_of!(Self, mac)])?; - self.mac - .copy_from_slice(mac.into_value()[..16].as_ref()); + self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); Ok(()) } } @@ -1284,7 +1278,8 @@ impl HandshakeState { biscuit_ct: &mut [u8], ) -> Result<&mut Self> { let mut biscuit = Secret::::zero(); // pt buffer - let mut biscuit: Ref<&mut [u8], Biscuit> = Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); + let mut biscuit: Ref<&mut [u8], Biscuit> = + Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); // calculate pt contents biscuit @@ -1339,7 +1334,8 @@ impl HandshakeState { // Allocate and decrypt the biscuit data let mut biscuit = Secret::::zero(); // pt buf - let mut biscuit: Ref<&mut [u8], Biscuit> = Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); + let mut biscuit: Ref<&mut [u8], Biscuit> = + Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); xaead::decrypt( biscuit.as_bytes_mut(), bk.get(srv).key.secret(), @@ -1414,11 +1410,7 @@ impl CryptoServer { impl CryptoServer { /// Implementation of the cryptographic protocol using the already /// established primitives - pub fn handle_initiation( - &mut self, - peer: PeerPtr, - ih: &mut InitHello, - ) -> Result { + pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { let mut hs = InitiatorHandshake::zero_with_timestamp(self); // IHI1 @@ -1460,11 +1452,7 @@ impl CryptoServer { Ok(peer) } - pub fn handle_init_hello( - &mut self, - ih: &InitHello, - rh: &mut RespHello, - ) -> Result { + pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello) -> Result { let mut core = HandshakeState::zero(); core.sidi = SessionId::from_slice(&ih.sidi); @@ -1523,11 +1511,7 @@ impl CryptoServer { Ok(peer) } - pub fn handle_resp_hello( - &mut self, - rh: &RespHello, - ic: &mut InitConf, - ) -> Result { + pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result { // RHI2 let peer = self .lookup_handshake(SessionId::from_slice(&rh.sidi)) @@ -1619,11 +1603,7 @@ impl CryptoServer { Ok(peer) } - pub fn handle_init_conf( - &mut self, - ic: &InitConf, - rc: &mut EmptyData, - ) -> Result { + pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit( From 8ea253f86bef8f974becc90042a5d58231407de5 Mon Sep 17 00:00:00 2001 From: Aaron Kaiser Date: Thu, 8 Feb 2024 16:18:10 +0100 Subject: [PATCH 03/19] refactor: use memoffset crate instead of unstable offset_of feature --- flake.nix | 6 +++--- rosenpass/src/lib.rs | 2 -- rosenpass/src/protocol.rs | 7 ++++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index f166df5..5d0a559 100644 --- a/flake.nix +++ b/flake.nix @@ -112,9 +112,9 @@ # suitable Rust toolchain toolchain = with inputs.fenix.packages.${system}; combine [ - latest.cargo - latest.rustc - targets.${target}.latest.rust-std + stable.cargo + stable.rustc + targets.${target}.stable.rust-std ]; # naersk with a custom toolchain diff --git a/rosenpass/src/lib.rs b/rosenpass/src/lib.rs index 29e7b49..a561dc0 100644 --- a/rosenpass/src/lib.rs +++ b/rosenpass/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(offset_of)] - pub mod app_server; pub mod cli; pub mod config; diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 6a15a3b..4e8bf78 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -70,10 +70,11 @@ use std::collections::hash_map::{ HashMap, }; use std::convert::Infallible; -use std::mem::{offset_of, size_of}; +use std::mem::size_of; use anyhow::{bail, ensure, Context, Result}; +use memoffset::span_of; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; @@ -1175,7 +1176,7 @@ where pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { let mac = hash_domains::mac()? .mix(peer.get(srv).spkt.secret())? - .mix(&self.as_bytes()[..offset_of!(Self, mac)])?; + .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); Ok(()) } @@ -1189,7 +1190,7 @@ where pub fn check_seal(&self, srv: &CryptoServer) -> Result { let expected = hash_domains::mac()? .mix(srv.spkm.secret())? - .mix(&self.as_bytes()[..offset_of!(Self, mac)])?; + .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; Ok(constant_time::memcmp( &self.mac, &expected.into_value()[..16], From 81487b103d3dad97506f37cc168604343fd5cd89 Mon Sep 17 00:00:00 2001 From: Aaron Kaiser Date: Wed, 21 Feb 2024 13:59:49 +0100 Subject: [PATCH 04/19] refactor: Get rid of comment and unessary truncation of buffer --- rosenpass/src/msgs.rs | 2 -- rosenpass/src/protocol.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index a400891..78ebcb6 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -15,8 +15,6 @@ use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use std::mem::size_of; use zerocopy::{AsBytes, FromBytes, FromZeroes}; -// Macro magic //////////////////////////////////////////////////////////////// - #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct Envelope { diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index 4e8bf78..8f6aa70 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -746,7 +746,7 @@ impl CryptoServer { self.handle_initiation(peer, &mut msg.payload)?; let len = self.seal_and_commit_msg(peer, MsgType::InitHello, &mut msg)?; peer.hs() - .store_msg_for_retransmission(self, &msg.as_bytes()[..len])?; + .store_msg_for_retransmission(self, msg.as_bytes())?; Ok(len) } } From e96968b8bcd22002b38a8c29588d74d7e1bedfb7 Mon Sep 17 00:00:00 2001 From: James Brownlee Date: Wed, 7 Feb 2024 16:39:32 -0500 Subject: [PATCH 05/19] adding dos protection code --- analysis/01_secrecy.entry.mpv | 21 +++ analysis/02_availability.entry.mpv | 20 +++ analysis/04_dos_protection.entry.mpv | 136 +++++++++++++++++++ analysis/config.mpv | 12 ++ analysis/rosenpass/cookie.mpv | 36 +++++ analysis/rosenpass/oracles.mpv | 188 ++++++++++++++++++--------- analysis/rosenpass/protocol.mpv | 17 ++- 7 files changed, 365 insertions(+), 65 deletions(-) create mode 100644 analysis/04_dos_protection.entry.mpv create mode 100644 analysis/rosenpass/cookie.mpv diff --git a/analysis/01_secrecy.entry.mpv b/analysis/01_secrecy.entry.mpv index 7386339..6be684f 100644 --- a/analysis/01_secrecy.entry.mpv +++ b/analysis/01_secrecy.entry.mpv @@ -3,12 +3,33 @@ #define SESSION_START_EVENTS 0 #define RANDOMIZED_CALL_IDS 0 + #include "config.mpv" #include "prelude/basic.mpv" #include "crypto/key.mpv" #include "crypto/kem.mpv" + #include "rosenpass/oracles.mpv" +nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis]. +nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. +nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. +nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis]. +nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis]. +nounif v:key; attacker(prepare_key( v ))/6211[hypothesis]. +nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. +nounif Spk:kem_sk_tmpl; + attacker(Creveal_kem_pk(Spk))/6110[conclusion]. +nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; + attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. +nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; + attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. +nounif rh:RespHello_t; + attacker(Cresp_hello( *rh ))/6107[conclusion]. +nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; + attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. + let main = rosenpass_main. @lemma "state coherence, initiator: Initiator accepting a RespHello message implies they also generated the associated InitHello message" diff --git a/analysis/02_availability.entry.mpv b/analysis/02_availability.entry.mpv index 6d05e9a..1d0807c 100644 --- a/analysis/02_availability.entry.mpv +++ b/analysis/02_availability.entry.mpv @@ -10,6 +10,26 @@ let main = rosenpass_main. +nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis]. +nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. +nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. +nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis]. +nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis]. +nounif v:key; attacker(prepare_key( v ))/6211[hypothesis]. +nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. +nounif Spk:kem_sk_tmpl; + attacker(Creveal_kem_pk(Spk))/6110[conclusion]. +nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; + attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. +nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; + attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. +nounif rh:RespHello_t; + attacker(Cresp_hello( *rh ))/6107[conclusion]. +nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; + attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. + + @lemma "non-interruptability: Adv cannot prevent a genuine InitHello message from being accepted" lemma ih:InitHello_t, psk:key, sski:kem_sk, sskr:kem_sk; event(IHRjct(ih, psk, sskr, kem_pub(sski))) diff --git a/analysis/04_dos_protection.entry.mpv b/analysis/04_dos_protection.entry.mpv new file mode 100644 index 0000000..e436577 --- /dev/null +++ b/analysis/04_dos_protection.entry.mpv @@ -0,0 +1,136 @@ +#define CHAINING_KEY_EVENTS 1 +#define MESSAGE_TRANSMISSION_EVENTS 0 +#define SESSION_START_EVENTS 0 +#define RANDOMIZED_CALL_IDS 0 +#define COOKIE_EVENTS 1 +#define KEM_EVENTS 1 + +#include "config.mpv" +#include "prelude/basic.mpv" +#include "crypto/key.mpv" +#include "crypto/kem.mpv" +#include "rosenpass/handshake_state.mpv" + +/* The cookie data structure is implemented based on the WireGuard protocol. + * The ip and port is based purely on the public key and the implementation of the private cookie key is intended to mirror the biscuit key. + * The code tests the response to a possible DOS attack by setting up alternative branches for the protocol + * processes: Oinit_conf, Oinit_hello and resp_hello to simulate what happens when the responder or initiator is overloaded. + * When under heavy load a valid cookie is required. When such a cookie is not present a cookie message is sent as a response. + * Queries then test to make sure that expensive KEM operations are only conducted after a cookie has been successfully validated. + */ + +type CookieMsg_t. +fun CookieMsg( + SessionId, // sender + bits, // nonce + bits // cookie +) : CookieMsg_t [data]. + +#define COOKIE_EVENTS(eventLbl) \ + COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (SessionId, SessionId, Atom).) \ + COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (SessionId, SessionId, Atom).) \ + COOKIE_EV(event MCAT(eventLbl, _CookieSent) (SessionId, SessionId, Atom, CookieMsg_t).) + +fun cookie_key(kem_sk) : key [private]. +fun ip_and_port(kem_pk):bits. +letfun create_mac2_key(sskm:kem_sk, spkt:kem_pk) = prf(cookie_key(sskm), ip_and_port(spkt)). +letfun create_cookie(sskm:kem_sk, spkm:kem_pk, spkt:kem_pk, nonce:bits, msg:bits) = xaead_enc(lprf2(COOKIE, kem_pk2b(spkm), nonce), + k2b(create_mac2_key(sskm, spkm)), msg). + +#define COOKIE_PROCESS(eventLbl, innerFunc) \ + new nonce:bits; \ + in(C, Ccookie(mac1, mac2)); \ + COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (sidi, sidr, call);) \ + msgB <- Envelope(mac1, msg); \ + mac2_key <- create_mac2_key(sskm, spkt); \ + if k2b(create_mac2(mac2_key, msgB)) = mac2 then \ + COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (sidi, sidr, call);) \ + innerFunc \ + else \ + cookie <- create_cookie(sskm, spkm, spkt, nonce, msg); \ + cookie_msg <- CookieMsg(sidi, nonce, cookie); \ + COOKIE_EV(event MCAT(eventLbl, _CookieSent) (sidi, sidr, call, cookie_msg);) \ + out(C, cookie_msg). \ + +#include "rosenpass/oracles.mpv" + +#include "rosenpass/responder.macro" +COOKIE_EVENTS(Oinit_conf) +let Oinit_conf_underLoad() = + in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic)); + in(C, last_cookie:bits); + + msg <- IC2b(ic); + let InitConf(sidi, sidr, biscuit, auth) = ic in + + new call:Atom; + + SETUP_HANDSHAKE_STATE() + + COOKIE_PROCESS(Oinit_conf, Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic, call)) + +#include "rosenpass/responder.macro" +COOKIE_EVENTS(Oinit_hello) +let Oinit_hello_underLoad() = + + in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih)); + in(C, Oinit_hello_last_cookie:key); + new call:Atom; + + msg <- IH2b(ih); + let InitHello(sidi, epki, sctr, pidic, auth) = ih in + SETUP_HANDSHAKE_STATE() + + COOKIE_PROCESS(Oinit_hello, Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, Oinit_hello_last_cookie, C, call)) + +let rosenpass_dos_main() = 0 + | !Oreveal_kem_pk + | REP(INITIATOR_BOUND, Oinitiator) + | REP(RESPONDER_BOUND, Oinit_hello) + | REP(RESPONDER_BOUND, Oinit_conf) + | REP(RESPONDER_BOUND, Oinit_hello_underLoad) + | REP(RESPONDER_BOUND, Oinit_conf_underLoad). + +let main = rosenpass_dos_main. + +select cookie:CookieMsg_t; attacker(cookie)/6220[hypothesis]. +nounif v:key; attacker(prepare_key( v ))/6217[hypothesis]. +nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. +nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. +nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. + +// nounif Spk:kem_sk_tmpl; +// attacker(Creveal_kem_pk(Spk))/6110[conclusion]. +// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; +// attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. +// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; +// attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. +nounif rh:RespHello_t; + attacker(Cresp_hello( *rh ))/6107[conclusion]. +nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; + attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. + +@reachable "DOS protection: cookie sent" +query sidi:SessionId, sidr:SessionId, call:Atom, cookieMsg:CookieMsg_t; + event (Oinit_hello_CookieSent(sidi, sidr, call, cookieMsg)). + +@lemma "DOS protection: Oinit_hello kem use when under load implies validated cookie" +lemma sidi:SessionId, sidr:SessionId, call:Atom; +event(Oinit_hello_UnderLoadEV(sidi, sidr, call)) + && event(Oinit_hello_KemUse(sidi, sidr, call)) + ==> event(Oinit_hello_CookieValidated(sidi, sidr, call)). + +@lemma "DOS protection: Oinit_conf kem use when under load implies validated cookie" +lemma sidi:SessionId, sidr:SessionId, call:Atom; +event(Oinit_conf_UnderLoadEV(sidi, sidr, call)) + && event(Oinit_conf_KemUse(sidi, sidr, call)) + ==> event(Oinit_conf_CookieValidated(sidi, sidr, call)). + +@lemma "DOS protection: Oresp_hello kem use when under load implies validated cookie" +lemma sidi:SessionId, sidr:SessionId, call:Atom; +event(Oresp_hello_UnderLoadEV(sidi, sidr, call)) + && event(Oresp_hello_KemUse(sidi, sidr, call)) + ==> event(Oresp_hello_CookieValidated(sidi, sidr, call)). + diff --git a/analysis/config.mpv b/analysis/config.mpv index 5fed8df..4f7deea 100644 --- a/analysis/config.mpv +++ b/analysis/config.mpv @@ -88,6 +88,18 @@ set verboseCompleted=VERBOSE. #define SES_EV(...) #endif +#if COOKIE_EVENTS +#define COOKIE_EV(...) __VA_ARGS__ +#else +#define COOKIE_EV(...) +#endif + +#if KEM_EVENTS +#define KEM_EV(...) __VA_ARGS__ +#else +#define KEM_EV(...) +#endif + (* TODO: Authentication timing properties *) (* TODO: Proof that every adversary submitted package is equivalent to one generated by the proper algorithm using different coins. This probably requires introducing an oracle that extracts the coins used and explicitly adding the notion of coins used for Packet->Packet steps and an inductive RNG notion. *) diff --git a/analysis/rosenpass/cookie.mpv b/analysis/rosenpass/cookie.mpv new file mode 100644 index 0000000..d28fdef --- /dev/null +++ b/analysis/rosenpass/cookie.mpv @@ -0,0 +1,36 @@ + +fun cookie_key(kem_sk) : key [private]. +fun ip_and_port(kem_pk):bits. +letfun create_mac2_key(sskm:kem_sk, spkt:kem_pk) = prf(cookie_key(sskm), ip_and_port(spkt)). + +letfun create_cookie(sskm:kem_sk, spkm:kem_pk, spkt:kem_pk, nonce:bits, msg:bits) = xaead_enc(lprf2(COOKIE, kem_pk2b(spkm), nonce), + k2b(create_mac2_key(sskm, spkm)), msg). + +type CookieMsg_t. +fun CookieMsg( + SessionId, // sender + bits, // nonce + bits // cookie +) : CookieMsg_t [data]. + + +#define COOKIE_PROCESS(eventLbl, innerFunc) \ + in(C, Ccookie(mac1, mac2)); \ + COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (spkm, spkt, last_cookie);) \ + msgB <- Envelope(mac1, RH2b(rh)); \ + mac2_key <- create_mac2_key(sskm, spkt) \ + let RespHello(sidi, sidr, ecti, scti, biscuit, auth) = rh in \ + if Envelope(mac2_key, msgB) = mac2 then \ + COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (spkm, last_cookie);) \ + innerFunc \ + else \ + new nonce:bits; \ + cookie <- create_cookie(sskm, spkm, spkt, nonce, msg) \ + cookie_msg <- CookieMsg(sidi, nonce, cookie); \ + COOKIE_EV(event MCAT(eventLbl, _CookieSent) (spkm, cookie, cookie_k, cookie_msg);) \ + out(C, cookie_msg). + +#define COOKIE_EVENTS(eventLbl) \ + COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (kem_pk, kem_pk, bits).) \ + COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (kem_pk, bits, key, CookieMsg_t).) \ + COOKIE_EV(event MCAT(eventLbl, _CookieSent) (kem_pk, bits).) diff --git a/analysis/rosenpass/oracles.mpv b/analysis/rosenpass/oracles.mpv index 9212ac7..17421c9 100644 --- a/analysis/rosenpass/oracles.mpv +++ b/analysis/rosenpass/oracles.mpv @@ -41,25 +41,32 @@ restriction s:seed, p1:Atom, p2:Atom, ad1:Atom, ad2:Atom; event(ConsumeSeed(p1, s, ad1)) && event(ConsumeSeed(p2, s, ad2)) ==> p1 = p2 && ad1 = ad2. +letfun create_mac2(k:key, msg:bits) = prf(k,msg). + #include "rosenpass/responder.macro" fun Cinit_conf(kem_sk_tmpl, key_tmpl, kem_pk_tmpl, InitConf_t) : Atom [data]. CK_EV( event OskOinit_conf(key, key). ) MTX_EV( event ICRjct(InitConf_t, key, kem_sk, kem_pk). ) SES_EV( event ResponderSession(InitConf_t, key). ) +KEM_EV(event Oinit_conf_KemUse(SessionId, SessionId, Atom).) +#ifdef KEM_EVENTS + restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom; + event(Oinit_conf_KemUse(sidi, sidr, ad1)) && event(Oinit_conf_KemUse(sidi, sidr, ad2)) + ==> ad1 = ad2. +#endif event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom). -let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t) = -#if RANDOMIZED_CALL_IDS - new call:Atom; -#else - call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic); -#endif +fun Ccookie(key, bits) : Atom[data]. + +let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t, call:Atom) = SETUP_HANDSHAKE_STATE() eski <- kem_sk0; epki <- kem_pk0; let try_ = ( + let InitConf(sidi, sidr, biscuit, auth) = ic in + KEM_EV(event Oinit_conf_KemUse(sidi, sidr, call);) INITCONF_CONSUME() event ConsumeBiscuit(biscuit_no, sskm, spkt, call); CK_EV( event OskOinit_conf(ck_rh, osk); ) @@ -76,13 +83,19 @@ let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:Ini ). let Oinit_conf() = + in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic)); - Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic). + #if RANDOMIZED_CALL_IDS + new call:Atom; + #else + call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic); + #endif + + Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic, call). restriction biscuit_no:Atom, sskm:kem_sk, spkr:kem_pk, ad1:Atom, ad2:Atom; event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad1)) && event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad2)) ==> ad1 = ad2. - // TODO: Restriction biscuit no invalidation #include "rosenpass/initiator.macro" @@ -91,27 +104,56 @@ CK_EV( event OskOresp_hello(key, key, key). ) MTX_EV( event RHRjct(RespHello_t, key, kem_sk, kem_pk). ) MTX_EV( event ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). ) SES_EV( event InitiatorSession(RespHello_t, key). ) -let Oresp_hello(HS_DECL_ARGS, C_in:channel) = - in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth))); - rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth); - /* try */ let ic = ( - ck_ini <- ck; - RESPHELLO_CONSUME() - ck_ih <- ck; - INITCONF_PRODUCE() - CK_EV (event OskOresp_hello(ck_ini, ck_ih, osk); ) // TODO: Queries testing that there is no duplication - MTX_EV( event ICSent(rh, ic, psk, sski, spkr); ) - SES_EV( event InitiatorSession(rh, osk); ) - ic - /* success */ ) in ( - out(C_in, Envelope(create_mac(spkt, IC2b(ic)), IC2b(ic))) - /* fail */ ) else ( -#if MESSAGE_TRANSMISSION_EVENTS - event RHRjct(rh, psk, sski, spkr) -#else - 0 + +KEM_EV(event Oresp_hello_KemUse(SessionId, SessionId, Atom).) +#ifdef KEM_EVENTS +restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom; + event(Oresp_hello_KemUse(sidi, sidr, ad1)) && event(Oresp_hello_KemUse(sidi, sidr, ad2)) + ==> ad1 = ad2. +#endif + +#ifdef COOKIE_EVENTS +COOKIE_EVENTS(Oresp_hello) +#endif +let Oresp_hello(HS_DECL_ARGS, C_in:channel, call:Atom) = + in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth))); + in(C_in, mac2_key:key); + rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth); + #ifdef COOKIE_EVENTS + msg <- RH2b(rh); + + COOKIE_PROCESS(Oresp_hello, + #endif + /* try */ let ic = ( + ck_ini <- ck; + KEM_EV(event Oresp_hello_KemUse(sidi, sidr, call);) + RESPHELLO_CONSUME() + ck_ih <- ck; + INITCONF_PRODUCE() + CK_EV (event OskOresp_hello(ck_ini, ck_ih, osk); ) // TODO: Queries testing that there is no duplication + MTX_EV( event ICSent(rh, ic, psk, sski, spkr); ) + SES_EV( event InitiatorSession(rh, osk); ) + ic + /* success */ ) in ( + icbits <- IC2b(ic); + mac <- create_mac(spkt, icbits); + mac2 <- create_mac2(mac2_key, mac_envelope2b(mac)); + out(C_in, ic); + out(C_in, mac); + out(C_in, mac2) + + /* fail */ ) else ( + #if MESSAGE_TRANSMISSION_EVENTS + event RHRjct(rh, psk, sski, spkr) + #else + 0 + #endif + ) +#ifdef COOKIE_EVENTS + ) +#else + . #endif - ). // TODO: Restriction: Biscuit no invalidation @@ -122,13 +164,15 @@ MTX_EV( event IHRjct(InitHello_t, key, kem_sk, kem_pk). ) MTX_EV( event RHSent(InitHello_t, RespHello_t, key, kem_sk, kem_pk). ) event ConsumeSidr(SessionId, Atom). event ConsumeBn(Atom, kem_sk, kem_pk, Atom). +KEM_EV(event Oinit_hello_KemUse(SessionId, SessionId, Atom).) -let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt: kem_sk_tmpl, Septi: seed_tmpl, Sspti: seed_tmpl, ih: InitHello_t, C_out:channel) = -#if RANDOMIZED_CALL_IDS - new call:Atom; -#else - call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih); +#ifdef KEM_EVENTS +restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom; + event(Oinit_hello_KemUse(sidi, sidr, ad1)) && event(Oinit_hello_KemUse(sidi, sidr, ad2)) + ==> ad1 = ad2. #endif + +let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt: kem_sk_tmpl, Septi: seed_tmpl, Sspti: seed_tmpl, ih: InitHello_t, mac2_key:key, C_out:channel, call:Atom) = // TODO: This is ugly let InitHello(sidi, epki, sctr, pidiC, auth) = ih in @@ -143,8 +187,10 @@ let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:k spti <- rng_key(setup_seed(Sspti)); // RHR5 event ConsumeSeed(Epti, setup_seed(Septi), call); event ConsumeSeed(Spti, setup_seed(Sspti), call); + // out(C_out, spkt); let rh = ( + KEM_EV(event Oinit_hello_KemUse(sidi, sidr, call);) INITHELLO_CONSUME() ck_ini <- ck; RESPHELLO_PRODUCE() @@ -152,7 +198,13 @@ let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:k MTX_EV( event RHSent(ih, rh, psk, sskr, spki); ) rh /* success */ ) in ( - out(C_out, Envelope(create_mac(spkt, RH2b(rh)), RH2b(rh))) + rhbits <- RH2b(rh); + mac <- create_mac(spkt, rhbits); + + out(C_out, rh); + out(C_out, mac); + mac2 <- create_mac2(mac2_key, mac_envelope2b(mac)); + out(C_out, mac2) /* fail */ ) else ( #if MESSAGE_TRANSMISSION_EVENTS @@ -164,7 +216,15 @@ let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:k let Oinit_hello() = in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih)); - Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, C). + in(C, mac2_key:key); + + #if RANDOMIZED_CALL_IDS + new call:Atom; + #else + call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih); + #endif + + Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, mac2_key, C, call). restriction sid:SessionId, ad1:Atom, ad2:Atom; event(ConsumeSidr(sid, ad1)) && event(ConsumeSidr(sid, ad2)) @@ -182,19 +242,22 @@ fun Cinitiator(SessionId, kem_sk_tmpl, key_tmpl, kem_pk_tmpl, seed_tmpl, seed_tm CK_EV( event OskOinitiator_ck(key). ) CK_EV( event OskOinitiator(key, key, kem_sk, kem_pk, key). ) MTX_EV( event IHSent(InitHello_t, key, kem_sk, kem_pk). ) +KEM_EV(event Oinitiator_inner_KemUse(SessionId, SessionId, Atom).) + +#ifdef KEM_EVENTS +restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom; + event(Oinitiator_inner_KemUse(sidi, sidr, ad1)) && event(Oinitiator_inner_KemUse(sidi, sidr, ad2)) + ==> ad1 = ad2. +#endif event ConsumeSidi(SessionId, Atom). -let Oinitiator_inner(sidi: SessionId, Ssskm: kem_sk_tmpl, Spsk: key_tmpl, Sspkt: kem_sk_tmpl, Seski: seed_tmpl, Ssptr: seed_tmpl, C_out:channel) = - #if RANDOMIZED_CALL_IDS - new call:Atom; - #else - call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr); - #endif +let Oinitiator_inner(sidi: SessionId, Ssskm: kem_sk_tmpl, Spsk: key_tmpl, Sspkt: kem_sk_tmpl, Seski: seed_tmpl, Ssptr: seed_tmpl, last_cookie:key, C_out:channel, call:Atom) = SETUP_HANDSHAKE_STATE() - sidr <- sid0; + KEM_EV(event Oinitiator_inner_KemUse(sidi, sidr, call);) + RNG_KEM_PAIR(eski, epki, Seski) // IHI3 sptr <- rng_key(setup_seed(Ssptr)); // IHI5 event ConsumeSidi(sidi, call); @@ -205,12 +268,29 @@ let Oinitiator_inner(sidi: SessionId, Ssskm: kem_sk_tmpl, Spsk: key_tmpl, Sspkt: CK_EV( event OskOinitiator_ck(ck); ) CK_EV( event OskOinitiator(ck, psk, sski, spkr, sptr); ) MTX_EV( event IHSent(ih, psk, sski, spkr); ) - out(C_out, Envelope(create_mac(spkt, IH2b(ih)), IH2b(ih))); - Oresp_hello(HS_PASS_ARGS, C_out). + + out(C_out, ih); + ihbits <- IH2b(ih); + mac <- create_mac(spkt, ihbits); + out(C_out, mac); + mac2 <- create_mac2(last_cookie, mac_envelope2b(mac)); + out(C_out, mac2); + + Oresp_hello(HS_PASS_ARGS, C_out, call). let Oinitiator() = + in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr)); - Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, C). + + #if RANDOMIZED_CALL_IDS + new call:Atom; + #else + call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr); + #endif + + in(C, last_cookie:key); + Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, last_cookie, C, call). + restriction sid:SessionId, ad1:Atom, ad2:Atom; event(ConsumeSidi(sid, ad1)) && event(ConsumeSidi(sid, ad2)) @@ -231,21 +311,3 @@ let rosenpass_main() = 0 | REP(RESPONDER_BOUND, Oinit_hello) | REP(RESPONDER_BOUND, Oinit_conf). -nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis]. -nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. -nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. -nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. -nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis]. -nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis]. -nounif v:key; attacker(prepare_key( v ))/6211[hypothesis]. -nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. -nounif Spk:kem_sk_tmpl; - attacker(Creveal_kem_pk(Spk))/6110[conclusion]. -nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; - attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. -nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; - attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. -nounif rh:RespHello_t; - attacker(Cresp_hello( *rh ))/6107[conclusion]. -nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; - attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. diff --git a/analysis/rosenpass/protocol.mpv b/analysis/rosenpass/protocol.mpv index a900981..7bf2f1f 100644 --- a/analysis/rosenpass/protocol.mpv +++ b/analysis/rosenpass/protocol.mpv @@ -6,7 +6,21 @@ fun Envelope( key, bits ): bits [data]. -letfun create_mac(pk:kem_pk, payload:bits) = lprf2(MAC, kem_pk2b(pk), payload). + +type mac_envelope_t. +fun mac_envelope( + key, + bits +) : mac_envelope_t. + +fun mac_envelope2b(mac_envelope_t) : bits [typeConverter]. + +letfun create_mac(pk:kem_pk, payload:bits) = mac_envelope(lprf2(MAC, kem_pk2b(pk), payload), payload). + +fun mac_envelope_pk_test(mac_envelope_t, kem_pk) : bool + reduc forall pk:kem_pk, b:bits; + mac_envelope_pk_test(mac_envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(pk)), + b), b), pk) = true. type InitHello_t. fun InitHello( @@ -85,7 +99,6 @@ fun IC2b(InitConf_t) : bitstring [typeConverter]. ic <- InitConf(sidi, sidr, biscuit, auth); #define INITCONF_CONSUME() \ - let InitConf(sidi, sidr, biscuit, auth) = ic in \ LOAD_BISCUIT(biscuit_no, biscuit) /* ICR1 */ \ ENCRYPT_AND_MIX(rh_auth, empty) /* ICIR */ \ ck_rh <- ck; /* ---- */ /* TODO: Move into oracles.mpv */ \ From 8c469af6b14c6503a418dde236f856ff59492411 Mon Sep 17 00:00:00 2001 From: James Brownlee Date: Wed, 7 Feb 2024 16:40:57 -0500 Subject: [PATCH 06/19] adding identity hiding improvements: seperate files for responder and initiator tests test file that shows other participants leaking info has an effect general code clean up performance improvement: initiator and responder tests now run in ~10s --- .../03_identity_hiding_initiator.entry.mpv | 25 +++++ .../03_identity_hiding_responder.entry.mpv | 96 +++++++++++++++++++ analysis/03_identity_hiding_test.entry.mpv | 29 ++++++ .../03_identity_hiding.mpv} | 84 +++++++++++----- 4 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 analysis/03_identity_hiding_initiator.entry.mpv create mode 100644 analysis/03_identity_hiding_responder.entry.mpv create mode 100644 analysis/03_identity_hiding_test.entry.mpv rename analysis/{03_identity_hiding.entry.mpv => rosenpass/03_identity_hiding.mpv} (65%) diff --git a/analysis/03_identity_hiding_initiator.entry.mpv b/analysis/03_identity_hiding_initiator.entry.mpv new file mode 100644 index 0000000..2d63043 --- /dev/null +++ b/analysis/03_identity_hiding_initiator.entry.mpv @@ -0,0 +1,25 @@ +#define INITIATOR_TEST 1 + +#include "rosenpass/03_identity_hiding.mpv" + +// nounif a:Atom, s:seed, a2:Atom; +// ConsumeSeed(a, s, a2) / 6300[conclusion]. + +nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis]. +nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. +nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. +nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis]. +nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis]. +nounif v:key; attacker(prepare_key( v ))/6211[hypothesis]. +nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. +nounif Spk:kem_sk_tmpl; + attacker(Creveal_kem_pk(Spk))/6110[conclusion]. +nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; + attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion]. +nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; + attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion]. +nounif rh:RespHello_t; + attacker(Cresp_hello( *rh ))/6107[conclusion]. +nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; + attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion]. diff --git a/analysis/03_identity_hiding_responder.entry.mpv b/analysis/03_identity_hiding_responder.entry.mpv new file mode 100644 index 0000000..5908a72 --- /dev/null +++ b/analysis/03_identity_hiding_responder.entry.mpv @@ -0,0 +1,96 @@ +#define RESPONDER_TEST 1 + +#include "rosenpass/03_identity_hiding.mpv" + +// select k:kem_pk,ih: InitHello_t; attacker(prf(prf(prf(prf(key0, PROTOCOL), MAC), kem_pk2b(k) ), IH2b(ih))) phase 1/6300[hypothesis]. + +// select epki:kem_pk, sctr:bits, pidiC:bits, auth:bits, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits; +// mess(D, prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder1)))), +// IH2b(InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth))) +// ) [hypothesis, conclusion]. + +// select epki:kem_pk, sctr:bits, pidiC:bits, auth:bits, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits; +// attacker(choice[prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder1)))), +// IH2b(InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth))), + +// prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder2)))), +// IH2b(InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2)))] +// ) [hypothesis, conclusion]. + +// select +// attacker(prf(prf(key0,PROTOCOL),MAC)) [hypothesis, conclusion]. + +// select +// attacker(prf(key0,PROTOCOL)) [conclusion]. + +// select +// attacker(key0) [conclusion]. + +// select +// attacker(PROTOCOL) [conclusion]. + +// select +// attacker(kem_pub(trusted_kem_sk(responder1))) /9999 [hypothesis, conclusion]. + +// select +// attacker(kem_pub(trusted_kem_sk(responder2))) /9999 [hypothesis, conclusion]. + +// nounif ih:InitHello_t; +// attacker(ih) / 9999 [hypothesis]. + +// nounif rh:RespHello_t; +// attacker(rh) / 9999 [hypothesis]. + +// nounif ic:InitConf_t; +// attacker(ic) / 9999 [hypothesis]. + +// nounif k:key; +// attacker(ck_hs_enc( *k )) [hypothesis, conclusion]. + +// nounif k:key; +// attacker(ck_hs_enc( *k )) phase 1 [hypothesis, conclusion]. + +// nounif k:key, b:bits; +// attacker(ck_mix( *k , *b )) [hypothesis, conclusion]. + +// nounif k:key, b:bits; +// attacker(ck_mix( *k , *b ))phase 1 [hypothesis, conclusion]. + +// // select k:kem_pk, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits, epki:kem_pk, sctr:bits, pidiC:bits, auth:bits; +// // attacker(choice[Envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pub(trusted_kem_sk(responder1))), +// // InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2) +// // ), InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2)) +// // Envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pub(trusted_kem_sk(responder2))), +// // InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth)), +// // InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth)) +// // ]) / 9999[hypothesis, conclusion]. + +// nounif k:key, b1:bits, b2:bits; +// attacker(xaead_enc( *k, *b1, *b2)) / 9999[hypothesis,conclusion]. + +// nounif pk:kem_pk, k:key; +// attacker(kem_enc( *pk , *k )) / 9999[hypothesis,conclusion]. + +// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; +// attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/9999[hypothesis, conclusion]. +// nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; +// attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/9999[hypothesis, conclusion]. +// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; +// attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr )) /9999 [hypothesis, conclusion]. + +// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t; +// mess(C, Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/9999[hypothesis, conclusion]. +// nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t; +// mess(C, Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/9999[hypothesis, conclusion]. +// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl; +// mess(C, Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr )) /9999 [hypothesis, conclusion]. +// nounif rh:RespHello_t; +// attacker(Cresp_hello( *rh ))[conclusion]. +// nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis]. +// nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis]. +// nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis]. +// nounif v:seed; attacker(rng_key( v ))/6214[hypothesis]. +// nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis]. +// nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis]. +// nounif v:key; attacker(prepare_key( v ))/6211[hypothesis]. +// nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis]. diff --git a/analysis/03_identity_hiding_test.entry.mpv b/analysis/03_identity_hiding_test.entry.mpv new file mode 100644 index 0000000..1c7afc5 --- /dev/null +++ b/analysis/03_identity_hiding_test.entry.mpv @@ -0,0 +1,29 @@ +#define INITIATOR_TEST 1 +#define CUSTOM_MAIN 1 + +#include "rosenpass/03_identity_hiding.mpv" + +let Oinitiator_bad_actor_inner(sk_tmp:kem_sk_prec) = + + in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr)); + + #if RANDOMIZED_CALL_IDS + new call:Atom; + #else + call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr); + #endif + + in(C, last_cookie:key); + tmpl <- make_trusted_kem_sk(sk_tmp); + out(C, setup_kem_sk(tmpl)); + Oinitiator_inner(sidi, Ssskm, Spsk, tmpl, Seski, Ssptr, last_cookie, C, call). + +let Oinitiator_bad_actor() = + Oinitiator_bad_actor_inner(responder1) | Oinitiator_bad_actor_inner(responder2) | Oinitiator_bad_actor_inner(initiator1) | Oinitiator_bad_actor_inner(initiator2). + + +let identity_hiding_main2() = + 0 | Oinitiator_bad_actor() | rosenpass_main2() | participants_communication() | phase 1; secretCommunication(). + + +let main = identity_hiding_main2. diff --git a/analysis/03_identity_hiding.entry.mpv b/analysis/rosenpass/03_identity_hiding.mpv similarity index 65% rename from analysis/03_identity_hiding.entry.mpv rename to analysis/rosenpass/03_identity_hiding.mpv index 985fda0..112ed6c 100644 --- a/analysis/03_identity_hiding.entry.mpv +++ b/analysis/rosenpass/03_identity_hiding.mpv @@ -28,7 +28,6 @@ In this case the test uses secure rng and a fresh secure biscuit key. */ - #include "config.mpv" #define CHAINING_KEY_EVENTS 1 @@ -44,7 +43,6 @@ #include "rosenpass/oracles.mpv" #include "crypto/kem.mpv" -#define INITIATOR_TEST #define NEW_TRUSTED_SEED(name) \ new MCAT(name, _secret_seed):seed_prec; \ name <- make_trusted_seed(MCAT(name, _secret_seed)); \ @@ -57,52 +55,86 @@ free initiator1, initiator2:kem_sk_prec. free responder1, responder2:kem_sk_prec. let secure_init_hello(initiator: kem_sk_tmpl, sidi : SessionId, psk: key_tmpl, responder: kem_sk_tmpl) = + + new epkit:kem_pk; // epki + new sctrt:bits; // sctr + new pidiCt:bits; // pidiC + new autht:bits; // auth + NEW_TRUSTED_SEED(seski_trusted_seed) NEW_TRUSTED_SEED(ssptr_trusted_seed) - Oinitiator_inner(sidi, initiator, psk, responder, seski_trusted_seed, ssptr_trusted_seed, D). + new last_cookie:key; + new call:Atom; + + Oinitiator_inner(sidi, initiator, psk, responder, seski_trusted_seed, ssptr_trusted_seed, last_cookie, D, call). + +let secure_resp_hello(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, sidi:SessionId, sidr:SessionId, biscuit_no:Atom, psk:key_tmpl) = + + in(D, InitHello(=secure_sidi, epki, sctr, pidiC, auth)); -let secure_resp_hello(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, sidr:SessionId, sidi:SessionId, biscuit_no:Atom, psk:key_tmpl) = - in(D, Envelope(k, IH2b(InitHello(=sidi, epki, sctr, pidiC, auth)))); ih <- InitHello(sidi, epki, sctr, pidiC, auth); NEW_TRUSTED_SEED(septi_trusted_seed) NEW_TRUSTED_SEED(sspti_trusted_seed) - Oinit_hello_inner(sidr, biscuit_no, responder, psk, initiator, septi_trusted_seed, sspti_trusted_seed, ih, D). + new last_cookie:key; + new call:Atom; + + Oinit_hello_inner(sidr, biscuit_no, responder, psk, initiator, septi_trusted_seed, sspti_trusted_seed, ih, last_cookie, D, call). let secure_init_conf(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, psk:key_tmpl, sidi:SessionId, sidr:SessionId) = - in(D, Envelope(k3, IC2b(InitConf(=sidi, =sidr, biscuit, auth3)))); + in(D, InitConf(=sidi, =sidr, biscuit, auth3)); ic <- InitConf(sidi,sidr,biscuit, auth3); NEW_TRUSTED_SEED(seski_trusted_seed) NEW_TRUSTED_SEED(ssptr_trusted_seed) - Oinit_conf_inner(initiator, psk, responder, ic). + new last_cookie:key; + call <- Cinit_conf(initiator, psk, responder, ic); -let secure_communication(initiator: kem_sk_tmpl, responder:kem_sk_tmpl) = - secure_key <- prepare_key(secure_psk); - (!secure_init_hello(initiator, secure_sidi, secure_key, responder)) - | !secure_resp_hello(initiator, responder, secure_sidr, secure_sidi, secure_biscuit_no, secure_key) - | !(secure_init_conf(initiator, responder, secure_key, secure_sidi, secure_sidr)). + Oinit_conf_inner(initiator, psk, responder, ic, call). + +let secure_communication(initiator: kem_sk_tmpl, responder:kem_sk_tmpl, key:key) = + key_tmpl <- prepare_key(key); + (!secure_init_hello(initiator, secure_sidi, key_tmpl, responder)) + | !secure_resp_hello(initiator, responder, secure_sidi, secure_sidr, secure_biscuit_no, key_tmpl) + | !(secure_init_conf(initiator, responder, key_tmpl, secure_sidi, secure_sidr)). + +let participant_communication_initiator(participant:kem_sk_tmpl) = + in(C, responder:kem_sk_tmpl); + in(C, k:key); + secure_communication(participant, responder, k). + +let participant_communication_responder(participant:kem_sk_tmpl) = + in(C, initiator:kem_sk_tmpl); + in(C, k:key); + secure_communication(initiator, participant, k). + +let participants_communication() = + initiator1_tmpl <- make_trusted_kem_sk(initiator1); + initiator2_tmpl <- make_trusted_kem_sk(initiator2); + responder1_tmpl <- make_trusted_kem_sk(responder1); + responder2_tmpl <- make_trusted_kem_sk(responder2); + + !participant_communication_initiator(initiator1_tmpl) | !participant_communication_responder(initiator1_tmpl) + | !participant_communication_initiator(initiator2_tmpl) | !participant_communication_responder(initiator2_tmpl) + | !participant_communication_initiator(responder1_tmpl) | !participant_communication_responder(responder1_tmpl) + | !participant_communication_initiator(responder2_tmpl) | !participant_communication_responder(responder2_tmpl). let pipeChannel(D:channel, C:channel) = in(D, b:bits); out(C, b). -fun kem_private(kem_pk): kem_sk - reduc forall sk_tmpl:kem_sk; - kem_private(kem_pub(sk_tmpl)) = sk_tmpl[private]. - let secretCommunication() = + #ifdef INITIATOR_TEST - initiator_pk <- choice[setup_kem_pk(make_trusted_kem_sk(initiator1)), setup_kem_pk(make_trusted_kem_sk(initiator2))]; - initiator_seed <- prepare_kem_sk(kem_private(initiator_pk)); + initiator_seed <- choice[make_trusted_kem_sk(initiator1), make_trusted_kem_sk(initiator2)]; #else - initiator_seed <- prepare_kem_sk(trusted_kem_sk(initiator1)); + initiator_seed <- make_trusted_kem_sk(initiator1); #endif #ifdef RESPONDER_TEST - responder_pk <- choice[setup_kem_pk(make_trusted_kem_sk(responder1)), setup_kem_pk(make_trusted_kem_sk(responder2))]; - responder_seed <- prepare_kem_sk(kem_private(responder_pk)); + responder_seed <- choice[make_trusted_kem_sk(responder1), make_trusted_kem_sk(responder2)]; #else - responder_seed <- prepare_kem_sk(trusted_kem_sk(responder1)); + responder_seed <- make_trusted_kem_sk(responder1); #endif - secure_communication(initiator_seed, responder_seed) | !pipeChannel(D, C). + + secure_communication(initiator_seed, responder_seed, secure_psk) | !pipeChannel(D, C). let reveal_pks() = out(C, setup_kem_pk(make_trusted_kem_sk(responder1))); @@ -116,6 +148,8 @@ let rosenpass_main2() = | REP(RESPONDER_BOUND, Oinit_conf). let identity_hiding_main() = - 0 | reveal_pks() | rosenpass_main2() | phase 1; secretCommunication(). + 0 | reveal_pks() | rosenpass_main2() | participants_communication() | phase 1; secretCommunication(). +#ifndef CUSTOM_MAIN let main = identity_hiding_main. +#endif From 36c99c020e968c9d0ac9c418317d0ab7b3ba938f Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Wed, 28 Feb 2024 12:00:24 +0100 Subject: [PATCH 07/19] implement test to statistically check constant run time of memcmp (feature: constant_time_tests) --- Cargo.lock | 1 + constant-time/Cargo.toml | 6 +++ constant-time/src/lib.rs | 89 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 657d11d..ddd651e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1168,6 +1168,7 @@ name = "rosenpass-constant-time" version = "0.1.0" dependencies = [ "memsec", + "rand", "rosenpass-to", ] diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index 497f0f4..b07e4ef 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -11,6 +11,12 @@ readme = "readme.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +constant_time_tests = [] + [dependencies] rosenpass-to = { workspace = true } memsec = { workspace = true } + +[dev-dependencies] +rand = "0.8.5" diff --git a/constant-time/src/lib.rs b/constant-time/src/lib.rs index b5ea516..1d19563 100644 --- a/constant-time/src/lib.rs +++ b/constant-time/src/lib.rs @@ -77,3 +77,92 @@ pub fn increment(v: &mut [u8]) { *black_box(&mut carry) = black_box(black_box(c) as u8); } } + +#[cfg(all(test, feature = "constant_time_tests"))] +mod constant_time_tests { + use super::*; + use rand::seq::SliceRandom; + use rand::thread_rng; + use std::time::Instant; + + #[test] + /// tests whether [memcmp] actually runs in constant time + /// + /// This test function will run an equal amount of comparisons on two different sets of parameters: + /// - completely equal slices + /// - completely unequal slices. + /// All comparisons are executed in a randomized order. The test will fail if one of the + /// two sets is checked for equality significantly faster than the other set + /// (absolute correlation coefficient ≥ 0.01) + fn memcmp_runs_in_constant_time() { + // prepare data to compare + let n: usize = 1E6 as usize; // number of comparisons to run + let len = 1024; // length of each slice passed as parameters to the tested comparison function + let a1 = "a".repeat(len); + let a2 = a1.clone(); + let b = "b".repeat(len); + + let a1 = a1.as_bytes(); + let a2 = a2.as_bytes(); + let b = b.as_bytes(); + + // vector representing all timing tests + // + // Each element is a tuple of: + // 0: whether the test compared two equal slices + // 1: the duration needed for the comparison to run + let mut tests = (0..n) + .map(|i| (i < n / 2, std::time::Duration::ZERO)) + .collect::>(); + tests.shuffle(&mut thread_rng()); + + // run comparisons / call function to test + for test in tests.iter_mut() { + let now = Instant::now(); + if test.0 { + memcmp(a1, a2); + } else { + memcmp(a1, b); + } + test.1 = now.elapsed(); + // println!("eq: {}, elapsed: {:.2?}", test.0, test.1); + } + + // sort by execution time and calculate Pearson correlation coefficient + tests.sort_by_key(|v| v.1); + let tests = tests + .iter() + .map(|t| (if t.0 { 1_f64 } else { 0_f64 }, t.1.as_nanos() as f64)) + .collect::>(); + // averages + let (avg_x, avg_y): (f64, f64) = ( + tests.iter().map(|t| t.0).sum::() / n as f64, + tests.iter().map(|t| t.1).sum::() / n as f64, + ); + assert!((avg_x - 0.5).abs() < 1E-12); + // standard deviations + let sd_x = 0.5; + let sd_y = (1_f64 / n as f64 + * tests + .iter() + .map(|t| { + let difference = t.1 - avg_y; + difference * difference + }) + .sum::()) + .sqrt(); + // covariance + let cv = 1_f64 / n as f64 + * tests + .iter() + .map(|t| (t.0 - avg_x) * (t.1 - avg_y)) + .sum::(); + // Pearson correlation + let correlation = cv / (sd_x * sd_y); + println!("correlation: {:.6?}", correlation); + assert!( + correlation.abs() < 0.01, + "execution time correlates with result" + ) + } +} From 60235dc6ea6c3f6818c72ee8e7086ae906e551c0 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Wed, 28 Feb 2024 12:01:58 +0100 Subject: [PATCH 08/19] GihHub Workflow "Quality Control": add flag "--all-features" to cargo in order to run all available tests behind feature flags --- .github/workflows/qc.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 2f9a0db..9710890 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -121,7 +121,7 @@ jobs: # liboqs requires quite a lot of stack memory, thus we adjust # the default stack size picked for new threads (which is used # by `cargo test`) to be _big enough_. Setting it to 8 MiB - - run: RUST_MIN_STACK=8388608 cargo test --workspace + - run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features cargo-test-nix-devshell-x86_64-linux: runs-on: @@ -144,7 +144,7 @@ jobs: with: name: rosenpass authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - - run: nix develop --command cargo test --workspace + - run: nix develop --command cargo test --workspace --all-features cargo-fuzz: runs-on: ubuntu-latest From 27ba729c14aa601c0fd18ef76ffa6ceb3de6d62f Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Thu, 29 Feb 2024 12:23:27 +0100 Subject: [PATCH 09/19] move each primitive into its own module; add rough documentation This commit does not change anything about the implementations. --- constant-time/src/compare.rs | 24 +++++ constant-time/src/increment.rs | 48 +++++++++ constant-time/src/lib.rs | 181 +++------------------------------ constant-time/src/memcmp.rs | 110 ++++++++++++++++++++ constant-time/src/xor.rs | 34 +++++++ 5 files changed, 231 insertions(+), 166 deletions(-) create mode 100644 constant-time/src/compare.rs create mode 100644 constant-time/src/increment.rs create mode 100644 constant-time/src/memcmp.rs create mode 100644 constant-time/src/xor.rs diff --git a/constant-time/src/compare.rs b/constant-time/src/compare.rs new file mode 100644 index 0000000..d5e3605 --- /dev/null +++ b/constant-time/src/compare.rs @@ -0,0 +1,24 @@ +/// compares two slices of memory content and returns an integer indicating the relationship between +/// the slices +/// +/// ## Returns +/// - <0 if the first byte that does not match both slices has a lower value in `a` than in `b` +/// - 0 if the contents are equal +/// - >0 if the first byte that does not match both slices has a higher value in `a` than in `b` +/// +/// ## Leaks +/// If the two slices have differents lengths, the function will return immediately. This +/// effectively leaks the information whether the slices have equal length or not. This is widely +/// considered safe. +/// +/// The execution time of the function grows approx. linear with the length of the input. This is +/// considered safe. +/// +/// ## Tests +/// For discussion on how to ensure the constant-time execution of this function, see +/// +#[inline] +pub fn compare(a: &[u8], b: &[u8]) -> i32 { + assert!(a.len() == b.len()); + unsafe { memsec::memcmp(a.as_ptr(), b.as_ptr(), a.len()) } +} diff --git a/constant-time/src/increment.rs b/constant-time/src/increment.rs new file mode 100644 index 0000000..b1f031a --- /dev/null +++ b/constant-time/src/increment.rs @@ -0,0 +1,48 @@ +use core::hint::black_box; + +/// Interpret the given slice as a little-endian unsigned integer +/// and increment that integer. +/// +/// # Leaks +/// TODO: mention here if this function leaks any information, see +/// +/// +/// ## Tests +/// For discussion on how to ensure the constant-time execution of this function, see +/// +/// +/// # Examples +/// +/// ``` +/// use rosenpass_constant_time::increment as inc; +/// use rosenpass_to::To; +/// +/// fn testcase(v: &[u8], correct: &[u8]) { +/// let mut v = v.to_owned(); +/// inc(&mut v); +/// assert_eq!(&v, correct); +/// } +/// +/// testcase(b"", b""); +/// testcase(b"\x00", b"\x01"); +/// testcase(b"\x01", b"\x02"); +/// testcase(b"\xfe", b"\xff"); +/// testcase(b"\xff", b"\x00"); +/// testcase(b"\x00\x00", b"\x01\x00"); +/// testcase(b"\x01\x00", b"\x02\x00"); +/// testcase(b"\xfe\x00", b"\xff\x00"); +/// testcase(b"\xff\x00", b"\x00\x01"); +/// testcase(b"\x00\x00\x00\x00\x00\x00", b"\x01\x00\x00\x00\x00\x00"); +/// testcase(b"\x00\xa3\x00\x77\x00\x00", b"\x01\xa3\x00\x77\x00\x00"); +/// testcase(b"\xff\xa3\x00\x77\x00\x00", b"\x00\xa4\x00\x77\x00\x00"); +/// testcase(b"\xff\xff\xff\x77\x00\x00", b"\x00\x00\x00\x78\x00\x00"); +/// ``` +#[inline] +pub fn increment(v: &mut [u8]) { + let mut carry = 1u8; + for val in v.iter_mut() { + let (v, c) = black_box(*val).overflowing_add(black_box(carry)); + *black_box(val) = v; + *black_box(&mut carry) = black_box(black_box(c) as u8); + } +} diff --git a/constant-time/src/lib.rs b/constant-time/src/lib.rs index 1d19563..275a201 100644 --- a/constant-time/src/lib.rs +++ b/constant-time/src/lib.rs @@ -1,168 +1,17 @@ -use core::hint::black_box; +//! constant-time implementations of some primitives +//! +//! Rosenpass internal library providing basic constant-time operations. +//! +//! ## TODO +//! Figure out methodology to ensure that code is actually constant time, see +//! -use rosenpass_to::{with_destination, To}; +mod compare; +mod increment; +mod memcmp; +mod xor; -/// Xors the source into the destination -/// -/// # Examples -/// -/// ``` -/// 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(src: &[u8]) -> impl To<[u8], ()> + '_ { - with_destination(|dst: &mut [u8]| { - assert!(black_box(src.len()) == black_box(dst.len())); - for (dv, sv) in dst.iter_mut().zip(src.iter()) { - *black_box(dv) ^= black_box(*sv); - } - }) -} - -#[inline] -pub fn memcmp(a: &[u8], b: &[u8]) -> bool { - a.len() == b.len() - && unsafe { memsec::memeq(a.as_ptr() as *const u8, b.as_ptr() as *const u8, a.len()) } -} - -#[inline] -pub fn compare(a: &[u8], b: &[u8]) -> i32 { - assert!(a.len() == b.len()); - unsafe { memsec::memcmp(a.as_ptr(), b.as_ptr(), a.len()) } -} - -/// Interpret the given slice as a little-endian unsigned integer -/// and increment that integer. -/// -/// # Examples -/// -/// ``` -/// use rosenpass_constant_time::increment as inc; -/// use rosenpass_to::To; -/// -/// fn testcase(v: &[u8], correct: &[u8]) { -/// let mut v = v.to_owned(); -/// inc(&mut v); -/// assert_eq!(&v, correct); -/// } -/// -/// testcase(b"", b""); -/// testcase(b"\x00", b"\x01"); -/// testcase(b"\x01", b"\x02"); -/// testcase(b"\xfe", b"\xff"); -/// testcase(b"\xff", b"\x00"); -/// testcase(b"\x00\x00", b"\x01\x00"); -/// testcase(b"\x01\x00", b"\x02\x00"); -/// testcase(b"\xfe\x00", b"\xff\x00"); -/// testcase(b"\xff\x00", b"\x00\x01"); -/// testcase(b"\x00\x00\x00\x00\x00\x00", b"\x01\x00\x00\x00\x00\x00"); -/// testcase(b"\x00\xa3\x00\x77\x00\x00", b"\x01\xa3\x00\x77\x00\x00"); -/// testcase(b"\xff\xa3\x00\x77\x00\x00", b"\x00\xa4\x00\x77\x00\x00"); -/// testcase(b"\xff\xff\xff\x77\x00\x00", b"\x00\x00\x00\x78\x00\x00"); -/// ``` -#[inline] -pub fn increment(v: &mut [u8]) { - let mut carry = 1u8; - for val in v.iter_mut() { - let (v, c) = black_box(*val).overflowing_add(black_box(carry)); - *black_box(val) = v; - *black_box(&mut carry) = black_box(black_box(c) as u8); - } -} - -#[cfg(all(test, feature = "constant_time_tests"))] -mod constant_time_tests { - use super::*; - use rand::seq::SliceRandom; - use rand::thread_rng; - use std::time::Instant; - - #[test] - /// tests whether [memcmp] actually runs in constant time - /// - /// This test function will run an equal amount of comparisons on two different sets of parameters: - /// - completely equal slices - /// - completely unequal slices. - /// All comparisons are executed in a randomized order. The test will fail if one of the - /// two sets is checked for equality significantly faster than the other set - /// (absolute correlation coefficient ≥ 0.01) - fn memcmp_runs_in_constant_time() { - // prepare data to compare - let n: usize = 1E6 as usize; // number of comparisons to run - let len = 1024; // length of each slice passed as parameters to the tested comparison function - let a1 = "a".repeat(len); - let a2 = a1.clone(); - let b = "b".repeat(len); - - let a1 = a1.as_bytes(); - let a2 = a2.as_bytes(); - let b = b.as_bytes(); - - // vector representing all timing tests - // - // Each element is a tuple of: - // 0: whether the test compared two equal slices - // 1: the duration needed for the comparison to run - let mut tests = (0..n) - .map(|i| (i < n / 2, std::time::Duration::ZERO)) - .collect::>(); - tests.shuffle(&mut thread_rng()); - - // run comparisons / call function to test - for test in tests.iter_mut() { - let now = Instant::now(); - if test.0 { - memcmp(a1, a2); - } else { - memcmp(a1, b); - } - test.1 = now.elapsed(); - // println!("eq: {}, elapsed: {:.2?}", test.0, test.1); - } - - // sort by execution time and calculate Pearson correlation coefficient - tests.sort_by_key(|v| v.1); - let tests = tests - .iter() - .map(|t| (if t.0 { 1_f64 } else { 0_f64 }, t.1.as_nanos() as f64)) - .collect::>(); - // averages - let (avg_x, avg_y): (f64, f64) = ( - tests.iter().map(|t| t.0).sum::() / n as f64, - tests.iter().map(|t| t.1).sum::() / n as f64, - ); - assert!((avg_x - 0.5).abs() < 1E-12); - // standard deviations - let sd_x = 0.5; - let sd_y = (1_f64 / n as f64 - * tests - .iter() - .map(|t| { - let difference = t.1 - avg_y; - difference * difference - }) - .sum::()) - .sqrt(); - // covariance - let cv = 1_f64 / n as f64 - * tests - .iter() - .map(|t| (t.0 - avg_x) * (t.1 - avg_y)) - .sum::(); - // Pearson correlation - let correlation = cv / (sd_x * sd_y); - println!("correlation: {:.6?}", correlation); - assert!( - correlation.abs() < 0.01, - "execution time correlates with result" - ) - } -} +pub use compare::compare; +pub use increment::increment; +pub use memcmp::memcmp; +pub use xor::xor; diff --git a/constant-time/src/memcmp.rs b/constant-time/src/memcmp.rs new file mode 100644 index 0000000..637ef11 --- /dev/null +++ b/constant-time/src/memcmp.rs @@ -0,0 +1,110 @@ +/// compares two sclices of memory content and returns whether they are equal +/// +/// ## Leaks +/// If the two slices have differents lengths, the function will return immediately. This +/// effectively leaks the information whether the slices have equal length or not. This is widely +/// considered safe. +/// +/// The execution time of the function grows approx. linear with the length of the input. This is +/// considered safe. +/// +/// ## Tests +/// [`tests::memcmp_runs_in_constant_time`] runs a stasticial test that the equality of the two +/// input parameters does not correlate with the run time. +/// +/// For discussion on how to (further) ensure the constant-time execution of this function, +/// see +#[inline] +pub fn memcmp(a: &[u8], b: &[u8]) -> bool { + a.len() == b.len() + && unsafe { memsec::memeq(a.as_ptr() as *const u8, b.as_ptr() as *const u8, a.len()) } +} + +#[cfg(all(test, feature = "constant_time_tests"))] +mod tests { + use super::*; + use rand::seq::SliceRandom; + use rand::thread_rng; + use std::time::Instant; + + #[test] + /// tests whether [memcmp] actually runs in constant time + /// + /// This test function will run an equal amount of comparisons on two different sets of parameters: + /// - completely equal slices + /// - completely unequal slices. + /// All comparisons are executed in a randomized order. The test will fail if one of the + /// two sets is checked for equality significantly faster than the other set + /// (absolute correlation coefficient ≥ 0.01) + fn memcmp_runs_in_constant_time() { + // prepare data to compare + let n: usize = 1E6 as usize; // number of comparisons to run + let len = 1024; // length of each slice passed as parameters to the tested comparison function + let a1 = "a".repeat(len); + let a2 = a1.clone(); + let b = "b".repeat(len); + + let a1 = a1.as_bytes(); + let a2 = a2.as_bytes(); + let b = b.as_bytes(); + + // vector representing all timing tests + // + // Each element is a tuple of: + // 0: whether the test compared two equal slices + // 1: the duration needed for the comparison to run + let mut tests = (0..n) + .map(|i| (i < n / 2, std::time::Duration::ZERO)) + .collect::>(); + tests.shuffle(&mut thread_rng()); + + // run comparisons / call function to test + for test in tests.iter_mut() { + let now = Instant::now(); + if test.0 { + memcmp(a1, a2); + } else { + memcmp(a1, b); + } + test.1 = now.elapsed(); + // println!("eq: {}, elapsed: {:.2?}", test.0, test.1); + } + + // sort by execution time and calculate Pearson correlation coefficient + tests.sort_by_key(|v| v.1); + let tests = tests + .iter() + .map(|t| (if t.0 { 1_f64 } else { 0_f64 }, t.1.as_nanos() as f64)) + .collect::>(); + // averages + let (avg_x, avg_y): (f64, f64) = ( + tests.iter().map(|t| t.0).sum::() / n as f64, + tests.iter().map(|t| t.1).sum::() / n as f64, + ); + assert!((avg_x - 0.5).abs() < 1E-12); + // standard deviations + let sd_x = 0.5; + let sd_y = (1_f64 / n as f64 + * tests + .iter() + .map(|t| { + let difference = t.1 - avg_y; + difference * difference + }) + .sum::()) + .sqrt(); + // covariance + let cv = 1_f64 / n as f64 + * tests + .iter() + .map(|t| (t.0 - avg_x) * (t.1 - avg_y)) + .sum::(); + // Pearson correlation + let correlation = cv / (sd_x * sd_y); + println!("correlation: {:.6?}", correlation); + assert!( + correlation.abs() < 0.01, + "execution time correlates with result" + ) + } +} diff --git a/constant-time/src/xor.rs b/constant-time/src/xor.rs new file mode 100644 index 0000000..d347d1b --- /dev/null +++ b/constant-time/src/xor.rs @@ -0,0 +1,34 @@ +use core::hint::black_box; +use rosenpass_to::{with_destination, To}; + +/// Xors the source into the destination +/// +/// # Panics +/// If source and destination are of different sizes. +/// +/// # Leaks +/// TODO: mention here if this function leaks any information, see +/// +/// +/// ## Tests +/// For discussion on how to ensure the constant-time execution of this function, see +/// +/// +/// # Examples +/// +/// ``` +/// 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"); +/// ``` +#[inline] +pub fn xor(src: &[u8]) -> impl To<[u8], ()> + '_ { + with_destination(|dst: &mut [u8]| { + assert!(black_box(src.len()) == black_box(dst.len())); + for (dv, sv) in dst.iter_mut().zip(src.iter()) { + *black_box(dv) ^= black_box(*sv); + } + }) +} From fb91688672418c8c9a5d7893b87d00c375e64009 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Thu, 29 Feb 2024 10:32:24 +0100 Subject: [PATCH 10/19] add few comments to config.rs --- rosenpass/src/config.rs | 69 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index c61c62e..fc51c18 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -1,3 +1,12 @@ +//! Configuration readable from a config file. +//! +//! Rosenpass supports reading its configuration from a TOML file. This module contains a struct +//! [`Rosenpass`] which holds such a configuration. +//! +//! ## TODO +//! - support `~` in (https://github.com/rosenpass/rosenpass/issues/237) +//! - provide tooling to create config file from shell (https://github.com/rosenpass/rosenpass/issues/247) + use std::{ collections::HashSet, fs, @@ -12,53 +21,97 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Rosenpass { + /// path to the public key file pub public_key: PathBuf, + /// path to the secret key file pub secret_key: PathBuf, + /// list of [`SocketAddr`] to listen on + /// + /// Examples: + /// - `0.0.0.0:123` pub listen: Vec, + /// log verbosity + /// + /// This is subject to change. See [`Verbosity`] for details. #[serde(default)] pub verbosity: Verbosity, + + /// list of peers + /// + /// See the [`RosenpassPeer`] type for more information and examples. pub peers: Vec, + /// path to the file which provided this configuration + /// + /// This item is of course not read from the TOML but is added by the algorithm that parses + /// the config file. #[serde(skip)] pub config_file_path: PathBuf, } +/// ## TODO +/// - replace this type with [`log::LevelFilter`], also see https://github.com/rosenpass/rosenpass/pull/246 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Verbosity { Quiet, Verbose, } +/// ## TODO +/// - examples +/// - documentation #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct RosenpassPeer { + /// path to the public key of the peer pub public_key: PathBuf, + + /// ## TODO + /// - documentation pub endpoint: Option, + + /// path to the pre-shared key with the peer + /// + /// NOTE: this item can be skipped in the config if you do not use a pre-shared key with the peer pub pre_shared_key: Option, + /// ## TODO + /// - documentation #[serde(default)] pub key_out: Option, - // TODO make this field only available on binary builds, not on library builds + /// ## TODO + /// - documentation + /// - make this field only available on binary builds, not on library builds (https://github.com/rosenpass/rosenpass/issues/249) #[serde(flatten)] pub wg: Option, } +/// ## TODO +/// - documentation #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct WireGuard { + /// ## TODO + /// - documentation pub device: String, + + /// ## TODO + /// - documentation pub peer: String, + /// ## TODO + /// - documentation #[serde(default)] pub extra_params: Vec, } impl Rosenpass { - /// Load a config file from a file path + /// load configuration from a TOML file /// - /// no validation is conducted + /// NOTE: no validation is conducted, e.g. the paths specified in the configuration are not + /// checked whether they even exist. pub fn load>(p: P) -> anyhow::Result { let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?; @@ -83,18 +136,22 @@ impl Rosenpass { } /// Validate a configuration + /// + /// ## TODO + /// - check that files do not just exist but are also readable + /// - warn if neither out_key nor exchange_command of a peer is defined (v.i.) pub fn validate(&self) -> anyhow::Result<()> { - // check the public-key file exists + // check the public key file exists ensure!( self.public_key.is_file(), - "public-key file {:?} does not exist", + "could not find public-key file {:?}: no such file", self.public_key ); // check the secret-key file exists ensure!( self.secret_key.is_file(), - "secret-key file {:?} does not exist", + "could not find secret-key file {:?}: no such file", self.secret_key ); From 1a5ffdd4953cadb656dc60099bba757fcbae44e0 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Thu, 29 Feb 2024 11:34:06 +0100 Subject: [PATCH 11/19] resolve #237: resolve paths starting with "~/" in config file --- rosenpass/src/config.rs | 86 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index fc51c18..338b9de 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -112,10 +112,32 @@ impl Rosenpass { /// /// NOTE: no validation is conducted, e.g. the paths specified in the configuration are not /// checked whether they even exist. + /// + /// ## TODO + /// - consider using a different algorithm to determine home directory – the below one may + /// behave unexpectedly on Windows pub fn load>(p: P) -> anyhow::Result { + // read file and deserialize let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?; + // resolve `~` (see https://github.com/rosenpass/rosenpass/issues/237) + use util::resolve_path_with_tilde; + resolve_path_with_tilde(&mut config.public_key); + resolve_path_with_tilde(&mut config.secret_key); + for peer in config.peers.iter_mut() { + resolve_path_with_tilde(&mut peer.public_key); + if let Some(ref mut psk) = &mut peer.pre_shared_key { + resolve_path_with_tilde(psk); + } + if let Some(ref mut ko) = &mut peer.key_out { + resolve_path_with_tilde(ko); + } + } + + // add path to "self" config.config_file_path = p.as_ref().to_owned(); + + // return Ok(config) } @@ -499,3 +521,67 @@ mod test { ) } } + +pub mod util { + use std::path::PathBuf; + /// takes a path that can potentially start with a `~` and resolves that `~` to the user's home directory + /// + /// ## Example + /// ``` + /// use rosenpass::config::util::resolve_path_with_tilde; + /// std::env::set_var("HOME","/home/dummy"); + /// let mut path = std::path::PathBuf::from("~/foo.toml"); + /// resolve_path_with_tilde(&mut path); + /// assert!(path == std::path::PathBuf::from("/home/dummy/foo.toml")); + /// ``` + pub fn resolve_path_with_tilde(path: &mut PathBuf) { + if let Some(first_segment) = path.iter().next() { + if !path.has_root() && first_segment == "~" { + let home_dir = std::env::home_dir().unwrap_or_else(|| { + log::error!("config file contains \"~\" but can not determine home diretory"); + std::process::exit(1); + }); + let orig_path = path.clone(); + path.clear(); + path.push(home_dir); + for segment in orig_path.iter().skip(1) { + path.push(segment); + } + } + } + } + + #[cfg(test)] + mod test { + use super::*; + #[test] + fn test_resolve_path_with_tilde() { + let test = |path_str: &str, resolved: &str| { + let mut path = PathBuf::from(path_str); + resolve_path_with_tilde(&mut path); + assert!( + path == PathBuf::from(resolved), + "Path {:?} has been resolved to {:?} but should have been resolved to {:?}.", + path_str, + path, + resolved + ); + }; + // set environment because otherwise the test result would depend on the system running this + std::env::set_var("USER", "dummy"); + std::env::set_var("HOME", "/home/dummy"); + + // should resolve + test("~/foo.toml", "/home/dummy/foo.toml"); + test("~//foo", "/home/dummy/foo"); + test("~/../other_user/foo", "/home/dummy/../other_user/foo"); + + // should _not_ resolve + test("~foo/bar", "~foo/bar"); + test(".~/foo", ".~/foo"); + test("/~/foo.toml", "/~/foo.toml"); + test(r"~\foo", r"~\foo"); + test(r"C:\~\foo.toml", r"C:\~\foo.toml"); + } + } +} From 4dab97d84e134678d97c3741a4a812175cd04313 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Thu, 29 Feb 2024 11:40:45 +0100 Subject: [PATCH 12/19] use <> brackets around hyperlinks in comments because GitHub actions complained --- 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 338b9de..bdf96fe 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -4,8 +4,8 @@ //! [`Rosenpass`] which holds such a configuration. //! //! ## TODO -//! - support `~` in (https://github.com/rosenpass/rosenpass/issues/237) -//! - provide tooling to create config file from shell (https://github.com/rosenpass/rosenpass/issues/247) +//! - support `~` in +//! - provide tooling to create config file from shell use std::{ collections::HashSet, @@ -53,7 +53,7 @@ pub struct Rosenpass { } /// ## TODO -/// - replace this type with [`log::LevelFilter`], also see https://github.com/rosenpass/rosenpass/pull/246 +/// - replace this type with [`log::LevelFilter`], also see #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Verbosity { Quiet, @@ -84,7 +84,7 @@ pub struct RosenpassPeer { /// ## TODO /// - documentation - /// - make this field only available on binary builds, not on library builds (https://github.com/rosenpass/rosenpass/issues/249) + /// - make this field only available on binary builds, not on library builds #[serde(flatten)] pub wg: Option, } From 262e32fe355e03e3a71ca577f7422892fd11cf49 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Wed, 28 Feb 2024 18:22:25 +0100 Subject: [PATCH 13/19] resolve #92: add CLI argument to specify log level filter --- rosenpass/src/cli.rs | 59 +++++++++++++++++++++++++++++++++++++------ rosenpass/src/main.rs | 26 ++++++++++++++++--- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 132bf76..1482cf1 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -1,5 +1,5 @@ use anyhow::{bail, ensure}; -use clap::Parser; +use clap::{Parser, Subcommand}; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::file::StoreSecret; @@ -12,9 +12,50 @@ use crate::protocol::{SPk, SSk, SymKey}; use super::config; +/// struct holding all CLI arguments for `clap` crate to parse #[derive(Parser, Debug)] #[command(author, version, about, long_about)] -pub enum Cli { +pub struct CliArgs { + /// lowest log level to show – log messages at higher levels will be omitted + #[arg(long = "log-level", value_name = "LOG_LEVEL", group = "log-level")] + log_level: Option, + + /// show verbose log output – sets log level to "debug" + #[arg(short, long, group = "log-level")] + verbose: bool, + + /// show no log output – sets log level to "error" + #[arg(short, long, group = "log-level")] + quiet: bool, + + #[command(subcommand)] + pub command: CliCommand, +} + +impl CliArgs { + /// returns the log level filter set by CLI args + /// returns `None` if the user did not specify any log level filter via CLI + /// + /// NOTE: the clap feature of ["argument groups"](https://docs.rs/clap/latest/clap/_derive/_tutorial/chapter_3/index.html#argument-relations) + /// ensures that the user can not specify more than one of the possible log level arguments. + /// Note the `#[arg("group")]` in the [`CliArgs`] struct. + pub fn get_log_level(&self) -> Option { + if self.verbose { + return Some(log::LevelFilter::Info); + } + if self.quiet { + return Some(log::LevelFilter::Error); + } + if let Some(level_filter) = self.log_level { + return Some(level_filter); + } + None + } +} + +/// represents a command specified via CLI +#[derive(Subcommand, Debug)] +pub enum CliCommand { /// Start Rosenpass in server mode and carry on with the key exchange /// /// This will parse the configuration file and perform the key exchange @@ -104,12 +145,14 @@ pub enum Cli { Man, } -impl Cli { - pub fn run() -> anyhow::Result<()> { - let cli = Self::parse(); - - use Cli::*; - match cli { +impl CliCommand { + /// runs the command specified via CLI + /// + /// ## TODO + /// - This method consumes the [`CliCommand`] value. It might be wise to use a reference... + pub fn run(self) -> anyhow::Result<()> { + use CliCommand::*; + match self { Man => { let man_cmd = std::process::Command::new("man") .args(["1", "rosenpass"]) diff --git a/rosenpass/src/main.rs b/rosenpass/src/main.rs index c5cd7e1..e420a7e 100644 --- a/rosenpass/src/main.rs +++ b/rosenpass/src/main.rs @@ -1,13 +1,31 @@ +use clap::Parser; use log::error; -use rosenpass::cli::Cli; +use rosenpass::cli::CliArgs; use std::process::exit; /// Catches errors, prints them through the logger, then exits pub fn main() { - // default to displaying warning and error log messages only - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); + // parse CLI arguments + let args = CliArgs::parse(); - match Cli::run() { + // init logging + { + let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults) + if let Some(level) = args.get_log_level() { + log_builder.filter_level(level); // set log level filter from CLI args if available + } + log_builder.init(); + + // // check the effectiveness of the log level filter with the following lines: + // use log::{debug, error, info, trace, warn}; + // trace!("trace dummy"); + // debug!("debug dummy"); + // info!("info dummy"); + // warn!("warn dummy"); + // error!("error dummy"); + } + + match args.command.run() { Ok(_) => {} Err(e) => { error!("{e}"); From 971e49b894777fb427da4456119f84d6b38903f9 Mon Sep 17 00:00:00 2001 From: Ilka Schulz Date: Wed, 28 Feb 2024 19:06:50 +0100 Subject: [PATCH 14/19] debug-log change in log level filter via CLI parameter --- rosenpass/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rosenpass/src/main.rs b/rosenpass/src/main.rs index e420a7e..1952015 100644 --- a/rosenpass/src/main.rs +++ b/rosenpass/src/main.rs @@ -12,6 +12,7 @@ pub fn main() { { let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults) if let Some(level) = args.get_log_level() { + log::debug!("setting log level to {:?} (set via CLI parameter)", level); log_builder.filter_level(level); // set log level filter from CLI args if available } log_builder.init(); From 4f4e8e101813fb504cff800a6c33697859384b60 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Fri, 1 Mar 2024 18:57:21 +0100 Subject: [PATCH 15/19] config: drop deprecated std::env::home_dir() Instead use the `home` create. Signed-off-by: Paul Spooren --- Cargo.lock | 7 ++++--- Cargo.toml | 1 + rosenpass/Cargo.toml | 1 + rosenpass/src/config.rs | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddd651e..e7b4326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,11 +678,11 @@ checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1123,6 +1123,7 @@ dependencies = [ "clap 4.4.10", "criterion", "env_logger", + "home", "log", "memoffset", "mio", diff --git a/Cargo.toml b/Cargo.toml index 3647d92..f1e8414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,4 @@ oqs-sys = { version = "0.8", default-features = false, features = ['classic_mcel blake2 = "0.10.6" chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] } zerocopy = { version = "0.7.32", features = ["derive"] } +home = "0.5.9" diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 83d1680..a3c156b 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -33,6 +33,7 @@ clap = { workspace = true } mio = { workspace = true } rand = { workspace = true } zerocopy = { workspace = true } +home = { workspace = true } [build-dependencies] anyhow = { workspace = true } diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index bdf96fe..067802f 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -537,7 +537,7 @@ pub mod util { pub fn resolve_path_with_tilde(path: &mut PathBuf) { if let Some(first_segment) = path.iter().next() { if !path.has_root() && first_segment == "~" { - let home_dir = std::env::home_dir().unwrap_or_else(|| { + let home_dir = home::home_dir().unwrap_or_else(|| { log::error!("config file contains \"~\" but can not determine home diretory"); std::process::exit(1); }); From 7c1ada4b10b0e2becf160aae3b6f620fd4153671 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Fri, 1 Mar 2024 19:12:15 +0100 Subject: [PATCH 16/19] build: add link to manual Signed-off-by: Paul Spooren --- rosenpass/build.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rosenpass/build.rs b/rosenpass/build.rs index c1eb5b0..3ea3b4c 100644 --- a/rosenpass/build.rs +++ b/rosenpass/build.rs @@ -30,8 +30,7 @@ fn generate_man() -> String { return man; } - // TODO: Link to online manual here - "Cannot render manual page\n".into() + "Cannot render manual page. Please visit https://rosenpass.eu/docs/manuals/\n".into() } fn man() { From 478fadb80d993fd256852df19b72825e03a7c211 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Fri, 1 Mar 2024 18:08:08 +0100 Subject: [PATCH 17/19] ci: Enable aarch64-linux builds again Signed-off-by: Paul Spooren --- .github/workflows/nix.yaml | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 7c9c227..2584025 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -223,6 +223,29 @@ jobs: authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Build run: nix build .#packages.x86_64-linux.release-package --print-build-logs + aarch64-linux---release-package: + name: Build aarch64-linux.release-package + runs-on: + - ubuntu-latest + needs: + - aarch64-linux---rosenpass-oci-image + - aarch64-linux---rosenpass + steps: + - run: | + DEBIAN_FRONTEND=noninteractive + sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-unstable + extra_nix_config: | + system = aarch64-linux + - uses: cachix/cachix-action@v12 + with: + name: rosenpass + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Build + run: nix build .#packages.aarch64-linux.release-package --print-build-logs x86_64-linux---rosenpass: name: Build x86_64-linux.rosenpass runs-on: @@ -239,6 +262,27 @@ jobs: authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Build run: nix build .#packages.x86_64-linux.rosenpass --print-build-logs + aarch64-linux---rosenpass: + name: Build aarch64-linux.rosenpass + runs-on: + - ubuntu-latest + needs: [] + steps: + - run: | + DEBIAN_FRONTEND=noninteractive + sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-unstable + extra_nix_config: | + system = aarch64-linux + - uses: cachix/cachix-action@v12 + with: + name: rosenpass + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Build + run: nix build .#packages.aarch64-linux.rosenpass --print-build-logs x86_64-linux---rosenpass-oci-image: name: Build x86_64-linux.rosenpass-oci-image runs-on: @@ -256,6 +300,28 @@ jobs: authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Build run: nix build .#packages.x86_64-linux.rosenpass-oci-image --print-build-logs + aarch64-linux---rosenpass-oci-image: + name: Build aarch64-linux.rosenpass-oci-image + runs-on: + - ubuntu-latest + needs: + - aarch64-linux---rosenpass + steps: + - run: | + DEBIAN_FRONTEND=noninteractive + sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-unstable + extra_nix_config: | + system = aarch64-linux + - uses: cachix/cachix-action@v12 + with: + name: rosenpass + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Build + run: nix build .#packages.aarch64-linux.rosenpass-oci-image --print-build-logs x86_64-linux---rosenpass-static: name: Build x86_64-linux.rosenpass-static runs-on: From 13df700ef57ac5a9ca166dfdf8cc54518afb5f50 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Thu, 7 Mar 2024 11:38:20 +0100 Subject: [PATCH 18/19] flake: drop overlay due to upstream fix Upstream fix #216904 got fixed to remove the extra overlay. Signed-off-by: Paul Spooren --- flake.nix | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/flake.nix b/flake.nix index 5d0a559..332e9eb 100644 --- a/flake.nix +++ b/flake.nix @@ -35,24 +35,6 @@ # normal nixpkgs pkgs = import nixpkgs { inherit system; - - # TODO remove overlay once a fix for - # https://github.com/NixOS/nixpkgs/issues/216904 got merged - overlays = [ - ( - final: prev: { - iproute2 = prev.iproute2.overrideAttrs (old: - let - isStatic = prev.stdenv.hostPlatform.isStatic; - in - { - makeFlags = old.makeFlags ++ prev.lib.optional isStatic [ - "TC_CONFIG_NO_XT=y" - ]; - }); - } - ) - ]; }; # parsed Cargo.toml From 13a853ff429e431c1a4efbe688582e493247e42a Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sat, 9 Mar 2024 08:50:35 +0200 Subject: [PATCH 19/19] fix: Fix crate vulnerabilities --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7b4326..db8e6d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -868,9 +868,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -1332,9 +1332,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "spin" diff --git a/Cargo.toml b/Cargo.toml index f1e8414..835cc76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ 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", "std"] } -mio = { version = "0.8.9", features = ["net", "os-poll"] } +mio = { version = "0.8.11", features = ["net", "os-poll"] } oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] } blake2 = "0.10.6" chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] }