mirror of
https://github.com/zerotier/zssp.git
synced 2026-05-22 16:28:40 -07:00
fixed mistakes in docs
This commit is contained in:
@@ -231,7 +231,7 @@ pub trait ApplicationLayer<C: CryptoLayer>: Sized {
|
||||
fn initiator_disallows_downgrade(&mut self, session: &Arc<Session<C>>) -> bool;
|
||||
/// Function to accept sessions after final negotiation.
|
||||
///
|
||||
/// The implementor must verify that three arguments, `remote_static_key`, `identity` and
|
||||
/// The implementor must verify that the 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
|
||||
@@ -359,14 +359,19 @@ pub trait ApplicationLayer<C: CryptoLayer>: Sized {
|
||||
/// .map_err(to_err)?;
|
||||
/// let mut rows = stmt.query([peer]).map_err(to_err)?;
|
||||
/// if let Some(row) = rows.next().map_err(to_err)? {
|
||||
/// let peer_fp1: &[u8] = row.get_ref(0).map_err(to_err)?.as_bytes().map_err(to_err)?;
|
||||
/// let peer_fp2: Option<&[u8]> = row.get_ref(1).map_err(to_err)?.as_bytes_or_null().map_err(to_err)?;
|
||||
/// let peer_fp1 = row.get_ref(0).map_err(to_err)?.as_bytes_or_null().map_err(to_err)?;
|
||||
/// let peer_fp2 = row.get_ref(1).map_err(to_err)?.as_bytes_or_null().map_err(to_err)?;
|
||||
/// let peer_fp1 = if let Some(fp1) = peer_fp1 {
|
||||
/// fp1.try_into().map_err(to_err)?
|
||||
/// } else {
|
||||
/// &[0u8; RATCHET_SIZE]
|
||||
/// };
|
||||
/// let peer_fp2 = if let Some(fp2) = peer_fp2 {
|
||||
/// Some(fp2.try_into().map_err(to_err)?)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// if !update_data.compare_fingerprints(peer_fp1.try_into().map_err(to_err)?, peer_fp2) {
|
||||
/// if !update_data.compare_fingerprints(peer_fp1, peer_fp2) {
|
||||
/// return Ok(false);
|
||||
/// }
|
||||
/// } else {
|
||||
|
||||
@@ -43,11 +43,11 @@ impl RatchetState {
|
||||
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.
|
||||
/// Creates a new "zero" ratchet state, where the ratchet fingerprint is all zeros,
|
||||
/// the ratchet key is all zeros, and the chain length is 0.
|
||||
///
|
||||
/// This value is the default value of `RatchetState`.
|
||||
pub fn empty() -> Self {
|
||||
pub fn zero() -> Self {
|
||||
RatchetState {
|
||||
key: Zeroizing::new([0u8; RATCHET_SIZE]),
|
||||
fingerprint: Zeroizing::new([0u8; RATCHET_SIZE]),
|
||||
@@ -85,9 +85,7 @@ impl RatchetState {
|
||||
&self.key
|
||||
}
|
||||
/// 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.
|
||||
/// It could be all zeros if this is the "zero" ratchet state.
|
||||
///
|
||||
/// The ratchet fingerprint value is sensitive and should be hidden,
|
||||
/// but the security of ZSSP can survive having this value leaked.
|
||||
@@ -104,13 +102,13 @@ impl RatchetState {
|
||||
pub fn chain_len(&self) -> u64 {
|
||||
self.chain_len
|
||||
}
|
||||
/// 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 {
|
||||
/// Returns true if this is the "zero" ratchet state, where the ratchet fingerprint is all zeros,
|
||||
/// the ratchet key is all zeros, and the chain length is 0.
|
||||
pub fn is_zero(&self) -> bool {
|
||||
secure_eq(self.fingerprint(), &[0u8; RATCHET_SIZE])
|
||||
}
|
||||
/// Checks if the fingerprint of this ratchet state equals the
|
||||
/// fingerprint contained in argument `rf`.
|
||||
/// fingerprint contained in argument `rf`.
|
||||
///
|
||||
/// Uses constant time equality.
|
||||
pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool {
|
||||
@@ -134,7 +132,7 @@ impl RatchetState {
|
||||
}
|
||||
impl Default for RatchetState {
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +160,7 @@ impl RatchetStates {
|
||||
///
|
||||
/// This value is the default value of `RatchetStates`.
|
||||
pub fn new_initial_states() -> Self {
|
||||
Self { state1: RatchetState::empty(), state2: None }
|
||||
Self { state1: RatchetState::zero(), 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`.
|
||||
@@ -274,7 +272,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
/// 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 self.cur_state1_was_just_deleted && !self.cur_state1.is_empty() {
|
||||
if self.cur_state1_was_just_deleted && !self.cur_state1.is_zero() {
|
||||
return Some(self.cur_state1.fingerprint());
|
||||
}
|
||||
None
|
||||
@@ -290,7 +288,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
pub fn deleted_fingerprint2(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
if self.cur_state2_was_just_deleted {
|
||||
if let Some(rf) = self.cur_state2 {
|
||||
if !rf.is_empty() {
|
||||
if !rf.is_zero() {
|
||||
return Some(rf.fingerprint());
|
||||
}
|
||||
}
|
||||
@@ -305,7 +303,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
/// then the peer should be added to storage with the new ratchet states specified by this
|
||||
/// `CompareAndSwap` struct.
|
||||
pub fn cur_is_initial_states(&self) -> bool {
|
||||
self.cur_state1.is_empty() && self.cur_state2.is_none()
|
||||
self.cur_state1.is_zero() && self.cur_state2.is_none()
|
||||
}
|
||||
/// Compares the ratchet fingerprints of `cur_state1` and `cur_state2` with `rf1` and `rf2`.
|
||||
/// If they are equal this function will return `true`.
|
||||
|
||||
@@ -870,7 +870,7 @@ pub(crate) fn received_x3_trans<C: CryptoLayer, App: ApplicationLayer<C>>(
|
||||
let mut should_warn_missing_ratchet = false;
|
||||
|
||||
if (zeta.ratchet_state != state1) & (Some(&zeta.ratchet_state) != state2.as_ref()) {
|
||||
if !responder_disallows_downgrade && zeta.ratchet_state.is_empty() {
|
||||
if !responder_disallows_downgrade && zeta.ratchet_state.is_zero() {
|
||||
should_warn_missing_ratchet = true;
|
||||
} else {
|
||||
if !responder_silently_rejects {
|
||||
|
||||
@@ -155,6 +155,9 @@ pub trait ApplicationLayer<C: 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
|
||||
@@ -171,13 +174,23 @@ pub trait ApplicationLayer<C: 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<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.
|
||||
///
|
||||
/// The implementor must verify that `remote_static_key` and `identity` 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.
|
||||
///
|
||||
/// Corresponds to the **Accept** call of Transition Algorithm 4 within the ZSSP whitepaper.
|
||||
fn check_accept_session(
|
||||
&mut self,
|
||||
remote_static_key: &<C as CryptoLayer>::PublicKey,
|
||||
@@ -190,6 +203,20 @@ pub trait ApplicationLayer<C: 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],
|
||||
@@ -198,8 +225,9 @@ pub trait ApplicationLayer<C: CryptoLayer>: Sized {
|
||||
/// This function will be called whenever Alice attempts to open a session, or Bob attempts
|
||||
/// to verify Alice's identity.
|
||||
///
|
||||
/// If the peer's ratchet states could not be could, this function should return
|
||||
/// `RatchetState::new_initial_states()`.
|
||||
/// If the peer's ratchet states could not be found, this function should return `None`.
|
||||
/// A return value of `None` is equivalent to a return value of
|
||||
/// `Some(RatchetState::new_initial_states())`.
|
||||
///
|
||||
/// If a one-time-password has been pre-shared with this peer, `RatchetState::new_otp_states(...)`
|
||||
/// should be pre-saved to the storage backend as if it is a normal ratchet state.
|
||||
@@ -208,6 +236,8 @@ pub trait ApplicationLayer<C: 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: &<C as CryptoLayer>::PublicKey,
|
||||
@@ -242,6 +272,81 @@ pub trait ApplicationLayer<C: CryptoLayer>: Sized {
|
||||
/// it should be assumed that they also have the same ratchet key.
|
||||
///
|
||||
/// The implementations of `PartialEq` for `RatchetState` and `RatchetStates` do this by default.
|
||||
///
|
||||
/// # Example
|
||||
/// The following code is an example of how to implement this database operation with the
|
||||
/// library `rustqlite`, an interface for SQLite in rust.
|
||||
/// This code will not work out-of-the-box, it must be adapted based on how your application
|
||||
/// structures its SQLite database.
|
||||
/// Notably, this code lacks a means of securely indexing ratchet fingerprints.
|
||||
/// The function `get_peer_primary_key` is a placeholder for however your application defines
|
||||
/// the SQL primary key for a peer. The function `get_sql_conn` is a placeholder getter method
|
||||
/// on `self` for retrieving the database connection object.
|
||||
/// ```rs
|
||||
/// fn to_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Error {
|
||||
/// Error::new(std::io::ErrorKind::Other, e)
|
||||
/// }
|
||||
///
|
||||
/// fn save_ratchet_state(
|
||||
/// &mut self,
|
||||
/// remote_static_key: &P384PublicKey,
|
||||
/// session_data: &Peer<T>,
|
||||
/// update_data: CompareAndSwap<'_>,
|
||||
/// ) -> Result<bool, Error> {
|
||||
/// let peer = get_peer_primary_key(remote_static_key, session_data);
|
||||
/// let mut conn = get_sql_conn(self);
|
||||
/// // rustqlite will rollback this transaction if this struct is dropped.
|
||||
/// let trans = conn
|
||||
/// .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
|
||||
/// .map_err(to_err)?;
|
||||
/// {
|
||||
/// // Compare
|
||||
/// let mut stmt = trans
|
||||
/// .prepare_cached("SELECT ratchet_fp1, ratchet_fp2 FROM peers WHERE peer = ?1")
|
||||
/// .map_err(to_err)?;
|
||||
/// let mut rows = stmt.query([peer]).map_err(to_err)?;
|
||||
/// if let Some(row) = rows.next().map_err(to_err)? {
|
||||
/// let peer_fp1 = row.get_ref(0).map_err(to_err)?.as_bytes_or_null().map_err(to_err)?;
|
||||
/// let peer_fp2 = row.get_ref(1).map_err(to_err)?.as_bytes_or_null().map_err(to_err)?;
|
||||
/// let peer_fp1 = if let Some(fp1) = peer_fp1 {
|
||||
/// fp1.try_into().map_err(to_err)?
|
||||
/// } else {
|
||||
/// &[0u8; RATCHET_SIZE]
|
||||
/// };
|
||||
/// let peer_fp2 = if let Some(fp2) = peer_fp2 {
|
||||
/// Some(fp2.try_into().map_err(to_err)?)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// if !update_data.compare_fingerprints(peer_fp1, peer_fp2) {
|
||||
/// return Ok(false);
|
||||
/// }
|
||||
/// } else {
|
||||
/// return Ok(false);
|
||||
/// }
|
||||
/// }
|
||||
/// {
|
||||
/// // Swap
|
||||
/// let ns1 = update_data.new_state1;
|
||||
/// let ns2 = update_data.new_state2;
|
||||
/// let mut stmt = trans.prepare_cached("UPDATE peers SET ratchet_fp1 = ?1, ratchet_key1 = ?2, chain_len1 = ?3, ratchet_fp2 = ?4, ratchet_key2 = ?5, chain_len2 = ?6 WHERE salted_addr = ?7").map_err(to_err)?;
|
||||
/// stmt.execute((
|
||||
/// ns1.fingerprint(),
|
||||
/// ns1.key(),
|
||||
/// ns1.chain_len(),
|
||||
/// ns2.map(RatchetState::fingerprint),
|
||||
/// ns2.map(RatchetState::key),
|
||||
/// ns2.map(RatchetState::chain_len),
|
||||
/// salted_addr,
|
||||
/// ))
|
||||
/// .map_err(to_err)?;
|
||||
/// }
|
||||
/// // SQLite does its best to make sure committed transactions are actually written
|
||||
/// // to disk: https://www.sqlite.org/howtocorrupt.html.
|
||||
/// trans.commit().map_err(to_err)?;
|
||||
/// Ok(true)
|
||||
/// }
|
||||
/// ```
|
||||
fn save_ratchet_state(
|
||||
&mut self,
|
||||
remote_static_key: &<C as CryptoLayer>::PublicKey,
|
||||
|
||||
@@ -42,11 +42,11 @@ impl RatchetState {
|
||||
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.
|
||||
/// Creates a new "zero" ratchet state, where the ratchet fingerprint is all zeros,
|
||||
/// the ratchet key is all zeros, and the chain length is 0.
|
||||
///
|
||||
/// This value is the default value of `RatchetState`.
|
||||
pub fn empty() -> Self {
|
||||
pub fn zero() -> Self {
|
||||
RatchetState {
|
||||
key: Zeroizing::new([0u8; RATCHET_SIZE]),
|
||||
fingerprint: Zeroizing::new([0u8; RATCHET_SIZE]),
|
||||
@@ -82,9 +82,7 @@ impl RatchetState {
|
||||
&self.key
|
||||
}
|
||||
/// 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.
|
||||
/// It could be all zeros if this is the "zero" ratchet state.
|
||||
///
|
||||
/// The ratchet fingerprint value is sensitive and should be hidden,
|
||||
/// but the security of ZSSP can survive having this value leaked.
|
||||
@@ -101,13 +99,13 @@ impl RatchetState {
|
||||
pub fn chain_len(&self) -> u64 {
|
||||
self.chain_len
|
||||
}
|
||||
/// 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 {
|
||||
/// Returns true if this is the "zero" ratchet state, where the ratchet fingerprint is all zeros,
|
||||
/// the ratchet key is all zeros, and the chain length is 0.
|
||||
pub fn is_zero(&self) -> bool {
|
||||
secure_eq(self.fingerprint(), &[0u8; RATCHET_SIZE])
|
||||
}
|
||||
/// Checks if the fingerprint of this ratchet state equals the
|
||||
/// fingerprint contained in argument `rf`.
|
||||
/// fingerprint contained in argument `rf`.
|
||||
///
|
||||
/// Uses constant time equality.
|
||||
pub fn fingerprint_eq(&self, rf: &[u8; RATCHET_SIZE]) -> bool {
|
||||
@@ -131,7 +129,7 @@ impl RatchetState {
|
||||
}
|
||||
impl Default for RatchetState {
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +157,7 @@ impl RatchetStates {
|
||||
///
|
||||
/// This value is the default value of `RatchetStates`.
|
||||
pub fn new_initial_states() -> Self {
|
||||
Self { state1: RatchetState::empty(), state2: None }
|
||||
Self { state1: RatchetState::zero(), 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`.
|
||||
@@ -271,7 +269,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
/// 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 self.cur_state1_was_just_deleted && !self.cur_state1.is_empty() {
|
||||
if self.cur_state1_was_just_deleted && !self.cur_state1.is_zero() {
|
||||
return Some(self.cur_state1.fingerprint());
|
||||
}
|
||||
None
|
||||
@@ -287,7 +285,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
pub fn deleted_fingerprint2(&self) -> Option<&[u8; RATCHET_SIZE]> {
|
||||
if self.cur_state2_was_just_deleted {
|
||||
if let Some(rf) = self.cur_state2 {
|
||||
if !rf.is_empty() {
|
||||
if !rf.is_zero() {
|
||||
return Some(rf.fingerprint());
|
||||
}
|
||||
}
|
||||
@@ -302,7 +300,7 @@ impl<'a> CompareAndSwap<'a> {
|
||||
/// then the peer should be added to storage with the new ratchet states specified by this
|
||||
/// `CompareAndSwap` struct.
|
||||
pub fn cur_is_initial_states(&self) -> bool {
|
||||
self.cur_state1.is_empty() && self.cur_state2.is_none()
|
||||
self.cur_state1.is_zero() && self.cur_state2.is_none()
|
||||
}
|
||||
/// Compares the ratchet fingerprints of `cur_state1` and `cur_state2` with `rf1` and `rf2`.
|
||||
/// If they are equal this function will return `true`.
|
||||
|
||||
@@ -734,7 +734,7 @@ pub(crate) fn received_x3_trans<C: CryptoLayer, App: ApplicationLayer<C>>(
|
||||
let mut should_warn_missing_ratchet = false;
|
||||
|
||||
if (zeta.ratchet_state != state1) & (Some(&zeta.ratchet_state) != state2.as_ref()) {
|
||||
if !responder_disallows_downgrade && zeta.ratchet_state.is_empty() {
|
||||
if !responder_disallows_downgrade && zeta.ratchet_state.is_zero() {
|
||||
should_warn_missing_ratchet = true;
|
||||
} else {
|
||||
if !responder_silently_rejects {
|
||||
|
||||
Binary file not shown.
+4
-4
@@ -98,7 +98,7 @@ To motivate the need for ASKs further, consider that many authenticated key exch
|
||||
|
||||
The maintainers of the Noise protocol are aware of the need for ASKs, and are in the process of extending the protocol with an ASK mechanism \cite{noise_ask}. However, at this point in time this mechanism is still unofficial and may be replaced. It also uses specific capabilities of \text{HKDF} that \text{KBKDF} does not have. For that reason we have created our own ASK extension to the Noise protocol, based on the unofficial mechanism.
|
||||
|
||||
We add a function, $\text{GetAsk}(\texttt{label})$, to the Noise protocol that takes as input a static label, and produces as output two 256-bit keys. This function is implemented as a component of the Noise handshake state machine, and so it has access to the most recent values of the Noise handshake hash, \texttt{h}, and the Noise chaining key, \texttt{ck}. To preserve consistency with `MixKey' and `Split', we will produce two 512-bit hashes, and then truncate each to 256-bits to produce two keys.
|
||||
We add a function, $\text{GetAsk}(\texttt{label})$, to the Noise protocol that takes as input a static string, and produces as output two 256-bit keys. This function is implemented as a component of the Noise handshake state machine, and so it has access to the most recent values of the Noise handshake hash, \texttt{h}, and the Noise chaining key, \texttt{ck}. To preserve consistency with `MixKey' and `Split', we will produce two 512-bit hashes, and then truncate each to 256-bits to produce two keys.
|
||||
|
||||
$$\text{GetAsk}(\texttt{label}) := \text{KBKDF}(\texttt{h}, \texttt{label}, \texttt{ck}, 1024).$$
|
||||
|
||||
@@ -519,15 +519,15 @@ The following are the 4 cryptographic primitives ZKE is based upon. We will be a
|
||||
\end{definition}
|
||||
|
||||
\begin{definition}[H][KBKDF \cite{fips_kbkdf}]
|
||||
Let $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ be the KBKDF key derivation algorithm, instantiated in HMAC-Counter mode using SHA-512. Variable $\textit{K}_{\textit{IN}}$ is the input key material, $\textit{Label}$ is some static label, $\textit{Context}$ is additional input key material, and $N$ is the number of 512-bit outputs to be produced. $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ produces as output $(x_1,\ldots,x_N)$, a tuple of $N$ 512-bit outputs.
|
||||
Let $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ be the KBKDF key derivation algorithm, instantiated in HMAC-Counter mode using SHA-512. Variable $\textit{K}_{\textit{IN}}$ is the input key material, $\textit{Label}$ is some static string, $\textit{Context}$ is additional input key material, and $N$ is the number of 512-bit outputs to be produced. $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ produces as output $(x_1,\ldots,x_N)$, a tuple of $N$ 512-bit outputs.
|
||||
|
||||
Given $i\in \{1,\ldots,N\}$, we will use the notation $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)_i$ to refer to the $i$th output of $\KDF$. When assigning a single 512-bit output to a variable or field that is 256-bits in size, it is assumed that the output is being truncated to just the first 256-bits.
|
||||
Given $i\in \{1,\ldots,N\}$, we will use the notation $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)_i$ to refer to the $i$th output of $\KDF$. When assigning a 512-bit output to a variable or field that is 256-bits in size, it is assumed that the output is being truncated to just the first 256-bits.
|
||||
|
||||
So $k\gets \KDF(x, l, y, 3)_2$ represents computing KBKDF on inputs $\textit{K}_{\textit{IN}}= x,\, \textit{Label}=l,\, \textit{Context}=y$ and $L=3\cdot 512$, and setting variable $k$ equal to bits $512$ to $1023$ of the output. If $k$ is a variable of size 256-bits, then $k$ is set equal to bits $512$ to $767$ of the output.
|
||||
\end{definition}
|
||||
|
||||
\begin{definition}[H][AES-GCM \cite{fips_aesgcm}]
|
||||
Let $\AEAD(K, N, H, M)$ be the AES-GCM Authenticated Encryption with Additional Data algorithm, where $K$ is the encryption key, $N$ is the nonce or IV, $H$ is the additional authentication data, and $M$ is the plaintext message to be encrypted. $\AEAD(K, N, H, M)$ produces as output $c||t$, a ciphertext, $c$, concatenated with its 128-bit (16 byte) authentication tag, $t$.
|
||||
Let $\AEAD(K, N, H, M)$ be the AES-GCM Authenticated Encryption with Additional Data algorithm, where $K$ is the encryption key, $N$ is the AEAD nonce, $H$ is the additional authentication data, and $M$ is the plaintext message to be encrypted. $\AEAD(K, N, H, M)$ produces as output $c||t$, a ciphertext, $c$, concatenated with its 128-bit (16 byte) authentication tag, $t$.
|
||||
\end{definition}
|
||||
|
||||
\begin{definition}[H][P384 \cite{fips_p384}]
|
||||
|
||||
Reference in New Issue
Block a user