added the ability to mutate app

This commit is contained in:
Monica Moniot
2023-08-09 10:45:56 -04:00
parent 1e87d1ba75
commit 6b1fa53e1c
9 changed files with 300 additions and 258 deletions
+44 -44
View File
@@ -7,10 +7,11 @@
*/
use std::collections::HashMap;
use std::convert::Infallible;
use std::iter::ExactSizeIterator;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{mpsc, Arc};
use std::thread;
use std::time::{Duration, Instant};
@@ -18,7 +19,7 @@ use rand_core::OsRng;
use rand_core::RngCore;
use zssp_proto::application::{
AcceptAction, ApplicationLayer, RatchetState, RatchetStates, RatchetUpdate, Settings, RATCHET_SIZE,
AcceptAction, ApplicationLayer, RatchetState, RatchetStates, RatchetUpdate, Settings, RATCHET_SIZE, CryptoLayer,
};
use zssp_proto::crypto::P384KeyPair;
use zssp_proto::crypto_impl::{
@@ -31,7 +32,7 @@ const TEST_MTU: usize = 1500;
struct TestApplication {
time: Instant,
name: &'static str,
ratchets: Mutex<Ratchets>,
ratchets: Ratchets,
}
struct Ratchets {
@@ -44,8 +45,7 @@ impl Ratchets {
}
}
#[allow(unused)]
impl ApplicationLayer for &TestApplication {
impl CryptoLayer for TestApplication {
const SETTINGS: Settings = Settings {
initial_offer_timeout: Settings::INITIAL_OFFER_TIMEOUT_MS,
rekey_timeout: 60 * 1000,
@@ -63,18 +63,21 @@ impl ApplicationLayer for &TestApplication {
type KeyPair = P384CrateKeyPair;
type Kem = RustKyber1024PrivateKey;
type StorageError = std::convert::Infallible;
type SessionData = u128;
fn hello_requires_recognized_ratchet(&self) -> bool {
}
#[allow(unused)]
impl ApplicationLayer for &mut TestApplication {
type Crypto = TestApplication;
type StorageError = Infallible;
fn hello_requires_recognized_ratchet(&mut self) -> bool {
false
}
fn initiator_disallows_downgrade(&self, session: &Arc<Session<Self>>) -> bool {
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<TestApplication>>) -> bool {
true
}
fn check_accept_session(&self, remote_static_key: &Self::PublicKey, identity: &[u8]) -> AcceptAction<Self> {
fn check_accept_session(&mut self, remote_static_key: &P384CratePublicKey, identity: &[u8]) -> AcceptAction<TestApplication> {
AcceptAction {
session_data: Some(1),
responder_disallows_downgrade: true,
@@ -83,49 +86,46 @@ impl ApplicationLayer for &TestApplication {
}
fn restore_by_fingerprint(
&self,
&mut self,
ratchet_fingerprint: &[u8; RATCHET_SIZE],
) -> Result<Option<RatchetState>, Self::StorageError> {
let ratchets = self.ratchets.lock().unwrap();
Ok(ratchets.rf_map.get(ratchet_fingerprint).cloned())
) -> Result<Option<RatchetState>, Infallible> {
Ok(self.ratchets.rf_map.get(ratchet_fingerprint).cloned())
}
fn restore_by_identity(
&self,
remote_static_key: &Self::PublicKey,
session_data: &Self::SessionData,
) -> Result<Option<RatchetStates>, Self::StorageError> {
let ratchets = self.ratchets.lock().unwrap();
Ok(ratchets.peer_map.get(session_data).cloned())
&mut self,
remote_static_key: &P384CratePublicKey,
session_data: &u128,
) -> Result<Option<RatchetStates>, Infallible> {
Ok(self.ratchets.peer_map.get(session_data).cloned())
}
fn save_ratchet_state(
&self,
remote_static_key: &Self::PublicKey,
session_data: &Self::SessionData,
&mut self,
remote_static_key: &P384CratePublicKey,
session_data: &u128,
update_data: RatchetUpdate<'_>,
) -> Result<(), Self::StorageError> {
let mut ratchets = self.ratchets.lock().unwrap();
ratchets.peer_map.insert(*session_data, update_data.to_states());
) -> Result<(), Infallible> {
self.ratchets.peer_map.insert(*session_data, update_data.to_states());
if let Some(rf) = update_data.added_fingerprint() {
ratchets.rf_map.insert(*rf, update_data.state1.clone());
self.ratchets.rf_map.insert(*rf, update_data.state1.clone());
println!("[{}] new ratchet #{}", self.name, update_data.state1.chain_len());
}
if let Some(rf) = update_data.deleted_fingerprint1() {
ratchets.rf_map.remove(rf);
self.ratchets.rf_map.remove(rf);
}
if let Some(rf) = update_data.deleted_fingerprint2() {
ratchets.rf_map.remove(rf);
self.ratchets.rf_map.remove(rf);
}
Ok(())
}
fn time(&self) -> i64 {
fn time(&mut self) -> i64 {
self.time.elapsed().as_millis() as i64
}
fn event_log(&self, event: zssp_proto::LogEvent<Self>) {
fn event_log(&mut self, event: zssp_proto::LogEvent<TestApplication>) {
println!(">[{}] {:?}", self.name, event);
}
}
@@ -133,7 +133,7 @@ impl ApplicationLayer for &TestApplication {
fn alice_main(
run: &AtomicBool,
packet_success_rate: u32,
alice_app: &TestApplication,
mut alice_app: TestApplication,
alice_out: mpsc::SyncSender<Vec<u8>>,
alice_in: mpsc::Receiver<Vec<u8>>,
recursive_out: mpsc::SyncSender<Vec<u8>>,
@@ -141,7 +141,7 @@ fn alice_main(
bob_pubkey: P384CratePublicKey,
) {
let startup_time = std::time::Instant::now();
let mut context = zssp_proto::Context::<&TestApplication>::new(alice_keypair, OsRng);
let mut context = zssp_proto::Context::<TestApplication>::new(alice_keypair, OsRng);
let mut next_service = startup_time.elapsed().as_millis() as i64 + 500;
let test_data = [1u8; TEST_MTU * 10];
let mut up = false;
@@ -153,7 +153,7 @@ fn alice_main(
alice_session = Some(
context
.open(
alice_app,
&mut alice_app,
|b| alice_out.send(b).is_ok(),
TEST_MTU,
bob_pubkey.clone(),
@@ -173,7 +173,7 @@ fn alice_main(
use zssp_proto::result::ReceiveOk::*;
use zssp_proto::result::SessionEvent::*;
match context.receive(
alice_app,
&mut alice_app,
|b| alice_out.send(b).is_ok(),
TEST_MTU,
|_| Some((|b| alice_out.send(b).is_ok(), TEST_MTU)),
@@ -228,7 +228,7 @@ fn alice_main(
if current_time >= next_service {
next_service =
current_time + context.service(alice_app, |_| Some((|b| alice_out.send(b).is_ok(), TEST_MTU)));
current_time + context.service(&mut alice_app, |_| Some((|b| alice_out.send(b).is_ok(), TEST_MTU)));
}
}
}
@@ -236,14 +236,14 @@ fn alice_main(
fn bob_main(
run: &AtomicBool,
packet_success_rate: u32,
bob_app: &TestApplication,
mut bob_app: TestApplication,
bob_out: mpsc::SyncSender<Vec<u8>>,
bob_in: mpsc::Receiver<Vec<u8>>,
recursive_out: mpsc::SyncSender<Vec<u8>>,
bob_keypair: P384CrateKeyPair,
) {
let startup_time = std::time::Instant::now();
let mut context = zssp_proto::Context::<&TestApplication>::new(bob_keypair, OsRng);
let mut context = zssp_proto::Context::<TestApplication>::new(bob_keypair, OsRng);
let mut last_speed_metric = startup_time.elapsed().as_millis() as i64;
let mut next_service = last_speed_metric + 500;
let mut transferred = 0u64;
@@ -260,7 +260,7 @@ fn bob_main(
use zssp_proto::result::ReceiveOk::*;
use zssp_proto::result::SessionEvent::*;
match context.receive(
bob_app,
&mut bob_app,
|b| bob_out.send(b).is_ok(),
TEST_MTU,
|_| Some((|b| bob_out.send(b).is_ok(), TEST_MTU)),
@@ -305,7 +305,7 @@ fn bob_main(
}
if current_time >= next_service {
next_service = current_time + context.service(bob_app, |_| Some((|b| bob_out.send(b).is_ok(), TEST_MTU)));
next_service = current_time + context.service(&mut bob_app, |_| Some((|b| bob_out.send(b).is_ok(), TEST_MTU)));
}
}
}
@@ -317,14 +317,14 @@ fn core(time: u64, packet_success_rate: u32) {
let alice_app = TestApplication {
time: Instant::now(),
name: "alice",
ratchets: Mutex::new(Ratchets::new()),
ratchets: Ratchets::new(),
};
let bob_keypair = P384CrateKeyPair::generate(&mut OsRng);
let bob_pubkey = bob_keypair.public_key();
let bob_app = TestApplication {
time: Instant::now(),
name: "bob",
ratchets: Mutex::new(Ratchets::new()),
ratchets: Ratchets::new(),
};
let (alice_out, bob_in) = mpsc::sync_channel::<Vec<u8>>(256);
@@ -338,7 +338,7 @@ fn core(time: u64, packet_success_rate: u32) {
alice_main(
run,
packet_success_rate,
&alice_app,
alice_app,
alice_out,
alice_in,
bob_out,
@@ -351,7 +351,7 @@ fn core(time: u64, packet_success_rate: u32) {
bob_main(
run,
packet_success_rate,
&bob_app,
bob_app,
bob_out,
bob_in,
alice_out,
+23 -16
View File
@@ -83,11 +83,11 @@ impl Default for Settings {
}
}
/// Trait to implement to integrate the session into an application.
/// Trait to implement to integrate ZSSP into an application.
///
/// Templating the session on this trait lets the code here be almost entirely transport, OS,
/// and use case independent.
pub trait ApplicationLayer: Sized {
/// This is a container trait for all of the cryptographic algorithms ZSSP will use, and all of the
/// generic types that ZSSP will attach to sessions.
pub trait CryptoLayer: Sized {
/// These are constants that can be redefined from their defaults to change rekey
/// and negotiation timeout behavior. If two sides of a ZSSP session have different constants,
/// the protocol will tend to default to the smaller constants.
@@ -131,13 +131,20 @@ pub trait ApplicationLayer: Sized {
/// for ZSSP to achieve FIPS compliance.
type Kem: Kyber1024PrivateKey<Self::Rng>;
/// An arbitrary opaque object for use by the application that is attached to each session.
type SessionData;
}
/// Trait to implement to integrate ZSSP into an application.
///
/// Templating ZSSP on this trait lets the code here be almost entirely transport, OS,
/// and use case independent.
pub trait ApplicationLayer: Sized {
/// Specifies which concrete set of cryptography types will be used by this application.
type Crypto: CryptoLayer;
/// A user-defined error returned when the `ApplicationLayer` fails to access persistent storage
/// for a peer's ratchet states.
type StorageError: std::error::Error;
/// An arbitrary opaque object for use by the application that is attached to each session.
type SessionData;
/// Should return the current time in milliseconds. Does not have to be monotonic, nor synced
/// with remote peers (although both of these properties would help reliability slightly).
/// Used to determine if any current handshakes should be resent or timed-out, or if a session
@@ -169,14 +176,14 @@ pub trait ApplicationLayer: Sized {
/// least one party is misconfigured and got their ratchet keys corrupted or lost, or Bob has
/// been compromised and is being impersonated. An attacker must at least have Bob's private
/// static key to be able to ask Alice to downgrade.
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<Self>>) -> bool;
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<Self::Crypto>>) -> bool;
/// Function to accept sessions after final negotiation.
/// The second argument is the identity that the remote peer sent us. The application
/// must verify this identity is associated with the remote peer's static key.
/// To prevent desync, if this function specifies that we should connect, no other open session
/// with the same remote peer must exist. Drop or call expire on any pre-existing sessions
/// before returning.
fn check_accept_session(&mut self, remote_static_key: &Self::PublicKey, identity: &[u8]) -> AcceptAction<Self>;
fn check_accept_session(&mut self, remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey, identity: &[u8]) -> AcceptAction<Self::Crypto>;
/// Lookup a specific ratchet state based on its ratchet fingerprint.
/// This function will be called whenever Alice attempts to connect to us with a non-empty
@@ -204,8 +211,8 @@ pub trait ApplicationLayer: Sized {
/// function `ApplicationLayer::check_accept_session`.
fn restore_by_identity(
&mut self,
remote_static_key: &Self::PublicKey,
session_data: &Self::SessionData,
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
) -> Result<Option<RatchetStates>, Self::StorageError>;
/// Atomically commit the update specified by `update_data` to storage, or return an error if
/// the update could not be made.
@@ -225,8 +232,8 @@ pub trait ApplicationLayer: Sized {
/// Otherwise, when we restart, we will not be allowed to reconnect.
fn save_ratchet_state(
&mut self,
remote_static_key: &Self::PublicKey,
session_data: &Self::SessionData,
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
update_data: RatchetUpdate<'_>,
) -> Result<(), Self::StorageError>;
@@ -234,17 +241,17 @@ pub trait ApplicationLayer: Sized {
/// These are provided for debugging, logging or metrics purposes, and must be used for
/// nothing else. Do not base protocol-level decisions upon the events passed to this function.
#[cfg(feature = "logging")]
fn event_log(&mut self, event: LogEvent<'_, Self>);
fn event_log(&mut self, event: LogEvent<'_, Self::Crypto>);
}
/// A collection of fields specifying how to complete the key exchange with a specific remote peer,
/// used by Bob, the responder, at the very last stage of the key exchange.
///
/// Corresponds to the *Accept* callback of Transition Algorithm 4.
pub struct AcceptAction<App: ApplicationLayer> {
pub struct AcceptAction<Crypto: CryptoLayer> {
/// The data object to be attached to the session if we successfully connect.
/// If this field is None then we will not connect to this remote peer.
pub session_data: Option<App::SessionData>,
pub session_data: Option<Crypto::SessionData>,
/// Whether or not we will accept a connection with the remote peer when they do not have a
/// ratchet key that we think they should have.
pub responder_disallows_downgrade: bool,
+39 -39
View File
@@ -6,7 +6,7 @@ use std::hash::Hash;
use std::num::NonZeroU32;
use std::sync::{Arc, Weak};
use crate::application::ApplicationLayer;
use crate::application::{ApplicationLayer, CryptoLayer};
use crate::challenge::ChallengeContext;
use crate::crypto::{AES_256_KEY_SIZE, AES_GCM_NONCE_SIZE};
use crate::fragmentation::{send_with_fragmentation, DefragBuffer};
@@ -31,21 +31,21 @@ pub(crate) use log;
/// defragment incoming packets that are not yet associated with a session.
///
/// Internally this is just a clonable Arc, so it can be safely shared with multiple threads.
pub struct Context<App: ApplicationLayer>(Arc<ContextInner<App>>);
impl<App: ApplicationLayer> Clone for Context<App> {
pub struct Context<Crypto: CryptoLayer>(Arc<ContextInner<Crypto>>);
impl<Crypto: CryptoLayer> Clone for Context<Crypto> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
pub(crate) type SessionMap<App> = RefCell<HashMap<NonZeroU32, Weak<Session<App>>>>;
pub(crate) type SessionMap<Crypto> = RefCell<HashMap<NonZeroU32, Weak<Session<Crypto>>>>;
pub(crate) struct ContextInner<App: ApplicationLayer> {
pub(crate) rng: RefCell<App::Rng>,
pub(crate) s_secret: App::KeyPair,
pub(crate) session_map: SessionMap<App>,
pub(crate) sessions: RefCell<HashMap<*const Session<App>, Weak<Session<App>>>>,
pub(crate) b2_map: RefCell<HashMap<NonZeroU32, StateB2<App>>>,
pub(crate) struct ContextInner<Crypto: CryptoLayer> {
pub(crate) rng: RefCell<Crypto::Rng>,
pub(crate) s_secret: Crypto::KeyPair,
pub(crate) session_map: SessionMap<Crypto>,
pub(crate) sessions: RefCell<HashMap<*const Session<Crypto>, Weak<Session<Crypto>>>>,
pub(crate) b2_map: RefCell<HashMap<NonZeroU32, StateB2<Crypto>>>,
hello_defrag: RefCell<DefragBuffer>,
challenge: RefCell<ChallengeContext>,
@@ -62,9 +62,9 @@ fn to_packet_nonce(n: &[u8; AES_GCM_NONCE_SIZE]) -> &[u8; PACKET_NONCE_SIZE] {
(&n[n.len() - PACKET_NONCE_SIZE..]).try_into().unwrap()
}
impl<App: ApplicationLayer> Context<App> {
impl<Crypto: CryptoLayer> Context<Crypto> {
/// Create a new session context.
pub fn new(static_secret_key: App::KeyPair, mut rng: App::Rng) -> Self {
pub fn new(static_secret_key: Crypto::KeyPair, mut rng: Crypto::Rng) -> Self {
let challenge = ChallengeContext::new(&mut rng);
Self(Arc::new(ContextInner {
rng: RefCell::new(rng),
@@ -93,15 +93,15 @@ impl<App: ApplicationLayer> Context<App> {
/// object
/// * `identity` - Payload to be sent to Bob that contains the information necessary
/// for the upper protocol to authenticate and approve of Alice's identity
pub fn open(
pub fn open<App: ApplicationLayer>(
&mut self,
app: App,
send: impl FnMut(Vec<u8>) -> bool,
mut mtu: usize,
static_remote_key: App::PublicKey,
session_data: App::SessionData,
static_remote_key: Crypto::PublicKey,
session_data: Crypto::SessionData,
identity: Vec<u8>,
) -> Result<Arc<Session<App>>, OpenError<App::StorageError>> {
) -> Result<Arc<Session<Crypto>>, OpenError<App::StorageError>> where App: ApplicationLayer<Crypto = Crypto>{
mtu = mtu.max(MIN_TRANSPORT_MTU);
if identity.len() > IDENTITY_MAX_SIZE {
return Err(OpenError::IdentityTooLarge);
@@ -111,13 +111,13 @@ impl<App: ApplicationLayer> Context<App> {
// Process zeta layer.
trans_to_a1(
app,
&ctx,
ctx,
static_remote_key,
session_data,
identity,
|Packet(kid, nonce, payload): &Packet| {
// Process fragmentation layer.
send_with_fragmentation::<App>(send, mtu, *kid, to_packet_nonce(&nonce), payload, None);
send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(&nonce), payload, None);
},
)
}
@@ -130,15 +130,15 @@ impl<App: ApplicationLayer> Context<App> {
/// * `send_to` - Function to get senders for existing sessions, permitting MTU and path lookup
/// * `remote_address` - Whatever the remote address is, as long as you can Hash it
/// * `raw_fragment` - Buffer containing incoming wire packet
pub fn receive<SendFn: FnMut(Vec<u8>) -> bool>(
pub fn receive<App, SendFn: FnMut(Vec<u8>) -> bool>(
&mut self,
mut app: App,
send_unassociated_reply: impl FnMut(Vec<u8>) -> bool,
mut send_unassociated_mtu: usize,
send_to: impl FnOnce(&Arc<Session<App>>) -> Option<(SendFn, usize)>,
send_to: impl FnOnce(&Arc<Session<Crypto>>) -> Option<(SendFn, usize)>,
remote_address: &impl Hash,
raw_fragment: Vec<u8>,
) -> Result<ReceiveOk<App>, ReceiveError<App::StorageError>> {
) -> Result<ReceiveOk<Crypto>, ReceiveError<App::StorageError>> where App: ApplicationLayer<Crypto = Crypto> {
use crate::result::FaultType::*;
send_unassociated_mtu = send_unassociated_mtu.max(MIN_TRANSPORT_MTU);
let ctx = &self.0;
@@ -187,7 +187,7 @@ impl<App: ApplicationLayer> Context<App> {
|Packet(kid, nonce, payload): &Packet, hk: Option<&[u8; AES_256_KEY_SIZE]>| {
if let Some((send_fragment, mut mtu)) = send_to(&session) {
mtu = mtu.max(MIN_TRANSPORT_MTU);
send_with_fragmentation::<App>(
send_with_fragmentation::<Crypto>(
send_fragment,
mtu,
*kid,
@@ -201,7 +201,7 @@ impl<App: ApplicationLayer> Context<App> {
let (p, _) = from_nonce(&pn);
let ret = match p {
PACKET_TYPE_DATA => {
received_payload_in_place(&mut zeta, kid_recv, to_aes_nonce(&pn), &mut assembled_packet)?;
received_payload_in_place::<App>(&mut zeta, kid_recv, to_aes_nonce(&pn), &mut assembled_packet)?;
SessionEvent::Data(assembled_packet)
}
PACKET_TYPE_HANDSHAKE_RESPONSE => {
@@ -286,7 +286,7 @@ impl<App: ApplicationLayer> Context<App> {
}
PACKET_TYPE_SESSION_REJECTED => {
log!(app, ReceivedRawD);
received_d_trans(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
received_d_trans::<App>(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
log!(app, DIsAuthClosedSession(&session));
SessionEvent::Rejected
}
@@ -323,7 +323,7 @@ impl<App: ApplicationLayer> Context<App> {
kid_recv,
assembled_packet,
|Packet(kid, nonce, payload), hk| {
send_with_fragmentation::<App>(
send_with_fragmentation::<Crypto>(
send_unassociated_reply,
send_unassociated_mtu,
*kid,
@@ -369,7 +369,7 @@ impl<App: ApplicationLayer> Context<App> {
log!(app, ReceivedRawX1);
// Process recv challenge layer.
let challenge_start = assembled_packet.len() - CHALLENGE_SIZE;
let result = ctx.challenge.borrow_mut().process_hello::<App::Hash>(
let result = ctx.challenge.borrow_mut().process_hello::<Crypto::Hash>(
remote_address,
(&assembled_packet[challenge_start..]).try_into().unwrap(),
);
@@ -379,7 +379,7 @@ impl<App: ApplicationLayer> Context<App> {
challenge_packet.extend(&assembled_packet[..KID_SIZE]);
challenge_packet.extend(&challenge);
let nonce = to_nonce(PACKET_TYPE_CHALLENGE, ctx.rng.borrow_mut().next_u64());
send_with_fragmentation::<App>(
send_with_fragmentation::<Crypto>(
send_unassociated_reply,
send_unassociated_mtu,
0,
@@ -401,7 +401,7 @@ impl<App: ApplicationLayer> Context<App> {
to_aes_nonce(&n),
assembled_packet,
|Packet(kid, nonce, payload), hk| {
send_with_fragmentation::<App>(
send_with_fragmentation::<Crypto>(
send_unassociated_reply,
send_unassociated_mtu,
*kid,
@@ -424,7 +424,7 @@ impl<App: ApplicationLayer> Context<App> {
{
if let Some(Some(session)) = ctx.session_map.borrow_mut().get(&kid_recv).map(|r| r.upgrade()) {
let mut zeta = session.0.borrow_mut();
respond_to_challenge(
respond_to_challenge::<App>(
&mut zeta,
&ctx.rng,
&assembled_packet[KID_SIZE..].try_into().unwrap(),
@@ -454,7 +454,7 @@ impl<App: ApplicationLayer> Context<App> {
/// * `payload` - Data to send
pub fn send(
&mut self,
session: &Arc<Session<App>>,
session: &Arc<Session<Crypto>>,
send: impl FnMut(Vec<u8>) -> bool,
mut mtu: usize,
payload: Vec<u8>,
@@ -462,8 +462,8 @@ impl<App: ApplicationLayer> Context<App> {
debug_assert_eq!(session.0.borrow().ctx.as_ptr(), Arc::as_ptr(&self.0));
mtu = mtu.max(MIN_TRANSPORT_MTU);
let mut zeta = session.0.borrow_mut();
send_payload(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
send_with_fragmentation::<App>(send, mtu, *kid, to_packet_nonce(nonce), &payload, hk);
send_payload::<Crypto>(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(nonce), &payload, hk);
})
}
@@ -474,11 +474,11 @@ impl<App: ApplicationLayer> Context<App> {
/// a problem. It is completely fine to call this function more often than the returned interval.
///
/// * `send_to` - Function to get a sender and an MTU to send something over an active session
pub fn service<SendFn: FnMut(Vec<u8>) -> bool>(
pub fn service<App, SendFn: FnMut(Vec<u8>) -> bool>(
&mut self,
mut app: App,
mut send_to: impl FnMut(&Arc<Session<App>>) -> Option<(SendFn, usize)>,
) -> i64 {
mut send_to: impl FnMut(&Arc<Session<Crypto>>) -> Option<(SendFn, usize)>,
) -> i64 where App: ApplicationLayer<Crypto = Crypto> {
let ctx = &self.0;
let sessions = ctx.sessions.borrow_mut();
let current_time = app.time();
@@ -495,7 +495,7 @@ impl<App: ApplicationLayer> Context<App> {
|Packet(kid, nonce, payload): &Packet, hk| {
if let Some((send_fragment, mut mtu)) = send_to(&session) {
mtu = mtu.max(MIN_TRANSPORT_MTU);
send_with_fragmentation::<App>(
send_with_fragmentation::<Crypto>(
send_fragment,
mtu,
*kid,
@@ -507,10 +507,10 @@ impl<App: ApplicationLayer> Context<App> {
},
);
next_timer = next_timer.min(zeta.next_timer());
zeta.defrag.service::<App>(current_time);
zeta.defrag.service(current_time);
}
}
ctx.hello_defrag.borrow_mut().service::<App>(current_time);
(App::SETTINGS.resend_time as i64).min(next_timer - current_time)
ctx.hello_defrag.borrow_mut().service(current_time);
(Crypto::SETTINGS.resend_time as i64).min(next_timer - current_time)
}
}
+35
View File
@@ -27,3 +27,38 @@ pub use p384;
pub use pqc_kyber;
#[cfg(feature = "sha2")]
pub use sha2;
/*
TODO: wrangle the feature flags so we can provide the default set of crypto implementations below.
use crate::application::{Settings, CryptoLayer};
#[cfg(feature = "default")]
pub trait CrateCryptoLayer {
/// These are constants that can be redefined from their defaults to change rekey
/// and negotiation timeout behavior. If two sides of a ZSSP session have different constants,
/// the protocol will tend to default to the smaller constants.
const SETTINGS: Settings = Settings::new_ms();
/// A user-defined error returned when the `ApplicationLayer` fails to access persistent storage
/// for a peer's ratchet states.
type StorageError: std::error::Error;
/// An arbitrary opaque object for use by the application that is attached to each session.
type SessionData;
}
use rand_core::OsRng;
#[cfg(feature = "default")]
impl<Crypto: CrateCryptoLayer> CryptoLayer for Crypto {
type Rng = OsRng;
type Prp = Aes256Crate;
type Aead = AesGcmCrate;
type Hash = Sha512Crate;
type PublicKey = P384CratePublicKey;
type KeyPair = P384CrateKeyPair;
type Kem = RustKyber1024PrivateKey;
type StorageError = Crypto::StorageError;
type SessionData = Crypto::SessionData;
}
*/
+6 -6
View File
@@ -4,7 +4,7 @@ use std::collections::HashMap;
use zeroize::Zeroizing;
use crate::application::ApplicationLayer;
use crate::application::{CryptoLayer, ApplicationLayer};
use crate::crypto::{Aes256Prp, AES_256_KEY_SIZE};
use crate::proto::*;
use crate::result::{byzantine_fault, ReceiveError};
@@ -28,7 +28,7 @@ fn create_fragment_header(
}
/// Corresponds to the fragmentation algorithm described in Section 6.
pub fn send_with_fragmentation<App: ApplicationLayer>(
pub fn send_with_fragmentation<Crypto: CryptoLayer>(
mut send: impl FnMut(Vec<u8>) -> bool,
mtu: usize,
identifier: u32,
@@ -52,7 +52,7 @@ pub fn send_with_fragmentation<App: ApplicationLayer>(
fragment.extend(&packet[i..j]);
if let Some(hk_send) = hk_send {
App::Prp::encrypt_in_place(
Crypto::Prp::encrypt_in_place(
hk_send,
(&mut fragment[HEADER_AUTH_START..HEADER_AUTH_END]).try_into().unwrap(),
);
@@ -95,7 +95,7 @@ impl DefragBuffer {
}
if let Some(hk_recv) = self.hk_recv.as_ref() {
App::Prp::decrypt_in_place(
<App::Crypto as CryptoLayer>::Prp::decrypt_in_place(
hk_recv,
(&mut raw_fragment[HEADER_AUTH_START..HEADER_AUTH_END])
.try_into()
@@ -115,7 +115,7 @@ impl DefragBuffer {
return Err(e);
}
let expiration_time = current_time + App::SETTINGS.fragment_assembly_timeout as i64;
let expiration_time = current_time + App::Crypto::SETTINGS.fragment_assembly_timeout as i64;
let mut map = self.fragment_map.borrow_mut();
match map.entry(n) {
Entry::Occupied(mut entry) => {
@@ -157,7 +157,7 @@ impl DefragBuffer {
}
}
pub fn service<App: ApplicationLayer>(&self, current_time: i64) {
pub fn service(&self, current_time: i64) {
let mut map = self.fragment_map.borrow_mut();
map.retain(|_, buffer| buffer.expiration_time < current_time);
}
+22 -22
View File
@@ -1,23 +1,23 @@
use std::sync::Arc;
use crate::application::ApplicationLayer;
use crate::application::CryptoLayer;
use crate::Session;
/// ZSSP events that might be interesting to log or aggregate into metrics.
#[allow(missing_docs)]
pub enum LogEvent<'a, App: ApplicationLayer> {
ResentX1(&'a Arc<Session<App>>),
TimeoutX1(&'a Arc<Session<App>>),
pub enum LogEvent<'a, Crypto: CryptoLayer> {
ResentX1(&'a Arc<Session<Crypto>>),
TimeoutX1(&'a Arc<Session<Crypto>>),
TimeoutX2,
ResentX3(&'a Arc<Session<App>>),
TimeoutX3(&'a Arc<Session<App>>),
ResentKeyConfirm(&'a Arc<Session<App>>),
TimeoutKeyConfirm(&'a Arc<Session<App>>),
StartedRekeyingSentK1(&'a Arc<Session<App>>),
ResentK1(&'a Arc<Session<App>>),
TimeoutK1(&'a Arc<Session<App>>),
ResentK2(&'a Arc<Session<App>>),
TimeoutK2(&'a Arc<Session<App>>),
ResentX3(&'a Arc<Session<Crypto>>),
TimeoutX3(&'a Arc<Session<Crypto>>),
ResentKeyConfirm(&'a Arc<Session<Crypto>>),
TimeoutKeyConfirm(&'a Arc<Session<Crypto>>),
StartedRekeyingSentK1(&'a Arc<Session<Crypto>>),
ResentK1(&'a Arc<Session<Crypto>>),
TimeoutK1(&'a Arc<Session<Crypto>>),
ResentK2(&'a Arc<Session<Crypto>>),
TimeoutK2(&'a Arc<Session<Crypto>>),
/// `(packet_type, packet_counter, fragment_no, fragment_count)`
ReceivedRawFragment(u8, u64, usize, usize),
ReceivedRawX1,
@@ -25,24 +25,24 @@ pub enum LogEvent<'a, App: ApplicationLayer> {
X1SucceededChallenge,
X1IsAuthSentX2,
ReceivedRawChallenge,
ChallengeIsAuth(&'a Arc<Session<App>>),
ChallengeIsAuth(&'a Arc<Session<Crypto>>),
ReceivedRawX2,
X2IsAuthSentX3(&'a Arc<Session<App>>),
X2IsAuthSentX3(&'a Arc<Session<Crypto>>),
ReceivedRawX3,
X3IsAuthSentKeyConfirm(&'a Arc<Session<App>>),
X3IsAuthSentKeyConfirm(&'a Arc<Session<Crypto>>),
ReceivedRawKeyConfirm,
KeyConfirmIsAuthSentAck(&'a Arc<Session<App>>),
KeyConfirmIsAuthSentAck(&'a Arc<Session<Crypto>>),
ReceivedRawAck,
AckIsAuth(&'a Arc<Session<App>>),
AckIsAuth(&'a Arc<Session<Crypto>>),
ReceivedRawK1,
K1IsAuthSentK2(&'a Arc<Session<App>>),
K1IsAuthSentK2(&'a Arc<Session<Crypto>>),
ReceivedRawK2,
K2IsAuthSentKeyConfirm(&'a Arc<Session<App>>),
K2IsAuthSentKeyConfirm(&'a Arc<Session<Crypto>>),
ReceivedRawD,
DIsAuthClosedSession(&'a Arc<Session<App>>),
DIsAuthClosedSession(&'a Arc<Session<Crypto>>),
}
impl<'a, App: ApplicationLayer> std::fmt::Debug for LogEvent<'a, App> {
impl<'a, Crypto: CryptoLayer> std::fmt::Debug for LogEvent<'a, Crypto> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ResentX1(_) => f.debug_tuple("ResentX1").finish(),
+3 -3
View File
@@ -1,6 +1,6 @@
use std::sync::Arc;
use crate::application::ApplicationLayer;
use crate::application::CryptoLayer;
use crate::Session;
/// An error that can occur when attempting to open a session.
@@ -121,13 +121,13 @@ pub(crate) use byzantine_fault;
/// Result generated by the context packet receive function, with possible payloads.
#[derive(Clone)]
pub enum ReceiveOk<App: ApplicationLayer> {
pub enum ReceiveOk<Crypto: CryptoLayer> {
/// Packet superficially appeared valid but is not associated with a session yet.
/// This can occur because the packet was only a fragment of a larger packet,
/// or if it was a control packet that does not go through full Noise authentication.
Unassociated,
/// Packet was authentic and belongs to this specific session.
Session(Arc<Session<App>>, SessionEvent),
Session(Arc<Session<Crypto>>, SessionEvent),
}
/// Something that can occur to an associated session when a packet is received successfully,
/// including receiving a payload of decrypted, authenticated data.
+14 -14
View File
@@ -2,19 +2,19 @@ use std::marker::PhantomData;
use zeroize::Zeroizing;
use crate::application::ApplicationLayer;
use crate::application::CryptoLayer;
use crate::crypto::*;
use crate::proto::*;
pub struct SymmetricState<App: ApplicationLayer> {
pub struct SymmetricState<Crypto: CryptoLayer> {
k: Zeroizing<[u8; AES_256_KEY_SIZE]>,
ck: Zeroizing<[u8; HASHLEN]>,
h: [u8; HASHLEN],
/// If anyone knows a better way to get rid of the "parameter `App` is never used" error please
/// let me know.
_app: PhantomData<fn() -> App::SessionData>,
/// If anyone knows a better way to get rid of the "parameter `Crypto` is never used" error
/// please let me know.
_app: PhantomData<fn() -> Crypto::SessionData>,
}
impl<App: ApplicationLayer> Clone for SymmetricState<App> {
impl<Crypto: CryptoLayer> Clone for SymmetricState<Crypto> {
fn clone(&self) -> Self {
Self {
k: self.k.clone(),
@@ -25,7 +25,7 @@ impl<App: ApplicationLayer> Clone for SymmetricState<App> {
}
}
impl<App: ApplicationLayer> SymmetricState<App> {
impl<Crypto: CryptoLayer> SymmetricState<Crypto> {
/// HMAC-SHA512 key derivation based on KBKDF Counter Mode:
/// https://csrc.nist.gov/publications/detail/sp/800-108/rev-1/final.
/// Cryptographically this isn't meaningfully different from
@@ -55,18 +55,18 @@ impl<App: ApplicationLayer> SymmetricState<App> {
buffer.extend(&(num_outputs * 8 * HASHLEN as u16).to_be_bytes());
debug_assert!(num_outputs >= 1);
*output1 = App::Hash::hmac(input_key_material, &buffer);
*output1 = Crypto::Hash::hmac(input_key_material, &buffer);
if let Some(output2) = output2 {
debug_assert!(num_outputs >= 2);
buffer[0] = 2;
*output2 = App::Hash::hmac(input_key_material, &buffer);
*output2 = Crypto::Hash::hmac(input_key_material, &buffer);
}
if let Some(output3) = output3 {
debug_assert!(num_outputs >= 3);
buffer[0] = 3;
*output3 = App::Hash::hmac(input_key_material, &buffer);
*output3 = Crypto::Hash::hmac(input_key_material, &buffer);
}
}
@@ -98,7 +98,7 @@ impl<App: ApplicationLayer> SymmetricState<App> {
}
/// Corresponds to Noise `MixHash`.
pub fn mix_hash(&mut self, data: &[u8]) {
let mut hash = App::Hash::new();
let mut hash = Crypto::Hash::new();
hash.update(&self.h);
hash.update(data);
self.h = hash.finish();
@@ -129,7 +129,7 @@ impl<App: ApplicationLayer> SymmetricState<App> {
plaintext_start: usize,
buffer: &mut Vec<u8>,
) {
let tag = App::Aead::encrypt_in_place(&self.k, &iv, Some(&self.h), &mut buffer[plaintext_start..]);
let tag = Crypto::Aead::encrypt_in_place(&self.k, &iv, Some(&self.h), &mut buffer[plaintext_start..]);
buffer.extend(&tag);
self.mix_hash(&buffer[plaintext_start..]);
}
@@ -141,11 +141,11 @@ impl<App: ApplicationLayer> SymmetricState<App> {
buffer: &mut [u8],
tag: [u8; AES_GCM_TAG_SIZE],
) -> bool {
let mut hash = App::Hash::new();
let mut hash = Crypto::Hash::new();
hash.update(&self.h);
hash.update(buffer);
hash.update(&tag);
let ret = App::Aead::decrypt_in_place(&self.k, &iv, Some(&self.h), buffer, &tag);
let ret = Crypto::Aead::decrypt_in_place(&self.k, &iv, Some(&self.h), buffer, &tag);
self.h = hash.finish();
ret
}
+114 -114
View File
File diff suppressed because it is too large Load Diff