mirror of
https://github.com/zerotier/zssp.git
synced 2026-05-22 16:28:40 -07:00
updated docs
This commit is contained in:
@@ -13,9 +13,9 @@ use crate::proto::*;
|
||||
/// Corresponds to the Ratchet Key and Ratchet Fingerprint described in Section 3.
|
||||
#[derive(Clone, Eq)]
|
||||
pub struct RatchetState {
|
||||
pub key: Zeroizing<[u8; RATCHET_SIZE]>,
|
||||
pub fingerprint: Option<Zeroizing<[u8; RATCHET_SIZE]>>,
|
||||
pub chain_len: u64,
|
||||
pub(crate) key: Zeroizing<[u8; RATCHET_SIZE]>,
|
||||
pub(crate) fingerprint: Option<Zeroizing<[u8; RATCHET_SIZE]>>,
|
||||
pub(crate) chain_len: u64,
|
||||
}
|
||||
impl PartialEq for RatchetState {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
@@ -35,9 +35,14 @@ impl std::hash::Hash for RatchetState {
|
||||
}
|
||||
}
|
||||
impl RatchetState {
|
||||
/// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length.
|
||||
pub fn new(key: Zeroizing<[u8; RATCHET_SIZE]>, fingerprint: Zeroizing<[u8; RATCHET_SIZE]>, chain_len: u64) -> Self {
|
||||
RatchetState { key, fingerprint: Some(fingerprint), chain_len }
|
||||
}
|
||||
/// Creates a new ratchet state from the given ratchet key, ratchet fingerprint and chain length.
|
||||
///
|
||||
/// The caller should make sure any copies of these values are deleted from memory once they are
|
||||
/// no longer needed.
|
||||
pub fn new_raw(key: [u8; RATCHET_SIZE], fingerprint: [u8; RATCHET_SIZE], chain_len: u64) -> Self {
|
||||
RatchetState {
|
||||
key: Zeroizing::new(key),
|
||||
@@ -45,6 +50,28 @@ impl RatchetState {
|
||||
chain_len,
|
||||
}
|
||||
}
|
||||
/// The ratchet key for this ratchet state. This is directly mixed into the master secret of a
|
||||
/// session and so is very sensitive. All operations upon a ratchet key must be implemented
|
||||
/// in constant time. The user should prefer to do nothing with the ratchet key besides copying
|
||||
/// it to or from a storage device.
|
||||
///
|
||||
/// If `fingerprint` returns `None` then this is the "empty" ratchet state and the key will be
|
||||
/// all zeros.
|
||||
pub fn key(&self) -> &[u8; RATCHET_SIZE] {
|
||||
&self.key
|
||||
}
|
||||
/// Ratchet keys and fingerprints are "chained together", where each set is derived from the
|
||||
/// previous set.
|
||||
///
|
||||
/// This function outputs the total length of that chain, as in the total number of previous
|
||||
/// ratchet states that this ratchet state was derived from.
|
||||
pub fn chain_len(&self) -> u64 {
|
||||
self.chain_len
|
||||
}
|
||||
/// Creates a new "empty" ratchet state, where the ratchet fingerprint is the
|
||||
/// empty string, the ratchet key is all zeros, and the chain length is 0.
|
||||
///
|
||||
/// This value is the default value of `RatchetState`.
|
||||
pub fn empty() -> Self {
|
||||
RatchetState {
|
||||
key: Zeroizing::new([0u8; RATCHET_SIZE]),
|
||||
@@ -52,6 +79,9 @@ impl RatchetState {
|
||||
chain_len: 0,
|
||||
}
|
||||
}
|
||||
/// Creates a new ratchet state derived from a one-time-password. If both sides of a session use
|
||||
/// the same one-time-password then they can use this ratchet state to connect with each other
|
||||
/// for the first time.
|
||||
pub fn new_from_otp<Hmac: Sha512Hmac>(otp: &[u8]) -> RatchetState {
|
||||
let mut buffer = ArrayVec::<u8, 23>::new();
|
||||
buffer.push(1);
|
||||
@@ -69,12 +99,25 @@ impl RatchetState {
|
||||
|
||||
Self::new(rk, rf, 1)
|
||||
}
|
||||
/// Returns true if this is the "empty" ratchet state, where the ratchet fingerprint is the
|
||||
/// empty string, the ratchet key is all zeros, and the chain length is 0.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.fingerprint.is_none()
|
||||
}
|
||||
/// Checks if the fingerprint of this ratchet state equals the fingerprint contained in argument
|
||||
/// `rf`. Uses constant time equality.
|
||||
pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool {
|
||||
self.fingerprint.as_ref().map_or(false, |rf0| secure_eq(rf0, rf))
|
||||
}
|
||||
/// The ratchet fingerprint for this ratchet state.
|
||||
///
|
||||
/// If this returns `None` then the ratchet fingerprint is the empty string.
|
||||
/// This is the "empty" ratchet state and the key will be all zeros.
|
||||
///
|
||||
/// The ratchet fingerprint value is sensitive and should be hidden,
|
||||
/// but the security of ZSSP can survive having this value leaked.
|
||||
/// Operations on a ratchet fingerprint should be implemented in constant time,
|
||||
/// but it is ok if they are not.
|
||||
pub fn fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
self.fingerprint.as_deref()
|
||||
}
|
||||
@@ -85,23 +128,38 @@ impl Default for RatchetState {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pair of ratchet states.
|
||||
/// An ordered pair of two ratchet states.
|
||||
/// It is expected that an instance of this object will be saved to a storage device per-peer,
|
||||
/// and be restore-able via the `ApplicationLayer` trait.
|
||||
///
|
||||
/// This corresponds to the possible values of abstract variables `rf` and `rk` found in Section 4.3.
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RatchetStates {
|
||||
/// The first ratchet state from the pair.
|
||||
pub state1: RatchetState,
|
||||
/// The second ratchet state from the pair. It can, and usually will be `None`.
|
||||
pub state2: Option<RatchetState>,
|
||||
}
|
||||
impl RatchetStates {
|
||||
/// Creates a new pair of ratchet states. The order of the arguments matters, and it should be
|
||||
/// the same order that was originally given by an instance of the `RatchetUpdate` struct.
|
||||
pub fn new(state1: RatchetState, state2: Option<RatchetState>) -> Self {
|
||||
Self { state1, state2 }
|
||||
}
|
||||
/// Creates a new initial pair of ratchet states, where the first ratchet state is the empty
|
||||
/// ratchet state and the second is `None`.
|
||||
///
|
||||
/// This value is the default value of `RatchetStates`.
|
||||
pub fn new_initial_states() -> Self {
|
||||
Self { state1: RatchetState::empty(), state2: None }
|
||||
}
|
||||
/// Creates a new initial pair of ratchet states from a one-time password.
|
||||
/// The first ratchet state will be derived from this password, while the second will be `None`.
|
||||
///
|
||||
/// If both sides of a session use the same one-time-password then they can use this pair to
|
||||
/// connect with each other for the first time. This pair can be generated with this function,
|
||||
/// saved to persistent storage, and eventually restored by the `ApplicationLayer` when we
|
||||
/// attempt to form a session with the correct peer.
|
||||
pub fn new_otp_states<Hmac: Sha512Hmac>(otp: &[u8]) -> Self {
|
||||
Self {
|
||||
state1: RatchetState::new_from_otp::<Hmac>(otp),
|
||||
@@ -173,9 +231,10 @@ impl<'a> RatchetUpdate<'a> {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// If this updated specifies to delete two ratchet fingerprints, this function will return the second one.
|
||||
/// `deleted_fingerprint1` will return the first one.
|
||||
/// The returned ratchet fingerprint will always be the ratchet fingerprint of field `deleted_state2`.
|
||||
/// If this updated specifies to delete two ratchet fingerprints, this function will return the
|
||||
/// second one. `deleted_fingerprint1` will return the first one.
|
||||
/// The returned ratchet fingerprint will always be the ratchet fingerprint of field
|
||||
/// `deleted_state2`.
|
||||
///
|
||||
/// It is exceptionally rare that there will be more than one ratchet fingerprint to be deleted.
|
||||
/// Care should be taken to make sure that this update will still be correctly committed in the
|
||||
|
||||
@@ -170,13 +170,19 @@ impl Default for RatchetStates {
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of references to ratchet states specifying how a remote peer's persistent
|
||||
/// storage should be updated.
|
||||
/// A set of references to ratchet states specifying how a remote peer's persistent storage should
|
||||
/// be updated. This struct is designed to provide any and all potentially needed data for
|
||||
/// maintaining a store of these ratchet states. It should be straightforward to commit these updates
|
||||
/// to anything from an in-memory hashtable to a disk-based database.
|
||||
///
|
||||
/// There should be only up to two ratchet states saved to storage at a time per peer.
|
||||
/// Every time a new ratchet state is generated, a previous ratchet state will be deleted.
|
||||
/// There will only be up to two ratchet states saved to storage at a time per peer.
|
||||
/// Every time a third ratchet state is generated, a previous ratchet state will be deleted.
|
||||
///
|
||||
/// These are sensitive values should they ought to be securely stored.
|
||||
/// These are sensitive values should they ought to be securely stored, with restricted read-write
|
||||
/// permissions if stored on disk.
|
||||
///
|
||||
/// To prevent desync or resource leakage, these updates should be committed atomically. If that is
|
||||
/// not possible, then new ratchet states should be written before old ratchet states are deleted
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RatchetUpdate<'a> {
|
||||
/// The ratchet key and fingerprint to store in the first slot.
|
||||
@@ -193,12 +199,15 @@ pub struct RatchetUpdate<'a> {
|
||||
pub deleted_state2: Option<&'a RatchetState>,
|
||||
}
|
||||
impl<'a> RatchetUpdate<'a> {
|
||||
/// Clones the `state1` and `state2` pair of ratchet states out of this struct and into a
|
||||
/// `RatchetStates` instance.
|
||||
/// Returns the final `RatchetStates` that should be the only thing saved after this update is
|
||||
/// fully committed. Future calls to `ApplicationLayer::restore_by_identity` should return this struct.
|
||||
pub fn to_states(&self) -> RatchetStates {
|
||||
RatchetStates::new(self.state1.clone(), self.state2.cloned())
|
||||
}
|
||||
/// Returns the new ratchet fingerprint that was added during this update, if any.
|
||||
/// If this update specifies adding a brand new ratchet fingerprint, this function will return it.
|
||||
/// The returned ratchet fingerprint will always be the ratchet fingerprint of field `state1`.
|
||||
///
|
||||
/// If a fingerprint is returned then it is guaranteed that `state1_was_just_added` will be true.
|
||||
pub fn added_fingerprint(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
if self.state1_was_just_added {
|
||||
self.state1.fingerprint()
|
||||
@@ -206,7 +215,12 @@ impl<'a> RatchetUpdate<'a> {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns the first ratchet fingerprint that was deleted during this update, if any.
|
||||
/// If this updated specifies to delete an old ratchet fingerprint, this function will return it.
|
||||
/// The returned ratchet fingerprint will always be the ratchet fingerprint of field
|
||||
/// `deleted_state1`.
|
||||
///
|
||||
/// There may be a second ratchet fingerprint to be deleted, which function
|
||||
/// `deleted_fingerprint2` will return.
|
||||
pub fn deleted_fingerprint1(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
if let Some(rs) = &self.deleted_state1 {
|
||||
rs.fingerprint()
|
||||
@@ -214,12 +228,14 @@ impl<'a> RatchetUpdate<'a> {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns the second fingerprint that was deleted during this update, if there was more than
|
||||
/// one ratchet state deleted during this update.
|
||||
/// If this updated specifies to delete two ratchet fingerprints, this function will return the
|
||||
/// second one. `deleted_fingerprint1` will return the first one.
|
||||
/// The returned ratchet fingerprint will always be the ratchet fingerprint of field
|
||||
/// `deleted_state2`.
|
||||
///
|
||||
/// It is extremely rare that this function ever returns `Some`, but it is possible.
|
||||
/// Users should explicitly test that their implementation is able to correctly delete more than
|
||||
/// 1 ratchet state at a time, because it is unlikely to occur under normal conditions.
|
||||
/// It is exceptionally rare that there will be more than one ratchet fingerprint to be deleted.
|
||||
/// Care should be taken to make sure that this update will still be correctly committed in the
|
||||
/// rare event that this returns `Some`.
|
||||
pub fn deleted_fingerprint2(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
if let Some(rs) = &self.deleted_state2 {
|
||||
rs.fingerprint()
|
||||
|
||||
Reference in New Issue
Block a user