mirror of
https://github.com/zerotier/zssp.git
synced 2026-05-22 16:28:40 -07:00
renamed and refactored in reference impl
This commit is contained in:
@@ -65,9 +65,7 @@ impl CryptoLayer for TestApplication {
|
||||
type SessionData = u128;
|
||||
}
|
||||
#[allow(unused)]
|
||||
impl ApplicationLayer for &mut TestApplication {
|
||||
type Crypto = TestApplication;
|
||||
|
||||
impl ApplicationLayer<TestApplication> for &mut TestApplication {
|
||||
fn hello_requires_recognized_ratchet(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -44,9 +44,7 @@ impl CryptoLayer for MyApp {
|
||||
/// In this example for simplicity we won't be hooking up ratchet keys to a filesystem backend.
|
||||
/// They are dropped and peers ignore if they are missing.
|
||||
#[allow(unused)]
|
||||
impl ApplicationLayer for &mut MyApp {
|
||||
type Crypto = MyApp;
|
||||
|
||||
impl ApplicationLayer<MyApp> for &mut MyApp {
|
||||
fn hello_requires_recognized_ratchet(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -139,10 +139,7 @@ pub trait CryptoLayer: Sized {
|
||||
///
|
||||
/// 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;
|
||||
|
||||
pub trait ApplicationLayer<C: CryptoLayer>: Sized {
|
||||
/// 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
|
||||
@@ -174,7 +171,7 @@ 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::Crypto>>) -> bool;
|
||||
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<C>>) -> 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.
|
||||
@@ -183,9 +180,9 @@ pub trait ApplicationLayer: Sized {
|
||||
/// before returning.
|
||||
fn check_accept_session(
|
||||
&mut self,
|
||||
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
|
||||
remote_static_key: &<C as CryptoLayer>::PublicKey,
|
||||
identity: &[u8],
|
||||
) -> AcceptAction<Self::Crypto>;
|
||||
) -> AcceptAction<C>;
|
||||
|
||||
/// 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
|
||||
@@ -213,8 +210,8 @@ pub trait ApplicationLayer: Sized {
|
||||
/// function `ApplicationLayer::check_accept_session`.
|
||||
fn restore_by_identity(
|
||||
&mut self,
|
||||
remote_static_key: &<Self::Crypto as CryptoLayer>::PublicKey,
|
||||
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
|
||||
remote_static_key: &<C as CryptoLayer>::PublicKey,
|
||||
session_data: &<C as CryptoLayer>::SessionData,
|
||||
) -> Result<Option<RatchetStates>, std::io::Error>;
|
||||
/// Atomically commit the update specified by `update_data` to storage, or return an error if
|
||||
/// the update could not be made.
|
||||
@@ -234,8 +231,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::Crypto as CryptoLayer>::PublicKey,
|
||||
session_data: &<Self::Crypto as CryptoLayer>::SessionData,
|
||||
remote_static_key: &<C as CryptoLayer>::PublicKey,
|
||||
session_data: &<C as CryptoLayer>::SessionData,
|
||||
update_data: RatchetUpdate<'_>,
|
||||
) -> Result<(), std::io::Error>;
|
||||
|
||||
@@ -244,7 +241,7 @@ pub trait ApplicationLayer: Sized {
|
||||
/// nothing else. Do not base protocol-level decisions upon the events passed to this function.
|
||||
#[cfg(feature = "logging")]
|
||||
#[allow(unused)]
|
||||
fn event_log(&mut self, event: LogEvent<'_, Self::Crypto>) {}
|
||||
fn event_log(&mut self, event: LogEvent<'_, C>) {}
|
||||
}
|
||||
|
||||
/// A collection of fields specifying how to complete the key exchange with a specific remote peer,
|
||||
|
||||
+50
-57
@@ -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<Crypto: CryptoLayer>(Arc<ContextInner<Crypto>>);
|
||||
impl<Crypto: CryptoLayer> Clone for Context<Crypto> {
|
||||
pub struct Context<C: CryptoLayer>(Arc<ContextInner<C>>);
|
||||
impl<C: CryptoLayer> Clone for Context<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type SessionMap<Crypto> = RefCell<HashMap<NonZeroU32, Weak<Session<Crypto>>>>;
|
||||
pub(crate) type SessionMap<C> = RefCell<HashMap<NonZeroU32, Weak<Session<C>>>>;
|
||||
|
||||
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>>>,
|
||||
pub(crate) struct ContextInner<C: CryptoLayer> {
|
||||
pub(crate) rng: RefCell<C::Rng>,
|
||||
pub(crate) s_secret: C::KeyPair,
|
||||
pub(crate) session_map: SessionMap<C>,
|
||||
pub(crate) sessions: RefCell<HashMap<*const Session<C>, Weak<Session<C>>>>,
|
||||
pub(crate) b2_map: RefCell<HashMap<NonZeroU32, StateB2<C>>>,
|
||||
|
||||
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<Crypto: CryptoLayer> Context<Crypto> {
|
||||
impl<C: CryptoLayer> Context<C> {
|
||||
/// Create a new session context.
|
||||
pub fn new(static_secret_key: Crypto::KeyPair, mut rng: Crypto::Rng) -> Self {
|
||||
pub fn new(static_secret_key: C::KeyPair, mut rng: C::Rng) -> Self {
|
||||
let challenge = ChallengeContext::new(&mut rng);
|
||||
Self(Arc::new(ContextInner {
|
||||
rng: RefCell::new(rng),
|
||||
@@ -93,18 +93,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
/// 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<App: ApplicationLayer>(
|
||||
pub fn open<App: ApplicationLayer<C>>(
|
||||
&mut self,
|
||||
app: App,
|
||||
send: impl FnMut(Vec<u8>) -> bool,
|
||||
mut mtu: usize,
|
||||
static_remote_key: Crypto::PublicKey,
|
||||
session_data: Crypto::SessionData,
|
||||
static_remote_key: C::PublicKey,
|
||||
session_data: C::SessionData,
|
||||
identity: Vec<u8>,
|
||||
) -> Result<Arc<Session<Crypto>>, OpenError>
|
||||
where
|
||||
App: ApplicationLayer<Crypto = Crypto>,
|
||||
{
|
||||
) -> Result<Arc<Session<C>>, OpenError> {
|
||||
mtu = mtu.max(MIN_TRANSPORT_MTU);
|
||||
if identity.len() > IDENTITY_MAX_SIZE {
|
||||
return Err(OpenError::IdentityTooLarge);
|
||||
@@ -120,7 +117,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
identity,
|
||||
|Packet(kid, nonce, payload): &Packet| {
|
||||
// Process fragmentation layer.
|
||||
let _ = send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(&nonce), payload, None);
|
||||
let _ = send_with_fragmentation::<C>(send, mtu, *kid, to_packet_nonce(&nonce), payload, None);
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -133,18 +130,15 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
/// * `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<App, SendFn: FnMut(Vec<u8>) -> bool>(
|
||||
pub fn receive<App: ApplicationLayer<C>, 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<Crypto>>) -> Option<(SendFn, usize)>,
|
||||
send_to: impl FnOnce(&Arc<Session<C>>) -> Option<(SendFn, usize)>,
|
||||
remote_address: &impl Hash,
|
||||
raw_fragment: Vec<u8>,
|
||||
) -> Result<ReceiveOk<Crypto>, ReceiveError>
|
||||
where
|
||||
App: ApplicationLayer<Crypto = Crypto>,
|
||||
{
|
||||
) -> Result<ReceiveOk<C>, ReceiveError> {
|
||||
use crate::result::FaultType::*;
|
||||
send_unassociated_mtu = send_unassociated_mtu.max(MIN_TRANSPORT_MTU);
|
||||
let ctx = &self.0;
|
||||
@@ -158,7 +152,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
let mut zeta = session.0.borrow_mut();
|
||||
let result =
|
||||
zeta.defrag
|
||||
.received_fragment::<App>(raw_fragment, app.time(), |n, frag_no, frag_count| {
|
||||
.received_fragment::<C, App>(raw_fragment, app.time(), |n, frag_no, frag_count| {
|
||||
let (p, c) = from_nonce(n);
|
||||
if p != PACKET_TYPE_DATA {
|
||||
log!(app, ReceivedRawFragment(p, c, frag_no, frag_count));
|
||||
@@ -193,7 +187,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
|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);
|
||||
let _ = send_with_fragmentation::<Crypto>(
|
||||
let _ = send_with_fragmentation::<C>(
|
||||
send_fragment,
|
||||
mtu,
|
||||
*kid,
|
||||
@@ -207,7 +201,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
let (p, _) = from_nonce(&pn);
|
||||
let ret = match p {
|
||||
PACKET_TYPE_DATA => {
|
||||
received_payload_in_place::<App>(
|
||||
received_payload_in_place::<C, App>(
|
||||
&mut zeta,
|
||||
kid_recv,
|
||||
to_aes_nonce(&pn),
|
||||
@@ -297,7 +291,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
}
|
||||
PACKET_TYPE_SESSION_REJECTED => {
|
||||
log!(app, ReceivedRawD);
|
||||
received_d_trans::<App>(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
|
||||
received_d_trans::<C, App>(&mut zeta, kid_recv, to_aes_nonce(&pn), assembled_packet)?;
|
||||
log!(app, DIsAuthClosedSession(&session));
|
||||
SessionEvent::Rejected
|
||||
}
|
||||
@@ -313,17 +307,19 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
if let Entry::Occupied(mut entry) = b2_map.entry(kid_recv) {
|
||||
let zeta = entry.get_mut();
|
||||
// Process recv fragmentation layer.
|
||||
let result =
|
||||
zeta.defrag
|
||||
.received_fragment::<App>(raw_fragment, app.time(), |n, frag_no, frag_count| {
|
||||
let (p, c) = from_nonce(n);
|
||||
log!(app, ReceivedRawFragment(p, c, frag_no, frag_count));
|
||||
if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(byzantine_fault!(InvalidPacket, true))
|
||||
}
|
||||
})?;
|
||||
let result = zeta.defrag.received_fragment::<C, App>(
|
||||
raw_fragment,
|
||||
app.time(),
|
||||
|n, frag_no, frag_count| {
|
||||
let (p, c) = from_nonce(n);
|
||||
log!(app, ReceivedRawFragment(p, c, frag_no, frag_count));
|
||||
if p == PACKET_TYPE_HANDSHAKE_COMPLETION && c == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(byzantine_fault!(InvalidPacket, true))
|
||||
}
|
||||
},
|
||||
)?;
|
||||
if let Some((_, assembled_packet)) = result {
|
||||
log!(app, ReceivedRawX3);
|
||||
let zeta = entry.remove();
|
||||
@@ -334,7 +330,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
kid_recv,
|
||||
assembled_packet,
|
||||
|Packet(kid, nonce, payload), hk| {
|
||||
let _ = send_with_fragmentation::<Crypto>(
|
||||
let _ = send_with_fragmentation::<C>(
|
||||
send_unassociated_reply,
|
||||
send_unassociated_mtu,
|
||||
*kid,
|
||||
@@ -362,7 +358,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
}
|
||||
} else {
|
||||
// Process recv fragmentation layer.
|
||||
let result = ctx.hello_defrag.borrow_mut().received_fragment::<App>(
|
||||
let result = ctx.hello_defrag.borrow_mut().received_fragment::<C, App>(
|
||||
raw_fragment,
|
||||
app.time(),
|
||||
|n, frag_no, frag_count| {
|
||||
@@ -381,7 +377,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
log!(app, ReceivedRawX1);
|
||||
// Process recv challenge layer.
|
||||
let challenge_start = assembled_packet.len() - CHALLENGE_SIZE;
|
||||
let result = ctx.challenge.borrow_mut().process_hello::<Crypto::Hash>(
|
||||
let result = ctx.challenge.borrow_mut().process_hello::<C::Hash>(
|
||||
remote_address,
|
||||
(&assembled_packet[challenge_start..]).try_into().unwrap(),
|
||||
);
|
||||
@@ -391,7 +387,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
challenge_packet.extend(&assembled_packet[..KID_SIZE]);
|
||||
challenge_packet.extend(&challenge);
|
||||
let nonce = to_nonce(PACKET_TYPE_CHALLENGE, ctx.rng.borrow_mut().next_u64());
|
||||
let _ = send_with_fragmentation::<Crypto>(
|
||||
let _ = send_with_fragmentation::<C>(
|
||||
send_unassociated_reply,
|
||||
send_unassociated_mtu,
|
||||
0,
|
||||
@@ -413,7 +409,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
to_aes_nonce(&n),
|
||||
assembled_packet,
|
||||
|Packet(kid, nonce, payload), hk| {
|
||||
let _ = send_with_fragmentation::<Crypto>(
|
||||
let _ = send_with_fragmentation::<C>(
|
||||
send_unassociated_reply,
|
||||
send_unassociated_mtu,
|
||||
*kid,
|
||||
@@ -436,7 +432,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
{
|
||||
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::<App>(
|
||||
respond_to_challenge::<C, App>(
|
||||
&mut zeta,
|
||||
&ctx.rng,
|
||||
&assembled_packet[KID_SIZE..].try_into().unwrap(),
|
||||
@@ -466,7 +462,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
/// * `payload` - Data to send
|
||||
pub fn send(
|
||||
&mut self,
|
||||
session: &Arc<Session<Crypto>>,
|
||||
session: &Arc<Session<C>>,
|
||||
send: impl FnMut(Vec<u8>) -> bool,
|
||||
mut mtu: usize,
|
||||
payload: Vec<u8>,
|
||||
@@ -474,8 +470,8 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
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::<Crypto>(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
|
||||
let result = send_with_fragmentation::<Crypto>(send, mtu, *kid, to_packet_nonce(nonce), &payload, hk);
|
||||
send_payload::<C>(&mut zeta, payload, |Packet(kid, nonce, payload), hk| {
|
||||
let result = send_with_fragmentation::<C>(send, mtu, *kid, to_packet_nonce(nonce), &payload, hk);
|
||||
if matches!(result, Err(true)) {
|
||||
return Err(SendError::DataTooLarge);
|
||||
}
|
||||
@@ -490,14 +486,11 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
/// 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<App, SendFn: FnMut(Vec<u8>) -> bool>(
|
||||
pub fn service<App: ApplicationLayer<C>, SendFn: FnMut(Vec<u8>) -> bool>(
|
||||
&mut self,
|
||||
mut app: App,
|
||||
mut send_to: impl FnMut(&Arc<Session<Crypto>>) -> Option<(SendFn, usize)>,
|
||||
) -> i64
|
||||
where
|
||||
App: ApplicationLayer<Crypto = Crypto>,
|
||||
{
|
||||
mut send_to: impl FnMut(&Arc<Session<C>>) -> Option<(SendFn, usize)>,
|
||||
) -> i64 {
|
||||
let ctx = &self.0;
|
||||
let sessions = ctx.sessions.borrow_mut();
|
||||
let current_time = app.time();
|
||||
@@ -514,7 +507,7 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
|Packet(kid, nonce, payload): &Packet, hk| {
|
||||
if let Some((send_fragment, mut mtu)) = send_to(&session) {
|
||||
mtu = mtu.max(MIN_TRANSPORT_MTU);
|
||||
let _ = send_with_fragmentation::<Crypto>(
|
||||
let _ = send_with_fragmentation::<C>(
|
||||
send_fragment,
|
||||
mtu,
|
||||
*kid,
|
||||
@@ -530,6 +523,6 @@ impl<Crypto: CryptoLayer> Context<Crypto> {
|
||||
}
|
||||
}
|
||||
ctx.hello_defrag.borrow_mut().service(current_time);
|
||||
(Crypto::SETTINGS.resend_time as i64).min(next_timer - current_time)
|
||||
(C::SETTINGS.resend_time as i64).min(next_timer - current_time)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ impl DefragBuffer {
|
||||
}
|
||||
|
||||
/// Corresponds to the authentication and defragmentation algorithm described in Section 6.1.
|
||||
pub fn received_fragment<App: ApplicationLayer>(
|
||||
pub fn received_fragment<C: CryptoLayer, App: ApplicationLayer<C>>(
|
||||
&self,
|
||||
mut raw_fragment: Vec<u8>,
|
||||
current_time: i64,
|
||||
@@ -98,7 +98,7 @@ impl DefragBuffer {
|
||||
}
|
||||
|
||||
if let Some(hk_recv) = self.hk_recv.as_ref() {
|
||||
<App::Crypto as CryptoLayer>::Prp::decrypt_in_place(
|
||||
C::Prp::decrypt_in_place(
|
||||
hk_recv,
|
||||
(&mut raw_fragment[HEADER_AUTH_START..HEADER_AUTH_END])
|
||||
.try_into()
|
||||
@@ -118,7 +118,7 @@ impl DefragBuffer {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
let expiration_time = current_time + App::Crypto::SETTINGS.fragment_assembly_timeout as i64;
|
||||
let expiration_time = current_time + C::SETTINGS.fragment_assembly_timeout as i64;
|
||||
let mut map = self.fragment_map.borrow_mut();
|
||||
match map.entry(n) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
|
||||
+129
-147
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user