mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 06:23:08 -08:00
refactor: replace lenses library with the zerocopy crate
This commit is contained in:
committed by
Karolin Varner
parent
e38a6b8ed4
commit
a996b08279
31
Cargo.lock
generated
31
Cargo.lock
generated
@@ -1131,7 +1131,6 @@ dependencies = [
|
|||||||
"rosenpass-cipher-traits",
|
"rosenpass-cipher-traits",
|
||||||
"rosenpass-ciphers",
|
"rosenpass-ciphers",
|
||||||
"rosenpass-constant-time",
|
"rosenpass-constant-time",
|
||||||
"rosenpass-lenses",
|
|
||||||
"rosenpass-secret-memory",
|
"rosenpass-secret-memory",
|
||||||
"rosenpass-to",
|
"rosenpass-to",
|
||||||
"rosenpass-util",
|
"rosenpass-util",
|
||||||
@@ -1141,6 +1140,7 @@ dependencies = [
|
|||||||
"test_bin",
|
"test_bin",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1185,14 +1185,6 @@ dependencies = [
|
|||||||
"stacker",
|
"stacker",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rosenpass-lenses"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"paste",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rosenpass-oqs"
|
name = "rosenpass-oqs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1848,6 +1840,27 @@ dependencies = [
|
|||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ members = [
|
|||||||
"to",
|
"to",
|
||||||
"fuzz",
|
"fuzz",
|
||||||
"secret-memory",
|
"secret-memory",
|
||||||
"lenses",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
@@ -31,7 +30,6 @@ rosenpass-ciphers = { path = "ciphers" }
|
|||||||
rosenpass-to = { path = "to" }
|
rosenpass-to = { path = "to" }
|
||||||
rosenpass-secret-memory = { path = "secret-memory" }
|
rosenpass-secret-memory = { path = "secret-memory" }
|
||||||
rosenpass-oqs = { path = "oqs" }
|
rosenpass-oqs = { path = "oqs" }
|
||||||
rosenpass-lenses = { path = "lenses" }
|
|
||||||
criterion = "0.4.0"
|
criterion = "0.4.0"
|
||||||
test_bin = "0.4.0"
|
test_bin = "0.4.0"
|
||||||
libfuzzer-sys = "0.4"
|
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'] }
|
oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||||
blake2 = "0.10.6"
|
blake2 = "0.10.6"
|
||||||
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] }
|
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] }
|
||||||
|
zerocopy = { version = "0.7.32", features = ["derive"] }
|
||||||
|
|||||||
@@ -112,9 +112,9 @@
|
|||||||
|
|
||||||
# suitable Rust toolchain
|
# suitable Rust toolchain
|
||||||
toolchain = with inputs.fenix.packages.${system}; combine [
|
toolchain = with inputs.fenix.packages.${system}; combine [
|
||||||
stable.cargo
|
latest.cargo
|
||||||
stable.rustc
|
latest.rustc
|
||||||
targets.${target}.stable.rust-std
|
targets.${target}.latest.rust-std
|
||||||
];
|
];
|
||||||
|
|
||||||
# naersk with a custom toolchain
|
# naersk with a custom toolchain
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "rosenpass-lenses"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
|
||||||
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 }
|
|
||||||
@@ -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.
|
|
||||||
@@ -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<T> = Result<T, LenseError>;
|
|
||||||
|
|
||||||
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<std::ops::Range<usize>> + ?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<std::ops::Range<usize>> + ?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<Self::__ContainerType, $( $($generic),+ )? >>;
|
|
||||||
|
|
||||||
/// Create a lense to the byte slice, automatically truncating oversized buffers
|
|
||||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> [< $type Ext >] for &'a [u8] {
|
|
||||||
type __ContainerType = &'a [u8];
|
|
||||||
|
|
||||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
|
||||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
|
||||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
|
||||||
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<Self::__ContainerType, $( $($generic),+ )? >> {
|
|
||||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
|
||||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
|
||||||
let required_size = $( $len + )+ 0;
|
|
||||||
::rosenpass_lenses::LenseError::ensure_sufficient_buffer_size(self.len(), required_size)?;
|
|
||||||
[< $type Ext >]::[< $type:snake >](&mut self[..required_size])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
);
|
|
||||||
@@ -20,7 +20,6 @@ rosenpass-ciphers = { workspace = true }
|
|||||||
rosenpass-cipher-traits = { workspace = true }
|
rosenpass-cipher-traits = { workspace = true }
|
||||||
rosenpass-to = { workspace = true }
|
rosenpass-to = { workspace = true }
|
||||||
rosenpass-secret-memory = { workspace = true }
|
rosenpass-secret-memory = { workspace = true }
|
||||||
rosenpass-lenses = { workspace = true }
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
static_assertions = { workspace = true }
|
static_assertions = { workspace = true }
|
||||||
memoffset = { workspace = true }
|
memoffset = { workspace = true }
|
||||||
@@ -33,6 +32,7 @@ toml = { workspace = true }
|
|||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
mio = { workspace = true }
|
mio = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
zerocopy = { workspace = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use rosenpass_lenses::LenseError;
|
#![feature(offset_of)]
|
||||||
|
|
||||||
pub mod app_server;
|
pub mod app_server;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
@@ -15,10 +15,3 @@ pub enum RosenpassError {
|
|||||||
InvalidMessageType(u8),
|
InvalidMessageType(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LenseError> for RosenpassError {
|
|
||||||
fn from(value: LenseError) -> Self {
|
|
||||||
match value {
|
|
||||||
LenseError::BufferSizeMismatch => RosenpassError::BufferSizeMismatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,129 +6,111 @@
|
|||||||
//! always serialized instance of the data in question. This is closely related
|
//! always serialized instance of the data in question. This is closely related
|
||||||
//! to the concept of lenses in function programming; more on that here:
|
//! 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)
|
//! [https://sinusoid.es/misc/lager/lenses.pdf](https://sinusoid.es/misc/lager/lenses.pdf)
|
||||||
//!
|
//! To achieve this we utilize the zerocopy library.
|
||||||
//! # 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(())
|
|
||||||
//! # }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use super::RosenpassError;
|
use super::RosenpassError;
|
||||||
|
use std::mem::size_of;
|
||||||
use rosenpass_cipher_traits::Kem;
|
use rosenpass_cipher_traits::Kem;
|
||||||
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
||||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||||
use rosenpass_lenses::{lense, LenseView};
|
use zerocopy::{AsBytes, FromBytes, FromZeroes};
|
||||||
|
|
||||||
// Macro magic ////////////////////////////////////////////////////////////////
|
// Macro magic ////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
lense! { Envelope<M> :=
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||||
|
pub struct Envelope<M: AsBytes + FromBytes> {
|
||||||
/// [MsgType] of this message
|
/// [MsgType] of this message
|
||||||
msg_type: 1,
|
pub msg_type: u8,
|
||||||
/// Reserved for future use
|
/// Reserved for future use
|
||||||
reserved: 3,
|
pub reserved: [u8; 3],
|
||||||
/// The actual Paylod
|
/// The actual Paylod
|
||||||
payload: M::LEN,
|
pub payload: M,
|
||||||
/// Message Authentication Code (mac) over all bytes until (exclusive)
|
/// Message Authentication Code (mac) over all bytes until (exclusive)
|
||||||
/// `mac` itself
|
/// `mac` itself
|
||||||
mac: 16,
|
pub mac: [u8; 16],
|
||||||
/// Currently unused, TODO: do something with this
|
/// Currently unused, TODO: do something with this
|
||||||
cookie: 16
|
pub cookie: [u8; 16]
|
||||||
}
|
}
|
||||||
|
|
||||||
lense! { InitHello :=
|
#[repr(packed)]
|
||||||
|
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||||
|
pub struct InitHello {
|
||||||
/// Randomly generated connection id
|
/// Randomly generated connection id
|
||||||
sidi: 4,
|
pub sidi: [u8; 4],
|
||||||
/// Kyber 512 Ephemeral Public Key
|
/// Kyber 512 Ephemeral Public Key
|
||||||
epki: EphemeralKem::PK_LEN,
|
pub epki: [u8; EphemeralKem::PK_LEN],
|
||||||
/// Classic McEliece Ciphertext
|
/// Classic McEliece Ciphertext
|
||||||
sctr: StaticKem::CT_LEN,
|
pub sctr: [u8; StaticKem::CT_LEN],
|
||||||
/// Encryped: 16 byte hash of McEliece initiator static key
|
/// 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)
|
/// 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
|
/// Randomly generated connection id
|
||||||
sidr: 4,
|
pub sidr: [u8; 4],
|
||||||
/// Copied from InitHello
|
/// Copied from InitHello
|
||||||
sidi: 4,
|
pub sidi: [u8; 4],
|
||||||
/// Kyber 512 Ephemeral Ciphertext
|
/// Kyber 512 Ephemeral Ciphertext
|
||||||
ecti: EphemeralKem::CT_LEN,
|
pub ecti: [u8; EphemeralKem::CT_LEN],
|
||||||
/// Classic McEliece Ciphertext
|
/// Classic McEliece Ciphertext
|
||||||
scti: StaticKem::CT_LEN,
|
pub scti: [u8; StaticKem::CT_LEN],
|
||||||
/// Empty encrypted message (just an auth tag)
|
/// Empty encrypted message (just an auth tag)
|
||||||
auth: aead::TAG_LEN,
|
pub auth: [u8; aead::TAG_LEN],
|
||||||
/// Responders handshake state in encrypted form
|
/// 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
|
/// Copied from InitHello
|
||||||
sidi: 4,
|
pub sidi: [u8; 4],
|
||||||
/// Copied from RespHello
|
/// Copied from RespHello
|
||||||
sidr: 4,
|
pub sidr: [u8; 4],
|
||||||
/// Responders handshake state in encrypted form
|
/// Responders handshake state in encrypted form
|
||||||
biscuit: BISCUIT_CT_LEN,
|
pub biscuit: [u8; BISCUIT_CT_LEN],
|
||||||
/// Empty encrypted message (just an auth tag)
|
/// 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
|
/// Copied from RespHello
|
||||||
sid: 4,
|
pub sid: [u8; 4],
|
||||||
/// Nonce
|
/// Nonce
|
||||||
ctr: 8,
|
pub ctr: [u8; 8],
|
||||||
/// Empty encrypted message (just an auth tag)
|
/// 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
|
/// H(spki) – Ident ifies the initiator
|
||||||
pidi: KEY_LEN,
|
pub pidi: [u8; KEY_LEN],
|
||||||
/// The biscuit number (replay protection)
|
/// The biscuit number (replay protection)
|
||||||
biscuit_no: 12,
|
pub biscuit_no: [u8; 12],
|
||||||
/// Chaining key
|
/// Chaining key
|
||||||
ck: KEY_LEN
|
pub ck: [u8; KEY_LEN]
|
||||||
}
|
}
|
||||||
|
|
||||||
lense! { DataMsg :=
|
#[repr(packed)]
|
||||||
dummy: 4
|
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||||
|
pub struct DataMsg {
|
||||||
|
pub dummy: [u8; 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
lense! { CookieReply :=
|
#[repr(packed)]
|
||||||
dummy: 4
|
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||||
|
pub struct CookieReply {
|
||||||
|
pub dummy: [u8; 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traits /////////////////////////////////////////////////////////////////////
|
// Traits /////////////////////////////////////////////////////////////////////
|
||||||
@@ -178,7 +160,7 @@ impl TryFrom<u8> for MsgType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// length in bytes of an unencrypted Biscuit (plain text)
|
/// 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::<Biscuit>();
|
||||||
|
|
||||||
/// Length in bytes of an encrypted Biscuit (cipher text)
|
/// Length in bytes of an encrypted Biscuit (cipher text)
|
||||||
pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN;
|
pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ use std::collections::hash_map::{
|
|||||||
HashMap,
|
HashMap,
|
||||||
};
|
};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
use std::mem::{size_of,offset_of};
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Context, Result};
|
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::kem::{EphemeralKem, StaticKem};
|
||||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||||
use rosenpass_constant_time as constant_time;
|
use rosenpass_constant_time as constant_time;
|
||||||
use rosenpass_lenses::LenseView;
|
|
||||||
use rosenpass_secret_memory::{Public, Secret};
|
use rosenpass_secret_memory::{Public, Secret};
|
||||||
use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
|
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 //////////////////////////
|
// CONSTANTS & SETTINGS //////////////////////////
|
||||||
|
|
||||||
/// Size required to fit any message in binary form
|
/// Size required to fit any message in binary form
|
||||||
pub const RTX_BUFFER_SIZE: usize = max_usize(
|
pub const RTX_BUFFER_SIZE: usize = max_usize(
|
||||||
<Envelope<(), InitHello<()>> as LenseView>::LEN,
|
size_of::<Envelope<InitHello>>(),
|
||||||
<Envelope<(), InitConf<()>> as LenseView>::LEN,
|
size_of::<Envelope<InitConf>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A type for time, e.g. for backoff before re-tries
|
/// 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 remove unnecessary copying between global tx_buf and per-peer buf
|
||||||
// TODO move retransmission storage to io server
|
// TODO move retransmission storage to io server
|
||||||
pub fn initiate_handshake(&mut self, peer: PeerPtr, tx_buf: &mut [u8]) -> Result<usize> {
|
pub fn initiate_handshake(&mut self, peer: PeerPtr, tx_buf: &mut [u8]) -> Result<usize> {
|
||||||
let mut msg = tx_buf.envelope_truncating::<InitHello<()>>()?; // Envelope::<InitHello>::default(); // TODO
|
// Envelope::<InitHello>::default(); // TODO
|
||||||
self.handle_initiation(peer, msg.payload_mut().init_hello()?)?;
|
let mut msg = truncating_cast_into::<Envelope<InitHello>>(tx_buf)?;
|
||||||
let len = self.seal_and_commit_msg(peer, MsgType::InitHello, msg)?;
|
self.handle_initiation(peer, &mut msg.payload)?;
|
||||||
|
let len = self.seal_and_commit_msg(peer, MsgType::InitHello, &mut msg)?;
|
||||||
peer.hs()
|
peer.hs()
|
||||||
.store_msg_for_retransmission(self, &tx_buf[..len])?;
|
.store_msg_for_retransmission(self, &msg.as_bytes()[..len])?;
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -793,50 +795,50 @@ impl CryptoServer {
|
|||||||
|
|
||||||
let peer = match rx_buf[0].try_into() {
|
let peer = match rx_buf[0].try_into() {
|
||||||
Ok(MsgType::InitHello) => {
|
Ok(MsgType::InitHello) => {
|
||||||
let msg_in = rx_buf.envelope::<InitHello<&[u8]>>()?;
|
let msg_in: Ref<&[u8], Envelope<InitHello>> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?;
|
||||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||||
|
|
||||||
let mut msg_out = tx_buf.envelope_truncating::<RespHello<&mut [u8]>>()?;
|
let mut msg_out = truncating_cast_into::<Envelope<RespHello>>(tx_buf)?;
|
||||||
let peer = self.handle_init_hello(
|
let peer = self.handle_init_hello(
|
||||||
msg_in.payload().init_hello()?,
|
&msg_in.payload,
|
||||||
msg_out.payload_mut().resp_hello()?,
|
&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
|
peer
|
||||||
}
|
}
|
||||||
Ok(MsgType::RespHello) => {
|
Ok(MsgType::RespHello) => {
|
||||||
let msg_in = rx_buf.envelope::<RespHello<&[u8]>>()?;
|
let msg_in: Ref<&[u8], Envelope<RespHello>> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?;
|
||||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||||
|
|
||||||
let mut msg_out = tx_buf.envelope_truncating::<InitConf<&mut [u8]>>()?;
|
let mut msg_out = truncating_cast_into::<Envelope<InitConf>>(tx_buf)?;
|
||||||
let peer = self.handle_resp_hello(
|
let peer = self.handle_resp_hello(
|
||||||
msg_in.payload().resp_hello()?,
|
&msg_in.payload,
|
||||||
msg_out.payload_mut().init_conf()?,
|
&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()
|
peer.hs()
|
||||||
.store_msg_for_retransmission(self, &tx_buf[..len])?;
|
.store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?;
|
||||||
exchanged = true;
|
exchanged = true;
|
||||||
peer
|
peer
|
||||||
}
|
}
|
||||||
Ok(MsgType::InitConf) => {
|
Ok(MsgType::InitConf) => {
|
||||||
let msg_in = rx_buf.envelope::<InitConf<&[u8]>>()?;
|
let msg_in: Ref<&[u8], Envelope<InitConf>> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?;
|
||||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||||
|
|
||||||
let mut msg_out = tx_buf.envelope_truncating::<EmptyData<&mut [u8]>>()?;
|
let mut msg_out = truncating_cast_into::<Envelope<EmptyData>>(tx_buf)?;
|
||||||
let peer = self.handle_init_conf(
|
let peer = self.handle_init_conf(
|
||||||
msg_in.payload().init_conf()?,
|
&msg_in.payload,
|
||||||
msg_out.payload_mut().empty_data()?,
|
&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;
|
exchanged = true;
|
||||||
peer
|
peer
|
||||||
}
|
}
|
||||||
Ok(MsgType::EmptyData) => {
|
Ok(MsgType::EmptyData) => {
|
||||||
let msg_in = rx_buf.envelope::<EmptyData<&[u8]>>()?;
|
let msg_in: Ref<&[u8], Envelope<EmptyData>> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?;
|
||||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
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::DataMsg) => bail!("DataMsg handling not implemented!"),
|
||||||
Ok(MsgType::CookieReply) => bail!("CookieReply 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
|
/// The message type is explicitly required here because it is very easy to
|
||||||
/// forget setting that, which creates subtle but far ranging errors.
|
/// forget setting that, which creates subtle but far ranging errors.
|
||||||
pub fn seal_and_commit_msg<M: LenseView>(
|
pub fn seal_and_commit_msg<M: AsBytes + FromBytes>(
|
||||||
&mut self,
|
&mut self,
|
||||||
peer: PeerPtr,
|
peer: PeerPtr,
|
||||||
msg_type: MsgType,
|
msg_type: MsgType,
|
||||||
mut msg: Envelope<&mut [u8], M>,
|
msg: &mut Ref<&mut [u8], Envelope<M>>,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
msg.msg_type_mut()[0] = msg_type as u8;
|
msg.msg_type = msg_type as u8;
|
||||||
msg.seal(peer, self)?;
|
msg.seal(peer, self)?;
|
||||||
Ok(<Envelope<(), M> as LenseView>::LEN)
|
Ok(size_of::<Envelope<M>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1170,32 +1172,32 @@ impl IniHsPtr {
|
|||||||
|
|
||||||
// CRYPTO/HANDSHAKE HANDLING /////////////////////
|
// CRYPTO/HANDSHAKE HANDLING /////////////////////
|
||||||
|
|
||||||
impl<M> Envelope<&mut [u8], M>
|
impl<M> Envelope<M>
|
||||||
where
|
where
|
||||||
M: LenseView,
|
M: AsBytes + FromBytes,
|
||||||
{
|
{
|
||||||
/// Calculate the message authentication code (`mac`)
|
/// Calculate the message authentication code (`mac`)
|
||||||
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
||||||
let mac = hash_domains::mac()?
|
let mac = hash_domains::mac()?
|
||||||
.mix(peer.get(srv).spkt.secret())?
|
.mix(peer.get(srv).spkt.secret())?
|
||||||
.mix(self.until_mac())?;
|
.mix(&self.as_bytes()[..offset_of!(Self, mac)])?;
|
||||||
self.mac_mut()
|
self.mac
|
||||||
.copy_from_slice(mac.into_value()[..16].as_ref());
|
.copy_from_slice(mac.into_value()[..16].as_ref());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> Envelope<&[u8], M>
|
impl<M> Envelope<M>
|
||||||
where
|
where
|
||||||
M: LenseView,
|
M: AsBytes + FromBytes,
|
||||||
{
|
{
|
||||||
/// Check the message authentication code
|
/// Check the message authentication code
|
||||||
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
||||||
let expected = hash_domains::mac()?
|
let expected = hash_domains::mac()?
|
||||||
.mix(srv.spkm.secret())?
|
.mix(srv.spkm.secret())?
|
||||||
.mix(self.until_mac())?;
|
.mix(&self.as_bytes()[..offset_of!(Self, mac)])?;
|
||||||
Ok(constant_time::memcmp(
|
Ok(constant_time::memcmp(
|
||||||
self.mac(),
|
&self.mac,
|
||||||
&expected.into_value()[..16],
|
&expected.into_value()[..16],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -1282,15 +1284,15 @@ impl HandshakeState {
|
|||||||
biscuit_ct: &mut [u8],
|
biscuit_ct: &mut [u8],
|
||||||
) -> Result<&mut Self> {
|
) -> Result<&mut Self> {
|
||||||
let mut biscuit = Secret::<BISCUIT_PT_LEN>::zero(); // pt buffer
|
let mut biscuit = Secret::<BISCUIT_PT_LEN>::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
|
// calculate pt contents
|
||||||
biscuit
|
biscuit
|
||||||
.pidi_mut()
|
.pidi
|
||||||
.copy_from_slice(peer.get(srv).pidt()?.as_slice());
|
.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
|
biscuit
|
||||||
.ck_mut()
|
.ck
|
||||||
.copy_from_slice(self.ck.clone().danger_into_secret().secret());
|
.copy_from_slice(self.ck.clone().danger_into_secret().secret());
|
||||||
|
|
||||||
// calculate ad contents
|
// calculate ad contents
|
||||||
@@ -1311,7 +1313,7 @@ impl HandshakeState {
|
|||||||
n[0] |= (bk.0 as u8 & 0x1) << 7;
|
n[0] |= (bk.0 as u8 & 0x1) << 7;
|
||||||
|
|
||||||
let k = bk.get(srv).key.secret();
|
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)?;
|
xaead::encrypt(biscuit_ct, k, &*n, &ad, pt)?;
|
||||||
|
|
||||||
self.mix(biscuit_ct)
|
self.mix(biscuit_ct)
|
||||||
@@ -1337,18 +1339,18 @@ impl HandshakeState {
|
|||||||
|
|
||||||
// Allocate and decrypt the biscuit data
|
// Allocate and decrypt the biscuit data
|
||||||
let mut biscuit = Secret::<BISCUIT_PT_LEN>::zero(); // pt buf
|
let mut biscuit = Secret::<BISCUIT_PT_LEN>::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(
|
xaead::decrypt(
|
||||||
biscuit.all_bytes_mut(),
|
biscuit.as_bytes_mut(),
|
||||||
bk.get(srv).key.secret(),
|
bk.get(srv).key.secret(),
|
||||||
&ad,
|
&ad,
|
||||||
biscuit_ct,
|
biscuit_ct,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Reconstruct the biscuit fields
|
// Reconstruct the biscuit fields
|
||||||
let no = BiscuitId::from_slice(biscuit.biscuit_no());
|
let no = BiscuitId::from_slice(&biscuit.biscuit_no);
|
||||||
let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(biscuit.ck())).dup();
|
let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck)).dup();
|
||||||
let pid = PeerId::from_slice(biscuit.pidi());
|
let pid = PeerId::from_slice(&biscuit.pidi);
|
||||||
|
|
||||||
// Reconstruct the handshake state
|
// Reconstruct the handshake state
|
||||||
let mut hs = Self { sidi, sidr, ck };
|
let mut hs = Self { sidi, sidr, ck };
|
||||||
@@ -1364,7 +1366,7 @@ impl HandshakeState {
|
|||||||
// indicates retransmission
|
// indicates retransmission
|
||||||
// TODO: Handle retransmissions without involving the crypto code
|
// TODO: Handle retransmissions without involving the crypto code
|
||||||
ensure!(
|
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"
|
"Rejecting biscuit: Outdated biscuit number"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1415,7 +1417,7 @@ impl CryptoServer {
|
|||||||
pub fn handle_initiation(
|
pub fn handle_initiation(
|
||||||
&mut self,
|
&mut self,
|
||||||
peer: PeerPtr,
|
peer: PeerPtr,
|
||||||
mut ih: InitHello<&mut [u8]>,
|
ih: &mut InitHello,
|
||||||
) -> Result<PeerPtr> {
|
) -> Result<PeerPtr> {
|
||||||
let mut hs = InitiatorHandshake::zero_with_timestamp(self);
|
let mut hs = InitiatorHandshake::zero_with_timestamp(self);
|
||||||
|
|
||||||
@@ -1424,25 +1426,25 @@ impl CryptoServer {
|
|||||||
|
|
||||||
// IHI2
|
// IHI2
|
||||||
hs.core.sidi.randomize();
|
hs.core.sidi.randomize();
|
||||||
ih.sidi_mut().copy_from_slice(&hs.core.sidi.value);
|
ih.sidi.copy_from_slice(&hs.core.sidi.value);
|
||||||
|
|
||||||
// IHI3
|
// IHI3
|
||||||
EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
||||||
ih.epki_mut().copy_from_slice(&hs.epki.value);
|
ih.epki.copy_from_slice(&hs.epki.value);
|
||||||
|
|
||||||
// IHI4
|
// IHI4
|
||||||
hs.core.mix(ih.sidi())?.mix(ih.epki())?;
|
hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?;
|
||||||
|
|
||||||
// IHI5
|
// IHI5
|
||||||
hs.core
|
hs.core
|
||||||
.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
ih.sctr_mut(),
|
ih.sctr.as_mut_slice(),
|
||||||
peer.get(self).spkt.secret(),
|
peer.get(self).spkt.secret(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// IHI6
|
// IHI6
|
||||||
hs.core
|
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
|
// IHI7
|
||||||
hs.core
|
hs.core
|
||||||
@@ -1450,7 +1452,7 @@ impl CryptoServer {
|
|||||||
.mix(peer.get(self).psk.secret())?;
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
|
||||||
// IHI8
|
// 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
|
// Update the handshake hash last (not changing any state on prior error
|
||||||
peer.hs().insert(self, hs)?;
|
peer.hs().insert(self, hs)?;
|
||||||
@@ -1460,30 +1462,30 @@ impl CryptoServer {
|
|||||||
|
|
||||||
pub fn handle_init_hello(
|
pub fn handle_init_hello(
|
||||||
&mut self,
|
&mut self,
|
||||||
ih: InitHello<&[u8]>,
|
ih: &InitHello,
|
||||||
mut rh: RespHello<&mut [u8]>,
|
rh: &mut RespHello,
|
||||||
) -> Result<PeerPtr> {
|
) -> Result<PeerPtr> {
|
||||||
let mut core = HandshakeState::zero();
|
let mut core = HandshakeState::zero();
|
||||||
|
|
||||||
core.sidi = SessionId::from_slice(ih.sidi());
|
core.sidi = SessionId::from_slice(&ih.sidi);
|
||||||
|
|
||||||
// IHR1
|
// IHR1
|
||||||
core.init(self.spkm.secret())?;
|
core.init(self.spkm.secret())?;
|
||||||
|
|
||||||
// IHR4
|
// IHR4
|
||||||
core.mix(ih.sidi())?.mix(ih.epki())?;
|
core.mix(&ih.sidi)?.mix(&ih.epki)?;
|
||||||
|
|
||||||
// IHR5
|
// IHR5
|
||||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
self.sskm.secret(),
|
self.sskm.secret(),
|
||||||
self.spkm.secret(),
|
self.spkm.secret(),
|
||||||
ih.sctr(),
|
&ih.sctr,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// IHR6
|
// IHR6
|
||||||
let peer = {
|
let peer = {
|
||||||
let mut peerid = PeerId::zero();
|
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)
|
self.find_peer(peerid)
|
||||||
.with_context(|| format!("No such peer {peerid:?}."))?
|
.with_context(|| format!("No such peer {peerid:?}."))?
|
||||||
};
|
};
|
||||||
@@ -1493,46 +1495,46 @@ impl CryptoServer {
|
|||||||
.mix(peer.get(self).psk.secret())?;
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
|
||||||
// IHR8
|
// IHR8
|
||||||
core.decrypt_and_mix(&mut [0u8; 0], ih.auth())?;
|
core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?;
|
||||||
|
|
||||||
// RHR1
|
// RHR1
|
||||||
core.sidr.randomize();
|
core.sidr.randomize();
|
||||||
rh.sidi_mut().copy_from_slice(core.sidi.as_ref());
|
rh.sidi.copy_from_slice(core.sidi.as_ref());
|
||||||
rh.sidr_mut().copy_from_slice(core.sidr.as_ref());
|
rh.sidr.copy_from_slice(core.sidr.as_ref());
|
||||||
|
|
||||||
// RHR3
|
// RHR3
|
||||||
core.mix(rh.sidr())?.mix(rh.sidi())?;
|
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
||||||
|
|
||||||
// RHR4
|
// RHR4
|
||||||
core.encaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(rh.ecti_mut(), ih.epki())?;
|
core.encaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(&mut rh.ecti, &ih.epki)?;
|
||||||
|
|
||||||
// RHR5
|
// RHR5
|
||||||
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
rh.scti_mut(),
|
&mut rh.scti,
|
||||||
peer.get(self).spkt.secret(),
|
peer.get(self).spkt.secret(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// RHR6
|
// RHR6
|
||||||
core.store_biscuit(self, peer, rh.biscuit_mut())?;
|
core.store_biscuit(self, peer, &mut rh.biscuit)?;
|
||||||
|
|
||||||
// RHR7
|
// RHR7
|
||||||
core.encrypt_and_mix(rh.auth_mut(), &[])?;
|
core.encrypt_and_mix(&mut rh.auth, &[])?;
|
||||||
|
|
||||||
Ok(peer)
|
Ok(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_resp_hello(
|
pub fn handle_resp_hello(
|
||||||
&mut self,
|
&mut self,
|
||||||
rh: RespHello<&[u8]>,
|
rh: &RespHello,
|
||||||
mut ic: InitConf<&mut [u8]>,
|
ic: &mut InitConf,
|
||||||
) -> Result<PeerPtr> {
|
) -> Result<PeerPtr> {
|
||||||
// RHI2
|
// RHI2
|
||||||
let peer = self
|
let peer = self
|
||||||
.lookup_handshake(SessionId::from_slice(rh.sidi()))
|
.lookup_handshake(SessionId::from_slice(&rh.sidi))
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"Got RespHello packet for non-existent session {:?}",
|
"Got RespHello packet for non-existent session {:?}",
|
||||||
rh.sidi()
|
rh.sidi
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.peer();
|
.peer();
|
||||||
@@ -1557,52 +1559,52 @@ impl CryptoServer {
|
|||||||
ensure!(
|
ensure!(
|
||||||
exp == got,
|
exp == got,
|
||||||
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
|
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
|
||||||
SessionId::from_slice(rh.sidi()),
|
SessionId::from_slice(&rh.sidi),
|
||||||
exp,
|
exp,
|
||||||
got
|
got
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut core = hs!().core.clone();
|
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
|
// TODO: decaps_and_mix should take Secret<> directly
|
||||||
// to save us from the repetitive secret unwrapping
|
// to save us from the repetitive secret unwrapping
|
||||||
|
|
||||||
// RHI3
|
// RHI3
|
||||||
core.mix(rh.sidr())?.mix(rh.sidi())?;
|
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
||||||
|
|
||||||
// RHI4
|
// RHI4
|
||||||
core.decaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
core.decaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
||||||
hs!().eski.secret(),
|
hs!().eski.secret(),
|
||||||
&*hs!().epki,
|
&*hs!().epki,
|
||||||
rh.ecti(),
|
&rh.ecti,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// RHI5
|
// RHI5
|
||||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
self.sskm.secret(),
|
self.sskm.secret(),
|
||||||
self.spkm.secret(),
|
self.spkm.secret(),
|
||||||
rh.scti(),
|
&rh.scti,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// RHI6
|
// RHI6
|
||||||
core.mix(rh.biscuit())?;
|
core.mix(&rh.biscuit)?;
|
||||||
|
|
||||||
// RHI7
|
// 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
|
// TODO: We should just authenticate the entire network package up to the auth
|
||||||
// tag as a pattern instead of mixing in fields separately
|
// tag as a pattern instead of mixing in fields separately
|
||||||
|
|
||||||
ic.sidi_mut().copy_from_slice(rh.sidi());
|
ic.sidi.copy_from_slice(&rh.sidi);
|
||||||
ic.sidr_mut().copy_from_slice(rh.sidr());
|
ic.sidr.copy_from_slice(&rh.sidr);
|
||||||
|
|
||||||
// ICI3
|
// ICI3
|
||||||
core.mix(ic.sidi())?.mix(ic.sidr())?;
|
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
||||||
ic.biscuit_mut().copy_from_slice(rh.biscuit());
|
ic.biscuit.copy_from_slice(&rh.biscuit);
|
||||||
|
|
||||||
// ICI4
|
// 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
|
// Split() – We move the secrets into the session; we do not
|
||||||
// delete the InitiatorHandshake, just clear it's secrets because
|
// delete the InitiatorHandshake, just clear it's secrets because
|
||||||
@@ -1619,26 +1621,26 @@ impl CryptoServer {
|
|||||||
|
|
||||||
pub fn handle_init_conf(
|
pub fn handle_init_conf(
|
||||||
&mut self,
|
&mut self,
|
||||||
ic: InitConf<&[u8]>,
|
ic: &InitConf,
|
||||||
mut rc: EmptyData<&mut [u8]>,
|
rc: &mut EmptyData,
|
||||||
) -> Result<PeerPtr> {
|
) -> Result<PeerPtr> {
|
||||||
// (peer, bn) ← LoadBiscuit(InitConf.biscuit)
|
// (peer, bn) ← LoadBiscuit(InitConf.biscuit)
|
||||||
// ICR1
|
// ICR1
|
||||||
let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit(
|
let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit(
|
||||||
self,
|
self,
|
||||||
ic.biscuit(),
|
&ic.biscuit,
|
||||||
SessionId::from_slice(ic.sidi()),
|
SessionId::from_slice(&ic.sidi),
|
||||||
SessionId::from_slice(ic.sidr()),
|
SessionId::from_slice(&ic.sidr),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// ICR2
|
// ICR2
|
||||||
core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?;
|
core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?;
|
||||||
|
|
||||||
// ICR3
|
// ICR3
|
||||||
core.mix(ic.sidi())?.mix(ic.sidr())?;
|
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
||||||
|
|
||||||
// ICR4
|
// ICR4
|
||||||
core.decrypt_and_mix(&mut [0u8; 0], ic.auth())?;
|
core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?;
|
||||||
|
|
||||||
// ICR5
|
// ICR5
|
||||||
if constant_time::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 {
|
if constant_time::compare(&*biscuit_no, &*peer.get(self).biscuit_used) > 0 {
|
||||||
@@ -1682,19 +1684,19 @@ impl CryptoServer {
|
|||||||
.get_mut(self)
|
.get_mut(self)
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.context("Cannot send acknowledgement. No session.")?;
|
.context("Cannot send acknowledgement. No session.")?;
|
||||||
rc.sid_mut().copy_from_slice(&ses.sidt.value);
|
rc.sid.copy_from_slice(&ses.sidt.value);
|
||||||
rc.ctr_mut().copy_from_slice(&ses.txnm.to_le_bytes());
|
rc.ctr.copy_from_slice(&ses.txnm.to_le_bytes());
|
||||||
ses.txnm += 1; // Increment nonce before encryption, just in case an error is raised
|
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();
|
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)
|
Ok(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_resp_conf(&mut self, rc: EmptyData<&[u8]>) -> Result<PeerPtr> {
|
pub fn handle_resp_conf(&mut self, rc: &EmptyData) -> Result<PeerPtr> {
|
||||||
let sid = SessionId::from_slice(rc.sid());
|
let sid = SessionId::from_slice(&rc.sid);
|
||||||
let hs = self
|
let hs = self
|
||||||
.lookup_handshake(sid)
|
.lookup_handshake(sid)
|
||||||
.with_context(|| format!("Got RespConf packet for non-existent session {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
|
// the unwrap can not fail, because the slice returned by ctr() is
|
||||||
// guaranteed to have the correct size
|
// 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");
|
ensure!(n >= s.txnt, "Stale nonce");
|
||||||
s.txnt = n;
|
s.txnt = n;
|
||||||
aead::decrypt(
|
aead::decrypt(
|
||||||
// pt, k, n, ad, ct
|
// pt, k, n, ad, ct
|
||||||
&mut [0u8; 0],
|
&mut [0u8; 0],
|
||||||
s.txkt.secret(),
|
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<T: FromBytes>(buf: &mut [u8]) -> Result<Ref<&mut [u8], T>, RosenpassError> {
|
||||||
|
Ok(Ref::new(&mut buf[..size_of::<T>()]).ok_or(RosenpassError::BufferSizeMismatch)?)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user