mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 06:23:08 -08:00
dev(rosenpass): add support for the shake256 hash function in the rosenpass crate
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey};
|
||||
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
@@ -45,21 +45,30 @@ fn keygen() -> Result<(SSk, SPk)> {
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
|
||||
fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> {
|
||||
let psk = SymKey::random();
|
||||
let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?);
|
||||
let (mut a, mut b) = (
|
||||
CryptoServer::new(ska, pka.clone()),
|
||||
CryptoServer::new(skb, pkb.clone()),
|
||||
);
|
||||
a.add_peer(Some(psk.clone()), pkb)?;
|
||||
b.add_peer(Some(psk), pka)?;
|
||||
// TODO: Adapt this to both protocol versions
|
||||
a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?;
|
||||
b.add_peer(Some(psk), pka, protocol_version)?;
|
||||
Ok((a, b))
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
fn criterion_benchmark_v02(c: &mut Criterion) {
|
||||
criterion_benchmark(c, ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
fn criterion_benchmark_v03(c: &mut Criterion) {
|
||||
criterion_benchmark(c, ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion, protocol_version: ProtocolVersion) {
|
||||
secret_policy_try_use_memfd_secrets();
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
let (mut a, mut b) = make_server_pair(protocol_version).unwrap();
|
||||
c.bench_function("cca_secret_alloc", |bench| {
|
||||
bench.iter(|| {
|
||||
SSk::zero();
|
||||
@@ -82,5 +91,6 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
criterion_group!(benches_v02, criterion_benchmark_v02);
|
||||
criterion_group!(benches_v03, criterion_benchmark_v03);
|
||||
criterion_main!(benches_v02, benches_v03);
|
||||
|
||||
@@ -50,6 +50,7 @@ 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;
|
||||
@@ -1042,11 +1043,12 @@ impl AppServer {
|
||||
outfile: Option<PathBuf>,
|
||||
broker_peer: Option<BrokerPeer>,
|
||||
hostname: Option<String>,
|
||||
protocol_version: ProtocolVersion
|
||||
) -> anyhow::Result<AppPeerPtr> {
|
||||
let PeerPtr(pn) = match &mut self.crypto_site {
|
||||
ConstructionSite::Void => bail!("Crypto server construction site is void"),
|
||||
ConstructionSite::Builder(builder) => builder.add_peer(psk, pk),
|
||||
ConstructionSite::Product(srv) => srv.add_peer(psk, pk)?,
|
||||
ConstructionSite::Builder(builder) => builder.add_peer(psk, pk, protocol_version),
|
||||
ConstructionSite::Product(srv) => srv.add_peer(psk, pk, protocol_version.into())?,
|
||||
};
|
||||
assert!(pn == self.peers.len());
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ 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;
|
||||
|
||||
/// Recursively calculate a concrete hash value for an API message type
|
||||
fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]> {
|
||||
@@ -12,8 +15,8 @@ fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]
|
||||
}
|
||||
|
||||
/// Print a hash literal for pasting into the Rosenpass source code
|
||||
fn print_literal(path: &[&str]) -> Result<()> {
|
||||
let val = calculate_hash_value(HashDomain::zero(), path)?;
|
||||
fn print_literal(path: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<()> {
|
||||
let val = calculate_hash_value(HashDomain::zero(shake_or_blake), path)?;
|
||||
let (last, prefix) = path.split_last().context("developer error!")?;
|
||||
let var_name = last.to_shouty_snake_case();
|
||||
|
||||
@@ -51,31 +54,33 @@ impl Tree {
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_code_inner(&self, prefix: &[&str]) -> Result<()> {
|
||||
fn gen_code_inner(&self, prefix: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<()> {
|
||||
let mut path = prefix.to_owned();
|
||||
path.push(self.name());
|
||||
|
||||
match self {
|
||||
Self::Branch(_, ref children) => {
|
||||
for c in children.iter() {
|
||||
c.gen_code_inner(&path)?
|
||||
c.gen_code_inner(&path, shake_or_blake.clone())?
|
||||
}
|
||||
}
|
||||
Self::Leaf(_) => print_literal(&path)?,
|
||||
Self::Leaf(_) => print_literal(&path, shake_or_blake)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_code(&self) -> Result<()> {
|
||||
self.gen_code_inner(&[])
|
||||
fn gen_code(&self, shake_or_blake: EitherShakeOrBlake) -> Result<()> {
|
||||
self.gen_code_inner(&[], shake_or_blake)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for generating hash-based message IDs for the IPC API
|
||||
fn main() -> Result<()> {
|
||||
|
||||
fn print_IPC_API_info(shake_or_blake: EitherShakeOrBlake, name: String) -> Result<()> {
|
||||
let tree = Tree::Branch(
|
||||
"Rosenpass IPC API".to_owned(),
|
||||
format!("Rosenpass IPC API {}", name).to_owned(),
|
||||
vec![Tree::Branch(
|
||||
"Rosenpass Protocol Server".to_owned(),
|
||||
vec![
|
||||
@@ -93,5 +98,9 @@ fn main() -> Result<()> {
|
||||
|
||||
println!("type RawMsgType = u128;");
|
||||
println!();
|
||||
tree.gen_code()
|
||||
tree.gen_code(shake_or_blake)
|
||||
}
|
||||
|
||||
print_IPC_API_info(EitherShakeOrBlake::Left(SHAKE256Core), " (SHAKE256)".to_owned())?;
|
||||
print_IPC_API_info(EitherShakeOrBlake::Right(Blake2bCore), " (Blake2b)".to_owned())
|
||||
}
|
||||
|
||||
@@ -490,6 +490,7 @@ impl CliArgs {
|
||||
cfg_peer.key_out,
|
||||
broker_peer,
|
||||
cfg_peer.endpoint.clone(),
|
||||
cfg_peer.protocol_version.into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,14 @@ pub enum Verbosity {
|
||||
Verbose,
|
||||
}
|
||||
|
||||
/// TODO: Documentation
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Default)]
|
||||
pub enum ProtocolVersion {
|
||||
#[default]
|
||||
V02,
|
||||
V03,
|
||||
}
|
||||
|
||||
/// Configuration data for a single Rosenpass peer
|
||||
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RosenpassPeer {
|
||||
@@ -138,6 +146,10 @@ pub struct RosenpassPeer {
|
||||
/// Information for supplying exchanged keys directly to WireGuard
|
||||
#[serde(flatten)]
|
||||
pub wg: Option<WireGuard>,
|
||||
|
||||
#[serde(default)]
|
||||
/// The protocol version to use for the exchange
|
||||
pub protocol_version: ProtocolVersion,
|
||||
}
|
||||
|
||||
/// Information for supplying exchanged keys directly to WireGuard
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
//! use rosenpass::{hash_domain, hash_domain_ns};
|
||||
//! use rosenpass::hash_domains::protocol;
|
||||
//!
|
||||
//! use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake;
|
||||
//!
|
||||
//! // Declaring a custom hash domain
|
||||
//! hash_domain_ns!(protocol, custom_domain, "my custom hash domain label");
|
||||
//!
|
||||
@@ -26,15 +28,18 @@
|
||||
//! hash_domain!(domain_separators, sep1, "1");
|
||||
//! hash_domain!(domain_separators, sep2, "2");
|
||||
//!
|
||||
//! // We use the SHAKE256 hash function for this example
|
||||
//! let hash_choice = EitherShakeOrBlake::Left(rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core);
|
||||
//!
|
||||
//! // Generating values under hasher1 with both domain separators
|
||||
//! let h1 = hasher1()?.mix(b"some data")?.dup();
|
||||
//! let h1v1 = h1.mix(&sep1()?)?.mix(b"More data")?.into_value();
|
||||
//! let h1v2 = h1.mix(&sep2()?)?.mix(b"More data")?.into_value();
|
||||
//! let h1 = hasher1(hash_choice.clone())?.mix(b"some data")?.dup();
|
||||
//! let h1v1 = h1.mix(&sep1(hash_choice.clone())?)?.mix(b"More data")?.into_value();
|
||||
//! let h1v2 = h1.mix(&sep2(hash_choice.clone())?)?.mix(b"More data")?.into_value();
|
||||
//!
|
||||
//! // Generating values under hasher2 with both domain separators
|
||||
//! let h2 = hasher2()?.mix(b"some data")?.dup();
|
||||
//! let h2v1 = h2.mix(&sep1()?)?.mix(b"More data")?.into_value();
|
||||
//! let h2v2 = h2.mix(&sep2()?)?.mix(b"More data")?.into_value();
|
||||
//! let h2 = hasher2(hash_choice.clone())?.mix(b"some data")?.dup();
|
||||
//! let h2v1 = h2.mix(&sep1(hash_choice.clone())?)?.mix(b"More data")?.into_value();
|
||||
//! let h2v2 = h2.mix(&sep2(hash_choice.clone())?)?.mix(b"More data")?.into_value();
|
||||
//!
|
||||
//! // All of the domain separators are now different, random strings
|
||||
//! let values = [h1v1, h1v2, h2v1, h2v2];
|
||||
@@ -49,6 +54,9 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use rosenpass_ciphers::hash_domain::HashDomain;
|
||||
use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake;
|
||||
use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore;
|
||||
use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core;
|
||||
|
||||
/// Declare a hash function
|
||||
///
|
||||
@@ -62,8 +70,8 @@ use rosenpass_ciphers::hash_domain::HashDomain;
|
||||
macro_rules! hash_domain_ns {
|
||||
($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => {
|
||||
$(#[$($attrss)*])*
|
||||
pub fn $name() -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> {
|
||||
let t = $base()?;
|
||||
pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> {
|
||||
let t = $base(hash_choice)?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t)
|
||||
}
|
||||
@@ -81,8 +89,8 @@ macro_rules! hash_domain_ns {
|
||||
macro_rules! hash_domain {
|
||||
($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => {
|
||||
$(#[$($attrss)*])*
|
||||
pub fn $name() -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> {
|
||||
let t = $base()?;
|
||||
pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> {
|
||||
let t = $base(hash_choice)?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t.into_value())
|
||||
}
|
||||
@@ -96,13 +104,20 @@ macro_rules! hash_domain {
|
||||
///
|
||||
/// This is generally used to create further hash-domains for specific purposes. See
|
||||
///
|
||||
/// TODO: Update documentation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// See the source file for details about how this is used concretely.
|
||||
///
|
||||
/// See the [module](self) documentation on how to use the hash domains in general
|
||||
pub fn protocol() -> Result<HashDomain> {
|
||||
HashDomain::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes())
|
||||
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()),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hash_domain_ns!(
|
||||
|
||||
@@ -4,7 +4,7 @@ use rosenpass_util::{
|
||||
result::ensure_or,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::config::ProtocolVersion;
|
||||
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -146,17 +146,18 @@ pub struct MissingKeypair;
|
||||
/// ```rust
|
||||
/// use rosenpass_util::build::Build;
|
||||
/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams, SPk, SymKey};
|
||||
/// use rosenpass::config::ProtocolVersion;
|
||||
///
|
||||
/// // We have to define the security policy before using Secrets.
|
||||
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
|
||||
/// secret_policy_use_only_malloc_secrets();
|
||||
///
|
||||
/// let keypair = Keypair::random();
|
||||
/// let peer1 = PeerParams { psk: Some(SymKey::random()), pk: SPk::random() };
|
||||
/// let peer2 = PeerParams { psk: None, pk: SPk::random() };
|
||||
/// let peer1 = PeerParams { psk: Some(SymKey::random()), pk: SPk::random(), protocol_version: ProtocolVersion::V02 };
|
||||
/// let peer2 = PeerParams { psk: None, pk: SPk::random(), protocol_version: ProtocolVersion::V02 };
|
||||
///
|
||||
/// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![peer1]);
|
||||
/// builder.add_peer(peer2.psk.clone(), peer2.pk);
|
||||
/// builder.add_peer(peer2.psk.clone(), peer2.pk, ProtocolVersion::V02);
|
||||
///
|
||||
/// let server = builder.build().expect("build failed");
|
||||
/// assert_eq!(server.peers.len(), 2);
|
||||
@@ -186,8 +187,8 @@ impl Build<CryptoServer> for BuildCryptoServer {
|
||||
|
||||
let mut srv = CryptoServer::new(sk, pk);
|
||||
|
||||
for (idx, PeerParams { psk, pk }) in self.peers.into_iter().enumerate() {
|
||||
let PeerPtr(idx2) = srv.add_peer(psk, pk)?;
|
||||
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.")
|
||||
}
|
||||
|
||||
@@ -208,6 +209,8 @@ pub struct PeerParams {
|
||||
pub psk: Option<SymKey>,
|
||||
/// Public key identifying the peer.
|
||||
pub pk: SPk,
|
||||
/// The used protocol version.
|
||||
pub protocol_version: ProtocolVersion,
|
||||
}
|
||||
|
||||
impl BuildCryptoServer {
|
||||
@@ -305,6 +308,7 @@ impl BuildCryptoServer {
|
||||
/// Adding peers to an existing builder:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass::config::ProtocolVersion;
|
||||
/// // We have to define the security policy before using Secrets.
|
||||
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
|
||||
/// secret_policy_use_only_malloc_secrets();
|
||||
@@ -323,7 +327,7 @@ impl BuildCryptoServer {
|
||||
/// // Now we've found a peer that should be added to the configuration
|
||||
/// let pre_shared_key = SymKey::random();
|
||||
/// let public_key = SPk::random();
|
||||
/// builder.with_added_peer(Some(pre_shared_key.clone()), public_key.clone());
|
||||
/// builder.with_added_peer(Some(pre_shared_key.clone()), public_key.clone(), ProtocolVersion::V02);
|
||||
///
|
||||
/// // New server instances will then start with the peer being registered already
|
||||
/// let server = builder.build().expect("build failed");
|
||||
@@ -333,16 +337,16 @@ 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) -> &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 });
|
||||
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) -> 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);
|
||||
self.with_added_peer(psk, pk, protocol_version);
|
||||
id
|
||||
}
|
||||
|
||||
@@ -356,6 +360,8 @@ impl BuildCryptoServer {
|
||||
///
|
||||
/// ```rust
|
||||
/// // We have to define the security policy before using Secrets.
|
||||
/// use rosenpass::config::ProtocolVersion;
|
||||
/// use rosenpass::hash_domains::protocol;
|
||||
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
|
||||
/// secret_policy_use_only_malloc_secrets();
|
||||
///
|
||||
@@ -365,7 +371,7 @@ impl BuildCryptoServer {
|
||||
/// let keypair = Keypair::random();
|
||||
/// let peer_pk = SPk::random();
|
||||
/// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![]);
|
||||
/// builder.add_peer(None, peer_pk);
|
||||
/// builder.add_peer(None, peer_pk, ProtocolVersion::V02);
|
||||
///
|
||||
/// // Extract configuration parameters from the decomissioned builder
|
||||
/// let (keypair_option, peers) = builder.take_parts();
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
//! # fn main() -> anyhow::Result<()> {
|
||||
//! // Set security policy for storing secrets
|
||||
//!
|
||||
//! use rosenpass::protocol::ProtocolVersion;
|
||||
//! secret_policy_try_use_memfd_secrets();
|
||||
//!
|
||||
//! // initialize secret and public key for peer a ...
|
||||
@@ -49,8 +50,8 @@
|
||||
//! let mut b = CryptoServer::new(peer_b_sk, peer_b_pk.clone());
|
||||
//!
|
||||
//! // introduce peers to each other
|
||||
//! a.add_peer(Some(psk.clone()), peer_b_pk)?;
|
||||
//! b.add_peer(Some(psk), peer_a_pk)?;
|
||||
//! a.add_peer(Some(psk.clone()), peer_b_pk, ProtocolVersion::V03)?;
|
||||
//! b.add_peer(Some(psk), peer_a_pk, ProtocolVersion::V03)?;
|
||||
//!
|
||||
//! // declare buffers for message exchange
|
||||
//! let (mut a_buf, mut b_buf) = (MsgBuf::zero(), MsgBuf::zero());
|
||||
|
||||
@@ -33,7 +33,9 @@ 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 //////////////////////////
|
||||
@@ -364,6 +366,30 @@ pub enum IndexKey {
|
||||
KnownInitConfResponse(KnownResponseHash),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ProtocolVersion {
|
||||
V02,
|
||||
V03
|
||||
}
|
||||
|
||||
impl ProtocolVersion {
|
||||
pub fn shake_or_blake(&self) -> EitherShakeOrBlake {
|
||||
match self {
|
||||
ProtocolVersion::V02 => EitherShakeOrBlake::Right(Blake2bCore),
|
||||
ProtocolVersion::V03 => EitherShakeOrBlake::Left(SHAKE256Core),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::config::ProtocolVersion> for ProtocolVersion {
|
||||
fn from(v: crate::config::ProtocolVersion) -> Self {
|
||||
match v {
|
||||
crate::config::ProtocolVersion::V02 => ProtocolVersion::V02,
|
||||
crate::config::ProtocolVersion::V03 => ProtocolVersion::V03,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A peer that the server can execute a key exchange with.
|
||||
///
|
||||
/// Peers generally live in [CryptoServer::peers]. [PeerNo] captures an array
|
||||
@@ -375,7 +401,7 @@ pub enum IndexKey {
|
||||
///
|
||||
/// ```
|
||||
/// use std::ops::DerefMut;
|
||||
/// use rosenpass::protocol::{SSk, SPk, SymKey, Peer};
|
||||
/// use rosenpass::protocol::{SSk, SPk, SymKey, Peer, ProtocolVersion};
|
||||
/// use rosenpass_ciphers::kem::StaticKem;
|
||||
/// use rosenpass_cipher_traits::Kem;
|
||||
///
|
||||
@@ -390,13 +416,13 @@ pub enum IndexKey {
|
||||
/// let psk = SymKey::random();
|
||||
///
|
||||
/// // Creation with a PSK
|
||||
/// let peer_psk = Peer::new(psk, spkt.clone());
|
||||
/// let peer_psk = Peer::new(psk, spkt.clone(), ProtocolVersion::V03);
|
||||
///
|
||||
/// // Creation without a PSK
|
||||
/// let peer_nopsk = Peer::new(SymKey::zero(), spkt);
|
||||
/// let peer_nopsk = Peer::new(SymKey::zero(), spkt, ProtocolVersion::V03);
|
||||
///
|
||||
/// // Create a second peer
|
||||
/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2);
|
||||
/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2, ProtocolVersion::V03);
|
||||
///
|
||||
/// // Peer ID does not depend on PSK, but it does depend on the public key
|
||||
/// assert_eq!(peer_psk.pidt()?, peer_nopsk.pidt()?);
|
||||
@@ -452,6 +478,9 @@ pub struct Peer {
|
||||
/// This allows us to perform retransmission for the purpose of dealing with packet loss
|
||||
/// on the network without having to account for it in the cryptographic code itself.
|
||||
pub known_init_conf_response: Option<KnownInitConfResponse>,
|
||||
|
||||
/// TODO: Documentation
|
||||
pub protocol_version: ProtocolVersion,
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
@@ -460,14 +489,14 @@ impl Peer {
|
||||
/// This is dirty but allows us to perform easy incremental construction of [Self].
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::protocol::{Peer, SymKey, SPk};
|
||||
/// use rosenpass::protocol::{Peer, SymKey, SPk, ProtocolVersion};
|
||||
/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
/// let p = Peer::zero();
|
||||
/// let p = Peer::zero(ProtocolVersion::V03);
|
||||
/// assert_eq!(p.psk.secret(), SymKey::zero().secret());
|
||||
/// assert_eq!(p.spkt, SPk::zero());
|
||||
/// // etc.
|
||||
/// ```
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero(protocol_version: ProtocolVersion) -> Self {
|
||||
Self {
|
||||
psk: SymKey::zero(),
|
||||
spkt: SPk::zero(),
|
||||
@@ -476,6 +505,7 @@ impl Peer {
|
||||
initiation_requested: false,
|
||||
handshake: None,
|
||||
known_init_conf_response: None,
|
||||
protocol_version: protocol_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -625,7 +655,7 @@ fn known_response_format() {
|
||||
response: Envelope::new_zeroed(),
|
||||
};
|
||||
let s = format!("{v:?}");
|
||||
assert!(s.contains("response")); // Smoke test only, its a formatter
|
||||
assert!(s.contains("response")); // Smoke test only, it's a formatter
|
||||
}
|
||||
|
||||
/// Known [EmptyData] response to an [InitConf] message
|
||||
@@ -804,11 +834,11 @@ pub trait Mortal {
|
||||
/// ```
|
||||
/// use std::ops::DerefMut;
|
||||
/// use rosenpass_ciphers::kem::StaticKem;
|
||||
/// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting};
|
||||
/// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting, ProtocolVersion};
|
||||
///
|
||||
/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
///
|
||||
/// let (peer, (_, spkt), mut srv) = ServerForTesting::new()?.tuple();
|
||||
/// let (peer, (_, spkt), mut srv) = ServerForTesting::new(ProtocolVersion::V03)?.tuple();
|
||||
///
|
||||
/// // Immutable access
|
||||
/// assert_eq!(peer.get(&srv).spkt, spkt);
|
||||
@@ -1344,7 +1374,7 @@ impl CryptoServer {
|
||||
///
|
||||
/// ```
|
||||
/// use std::ops::DerefMut;
|
||||
/// use rosenpass::protocol::{SSk, SPk, CryptoServer};
|
||||
/// use rosenpass::protocol::{SSk, SPk, CryptoServer, ProtocolVersion};
|
||||
/// use rosenpass_ciphers::kem::StaticKem;
|
||||
/// use rosenpass_cipher_traits::Kem;
|
||||
///
|
||||
@@ -1388,9 +1418,9 @@ impl CryptoServer {
|
||||
|
||||
/// Calculate the peer ID of this CryptoServer
|
||||
#[rustfmt::skip]
|
||||
pub fn pidm(&self) -> Result<PeerId> {
|
||||
pub fn pidm(&self, shake_or_blake: EitherShakeOrBlake) -> Result<PeerId> {
|
||||
Ok(Public::new(
|
||||
hash_domains::peerid()?
|
||||
hash_domains::peerid(shake_or_blake)?
|
||||
.mix(self.spkm.deref())?
|
||||
.into_value()))
|
||||
}
|
||||
@@ -1405,9 +1435,11 @@ impl CryptoServer {
|
||||
|
||||
/// Add a peer with an optional pre shared key (`psk`) and its public key (`pk`)
|
||||
///
|
||||
/// TODO: Adapt documentation
|
||||
///
|
||||
/// ```
|
||||
/// use std::ops::DerefMut;
|
||||
/// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer};
|
||||
/// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer, ProtocolVersion};
|
||||
/// use rosenpass_ciphers::kem::StaticKem;
|
||||
/// use rosenpass_cipher_traits::Kem;
|
||||
///
|
||||
@@ -1422,13 +1454,13 @@ impl CryptoServer {
|
||||
///
|
||||
/// let psk = SymKey::random();
|
||||
///
|
||||
/// let peer = srv.add_peer(Some(psk), spkt.clone())?;
|
||||
/// let peer = srv.add_peer(Some(psk), spkt.clone(), ProtocolVersion::V03)?;
|
||||
///
|
||||
/// assert_eq!(peer.get(&srv).spkt, spkt);
|
||||
///
|
||||
/// Ok::<(), anyhow::Error>(())
|
||||
/// ```
|
||||
pub fn add_peer(&mut self, psk: Option<SymKey>, pk: SPk) -> 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,
|
||||
@@ -1437,6 +1469,7 @@ impl CryptoServer {
|
||||
handshake: None,
|
||||
known_init_conf_response: None,
|
||||
initiation_requested: false,
|
||||
protocol_version: protocol_version,
|
||||
};
|
||||
let peerid = peer.pidt()?;
|
||||
let peerno = self.peers.len();
|
||||
@@ -1549,7 +1582,7 @@ impl CryptoServer {
|
||||
/// Retrieve the active biscuit key, cycling biscuit keys if necessary.
|
||||
///
|
||||
/// Two biscuit keys are maintained inside [Self::biscuit_keys]; they are
|
||||
/// considered fresh ([Lifecycle::Young]) for one [BISCUIT_EPOCH] after creation
|
||||
/// considered fresh ([Lifecycle::Young]) for one [BISCUIT_EPOCH] after creation,
|
||||
/// and they are considered stale ([Lifecycle::Retired]) for another [BISCUIT_EPOCH].
|
||||
///
|
||||
/// While young, they are used for encryption of biscuits and while retired they are
|
||||
@@ -1624,7 +1657,7 @@ impl Peer {
|
||||
/// # Examples
|
||||
///
|
||||
/// See example in [Self].
|
||||
pub fn new(psk: SymKey, pk: SPk) -> Peer {
|
||||
pub fn new(psk: SymKey, pk: SPk, protocol_version: ProtocolVersion) -> Peer {
|
||||
Peer {
|
||||
psk,
|
||||
spkt: pk,
|
||||
@@ -1633,11 +1666,12 @@ impl Peer {
|
||||
handshake: None,
|
||||
known_init_conf_response: None,
|
||||
initiation_requested: false,
|
||||
protocol_version: protocol_version,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the peer ID of the peer,
|
||||
/// as specified in the the [whitepaper](https://rosenpass.eu/whitepaper.pdf).
|
||||
/// as specified in the [whitepaper](https://rosenpass.eu/whitepaper.pdf).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1645,7 +1679,7 @@ impl Peer {
|
||||
#[rustfmt::skip]
|
||||
pub fn pidt(&self) -> Result<PeerId> {
|
||||
Ok(Public::new(
|
||||
hash_domains::peerid()?
|
||||
hash_domains::peerid(self.protocol_version.shake_or_blake())?
|
||||
.mix(self.spkt.deref())?
|
||||
.into_value()))
|
||||
}
|
||||
@@ -1658,20 +1692,22 @@ impl Session {
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::protocol::{Session, HandshakeRole};
|
||||
/// use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake;
|
||||
/// use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core;
|
||||
///
|
||||
/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
///
|
||||
/// let s = Session::zero();
|
||||
/// let s = Session::zero(EitherShakeOrBlake::Left(SHAKE256Core));
|
||||
/// assert_eq!(s.created_at, 0.0);
|
||||
/// assert_eq!(s.handshake_role, HandshakeRole::Initiator);
|
||||
/// ```
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self {
|
||||
Self {
|
||||
created_at: 0.0,
|
||||
sidm: SessionId::zero(),
|
||||
sidt: SessionId::zero(),
|
||||
handshake_role: HandshakeRole::Initiator,
|
||||
ck: SecretHashDomain::zero().dup(),
|
||||
ck: SecretHashDomain::zero(shake_or_blake).dup(),
|
||||
txkm: SymKey::zero(),
|
||||
txkt: SymKey::zero(),
|
||||
txnm: 0,
|
||||
@@ -1886,7 +1922,7 @@ impl Mortal for KnownInitConfResponsePtr {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::protocol::{Timing, Mortal, MortalExt, Lifecycle, CryptoServer};
|
||||
/// use rosenpass::protocol::{Timing, Mortal, MortalExt, Lifecycle, CryptoServer, ProtocolVersion};
|
||||
/// use rosenpass::protocol::testutils::{ServerForTesting, time_travel_forward};
|
||||
///
|
||||
/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
@@ -1896,7 +1932,7 @@ impl Mortal for KnownInitConfResponsePtr {
|
||||
/// const D : Timing = 24.0 * H;
|
||||
/// const Y : Timing = 356.0 * D;
|
||||
///
|
||||
/// let mut ts = ServerForTesting::new()?;
|
||||
/// let mut ts = ServerForTesting::new(ProtocolVersion::V03)?;
|
||||
///
|
||||
/// fn eq_up_to_minute(a: Timing, b: Timing) -> bool {
|
||||
/// (a - b) < M
|
||||
@@ -2136,7 +2172,7 @@ impl CryptoServer {
|
||||
let cookie_secret = cookie_secret.get(self).value.secret();
|
||||
let mut cookie_value = [0u8; 16];
|
||||
cookie_value.copy_from_slice(
|
||||
&hash_domains::cookie_value()?
|
||||
&hash_domains::cookie_value(EitherShakeOrBlake::Left(SHAKE256Core))?
|
||||
.mix(cookie_secret)?
|
||||
.mix(host_identification.encode())?
|
||||
.into_value()[..16],
|
||||
@@ -2152,7 +2188,7 @@ impl CryptoServer {
|
||||
let msg_in = Ref::<&[u8], Envelope<InitHello>>::new(rx_buf)
|
||||
.ok_or(RosenpassError::BufferSizeMismatch)?;
|
||||
expected.copy_from_slice(
|
||||
&hash_domains::cookie()?
|
||||
&hash_domains::cookie(EitherShakeOrBlake::Left(SHAKE256Core))?
|
||||
.mix(&cookie_value)?
|
||||
.mix(&msg_in.as_bytes()[span_of!(Envelope<InitHello>, msg_type..cookie)])?
|
||||
.into_value()[..16],
|
||||
@@ -2189,7 +2225,7 @@ impl CryptoServer {
|
||||
);
|
||||
|
||||
let cookie_value = active_cookie_value.unwrap();
|
||||
let cookie_key = hash_domains::cookie_key()?
|
||||
let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))?
|
||||
.mix(self.spkm.deref())?
|
||||
.into_value();
|
||||
|
||||
@@ -2269,24 +2305,43 @@ impl CryptoServer {
|
||||
|
||||
log::debug!("Rx {:?}, processing", msg_type);
|
||||
|
||||
let mut msg_out = truncating_cast_into::<Envelope<RespHello>>(tx_buf)?;
|
||||
|
||||
let peer = match msg_type {
|
||||
Ok(MsgType::InitHello) => {
|
||||
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 = truncating_cast_into::<Envelope<RespHello>>(tx_buf)?;
|
||||
let peer = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload)?;
|
||||
// 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, peer_hash_choice) = match peer_shake256 {
|
||||
Ok(peer ) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)),
|
||||
Err(_) => {
|
||||
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")
|
||||
}
|
||||
}
|
||||
};
|
||||
// Now, we make sure that the hash function used by the peer is the same as the one
|
||||
// that is specified in the local configuration.
|
||||
self.verify_hash_choice_match(peer, peer_hash_choice.clone())?;
|
||||
|
||||
ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken);
|
||||
|
||||
len = self.seal_and_commit_msg(peer, MsgType::RespHello, &mut msg_out)?;
|
||||
peer
|
||||
}
|
||||
Ok(MsgType::RespHello) => {
|
||||
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 = 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);
|
||||
|
||||
len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?;
|
||||
peer.hs()
|
||||
.store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?;
|
||||
@@ -2296,7 +2351,7 @@ impl CryptoServer {
|
||||
Ok(MsgType::InitConf) => {
|
||||
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 = truncating_cast_into::<Envelope<EmptyData>>(tx_buf)?;
|
||||
|
||||
@@ -2305,6 +2360,7 @@ 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);
|
||||
let cached = cached
|
||||
.get(self)
|
||||
.map(|v| v.response.borrow())
|
||||
@@ -2316,7 +2372,23 @@ impl CryptoServer {
|
||||
|
||||
// No cached response, actually call cryptographic handler
|
||||
None => {
|
||||
let peer = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload)?;
|
||||
// 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, 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));
|
||||
match peer_blake2b {
|
||||
Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)),
|
||||
Err(_) => bail!("No valid hash function found for InitHello")
|
||||
}
|
||||
}
|
||||
};
|
||||
// Now, we make sure that the hash function used by the peer is the same as the one
|
||||
// that is specified in the local configuration.
|
||||
self.verify_hash_choice_match(peer, peer_hash_choice.clone())?;
|
||||
ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken);
|
||||
|
||||
KnownInitConfResponsePtr::insert_for_request_msg(
|
||||
self,
|
||||
@@ -2336,9 +2408,8 @@ impl CryptoServer {
|
||||
Ok(MsgType::EmptyData) => {
|
||||
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)?
|
||||
self.handle_resp_conf(&msg_in, seal_broken.to_string())?
|
||||
}
|
||||
Ok(MsgType::CookieReply) => {
|
||||
let msg_in: Ref<&[u8], CookieReply> =
|
||||
@@ -2358,6 +2429,20 @@ impl CryptoServer {
|
||||
})
|
||||
}
|
||||
|
||||
/// TODO documentation
|
||||
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(()),
|
||||
EitherShakeOrBlake::Right(Blake2bCore) => bail!("Hash function mismatch"),
|
||||
},
|
||||
EitherShakeOrBlake::Right(Blake2bCore) => match peer_hash_choice {
|
||||
EitherShakeOrBlake::Left(SHAKE256Core) => bail!("Hash function mismatch"),
|
||||
EitherShakeOrBlake::Right(Blake2bCore) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is used to finalize a message in a transmission buffer
|
||||
/// while ensuring that the [Envelope::mac] and [Envelope::cookie]
|
||||
/// fields are properly filled.
|
||||
@@ -3096,7 +3181,7 @@ where
|
||||
{
|
||||
/// Internal business logic: Calculate the message authentication code (`mac`) and also append cookie value
|
||||
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
||||
let mac = hash_domains::mac()?
|
||||
let mac = hash_domains::mac(peer.get(srv).protocol_version.shake_or_blake())?
|
||||
.mix(peer.get(srv).spkt.deref())?
|
||||
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
|
||||
self.mac.copy_from_slice(mac.into_value()[..16].as_ref());
|
||||
@@ -3109,7 +3194,7 @@ where
|
||||
/// This is called inside [Self::seal] and does not need to be called again separately.
|
||||
pub fn seal_cookie(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
||||
if let Some(cookie_key) = &peer.cv().get(srv) {
|
||||
let cookie = hash_domains::cookie()?
|
||||
let cookie = hash_domains::cookie(peer.get(srv).protocol_version.shake_or_blake())?
|
||||
.mix(cookie_key.value.secret())?
|
||||
.mix(&self.as_bytes()[span_of!(Self, msg_type..cookie)])?;
|
||||
self.cookie
|
||||
@@ -3124,8 +3209,8 @@ where
|
||||
M: AsBytes + FromBytes,
|
||||
{
|
||||
/// Internal business logic: Check the message authentication code produced by [Self::seal]
|
||||
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
||||
let expected = hash_domains::mac()?
|
||||
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)])?;
|
||||
Ok(constant_time::memcmp(
|
||||
@@ -3137,11 +3222,11 @@ where
|
||||
|
||||
impl InitiatorHandshake {
|
||||
/// Zero initialization of an InitiatorHandshake, with up to date timestamp
|
||||
pub fn zero_with_timestamp(srv: &CryptoServer) -> Self {
|
||||
pub fn zero_with_timestamp(srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Self {
|
||||
InitiatorHandshake {
|
||||
created_at: srv.timebase.now(),
|
||||
next: HandshakeStateMachine::RespHello,
|
||||
core: HandshakeState::zero(),
|
||||
core: HandshakeState::zero(shake_or_blake),
|
||||
eski: ESk::zero(),
|
||||
epki: EPk::zero(),
|
||||
tx_at: 0.0,
|
||||
@@ -3156,37 +3241,37 @@ impl InitiatorHandshake {
|
||||
|
||||
impl HandshakeState {
|
||||
/// Zero initialization of an HandshakeState
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self {
|
||||
Self {
|
||||
sidi: SessionId::zero(),
|
||||
sidr: SessionId::zero(),
|
||||
ck: SecretHashDomain::zero().dup(),
|
||||
ck: SecretHashDomain::zero(shake_or_blake).dup(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Securely erase the chaining key
|
||||
pub fn erase(&mut self) {
|
||||
self.ck = SecretHashDomain::zero().dup();
|
||||
self.ck = SecretHashDomain::zero(self.ck.shake_or_blake().clone()).dup();
|
||||
}
|
||||
|
||||
/// 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()?.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()?)?.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()?)?.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)
|
||||
}
|
||||
@@ -3196,7 +3281,7 @@ 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()?)?.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)
|
||||
}
|
||||
@@ -3259,7 +3344,7 @@ impl HandshakeState {
|
||||
.copy_from_slice(self.ck.clone().danger_into_secret().secret());
|
||||
|
||||
// calculate ad contents
|
||||
let ad = hash_domains::biscuit_ad()?
|
||||
let ad = hash_domains::biscuit_ad(peer.get(srv).protocol_version.shake_or_blake())?
|
||||
.mix(srv.spkm.deref())?
|
||||
.mix(self.sidi.as_slice())?
|
||||
.mix(self.sidr.as_slice())?
|
||||
@@ -3288,12 +3373,13 @@ impl HandshakeState {
|
||||
biscuit_ct: &[u8],
|
||||
sidi: SessionId,
|
||||
sidr: SessionId,
|
||||
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);
|
||||
|
||||
// Calculate additional data fields
|
||||
let ad = hash_domains::biscuit_ad()?
|
||||
let ad = hash_domains::biscuit_ad(shake_or_blake)?
|
||||
.mix(srv.spkm.deref())?
|
||||
.mix(sidi.as_slice())?
|
||||
.mix(sidr.as_slice())?
|
||||
@@ -3312,18 +3398,17 @@ impl HandshakeState {
|
||||
|
||||
// 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);
|
||||
|
||||
// Reconstruct the handshake state
|
||||
let mut hs = Self { sidi, sidr, ck };
|
||||
hs.mix(biscuit_ct)?;
|
||||
|
||||
// Look up the associated peer
|
||||
let peer = srv
|
||||
.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();
|
||||
// Reconstruct the handshake state
|
||||
let mut hs = Self { sidi, sidr, ck };
|
||||
hs.mix(biscuit_ct)?;
|
||||
|
||||
Ok((peer, no, hs))
|
||||
}
|
||||
|
||||
@@ -3332,10 +3417,10 @@ 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) -> 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()?)?.into_secret();
|
||||
let tkr = ck.mix(&hash_domains::res_enc()?)?.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 {
|
||||
@@ -3373,7 +3458,7 @@ impl CryptoServer {
|
||||
.get(self)
|
||||
.as_ref()
|
||||
.with_context(|| format!("No current session for peer {:?}", peer))?;
|
||||
Ok(session.ck.mix(&hash_domains::osk()?)?.into_secret())
|
||||
Ok(session.ck.mix(&hash_domains::osk(peer.get(self).protocol_version.shake_or_blake())?)?.into_secret())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3381,7 +3466,7 @@ 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);
|
||||
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())?;
|
||||
@@ -3406,7 +3491,7 @@ impl CryptoServer {
|
||||
|
||||
// IHI6
|
||||
hs.core
|
||||
.encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm()?.as_ref())?;
|
||||
.encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm(peer.get(self).protocol_version.shake_or_blake())?.as_ref())?;
|
||||
|
||||
// IHI7
|
||||
hs.core
|
||||
@@ -3424,8 +3509,9 @@ impl CryptoServer {
|
||||
|
||||
/// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a
|
||||
/// [RespHello] message on the responder side.
|
||||
pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello) -> Result<PeerPtr> {
|
||||
let mut core = HandshakeState::zero();
|
||||
/// TODO: Document Hash Functon usage
|
||||
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);
|
||||
|
||||
@@ -3570,7 +3656,7 @@ impl CryptoServer {
|
||||
|
||||
// ICI7
|
||||
peer.session()
|
||||
.insert(self, core.enter_live(self, HandshakeRole::Initiator)?)?;
|
||||
.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;
|
||||
|
||||
@@ -3578,11 +3664,12 @@ impl CryptoServer {
|
||||
}
|
||||
|
||||
/// Core cryptographic protocol implementation: Parses an [InitConf] message and produces an
|
||||
/// [EmptyData] (responder confimation) message on the responder side.
|
||||
/// [EmptyData] (responder confirmation) message on the responder side.
|
||||
///
|
||||
/// This concludes the handshake on the cryptographic level; the [EmptyData] message is just
|
||||
/// an acknowledgement message telling the initiator to stop performing retransmissions.
|
||||
pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData) -> Result<PeerPtr> {
|
||||
/// TODO: documentation
|
||||
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(
|
||||
@@ -3590,6 +3677,7 @@ impl CryptoServer {
|
||||
&ic.biscuit,
|
||||
SessionId::from_slice(&ic.sidi),
|
||||
SessionId::from_slice(&ic.sidr),
|
||||
shake_or_blake,
|
||||
)?;
|
||||
|
||||
// ICR2
|
||||
@@ -3615,7 +3703,7 @@ impl CryptoServer {
|
||||
|
||||
// ICR7
|
||||
peer.session()
|
||||
.insert(self, core.enter_live(self, HandshakeRole::Responder)?)?;
|
||||
.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);
|
||||
@@ -3663,11 +3751,13 @@ 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, rc: &EmptyData) -> 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);
|
||||
let ses = hs.peer().session();
|
||||
|
||||
let exp = hs.get(self).as_ref().map(|h| h.next);
|
||||
@@ -3685,7 +3775,7 @@ impl CryptoServer {
|
||||
let s = ses.get_mut(self).as_mut().with_context(|| {
|
||||
format!("Cannot validate EmptyData message. Missing encryption session for {sid:?}")
|
||||
})?;
|
||||
// the unwrap can not fail, because the slice returned by ctr() is
|
||||
// the unwrapping can not fail, because the slice returned by ctr() is
|
||||
// guaranteed to have the correct size
|
||||
let n = u64::from_le_bytes(rc.ctr);
|
||||
ensure!(n >= s.txnt, "Stale nonce");
|
||||
@@ -3750,7 +3840,7 @@ impl CryptoServer {
|
||||
}?;
|
||||
|
||||
let spkt = peer.get(self).spkt.deref();
|
||||
let cookie_key = hash_domains::cookie_key()?.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)?;
|
||||
@@ -3795,15 +3885,16 @@ pub mod testutils {
|
||||
pub srv: CryptoServer,
|
||||
}
|
||||
|
||||
/// TODO: Document that the protocol verson s only used for creating the peer for testing
|
||||
impl ServerForTesting {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result<Self> {
|
||||
let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero());
|
||||
StaticKem::keygen(sskm.secret_mut(), spkm.deref_mut())?;
|
||||
let mut srv = CryptoServer::new(sskm, spkm);
|
||||
|
||||
let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero());
|
||||
StaticKem::keygen(sskt.secret_mut(), spkt.deref_mut())?;
|
||||
let peer = srv.add_peer(None, spkt.clone())?;
|
||||
let peer = srv.add_peer(None, spkt.clone(), protocol_version)?;
|
||||
|
||||
let peer_keys = (sskt, spkt);
|
||||
Ok(ServerForTesting {
|
||||
@@ -3868,6 +3959,17 @@ mod test {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn handles_incorrect_size_messages_v02() {
|
||||
handles_incorrect_size_messages(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn handles_incorrect_size_messages_v03() {
|
||||
handles_incorrect_size_messages(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
|
||||
/// Ensure that the protocol implementation can deal with truncated
|
||||
/// messages and with overlong messages.
|
||||
///
|
||||
@@ -3882,7 +3984,7 @@ mod test {
|
||||
///
|
||||
/// Through all this, the handshake should still successfully terminate;
|
||||
/// i.e. an exchanged key must be produced in both servers.
|
||||
fn handles_incorrect_size_messages() {
|
||||
fn handles_incorrect_size_messages(protocol_version: ProtocolVersion) {
|
||||
setup_logging();
|
||||
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
stacker::grow(8 * 1024 * 1024, || {
|
||||
@@ -3891,7 +3993,7 @@ mod test {
|
||||
|
||||
const PEER0: PeerPtr = PeerPtr(0);
|
||||
|
||||
let (mut me, mut they) = make_server_pair().unwrap();
|
||||
let (mut me, mut they) = make_server_pair(protocol_version).unwrap();
|
||||
let (mut msgbuf, mut resbuf) = (MsgBufPlus::zero(), MsgBufPlus::zero());
|
||||
|
||||
// Process the entire handshake
|
||||
@@ -3941,7 +4043,7 @@ mod test {
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
|
||||
fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> {
|
||||
// TODO: Copied from the benchmark; deduplicate
|
||||
let psk = SymKey::random();
|
||||
let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?);
|
||||
@@ -3949,19 +4051,29 @@ mod test {
|
||||
CryptoServer::new(ska, pka.clone()),
|
||||
CryptoServer::new(skb, pkb.clone()),
|
||||
);
|
||||
a.add_peer(Some(psk.clone()), pkb)?;
|
||||
b.add_peer(Some(psk), pka)?;
|
||||
a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?;
|
||||
b.add_peer(Some(psk), pka, protocol_version)?;
|
||||
Ok((a, b))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_regular_exchange() {
|
||||
fn test_regular_exchange_v02() {
|
||||
test_regular_exchange(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_regular_exchange_v03() {
|
||||
test_regular_exchange(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn test_regular_exchange(protocol_version: ProtocolVersion) {
|
||||
setup_logging();
|
||||
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
stacker::grow(8 * 1024 * 1024, || {
|
||||
type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
let (mut a, mut b) = make_server_pair(protocol_version).unwrap();
|
||||
|
||||
let mut a_to_b_buf = MsgBufPlus::zero();
|
||||
let mut b_to_a_buf = MsgBufPlus::zero();
|
||||
@@ -4017,12 +4129,22 @@ mod test {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_regular_init_conf_retransmit() {
|
||||
fn test_regular_init_conf_retransmit_v02() {
|
||||
test_regular_init_conf_retransmit(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_regular_init_conf_retransmit_v03() {
|
||||
test_regular_init_conf_retransmit(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn test_regular_init_conf_retransmit(protocol_version: ProtocolVersion) {
|
||||
setup_logging();
|
||||
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
stacker::grow(8 * 1024 * 1024, || {
|
||||
type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
let (mut a, mut b) = make_server_pair(protocol_version).unwrap();
|
||||
|
||||
let mut a_to_b_buf = MsgBufPlus::zero();
|
||||
let mut b_to_a_buf = MsgBufPlus::zero();
|
||||
@@ -4092,12 +4214,22 @@ mod test {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn cookie_reply_mechanism_responder_under_load() {
|
||||
fn cookie_reply_mechanism_responder_under_load_v02() {
|
||||
cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn cookie_reply_mechanism_responder_under_load_v03() {
|
||||
cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) {
|
||||
setup_logging();
|
||||
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
stacker::grow(8 * 1024 * 1024, || {
|
||||
type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
let (mut a, mut b) = make_server_pair(protocol_version.clone()).unwrap();
|
||||
|
||||
let mut a_to_b_buf = MsgBufPlus::zero();
|
||||
let mut b_to_a_buf = MsgBufPlus::zero();
|
||||
@@ -4136,7 +4268,7 @@ mod test {
|
||||
|
||||
assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young);
|
||||
|
||||
let expected_cookie_value = hash_domains::cookie_value()
|
||||
let expected_cookie_value = hash_domains::cookie_value(protocol_version.shake_or_blake())
|
||||
.unwrap()
|
||||
.mix(
|
||||
b.active_or_retired_cookie_secrets()[0]
|
||||
@@ -4189,12 +4321,22 @@ mod test {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn cookie_reply_mechanism_initiator_bails_on_message_under_load() {
|
||||
fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v02() {
|
||||
cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v03() {
|
||||
cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
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, || {
|
||||
type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
let (mut a, mut b) = make_server_pair(protocol_version).unwrap();
|
||||
|
||||
let mut a_to_b_buf = MsgBufPlus::zero();
|
||||
let mut b_to_a_buf = MsgBufPlus::zero();
|
||||
@@ -4249,10 +4391,19 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_conf_retransmission() -> anyhow::Result<()> {
|
||||
fn init_conf_retransmission_v02() -> Result<()> {
|
||||
init_conf_retransmission(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_conf_retransmission_v03() -> Result<()> {
|
||||
init_conf_retransmission(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn init_conf_retransmission(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
|
||||
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
|
||||
|
||||
fn keypair() -> anyhow::Result<(SSk, SPk)> {
|
||||
fn keypair() -> Result<(SSk, SPk)> {
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?;
|
||||
Ok((sk, pk))
|
||||
@@ -4261,7 +4412,7 @@ mod test {
|
||||
fn proc_initiation(
|
||||
srv: &mut CryptoServer,
|
||||
peer: PeerPtr,
|
||||
) -> anyhow::Result<Envelope<InitHello>> {
|
||||
) -> Result<Envelope<InitHello>> {
|
||||
let mut buf = MsgBuf::zero();
|
||||
srv.initiate_handshake(peer, buf.as_mut_slice())?
|
||||
.discard_result();
|
||||
@@ -4331,12 +4482,8 @@ mod test {
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
fn check_retransmission(
|
||||
srv: &mut CryptoServer,
|
||||
ic: &Envelope<InitConf>,
|
||||
ic_broken: &Envelope<InitConf>,
|
||||
rc: &Envelope<EmptyData>,
|
||||
) -> anyhow::Result<()> {
|
||||
// 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<()> {
|
||||
// Processing the same RespHello package again leads to retransmission (i.e. exactly the
|
||||
// same output)
|
||||
let rc_dup = proc_init_conf(srv, ic)?;
|
||||
@@ -4345,14 +4492,14 @@ 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);
|
||||
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
|
||||
check_faulty_proc_init_conf(srv, ic_broken);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
let (ska, pka) = keypair()?;
|
||||
let (skb, pkb) = keypair()?;
|
||||
@@ -4362,8 +4509,8 @@ mod test {
|
||||
let mut b = CryptoServer::new(skb, pkb.clone());
|
||||
|
||||
// introduce peers to each other
|
||||
let b_peer = a.add_peer(None, pkb)?;
|
||||
let a_peer = b.add_peer(None, pka)?;
|
||||
let b_peer = a.add_peer(None, pkb, protocol_version.clone())?;
|
||||
let a_peer = b.add_peer(None, pka, protocol_version.clone())?;
|
||||
|
||||
// Execute protocol up till the responder confirmation (EmptyData)
|
||||
let ih1 = proc_initiation(&mut a, b_peer)?;
|
||||
|
||||
@@ -26,7 +26,7 @@ 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);
|
||||
@@ -48,7 +48,16 @@ impl Drop for KillChild {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_integration_api_setup() -> anyhow::Result<()> {
|
||||
fn api_integration_api_setup_v02() -> anyhow::Result<()> {
|
||||
api_integration_api_setup(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_integration_api_setup_v03() -> anyhow::Result<()> {
|
||||
api_integration_api_setup(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn api_integration_api_setup(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
|
||||
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||
|
||||
let dir = TempDir::with_prefix("rosenpass-api-integration-test")?;
|
||||
@@ -96,6 +105,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> {
|
||||
peer: format!("{}", peer_b_wg_peer_id.fmt_b64::<8129>()),
|
||||
extra_params: vec![],
|
||||
}),
|
||||
protocol_version: protocol_version.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
@@ -116,6 +126,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> {
|
||||
endpoint: Some(peer_a_endpoint.to_owned()),
|
||||
pre_shared_key: None,
|
||||
wg: None,
|
||||
protocol_version: protocol_version.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use tempfile::TempDir;
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
use rosenpass::protocol::SymKey;
|
||||
use rosenpass::config::ProtocolVersion;
|
||||
|
||||
struct KillChild(std::process::Child);
|
||||
|
||||
@@ -37,7 +38,16 @@ impl Drop for KillChild {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_integration_test() -> anyhow::Result<()> {
|
||||
fn api_integration_test_v02() -> anyhow::Result<()> {
|
||||
api_integration_test(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
let dir = TempDir::with_prefix("rosenpass-api-integration-test")?;
|
||||
@@ -73,6 +83,7 @@ fn api_integration_test() -> anyhow::Result<()> {
|
||||
endpoint: None,
|
||||
pre_shared_key: None,
|
||||
wg: None,
|
||||
protocol_version: protocol_version.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
@@ -93,6 +104,7 @@ fn api_integration_test() -> anyhow::Result<()> {
|
||||
endpoint: Some(peer_a_endpoint.to_owned()),
|
||||
pre_shared_key: None,
|
||||
wg: None,
|
||||
protocol_version: protocol_version.clone(),
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
@@ -13,13 +13,23 @@ 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;
|
||||
use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt};
|
||||
|
||||
#[test]
|
||||
fn key_exchange_with_app_server() -> anyhow::Result<()> {
|
||||
fn key_exchange_with_app_server_v02() -> anyhow::Result<()> {
|
||||
key_exchange_with_app_server(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_exchange_with_app_server_v03() -> anyhow::Result<()> {
|
||||
key_exchange_with_app_server(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn key_exchange_with_app_server(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
|
||||
let tmpdir = tempfile::tempdir()?;
|
||||
let outfile_a = tmpdir.path().join("osk_a");
|
||||
let outfile_b = tmpdir.path().join("osk_b");
|
||||
@@ -57,7 +67,7 @@ fn key_exchange_with_app_server() -> anyhow::Result<()> {
|
||||
let port = otr_port;
|
||||
let hostname = is_client.then(|| format!("[::1]:{port}"));
|
||||
srv.app_srv
|
||||
.add_peer(psk, pk, outfile, broker_peer, hostname)?;
|
||||
.add_peer(psk, pk, outfile, broker_peer, hostname, protocol_version.clone())?;
|
||||
|
||||
srv.app_srv.event_loop()
|
||||
})
|
||||
|
||||
@@ -251,7 +251,7 @@ fn check_exchange_under_normal() {
|
||||
fs::remove_dir_all(&tmpdir).unwrap();
|
||||
}
|
||||
|
||||
// check that we can trigger a DoS condition and we can exchange keys under DoS
|
||||
// check that we can trigger a DoS condition, and we can exchange keys under DoS
|
||||
// This test creates a responder (server) with the feature flag "integration_test_always_under_load" to always be under load condition for the test.
|
||||
#[test]
|
||||
#[serial]
|
||||
|
||||
@@ -9,20 +9,26 @@ 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,
|
||||
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;
|
||||
|
||||
#[test]
|
||||
fn test_successful_exchange_with_poll() -> anyhow::Result<()> {
|
||||
fn test_successful_exchange_with_poll_v02() -> anyhow::Result<()> {
|
||||
test_successful_exchange_with_poll(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_successful_exchange_with_poll_v03() -> anyhow::Result<()> {
|
||||
test_successful_exchange_with_poll(ProtocolVersion::V03)
|
||||
}
|
||||
|
||||
fn test_successful_exchange_with_poll(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();
|
||||
|
||||
let mut sim = RosenpassSimulator::new()?;
|
||||
let mut sim = RosenpassSimulator::new(protocol_version)?;
|
||||
sim.poll_loop(150)?; // Poll 75 times
|
||||
let transcript = sim.transcript;
|
||||
|
||||
@@ -79,12 +85,21 @@ fn test_successful_exchange_with_poll() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_successful_exchange_under_packet_loss() -> anyhow::Result<()> {
|
||||
fn test_successful_exchange_under_packet_loss_v02() -> anyhow::Result<()> {
|
||||
test_successful_exchange_under_packet_loss(ProtocolVersion::V02)
|
||||
}
|
||||
|
||||
#[test]
|
||||
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<()> {
|
||||
// Set security policy for storing secrets; choose the one that is faster for testing
|
||||
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||
|
||||
// Create the simulator
|
||||
let mut sim = RosenpassSimulator::new()?;
|
||||
let mut sim = RosenpassSimulator::new(protocol_version)?;
|
||||
|
||||
// Make sure the servers are set to under load condition
|
||||
sim.srv_a.under_load = true;
|
||||
@@ -272,7 +287,7 @@ struct SimulatorServer {
|
||||
|
||||
impl RosenpassSimulator {
|
||||
/// Set up the simulator
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
fn new(protocol_version: ProtocolVersion) -> anyhow::Result<Self> {
|
||||
// Set up the first server
|
||||
let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.deref_mut())?;
|
||||
@@ -285,8 +300,8 @@ impl RosenpassSimulator {
|
||||
|
||||
// Generate a PSK and introduce the Peers to each other.
|
||||
let psk = SymKey::random();
|
||||
let peer_a = srv_a.add_peer(Some(psk.clone()), peer_b_pk)?;
|
||||
let peer_b = srv_b.add_peer(Some(psk), peer_a_pk)?;
|
||||
let peer_a = srv_a.add_peer(Some(psk.clone()), peer_b_pk, protocol_version.clone())?;
|
||||
let peer_b = srv_b.add_peer(Some(psk), peer_a_pk, protocol_version.clone())?;
|
||||
|
||||
// Set up the individual server data structures
|
||||
let srv_a = SimulatorServer::new(srv_a, peer_b);
|
||||
@@ -314,8 +329,8 @@ impl RosenpassSimulator {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Every call to poll produces one [TranscriptEvent] and
|
||||
/// and implicitly adds it to [Self:::transcript]
|
||||
/// Every call to poll produces one [TranscriptEvent]
|
||||
/// and implicitly adds it to [Self::transcript]
|
||||
fn poll(&mut self) -> anyhow::Result<&TranscriptEvent> {
|
||||
let ev = TranscriptEvent::begin_poll()
|
||||
.try_fold_with(|| self.poll_focus.poll(self))?
|
||||
|
||||
Reference in New Issue
Block a user