Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

document a few constants #473

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions rust/protocol/src/consts.rs

This file was deleted.

20 changes: 10 additions & 10 deletions rust/protocol/src/group_cipher.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
//
// Copyright 2020-2021 Signal Messenger, LLC.
// Copyright 2020-2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

use crate::{consts, crypto};

use crate::limits::MAX_FORWARD_JUMPS;
use crate::protocol::SENDERKEY_MESSAGE_CURRENT_VERSION;
use crate::sender_keys::{SenderKeyState, SenderMessageKey};
use crate::{
CiphertextMessageType, Context, KeyPair, ProtocolAddress, Result, SenderKeyDistributionMessage,
SenderKeyMessage, SenderKeyRecord, SenderKeyStore, SignalProtocolError,
crypto, CiphertextMessageType, Context, KeyPair, ProtocolAddress, Result,
SenderKeyDistributionMessage, SenderKeyMessage, SenderKeyRecord, SenderKeyStore,
SignalProtocolError,
};

use crate::protocol::SENDERKEY_MESSAGE_CURRENT_VERSION;
use crate::sender_keys::{SenderKeyState, SenderMessageKey};
use std::convert::TryFrom;

use rand::{CryptoRng, Rng};
use std::convert::TryFrom;
use uuid::Uuid;

pub async fn group_encrypt<R: Rng + CryptoRng>(
Expand Down Expand Up @@ -100,11 +100,11 @@ fn get_sender_key(
}

let jump = (iteration - current_iteration) as usize;
if jump > consts::MAX_FORWARD_JUMPS {
if jump > MAX_FORWARD_JUMPS {
log::error!(
"SenderKey distribution {} Exceeded future message limit: {}, current iteration: {})",
distribution_id,
consts::MAX_FORWARD_JUMPS,
MAX_FORWARD_JUMPS,
current_iteration
);
return Err(SignalProtocolError::InvalidMessage(
Expand Down
4 changes: 2 additions & 2 deletions rust/protocol/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020-2021 Signal Messenger, LLC.
// Copyright 2020-2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

Expand All @@ -23,13 +23,13 @@
// #![warn(missing_docs)]

mod address;
mod consts;
mod crypto;
mod curve;
pub mod error;
mod fingerprint;
mod group_cipher;
mod identity_key;
mod limits;
mod proto;
mod protocol;
mod ratchet;
Expand Down
76 changes: 76 additions & 0 deletions rust/protocol/src/limits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Copyright 2020-2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

#![warn(missing_docs)]

//! Various positive integers bounding the maximum size of other data structures.

/// The maximum number of dropped messages to allow in a row before reporting an error.
///
/// A Signal client session allows for out-of-order and dropped messages by providing a "chain
/// index" along with each message (tracked in
/// [`ChainKey::index`](crate::proto::storage::session_structure::chain::ChainKey::index)). Each
/// time the session iterates its ratcheting KDF, it increments a local integer counter, and
/// associates the resulting key material with the value of that counter in that session's key
/// store. This technique is used in the [Double Ratchet] protocol from
/// [`message_decrypt_prekey()`](crate::session_cipher::message_decrypt_prekey), as well as the
/// single ratchet sender key protocol in [`group_decrypt()`](crate::group_cipher::group_decrypt).
///
/// When the session receives a new message, it looks up the chain index provided with that message
/// to obtain the appropriate key material. If the chain index is greater than the value of the
/// local counter, then the session must iterate its ratcheting KDF until it produces the key
/// material for the specified chain index. The session stores all of the intermediate keys
/// generated by this process in between its previous counter and the new chain index, so that it
/// can decrypt any out-of-order messages that come later.
///
/// As a result, if a message is modified to provide an extremely large chain index, it could force
/// a session to fill up its key store with a huge number of entries, which could induce
/// a denial-of-service. Instead, if the session needs to iterate its ratcheting KDF more than this
/// limit, it bails out of attempting to decrypt the message, and instead returns an error.
///
/// [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/
pub const MAX_FORWARD_JUMPS: usize = 25_000;

/// The maximum number of per-message keys retained by a session to decrypt previous messages, in
/// case messages arrive out of order or missing.
///
/// As described in [`MAX_FORWARD_JUMPS`], the session retains a local database of key material
/// obtained by iterating a ratcheting KDF. Each time the ratcheting KDF is iterated, the session
/// increments a counter and adds an entry to its key store associated with that counter. When the
/// number of stored entries exceeds this limit, the session begins to evict the oldest entry from
/// its key store each time the ratcheting KDF is iterated to produce a new entry.
///
/// This limit therefore bounds the amount of memory consumed by any session's key store so that an
/// attacker can't induce a denial-of-service by modifying a message's chain index.
pub const MAX_MESSAGE_KEYS: usize = 2000;

/// The maximum number of backup chains to retain for out-of-order messages from a double
/// ratchet session.
///
/// In the [Double Ratchet] protocol, individual message keys are generated not just from the output
/// of a a ratcheting KDF, but also from a [DH] secret shared between both clients via the
/// [`SignalMessage::sender_ratchet_key()`](crate::protocol::SignalMessage::sender_ratchet_key)
/// field of each successive message. This means that unlike the sender key single ratchet method,
/// each chain of double ratchet message keys has an additional chunk of ratcheting state that gets
/// sent back and forth between participants, which can't be idempotently reproduced by just
/// iterating a single KDF.
///
/// In order to support out-of-order messages (and therefore out-of-order DH secrets), the double
/// ratchet session maintains multiple chains of contiguous messages. Similarly to
/// [`MAX_MESSAGE_KEYS`], this limit bounds the number of such chains to retain, and therefore
/// bounds the memory consumed by a session's key store so an attacker can't induce
/// a denial-of-service by modifying a message's DH field.
///
/// [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/
/// [DH]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
pub const MAX_RECEIVER_CHAINS: usize = 5;

/// The maximum number of sessions allowed for
/// [crate::proto::storage::RecordStructure::previous_sessions].
pub const ARCHIVED_STATES_MAX_LENGTH: usize = 40;

/// The maximum number of sender key states allowed for
/// [crate::proto::storage::SenderKeyRecordStructure::sender_key_states].
pub const MAX_SENDER_KEY_STATES: usize = 5;
14 changes: 7 additions & 7 deletions rust/protocol/src/sender_keys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020-2021 Signal Messenger, LLC.
// Copyright 2020-2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

Expand All @@ -10,8 +10,9 @@ use itertools::Itertools;
use prost::Message;

use crate::crypto::hmac_sha256;
use crate::limits::{MAX_MESSAGE_KEYS, MAX_SENDER_KEY_STATES};
use crate::proto::storage as storage_proto;
use crate::{consts, PrivateKey, PublicKey, SignalProtocolError};
use crate::{PrivateKey, PublicKey, SignalProtocolError};

/// A distinct error type to keep from accidentally propagating deserialization errors.
#[derive(Debug)]
Expand Down Expand Up @@ -210,7 +211,7 @@ impl SenderKeyState {
self.state
.sender_message_keys
.push(sender_message_key.as_protobuf());
while self.state.sender_message_keys.len() > consts::MAX_MESSAGE_KEYS {
while self.state.sender_message_keys.len() > MAX_MESSAGE_KEYS {
self.state.sender_message_keys.remove(0);
}
}
Expand Down Expand Up @@ -238,7 +239,7 @@ pub struct SenderKeyRecord {
impl SenderKeyRecord {
pub(crate) fn new_empty() -> Self {
Self {
states: VecDeque::with_capacity(consts::MAX_SENDER_KEY_STATES),
states: VecDeque::with_capacity(MAX_SENDER_KEY_STATES),
}
}

Expand Down Expand Up @@ -315,7 +316,7 @@ impl SenderKeyRecord {
Some(state) => state,
};

while self.states.len() >= consts::MAX_SENDER_KEY_STATES {
while self.states.len() >= MAX_SENDER_KEY_STATES {
self.states.pop_back();
}

Expand Down Expand Up @@ -490,8 +491,7 @@ mod sender_key_record_add_sender_key_state_tests {
#[test]
fn when_exceed_maximum_states_then_oldest_is_ejected() {
assert_eq!(
5,
consts::MAX_SENDER_KEY_STATES,
5, MAX_SENDER_KEY_STATES,
"Test written to expect this limit"
);

Expand Down
2 changes: 1 addition & 1 deletion rust/protocol/src/session_cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
SessionStore, SignalMessage, SignalProtocolError, SignedPreKeyStore,
};

use crate::consts::MAX_FORWARD_JUMPS;
use crate::limits::MAX_FORWARD_JUMPS;
use crate::ratchet::{ChainKey, MessageKeys};
use crate::state::{InvalidSessionError, SessionState};
use crate::{crypto, session};
Expand Down
13 changes: 7 additions & 6 deletions rust/protocol/src/state/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ use prost::Message;
use subtle::ConstantTimeEq;

use crate::ratchet::{ChainKey, MessageKeys, RootKey};
use crate::{IdentityKey, KeyPair, PrivateKey, PublicKey, SignalProtocolError};
use crate::{
IdentityKey, KeyPair, PreKeyId, PrivateKey, PublicKey, SignalProtocolError, SignedPreKeyId,
};

use crate::consts;
use crate::limits::{ARCHIVED_STATES_MAX_LENGTH, MAX_MESSAGE_KEYS, MAX_RECEIVER_CHAINS};
use crate::proto::storage::{session_structure, RecordStructure, SessionStructure};
use crate::state::{PreKeyId, SignedPreKeyId};

/// A distinct error type to keep from accidentally propagating deserialization errors.
#[derive(Debug)]
Expand Down Expand Up @@ -235,7 +236,7 @@ impl SessionState {

self.session.receiver_chains.push(chain);

if self.session.receiver_chains.len() > consts::MAX_RECEIVER_CHAINS {
if self.session.receiver_chains.len() > MAX_RECEIVER_CHAINS {
log::info!(
"Trimming excessive receiver_chain for session with base key {}, chain count: {}",
self.sender_ratchet_key_for_logging()
Expand Down Expand Up @@ -366,7 +367,7 @@ impl SessionState {
let mut updated_chain = chain_and_index.0;
updated_chain.message_keys.insert(0, new_keys);

if updated_chain.message_keys.len() > consts::MAX_MESSAGE_KEYS {
if updated_chain.message_keys.len() > MAX_MESSAGE_KEYS {
updated_chain.message_keys.pop();
}

Expand Down Expand Up @@ -578,7 +579,7 @@ impl SessionRecord {
// A non-fallible version of archive_current_state.
fn archive_current_state_inner(&mut self) {
if let Some(current_session) = self.current_session.take() {
if self.previous_sessions.len() >= consts::ARCHIVED_STATES_MAX_LENGTH {
if self.previous_sessions.len() >= ARCHIVED_STATES_MAX_LENGTH {
self.previous_sessions.pop();
}
self.previous_sessions
Expand Down