added better fingerprint support

This commit is contained in:
Monica Moniot
2023-11-21 15:21:19 -05:00
parent b62727f099
commit 7bd55a6467
6 changed files with 80 additions and 11 deletions
+5 -2
View File
@@ -63,6 +63,7 @@ impl CryptoLayer for TestApplication {
type SessionData = u128;
type IncomingPacketBuffer = Vec<u8>;
type FingerprintData = ();
}
#[allow(unused)]
impl ApplicationLayer<TestApplication> for &TestApplication {
@@ -82,6 +83,7 @@ impl ApplicationLayer<TestApplication> for &TestApplication {
&mut self,
remote_static_key: &CrateP384PublicKey,
identity: &[u8],
_: Option<&()>,
) -> AcceptAction<TestApplication> {
AcceptAction {
session_data: Some(1),
@@ -93,15 +95,16 @@ impl ApplicationLayer<TestApplication> for &TestApplication {
fn restore_by_fingerprint(
&mut self,
ratchet_fingerprint: &[u8; RATCHET_SIZE],
) -> Result<Option<RatchetState>, std::io::Error> {
) -> Result<Option<(RatchetState, ())>, std::io::Error> {
let ratchets = self.ratchets.lock().unwrap();
Ok(ratchets.rf_map.get(ratchet_fingerprint).cloned())
Ok(ratchets.rf_map.get(ratchet_fingerprint).cloned().map(|r| (r, ())))
}
fn restore_by_identity(
&mut self,
remote_static_key: &CrateP384PublicKey,
session_data: &u128,
_: Option<&()>,
) -> Result<Option<RatchetStates>, std::io::Error> {
let ratchets = self.ratchets.lock().unwrap();
Ok(ratchets.peer_map.get(session_data).cloned())
+4 -1
View File
@@ -53,6 +53,7 @@ impl AsRef<[u8]> for PooledVec {
#[allow(unused)]
impl DefaultCrypto for TestApplication {
type SessionData = ();
type LookupData = ();
type IncomingPacketBuffer = PooledVec;
}
@@ -76,6 +77,7 @@ impl ApplicationLayer<TestApplication> for &TestApplication {
&mut self,
remote_static_key: &CrateP384PublicKey,
identity: &[u8],
_: Option<&()>,
) -> AcceptAction<TestApplication> {
AcceptAction {
session_data: Some(()),
@@ -87,7 +89,7 @@ impl ApplicationLayer<TestApplication> for &TestApplication {
fn restore_by_fingerprint(
&mut self,
ratchet_fingerprint: &[u8; RATCHET_SIZE],
) -> Result<Option<RatchetState>, std::io::Error> {
) -> Result<Option<(RatchetState, ())>, std::io::Error> {
Ok(None)
}
@@ -95,6 +97,7 @@ impl ApplicationLayer<TestApplication> for &TestApplication {
&mut self,
remote_static_key: &CrateP384PublicKey,
session_data: &(),
_: Option<&()>,
) -> Result<Option<RatchetStates>, std::io::Error> {
Ok(None)
}
+60 -4
View File
@@ -153,6 +153,22 @@ pub trait CryptoLayer: Sized {
/// each session.
type SessionData;
/// Type for arbitrary opaque object that is attached to a new connection attempt if Alice sends
/// us a ratchet fingerprint recognized by `restore_by_fingerprint`.
///
/// If Alice continues to connect
/// with us, then this object will be passed to `check_accept_session` and `restore_by_identity`.
/// This is useful if the ratchet fingerprint was derived from a one-time password, in which
/// case `FingerprintData` can contain metadata regarding the one-time password. This can be
/// used by `check_accept_session` and `restore_by_identity` to perform additional
/// authentication checks, such as validating the one-time password as an invitation code.
///
/// `FingerprintData` can also be used with extreme caution to cache database resources that can
/// speed up the expected future calls to `check_accept_session` and `restore_by_identity`.
/// If this is done, the implementor is required in `check_accept_session` to verify that the
/// cached resources in `FingerprintData` indeed belong to the specified remote peer.
type FingerprintData;
/// Data type for incoming packet buffers.
///
/// This can be something like `Vec<u8>` or `Box<[u8]>` or it can be something like a pooled
@@ -190,6 +206,9 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// If this function is configured to always return true, it means peers will not be able to
/// connect to us unless they had a prior-established ratchet key with us. This is the best way
/// for the paranoid to enforce a manual allow-list.
///
/// Corresponds to the "Hello Requires Recognized Ratchet, π_1" security flag of Transition
/// Algorithm 2 within the ZSSP whitepaper.
fn hello_requires_recognized_ratchet(&mut self) -> bool;
/// This function is called if we, as Alice, attempted to open a session with Bob using a
/// non-empty ratchet key, but Bob does not have this ratchet key and wants to downgrade
@@ -206,15 +225,29 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: 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.
///
/// Corresponds to the "Initiator Disallows Downgrade, π_2" security flag of Transition
/// Algorithm 3 within the ZSSP whitepaper.
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<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.
///
/// The implementor must verify that three arguments, `remote_static_key`, `identity` and
/// optionally `fingerprint_data` all belong to the same remote peer, using whatever definition
/// of "same remote peer" that the upper protocol chooses.
/// `fingerprint_data` is an opaque type that is only `Some` if Alice sent us a ratchet
/// fingerprint that was successfully restored by `restore_by_fingerprint`.
///
/// 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: &Crypto::PublicKey, identity: &[u8]) -> AcceptAction<Crypto>;
///
/// Corresponds to the **Accept** call of Transition Algorithm 4 within the ZSSP whitepaper.
fn check_accept_session(
&mut self,
remote_static_key: &Crypto::PublicKey,
identity: &[u8],
fingerprint_data: Option<&Crypto::FingerprintData>,
) -> AcceptAction<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
@@ -222,10 +255,24 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
///
/// If a ratchet state with a matching fingerprint could not be found, this function should
/// return `Ok(None)`.
///
/// This function can also return an opaque `FingerprintData` object. If Alice continues to connect
/// with us, then this object will be passed to `check_accept_session` and `restore_by_identity`.
/// This is useful if the ratchet fingerprint was derived from a one-time password, in which
/// case `FingerprintData` can contain metadata regarding the one-time password. This can be
/// used by `check_accept_session` and `restore_by_identity` to perform additional
/// authentication checks, such as validating the one-time password as an invitation code.
///
/// `FingerprintData` can also be used with extreme caution to cache database resources that can
/// speed up the expected future calls to `check_accept_session` and `restore_by_identity`.
/// If this is done, the implementor is required in `check_accept_session` to verify that the
/// cached resources in `FingerprintData` indeed belong to the specified remote peer.
///
/// Corresponds to the **Restore** call of Transition Algorithm 2 within the ZSSP whitepaper.
fn restore_by_fingerprint(
&mut self,
ratchet_fingerprint: &[u8; RATCHET_SIZE],
) -> Result<Option<RatchetState>, std::io::Error>;
) -> Result<Option<(RatchetState, Crypto::FingerprintData)>, std::io::Error>;
/// Lookup the specific ratchet states based on the identity of the peer being communicated with.
/// This function will be called whenever Alice attempts to open a session, or Bob attempts
/// to verify Alice's identity.
@@ -240,10 +287,13 @@ pub trait ApplicationLayer<Crypto: CryptoLayer>: Sized {
/// This function is not responsible for deciding whether or not to connect to this remote peer.
/// Filtering peers should be done by the caller to `Context::open` as well as by the
/// function `ApplicationLayer::check_accept_session`.
///
/// Corresponds to the **Restore** call of Transition Algorithm 1 and 4 within the ZSSP whitepaper.
fn restore_by_identity(
&mut self,
remote_static_key: &Crypto::PublicKey,
session_data: &Crypto::SessionData,
fingerprint_data: Option<&Crypto::FingerprintData>,
) -> 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.
@@ -306,12 +356,18 @@ pub struct AcceptAction<Crypto: CryptoLayer> {
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.
///
/// Corresponds to the "Responder Disallows Downgrade, π_3" security flag of Transition
/// Algorithm 4 within the ZSSP whitepaper.
pub responder_disallows_downgrade: bool,
/// Whether or not to send an explicit rejection packet to the remote peer if we do not create
/// a session with them.
///
/// This field will not be used if `session_data` is `Some` and the remote peer passes all other
/// authentication checks.
///
/// Corresponds to the "Responder Silently Rejects, π_4" security flag of Transition
/// Algorithm 4 within the ZSSP whitepaper.
pub responder_silently_rejects: bool,
}
+2
View File
@@ -38,6 +38,7 @@ pub use openssl_sys;
#[cfg(feature = "default-crypto")]
pub trait DefaultCrypto {
type SessionData;
type LookupData;
type IncomingPacketBuffer: AsMut<[u8]> + AsRef<[u8]>;
}
#[cfg(feature = "default-crypto")]
@@ -54,5 +55,6 @@ impl<C: DefaultCrypto> crate::application::CryptoLayer for C {
type Kem = CrateKyber1024PrivateKey;
type SessionData = C::SessionData;
type FingerprintData = C::LookupData;
type IncomingPacketBuffer = C::IncomingPacketBuffer;
}
+1
View File
@@ -257,6 +257,7 @@ fn test_cache() {
type Kem = CrateKyber1024PrivateKey;
type SessionData = ();
type FingerprintData = ();
type IncomingPacketBuffer = Vec<u8>;
}
+8 -4
View File
@@ -68,6 +68,7 @@ pub(crate) struct MutableState<Crypto: CryptoLayer> {
/// Corresponds to State B_2 of the Zeta State Machine found in Section 4.1 - Definition 3.
pub(crate) struct StateB2<Crypto: CryptoLayer> {
ratchet_state: RatchetState,
lookup_data: Option<Crypto::FingerprintData>,
kid_send: NonZeroU32,
pub kid_recv: NonZeroU32,
pub hk_send: Zeroizing<[u8; AES_256_KEY_SIZE]>,
@@ -331,7 +332,7 @@ pub(crate) fn trans_to_a1<Crypto: CryptoLayer, App: ApplicationLayer<Crypto>>(
send: impl FnOnce(&mut [u8], Option<&Crypto::PrpEnc>),
) -> Result<(Arc<Session<Crypto>>, Option<i64>), OpenError> {
let RatchetStates { state1, state2 } = app
.restore_by_identity(&s_remote, &session_data)
.restore_by_identity(&s_remote, &session_data, None)
.map_err(OpenError::StorageError)?
.unwrap_or_default();
@@ -472,11 +473,13 @@ pub(crate) fn received_x1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
return Err(fault!(FailedAuth, true));
}
let mut lookup_data = None;
let mut ratchet_state = None;
while i + RATCHET_SIZE <= j {
match app.restore_by_fingerprint((&x1[i..i + RATCHET_SIZE]).try_into().unwrap()) {
Ok(None) => {}
Ok(Some(rs)) => {
Ok(Some((rs, data))) => {
lookup_data = Some(data);
ratchet_state = Some(rs);
break;
}
@@ -551,6 +554,7 @@ pub(crate) fn received_x1_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
e_secret,
noise,
defrag: Mutex::new(Fragged::new()),
lookup_data,
}),
app.time(),
);
@@ -838,7 +842,7 @@ pub(crate) fn received_x3_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
noise.get_ask(hmac, LABEL_KEX_KEY, &mut kek_send, &mut kek_recv);
let c = 0;
let action = app.check_accept_session(&s_remote, &x3[identity_start..identity_end]);
let action = app.check_accept_session(&s_remote, &x3[identity_start..identity_end], zeta.lookup_data.as_ref());
let responder_disallows_downgrade = action.responder_disallows_downgrade;
let responder_silently_rejects = action.responder_silently_rejects;
let create_reject = || {
@@ -854,7 +858,7 @@ pub(crate) fn received_x3_trans<Crypto: CryptoLayer, App: ApplicationLayer<Crypt
d
};
if let Some(session_data) = action.session_data {
let result = app.restore_by_identity(&s_remote, &session_data);
let result = app.restore_by_identity(&s_remote, &session_data, zeta.lookup_data.as_ref());
match result {
Ok(rss) => {
let RatchetStates { state1, state2 } = rss.unwrap_or_default();