mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-27 22:13:12 -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-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"
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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-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 }
|
||||
|
||||
@@ -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<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
|
||||
//! 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<M> :=
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct Envelope<M: AsBytes + FromBytes> {
|
||||
/// [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<u8> 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::<Biscuit>();
|
||||
|
||||
/// Length in bytes of an encrypted Biscuit (cipher text)
|
||||
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,
|
||||
};
|
||||
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(
|
||||
<Envelope<(), InitHello<()>> as LenseView>::LEN,
|
||||
<Envelope<(), InitConf<()>> as LenseView>::LEN,
|
||||
size_of::<Envelope<InitHello>>(),
|
||||
size_of::<Envelope<InitConf>>()
|
||||
);
|
||||
|
||||
/// 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<usize> {
|
||||
let mut msg = tx_buf.envelope_truncating::<InitHello<()>>()?; // Envelope::<InitHello>::default(); // TODO
|
||||
self.handle_initiation(peer, msg.payload_mut().init_hello()?)?;
|
||||
let len = self.seal_and_commit_msg(peer, MsgType::InitHello, msg)?;
|
||||
// Envelope::<InitHello>::default(); // TODO
|
||||
let mut msg = truncating_cast_into::<Envelope<InitHello>>(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::<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);
|
||||
|
||||
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(
|
||||
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::<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);
|
||||
|
||||
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(
|
||||
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::<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);
|
||||
|
||||
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(
|
||||
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::<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);
|
||||
|
||||
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<M: LenseView>(
|
||||
pub fn seal_and_commit_msg<M: AsBytes + FromBytes>(
|
||||
&mut self,
|
||||
peer: PeerPtr,
|
||||
msg_type: MsgType,
|
||||
mut msg: Envelope<&mut [u8], M>,
|
||||
msg: &mut Ref<&mut [u8], Envelope<M>>,
|
||||
) -> Result<usize> {
|
||||
msg.msg_type_mut()[0] = msg_type as u8;
|
||||
msg.msg_type = msg_type as u8;
|
||||
msg.seal(peer, self)?;
|
||||
Ok(<Envelope<(), M> as LenseView>::LEN)
|
||||
Ok(size_of::<Envelope<M>>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,32 +1172,32 @@ impl IniHsPtr {
|
||||
|
||||
// CRYPTO/HANDSHAKE HANDLING /////////////////////
|
||||
|
||||
impl<M> Envelope<&mut [u8], M>
|
||||
impl<M> Envelope<M>
|
||||
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<M> Envelope<&[u8], M>
|
||||
impl<M> Envelope<M>
|
||||
where
|
||||
M: LenseView,
|
||||
M: AsBytes + FromBytes,
|
||||
{
|
||||
/// Check the message authentication code
|
||||
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
||||
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::<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
|
||||
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::<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(
|
||||
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<PeerPtr> {
|
||||
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::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
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<PeerPtr> {
|
||||
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::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
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::<EphemeralKem, { EphemeralKem::SHK_LEN }>(rh.ecti_mut(), ih.epki())?;
|
||||
core.encaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(&mut rh.ecti, &ih.epki)?;
|
||||
|
||||
// RHR5
|
||||
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
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<PeerPtr> {
|
||||
// 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::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
||||
hs!().eski.secret(),
|
||||
&*hs!().epki,
|
||||
rh.ecti(),
|
||||
&rh.ecti,
|
||||
)?;
|
||||
|
||||
// RHI5
|
||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
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<PeerPtr> {
|
||||
// (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<PeerPtr> {
|
||||
let sid = SessionId::from_slice(rc.sid());
|
||||
pub fn handle_resp_conf(&mut self, rc: &EmptyData) -> Result<PeerPtr> {
|
||||
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<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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user