This commit is contained in:
Jan Winkelmann (keks)
2025-02-21 14:56:35 +01:00
parent 2ddd1488b3
commit 44e46895aa
19 changed files with 310 additions and 126 deletions

View File

@@ -1,7 +1,11 @@
pub trait KeyedHash<const KEY_LEN: usize, const HASH_LEN: usize> {
type Error;
fn keyed_hash(key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>;
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error>;
}
pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
@@ -9,5 +13,10 @@ pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
type OutputType;
type Error;
fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>;
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error>;
}

View File

@@ -4,9 +4,9 @@ use rosenpass_to::To;
use crate::keyed_hash as hash;
use crate::subtle::either_hash::EitherShakeOrBlake;
pub use hash::KEY_LEN;
use rosenpass_cipher_traits::KeyedHashInstance;
use crate::subtle::either_hash::EitherShakeOrBlake;
///
///```rust
@@ -125,7 +125,11 @@ impl SecretHashDomain {
/// as the content for the new [SecretHashDomain].
/// Both `k` and `d` have to be exactly [KEY_LEN] bytes in length.
/// TODO: docu
pub fn invoke_primitive(k: &[u8], d: &[u8], hash_choice: EitherShakeOrBlake) -> Result<SecretHashDomain> {
pub fn invoke_primitive(
k: &[u8],
d: &[u8],
hash_choice: EitherShakeOrBlake,
) -> Result<SecretHashDomain> {
let mut new_secret_key = Secret::zero();
hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?;
let mut r = SecretHashDomain(new_secret_key, hash_choice);

View File

@@ -1,18 +1,22 @@
use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance};
use anyhow::Result;
use crate::subtle::hash_functions::keyed_shake256::SHAKE256Core;
use crate::subtle::incorrect_hmac_blake2b::Blake2bCore;
use anyhow::Result;
use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance};
#[derive(Debug, Eq, PartialEq)]
pub enum EitherHash<const KEY_LEN: usize, const HASH_LEN: usize, Error,
pub enum EitherHash<
const KEY_LEN: usize,
const HASH_LEN: usize,
Error,
L: KeyedHash<KEY_LEN, HASH_LEN, Error = Error>,
R: KeyedHash<KEY_LEN, HASH_LEN, Error = Error>>
{
R: KeyedHash<KEY_LEN, HASH_LEN, Error = Error>,
> {
Left(L),
Right(R),
}
impl<const KEY_LEN: usize, const HASH_LEN: usize, Error, L, R> KeyedHashInstance<KEY_LEN, HASH_LEN> for EitherHash<KEY_LEN, HASH_LEN, Error, L, R>
impl<const KEY_LEN: usize, const HASH_LEN: usize, Error, L, R> KeyedHashInstance<KEY_LEN, HASH_LEN>
for EitherHash<KEY_LEN, HASH_LEN, Error, L, R>
where
L: KeyedHash<KEY_LEN, HASH_LEN, Error = Error>,
R: KeyedHash<KEY_LEN, HASH_LEN, Error = Error>,
@@ -21,7 +25,12 @@ where
type OutputType = [u8; HASH_LEN];
type Error = Error;
fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error> {
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
match self {
Self::Left(_) => L::keyed_hash(key, data, out),
Self::Right(_) => R::keyed_hash(key, data, out),

View File

@@ -1,8 +1,8 @@
use anyhow::ensure;
use zeroize::Zeroizing;
use rosenpass_cipher_traits::KeyedHash;
use rosenpass_constant_time::xor;
use rosenpass_to::{ops::copy_slice, with_destination, To};
use zeroize::Zeroizing;
use crate::subtle::hash_functions::blake2b;
use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash;

View File

@@ -1,6 +1,6 @@
use std::marker::PhantomData;
use anyhow::Result;
use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance};
use std::marker::PhantomData;
/// This is a helper to allow for type parameter inference when calling functions
/// that need a [KeyedHash].
@@ -19,11 +19,13 @@ impl<Static, const KEY_LEN: usize, const HASH_LEN: usize> InferKeyedHash<Static,
where
Static: KeyedHash<KEY_LEN, HASH_LEN, Error = anyhow::Error>,
{
pub const KEY_LEN : usize = KEY_LEN;
pub const KEY_LEN: usize = KEY_LEN;
pub const HASH_LEN: usize = HASH_LEN;
pub const fn new() -> Self {
Self { _phantom_keyed_hasher: PhantomData }
Self {
_phantom_keyed_hasher: PhantomData,
}
}
/// This just forwards to [KeyedHash::keyed_hash] of the type parameter `Static`
@@ -45,7 +47,12 @@ where
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize, Static: KeyedHash<KEY_LEN, HASH_LEN, Error = anyhow::Error>> KeyedHashInstance<KEY_LEN, HASH_LEN> for InferKeyedHash<Static, KEY_LEN, HASH_LEN> {
impl<
const KEY_LEN: usize,
const HASH_LEN: usize,
Static: KeyedHash<KEY_LEN, HASH_LEN, Error = anyhow::Error>,
> KeyedHashInstance<KEY_LEN, HASH_LEN> for InferKeyedHash<Static, KEY_LEN, HASH_LEN>
{
type KeyType = [u8; KEY_LEN];
type OutputType = [u8; HASH_LEN];
type Error = anyhow::Error;
@@ -57,7 +64,8 @@ impl<const KEY_LEN: usize, const HASH_LEN: usize, Static: KeyedHash<KEY_LEN, HAS
/// Helper traits /////////////////////////////////////////////
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Default for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Default
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN, Error = anyhow::Error>,
{
@@ -66,7 +74,8 @@ where
}
}
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Clone for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Clone
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN, Error = anyhow::Error>,
{
@@ -75,7 +84,8 @@ where
}
}
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Copy for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Copy
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN, Error = anyhow::Error>,
{

View File

@@ -1,13 +1,15 @@
use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash;
use anyhow::ensure;
use rosenpass_cipher_traits::KeyedHash;
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::Shake256;
use rosenpass_cipher_traits::KeyedHash;
use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SHAKE256Core<const KEY_LEN: usize, const HASH_LEN: usize>;
impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN> for SHAKE256Core<KEY_LEN, HASH_LEN> {
impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN>
for SHAKE256Core<KEY_LEN, HASH_LEN>
{
type Error = anyhow::Error;
/// TODO: Rework test
@@ -36,7 +38,11 @@ impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN> f
/// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87];
/// # assert_eq!(hash_data, expected_hash);
/// ```
fn keyed_hash(key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error> {
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
// Since SHAKE256 is a XOF, we fix the output length manually to what is required for the
// protocol.
ensure!(out.len() == HASH_LEN);
@@ -59,7 +65,6 @@ impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN> f
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize> SHAKE256Core<KEY_LEN, HASH_LEN> {
pub fn new() -> Self {
Self
@@ -83,7 +88,7 @@ impl<const KEY_LEN: usize, const HASH_LEN: usize> SHAKE256Core<KEY_LEN, HASH_LEN
/// # assert_eq!(hash_data, expected_hash);
/// ```
pub type SHAKE256<const KEY_LEN: usize, const HASH_LEN: usize> =
InferKeyedHash<SHAKE256Core<KEY_LEN, HASH_LEN>, KEY_LEN, HASH_LEN>;
InferKeyedHash<SHAKE256Core<KEY_LEN, HASH_LEN>, KEY_LEN, HASH_LEN>;
/// TODO: Documentation and more interesting test
/// ```rust
@@ -102,4 +107,3 @@ InferKeyedHash<SHAKE256Core<KEY_LEN, HASH_LEN>, KEY_LEN, HASH_LEN>;
/// # assert_eq!(hash_data, expected_hash);
/// ```
pub type SHAKE256_32 = SHAKE256<32, 32>;

View File

@@ -1,4 +1,4 @@
pub mod blake2b;
pub mod incorrect_hmac_blake2b;
pub mod keyed_shake256;
mod infer_keyed_hash;
pub mod keyed_shake256;

View File

@@ -8,8 +8,8 @@
pub mod chacha20poly1305_ietf;
#[cfg(feature = "experiment_libcrux")]
pub mod chacha20poly1305_ietf_libcrux;
pub mod xchacha20poly1305_ietf;
mod hash_functions;
pub mod either_hash;
mod hash_functions;
pub mod xchacha20poly1305_ietf;
pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256};

1
initial Submodule

Submodule initial added at f1122f1f62

View File

@@ -1,5 +1,7 @@
use anyhow::Result;
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey};
use rosenpass::protocol::{
CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey,
};
use std::ops::DerefMut;
use rosenpass_cipher_traits::Kem;

View File

@@ -42,6 +42,7 @@ use std::slice;
use std::time::Duration;
use std::time::Instant;
use crate::config::ProtocolVersion;
use crate::protocol::BuildCryptoServer;
use crate::protocol::HostIdentification;
use crate::{
@@ -50,7 +51,6 @@ use crate::{
};
use rosenpass_util::attempt;
use rosenpass_util::b64::B64Display;
use crate::config::ProtocolVersion;
/// The maximum size of a base64 encoded symmetric key (estimate)
pub const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3;
@@ -1043,7 +1043,7 @@ impl AppServer {
outfile: Option<PathBuf>,
broker_peer: Option<BrokerPeer>,
hostname: Option<String>,
protocol_version: ProtocolVersion
protocol_version: ProtocolVersion,
) -> anyhow::Result<AppPeerPtr> {
let PeerPtr(pn) = match &mut self.crypto_site {
ConstructionSite::Void => bail!("Crypto server construction site is void"),

View File

@@ -1,10 +1,10 @@
use anyhow::{Context, Result};
use heck::ToShoutySnakeCase;
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake;
use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore;
use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core;
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
/// Recursively calculate a concrete hash value for an API message type
fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]> {

View File

@@ -114,10 +114,11 @@ macro_rules! hash_domain {
pub fn protocol(hash_choice: EitherShakeOrBlake) -> Result<HashDomain> {
// TODO: Update this string that is mixed in?
match hash_choice {
EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()),
EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()),
EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice)
.mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()),
EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice)
.mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()),
}
}
hash_domain_ns!(

View File

@@ -1,11 +1,11 @@
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
use crate::config::ProtocolVersion;
use rosenpass_util::{
build::Build,
mem::{DiscardResultExt, SwapWithDefaultExt},
result::ensure_or,
};
use thiserror::Error;
use crate::config::ProtocolVersion;
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
#[derive(Debug, Clone)]
/// A pair of matching public/secret keys used to launch the crypto server.
@@ -187,7 +187,15 @@ impl Build<CryptoServer> for BuildCryptoServer {
let mut srv = CryptoServer::new(sk, pk);
for (idx, PeerParams { psk, pk , protocol_version}) in self.peers.into_iter().enumerate() {
for (
idx,
PeerParams {
psk,
pk,
protocol_version,
},
) in self.peers.into_iter().enumerate()
{
let PeerPtr(idx2) = srv.add_peer(psk, pk, protocol_version.into())?;
assert!(idx == idx2, "Peer id changed during CryptoServer construction from {idx} to {idx2}. This is a developer error.")
}
@@ -337,14 +345,28 @@ impl BuildCryptoServer {
/// assert_eq!(peer.spkt, public_key);
/// assert_eq!(peer_psk.secret(), pre_shared_key.secret());
/// ```
pub fn with_added_peer(&mut self, psk: Option<SymKey>, pk: SPk, protocol_version: ProtocolVersion) -> &mut Self {
pub fn with_added_peer(
&mut self,
psk: Option<SymKey>,
pk: SPk,
protocol_version: ProtocolVersion,
) -> &mut Self {
// TODO: Check here already whether peer was already added
self.peers.push(PeerParams { psk, pk, protocol_version });
self.peers.push(PeerParams {
psk,
pk,
protocol_version,
});
self
}
/// Add a new entry to the list of registered peers, with or without a pre-shared key.
pub fn add_peer(&mut self, psk: Option<SymKey>, pk: SPk, protocol_version: ProtocolVersion) -> PeerPtr {
pub fn add_peer(
&mut self,
psk: Option<SymKey>,
pk: SPk,
protocol_version: ProtocolVersion,
) -> PeerPtr {
let id = PeerPtr(self.peers.len());
self.with_added_peer(psk, pk, protocol_version);
id

View File

@@ -19,11 +19,15 @@ use std::{
use anyhow::{bail, ensure, Context, Result};
use rand::Fill as Randomize;
use crate::{hash_domains, msgs::*, RosenpassError};
use memoffset::span_of;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace};
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
use rosenpass_ciphers::keyed_hash;
use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake;
use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore;
use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core;
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
use rosenpass_constant_time as constant_time;
use rosenpass_secret_memory::{Public, PublicBox, Secret};
@@ -33,10 +37,6 @@ use rosenpass_util::functional::ApplyExt;
use rosenpass_util::mem::DiscardResultExt;
use rosenpass_util::{cat, mem::cpy_min, time::Timebase};
use zerocopy::{AsBytes, FromBytes, Ref};
use rosenpass_ciphers::subtle::either_hash::{EitherShakeOrBlake};
use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore;
use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core;
use crate::{hash_domains, msgs::*, RosenpassError};
// CONSTANTS & SETTINGS //////////////////////////
@@ -369,7 +369,7 @@ pub enum IndexKey {
#[derive(Debug, Clone)]
pub enum ProtocolVersion {
V02,
V03
V03,
}
impl ProtocolVersion {
@@ -1460,7 +1460,12 @@ impl CryptoServer {
///
/// Ok::<(), anyhow::Error>(())
/// ```
pub fn add_peer(&mut self, psk: Option<SymKey>, pk: SPk, protocol_version: ProtocolVersion) -> Result<PeerPtr> {
pub fn add_peer(
&mut self,
psk: Option<SymKey>,
pk: SPk,
protocol_version: ProtocolVersion,
) -> Result<PeerPtr> {
let peer = Peer {
psk: psk.unwrap_or_else(SymKey::zero),
spkt: pk,
@@ -2314,14 +2319,22 @@ impl CryptoServer {
// At this point, we do not know the hash functon used by the peer, thus we try both,
// with a preference for SHAKE256.
let peer_shake256 = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core));
let peer_shake256 = self.handle_init_hello(
&msg_in.payload,
&mut msg_out.payload,
EitherShakeOrBlake::Left(SHAKE256Core),
);
let (peer, peer_hash_choice) = match peer_shake256 {
Ok(peer ) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)),
Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)),
Err(_) => {
let peer_blake2b = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore));
let peer_blake2b = self.handle_init_hello(
&msg_in.payload,
&mut msg_out.payload,
EitherShakeOrBlake::Right(Blake2bCore),
);
match peer_blake2b {
Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)),
Err(_) => bail!("No valid hash function found for InitHello")
Err(_) => bail!("No valid hash function found for InitHello"),
}
}
};
@@ -2340,7 +2353,10 @@ impl CryptoServer {
let mut msg_out = truncating_cast_into::<Envelope<InitConf>>(tx_buf)?;
let peer = self.handle_resp_hello(&msg_in.payload, &mut msg_out.payload)?;
ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken);
ensure!(
msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?,
seal_broken
);
len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?;
peer.hs()
@@ -2352,7 +2368,6 @@ impl CryptoServer {
let msg_in: Ref<&[u8], Envelope<InitConf>> =
Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?;
let mut msg_out = truncating_cast_into::<Envelope<EmptyData>>(tx_buf)?;
// Check if we have a cached response
@@ -2360,7 +2375,13 @@ impl CryptoServer {
// Cached response; copy out of cache
Some(cached) => {
let peer = cached.peer();
ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken);
ensure!(
msg_in.check_seal(
self,
peer.get(self).protocol_version.shake_or_blake()
)?,
seal_broken
);
let cached = cached
.get(self)
.map(|v| v.response.borrow())
@@ -2374,14 +2395,22 @@ impl CryptoServer {
None => {
// At this point, we do not know the hash functon used by the peer, thus we try both,
// with a preference for SHAKE256.
let peer_shake256 = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core));
let peer_shake256 = self.handle_init_conf(
&msg_in.payload,
&mut msg_out.payload,
EitherShakeOrBlake::Left(SHAKE256Core),
);
let (peer, peer_hash_choice) = match peer_shake256 {
Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)),
Err(_) => {
let peer_blake2b = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore));
let peer_blake2b = self.handle_init_conf(
&msg_in.payload,
&mut msg_out.payload,
EitherShakeOrBlake::Right(Blake2bCore),
);
match peer_blake2b {
Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)),
Err(_) => bail!("No valid hash function found for InitHello")
Err(_) => bail!("No valid hash function found for InitHello"),
}
}
};
@@ -2430,7 +2459,11 @@ impl CryptoServer {
}
/// TODO documentation
fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: EitherShakeOrBlake) -> Result<()> {
fn verify_hash_choice_match(
&self,
peer: PeerPtr,
peer_hash_choice: EitherShakeOrBlake,
) -> Result<()> {
match peer.get(self).protocol_version.shake_or_blake() {
EitherShakeOrBlake::Left(SHAKE256Core) => match peer_hash_choice {
EitherShakeOrBlake::Left(SHAKE256Core) => Ok(()),
@@ -2439,7 +2472,7 @@ impl CryptoServer {
EitherShakeOrBlake::Right(Blake2bCore) => match peer_hash_choice {
EitherShakeOrBlake::Left(SHAKE256Core) => bail!("Hash function mismatch"),
EitherShakeOrBlake::Right(Blake2bCore) => Ok(()),
}
},
}
}
@@ -3209,7 +3242,11 @@ where
M: AsBytes + FromBytes,
{
/// Internal business logic: Check the message authentication code produced by [Self::seal]
pub fn check_seal(&self, srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Result<bool> {
pub fn check_seal(
&self,
srv: &CryptoServer,
shake_or_blake: EitherShakeOrBlake,
) -> Result<bool> {
let expected = hash_domains::mac(shake_or_blake)?
.mix(srv.spkm.deref())?
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
@@ -3257,21 +3294,31 @@ impl HandshakeState {
/// Initialize the handshake state with the responder public key and the protocol domain
/// separator
pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> {
self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())?.turn_secret().mix(spkr)?.dup();
self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())?
.turn_secret()
.mix(spkr)?
.dup();
Ok(self)
}
/// Mix some data into the chaining key. This is used for mixing cryptographic keys and public
/// data alike into the chaining key
pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> {
self.ck = self.ck.mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)?.mix(a)?.dup();
self.ck = self
.ck
.mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)?
.mix(a)?
.dup();
Ok(self)
}
/// Encrypt some data with a value derived from the current chaining key and mix that data
/// into the protocol state.
pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> {
let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret();
let k = self
.ck
.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?
.into_secret();
aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?;
self.mix(ct)
}
@@ -3281,7 +3328,10 @@ impl HandshakeState {
/// Makes sure that the same values are mixed into the chaining that where mixed in on the
/// sender side.
pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> {
let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret();
let k = self
.ck
.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?
.into_secret();
aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?;
self.mix(ct)
}
@@ -3373,7 +3423,7 @@ impl HandshakeState {
biscuit_ct: &[u8],
sidi: SessionId,
sidr: SessionId,
shake_or_blake: EitherShakeOrBlake
shake_or_blake: EitherShakeOrBlake,
) -> Result<(PeerPtr, BiscuitId, HandshakeState)> {
// The first bit of the biscuit indicates which biscuit key was used
let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize);
@@ -3404,7 +3454,11 @@ impl HandshakeState {
.find_peer(pid) // TODO: FindPeer should return a Result<()>
.with_context(|| format!("Could not decode biscuit for peer {pid:?}: No such peer."))?;
let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck), peer.get(srv).protocol_version.shake_or_blake()).dup();
let ck = SecretHashDomain::danger_from_secret(
Secret::from_slice(&biscuit.ck),
peer.get(srv).protocol_version.shake_or_blake(),
)
.dup();
// Reconstruct the handshake state
let mut hs = Self { sidi, sidr, ck };
hs.mix(biscuit_ct)?;
@@ -3417,10 +3471,19 @@ impl HandshakeState {
/// This called by either party.
///
/// `role` indicates whether the local peer was an initiator or responder in the handshake.
pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole, either_shake_or_blake: EitherShakeOrBlake) -> Result<Session> {
pub fn enter_live(
self,
srv: &CryptoServer,
role: HandshakeRole,
either_shake_or_blake: EitherShakeOrBlake,
) -> Result<Session> {
let HandshakeState { ck, sidi, sidr } = self;
let tki = ck.mix(&hash_domains::ini_enc(either_shake_or_blake.clone())?)?.into_secret();
let tkr = ck.mix(&hash_domains::res_enc(either_shake_or_blake)?)?.into_secret();
let tki = ck
.mix(&hash_domains::ini_enc(either_shake_or_blake.clone())?)?
.into_secret();
let tkr = ck
.mix(&hash_domains::res_enc(either_shake_or_blake)?)?
.into_secret();
let created_at = srv.timebase.now();
let (ntx, nrx) = (0, 0);
let (mysid, peersid, ktx, krx) = match role {
@@ -3458,7 +3521,12 @@ impl CryptoServer {
.get(self)
.as_ref()
.with_context(|| format!("No current session for peer {:?}", peer))?;
Ok(session.ck.mix(&hash_domains::osk(peer.get(self).protocol_version.shake_or_blake())?)?.into_secret())
Ok(session
.ck
.mix(&hash_domains::osk(
peer.get(self).protocol_version.shake_or_blake(),
)?)?
.into_secret())
}
}
@@ -3466,7 +3534,10 @@ impl CryptoServer {
/// Core cryptographic protocol implementation: Kicks of the handshake
/// on the initiator side, producing the InitHello message.
pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result<PeerPtr> {
let mut hs = InitiatorHandshake::zero_with_timestamp(self, peer.get(self).protocol_version.shake_or_blake());
let mut hs = InitiatorHandshake::zero_with_timestamp(
self,
peer.get(self).protocol_version.shake_or_blake(),
);
// IHI1
hs.core.init(peer.get(self).spkt.deref())?;
@@ -3490,8 +3561,11 @@ impl CryptoServer {
)?;
// IHI6
hs.core
.encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm(peer.get(self).protocol_version.shake_or_blake())?.as_ref())?;
hs.core.encrypt_and_mix(
ih.pidic.as_mut_slice(),
self.pidm(peer.get(self).protocol_version.shake_or_blake())?
.as_ref(),
)?;
// IHI7
hs.core
@@ -3510,7 +3584,12 @@ impl CryptoServer {
/// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a
/// [RespHello] message on the responder side.
/// TODO: Document Hash Functon usage
pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello, shake_or_blake: EitherShakeOrBlake) -> Result<PeerPtr> {
pub fn handle_init_hello(
&mut self,
ih: &InitHello,
rh: &mut RespHello,
shake_or_blake: EitherShakeOrBlake,
) -> Result<PeerPtr> {
let mut core = HandshakeState::zero(shake_or_blake);
core.sidi = SessionId::from_slice(&ih.sidi);
@@ -3655,8 +3734,14 @@ impl CryptoServer {
// we still need it for InitConf message retransmission to function.
// ICI7
peer.session()
.insert(self, core.enter_live(self, HandshakeRole::Initiator, peer.get(self).protocol_version.shake_or_blake())?)?;
peer.session().insert(
self,
core.enter_live(
self,
HandshakeRole::Initiator,
peer.get(self).protocol_version.shake_or_blake(),
)?,
)?;
hs_mut!().core.erase();
hs_mut!().next = HandshakeStateMachine::RespConf;
@@ -3669,7 +3754,12 @@ impl CryptoServer {
/// This concludes the handshake on the cryptographic level; the [EmptyData] message is just
/// an acknowledgement message telling the initiator to stop performing retransmissions.
/// TODO: documentation
pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData, shake_or_blake: EitherShakeOrBlake) -> Result<PeerPtr> {
pub fn handle_init_conf(
&mut self,
ic: &InitConf,
rc: &mut EmptyData,
shake_or_blake: EitherShakeOrBlake,
) -> Result<PeerPtr> {
// (peer, bn) ← LoadBiscuit(InitConf.biscuit)
// ICR1
let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit(
@@ -3702,8 +3792,14 @@ impl CryptoServer {
peer.get_mut(self).biscuit_used = biscuit_no;
// ICR7
peer.session()
.insert(self, core.enter_live(self, HandshakeRole::Responder, peer.get(self).protocol_version.shake_or_blake())?)?;
peer.session().insert(
self,
core.enter_live(
self,
HandshakeRole::Responder,
peer.get(self).protocol_version.shake_or_blake(),
)?,
)?;
// TODO: This should be part of the protocol specification.
// Abort any ongoing handshake from initiator role
peer.hs().take(self);
@@ -3751,13 +3847,20 @@ impl CryptoServer {
/// message then terminates the handshake.
///
/// The EmptyData message is just there to tell the initiator to abort retransmissions.
pub fn handle_resp_conf(&mut self, msg_in: &Ref<&[u8], Envelope<EmptyData>>, seal_broken: String) -> Result<PeerPtr> {
pub fn handle_resp_conf(
&mut self,
msg_in: &Ref<&[u8], Envelope<EmptyData>>,
seal_broken: String,
) -> Result<PeerPtr> {
let rc: &EmptyData = &msg_in.payload;
let sid = SessionId::from_slice(&rc.sid);
let hs = self
.lookup_handshake(sid)
.with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?;
ensure!(msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?, seal_broken);
ensure!(
msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?,
seal_broken
);
let ses = hs.peer().session();
let exp = hs.get(self).as_ref().map(|h| h.next);
@@ -3840,7 +3943,9 @@ impl CryptoServer {
}?;
let spkt = peer.get(self).spkt.deref();
let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))?.mix(spkt)?.into_value();
let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))?
.mix(spkt)?
.into_value();
let cookie_value = peer.cv().update_mut(self).unwrap();
xaead::decrypt(cookie_value, &cookie_key, &mac, &cr.inner.cookie_encrypted)?;
@@ -3969,7 +4074,6 @@ mod test {
handles_incorrect_size_messages(ProtocolVersion::V03)
}
/// Ensure that the protocol implementation can deal with truncated
/// messages and with overlong messages.
///
@@ -4268,20 +4372,21 @@ mod test {
assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young);
let expected_cookie_value = hash_domains::cookie_value(protocol_version.shake_or_blake())
.unwrap()
.mix(
b.active_or_retired_cookie_secrets()[0]
.unwrap()
.get(&b)
.value
.secret(),
)
.unwrap()
.mix(ip_addr_port_a.encode())
.unwrap()
.into_value()[..16]
.to_vec();
let expected_cookie_value =
hash_domains::cookie_value(protocol_version.shake_or_blake())
.unwrap()
.mix(
b.active_or_retired_cookie_secrets()[0]
.unwrap()
.get(&b)
.value
.secret(),
)
.unwrap()
.mix(ip_addr_port_a.encode())
.unwrap()
.into_value()[..16]
.to_vec();
assert_eq!(
PeerPtr(0).cv().get(&a).map(|x| &x.value.secret()[..]),
@@ -4331,7 +4436,9 @@ mod test {
cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03)
}
fn cookie_reply_mechanism_initiator_bails_on_message_under_load(protocol_version: ProtocolVersion) {
fn cookie_reply_mechanism_initiator_bails_on_message_under_load(
protocol_version: ProtocolVersion,
) {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || {
@@ -4409,10 +4516,7 @@ mod test {
Ok((sk, pk))
}
fn proc_initiation(
srv: &mut CryptoServer,
peer: PeerPtr,
) -> Result<Envelope<InitHello>> {
fn proc_initiation(srv: &mut CryptoServer, peer: PeerPtr) -> Result<Envelope<InitHello>> {
let mut buf = MsgBuf::zero();
srv.initiate_handshake(peer, buf.as_mut_slice())?
.discard_result();
@@ -4483,7 +4587,11 @@ mod test {
}
// we this as a closure in orer to use the protocol_version variable in it.
let check_retransmission = |srv: &mut CryptoServer, ic: &Envelope<InitConf>, ic_broken: &Envelope<InitConf>, rc: &Envelope<EmptyData>| -> Result<()> {
let check_retransmission = |srv: &mut CryptoServer,
ic: &Envelope<InitConf>,
ic_broken: &Envelope<InitConf>,
rc: &Envelope<EmptyData>|
-> Result<()> {
// Processing the same RespHello package again leads to retransmission (i.e. exactly the
// same output)
let rc_dup = proc_init_conf(srv, ic)?;
@@ -4492,7 +4600,11 @@ mod test {
// Though if we directly call handle_resp_hello() we get an error since
// retransmission is not being handled by the cryptographic code
let mut discard_resp_conf = EmptyData::new_zeroed();
let res = srv.handle_init_conf(&ic.payload, &mut discard_resp_conf, protocol_version.clone().shake_or_blake());
let res = srv.handle_init_conf(
&ic.payload,
&mut discard_resp_conf,
protocol_version.clone().shake_or_blake(),
);
assert!(res.is_err());
// Obviously, a broken InitConf message should still be rejected

View File

@@ -14,6 +14,8 @@ use rosenpass::api::{
self, add_listen_socket_response_status, add_psk_broker_response_status,
supply_keypair_response_status,
};
use rosenpass::config::ProtocolVersion;
use rosenpass::protocol::SymKey;
use rosenpass_util::{
b64::B64Display,
file::LoadValueB64,
@@ -26,8 +28,6 @@ use rosenpass_util::{
use std::os::fd::{AsFd, AsRawFd};
use tempfile::TempDir;
use zerocopy::AsBytes;
use rosenpass::config::ProtocolVersion;
use rosenpass::protocol::SymKey;
struct KillChild(std::process::Child);

View File

@@ -16,8 +16,8 @@ use rosenpass_util::{mem::DiscardResultExt, zerocopy::ZerocopySliceExt};
use tempfile::TempDir;
use zerocopy::AsBytes;
use rosenpass::protocol::SymKey;
use rosenpass::config::ProtocolVersion;
use rosenpass::protocol::SymKey;
struct KillChild(std::process::Child);
@@ -46,7 +46,6 @@ fn api_integration_test_v03() -> anyhow::Result<()> {
api_integration_test(ProtocolVersion::V03)
}
fn api_integration_test(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();

View File

@@ -9,11 +9,11 @@ use std::{
};
use anyhow::ensure;
use rosenpass::config::ProtocolVersion;
use rosenpass::{
app_server::{ipv4_any_binding, ipv6_any_binding, AppServer, AppServerTest, MAX_B64_KEY_SIZE},
protocol::{SPk, SSk, SymKey},
};
use rosenpass::config::ProtocolVersion;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_secret_memory::Secret;
@@ -66,8 +66,14 @@ fn key_exchange_with_app_server(protocol_version: ProtocolVersion) -> anyhow::Re
let outfile = Some(osk);
let port = otr_port;
let hostname = is_client.then(|| format!("[::1]:{port}"));
srv.app_srv
.add_peer(psk, pk, outfile, broker_peer, hostname, protocol_version.clone())?;
srv.app_srv.add_peer(
psk,
pk,
outfile,
broker_peer,
hostname,
protocol_version.clone(),
)?;
srv.app_srv.event_loop()
})

View File

@@ -9,7 +9,10 @@ use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_util::result::OkExt;
use rosenpass::protocol::{testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING};
use rosenpass::protocol::{
testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult,
ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING,
};
// TODO: Most of the utility functions in here should probably be moved to
// rosenpass::protocol::testutils;
@@ -94,7 +97,9 @@ fn test_successful_exchange_under_packet_loss_v03() -> anyhow::Result<()> {
test_successful_exchange_under_packet_loss(ProtocolVersion::V03)
}
fn test_successful_exchange_under_packet_loss(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
fn test_successful_exchange_under_packet_loss(
protocol_version: ProtocolVersion,
) -> anyhow::Result<()> {
// Set security policy for storing secrets; choose the one that is faster for testing
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();