Add key_info() to get key information.

This commit is contained in:
Adam Ierymenko 2023-03-01 09:22:10 -05:00
parent 8b6088f335
commit 652f7360f0
2 changed files with 47 additions and 5 deletions

View file

@ -14,6 +14,13 @@ struct TestApplication {
} }
impl zssp::ApplicationLayer for TestApplication { impl zssp::ApplicationLayer for TestApplication {
const REKEY_AFTER_USES: u64 = 131072;
const EXPIRE_AFTER_USES: u64 = 2147483648;
const REKEY_AFTER_TIME_MS: i64 = 1000 * 60 * 60 * 2;
const REKEY_AFTER_TIME_MS_MAX_JITTER: u32 = 1000 * 60 * 10;
const INCOMING_SESSION_NEGOTIATION_TIMEOUT_MS: i64 = 2000;
const RETRY_INTERVAL: i64 = 500;
type Data = (); type Data = ();
type IncomingPacketBuffer = Vec<u8>; type IncomingPacketBuffer = Vec<u8>;

View file

@ -133,6 +133,7 @@ struct SessionKey {
created_at_counter: u64, // Counter at which session was created created_at_counter: u64, // Counter at which session was created
rekey_at_counter: u64, // Rekey at or after this counter rekey_at_counter: u64, // Rekey at or after this counter
expire_at_counter: u64, // Hard error when this counter value is reached or exceeded expire_at_counter: u64, // Hard error when this counter value is reached or exceeded
ratchet_count: u64, // Number of rekey events
bob: bool, // Was this side "Bob" in this exchange? bob: bool, // Was this side "Bob" in this exchange?
} }
@ -926,8 +927,13 @@ impl<Application: ApplicationLayer> Context<Application> {
{ {
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
let _ = state.remote_session_id.insert(bob_session_id); let _ = state.remote_session_id.insert(bob_session_id);
let _ = let _ = state.keys[0].insert(SessionKey::new::<Application>(
state.keys[0].insert(SessionKey::new::<Application>(noise_es_ee_se_hk_psk, current_time, 2, false)); noise_es_ee_se_hk_psk,
1,
current_time,
2,
false,
));
state.current_key = 0; state.current_key = 0;
state.current_offer = Offer::None; state.current_offer = Offer::None;
} }
@ -1078,7 +1084,7 @@ impl<Application: ApplicationLayer> Context<Application> {
state: RwLock::new(State { state: RwLock::new(State {
remote_session_id: Some(incoming.alice_session_id), remote_session_id: Some(incoming.alice_session_id),
keys: [ keys: [
Some(SessionKey::new::<Application>(noise_es_ee_se_hk_psk, current_time, 2, true)), Some(SessionKey::new::<Application>(noise_es_ee_se_hk_psk, 1, current_time, 2, true)),
None, None,
], ],
current_key: 0, current_key: 0,
@ -1111,7 +1117,8 @@ impl<Application: ApplicationLayer> Context<Application> {
if let Some(session) = session { if let Some(session) = session {
let state = session.state.read().unwrap(); let state = session.state.read().unwrap();
if let Some(key) = state.keys[key_index].as_ref() { if let Some(key) = state.keys[key_index].as_ref() {
// Only the current "Alice" accepts rekeys initiated by the current "Bob." // Only the current "Alice" accepts rekeys initiated by the current "Bob." These roles
// flip with each rekey event.
if !key.bob { if !key.bob {
let mut c = key.get_receive_cipher(); let mut c = key.get_receive_cipher();
c.reset_init_gcm(&incoming_message_nonce); c.reset_init_gcm(&incoming_message_nonce);
@ -1142,10 +1149,16 @@ impl<Application: ApplicationLayer> Context<Application> {
send(Some(&session), &mut reply_buf); send(Some(&session), &mut reply_buf);
// The new "Bob" doesn't know yet if Alice has received the new key, so the
// new key is recorded as the "alt" (key_index ^ 1) but the current key is
// not advanced yet. This happens automatically the first time we receive a
// valid packet with the new key.
let next_ratchet_count = key.ratchet_count + 1;
drop(state); drop(state);
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
let _ = state.keys[key_index ^ 1].replace(SessionKey::new::<Application>( let _ = state.keys[key_index ^ 1].replace(SessionKey::new::<Application>(
next_session_key, next_session_key,
next_ratchet_count,
current_time, current_time,
counter.get(), counter.get(),
false, false,
@ -1192,11 +1205,16 @@ impl<Application: ApplicationLayer> Context<Application> {
)); ));
if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(next_session_key.as_bytes())) { if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(next_session_key.as_bytes())) {
// The new "Alice" knows Bob has the key since this is an ACK, so she can go
// ahead and set current_key to the new key. Then when she sends something
// to Bob the other side will automatically advance to the new key as well.
let next_ratchet_count = key.ratchet_count + 1;
drop(state); drop(state);
let next_key_index = key_index ^ 1; let next_key_index = key_index ^ 1;
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
let _ = state.keys[next_key_index].replace(SessionKey::new::<Application>( let _ = state.keys[next_key_index].replace(SessionKey::new::<Application>(
next_session_key, next_session_key,
next_ratchet_count,
current_time, current_time,
session.send_counter.load(Ordering::Acquire), session.send_counter.load(Ordering::Acquire),
true, true,
@ -1297,6 +1315,16 @@ impl<Application: ApplicationLayer> Session<Application> {
state.keys[state.current_key].is_some() state.keys[state.current_key].is_some()
} }
/// Get the ratchet count and a hash fingerprint of the current active key.
pub fn key_info(&self) -> Option<(u64, [u8; 48])> {
let state = self.state.read().unwrap();
if let Some(key) = state.keys[state.current_key].as_ref() {
Some((key.ratchet_count, SHA384::hash(key.ratchet_key.as_bytes())))
} else {
None
}
}
/// Send a rekey init message. /// Send a rekey init message.
/// ///
/// This is called from the session context's service() method when it's time to rekey. /// This is called from the session context's service() method when it's time to rekey.
@ -1473,7 +1501,13 @@ fn assemble_fragments_into<A: ApplicationLayer>(fragments: &[A::IncomingPacketBu
} }
impl SessionKey { impl SessionKey {
fn new<Application: ApplicationLayer>(key: Secret<BASE_KEY_SIZE>, current_time: i64, current_counter: u64, role_is_bob: bool) -> Self { fn new<Application: ApplicationLayer>(
key: Secret<BASE_KEY_SIZE>,
ratchet_count: u64,
current_time: i64,
current_counter: u64,
role_is_bob: bool,
) -> Self {
let a2b = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(key.as_bytes()); let a2b = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(key.as_bytes());
let b2a = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(key.as_bytes()); let b2a = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(key.as_bytes());
let (receive_key, send_key) = if role_is_bob { let (receive_key, send_key) = if role_is_bob {
@ -1496,6 +1530,7 @@ impl SessionKey {
created_at_counter: current_counter, created_at_counter: current_counter,
rekey_at_counter: current_counter.checked_add(Application::REKEY_AFTER_USES).unwrap(), rekey_at_counter: current_counter.checked_add(Application::REKEY_AFTER_USES).unwrap(),
expire_at_counter: current_counter.checked_add(Application::EXPIRE_AFTER_USES).unwrap(), expire_at_counter: current_counter.checked_add(Application::EXPIRE_AFTER_USES).unwrap(),
ratchet_count,
bob: role_is_bob, bob: role_is_bob,
} }
} }