implemented noise_KKpsk0

This commit is contained in:
mamoniot 2023-03-13 11:21:09 -04:00
parent f66a2a7ef9
commit 16c35a5d07
No known key found for this signature in database
GPG key ID: ADCCDBBE0E3D3B3B
2 changed files with 127 additions and 91 deletions

View file

@ -62,7 +62,7 @@ fn alice_main(
},
TEST_MTU,
bob_app.identity_key.public_key_bytes(),
bob_app.identity_key.public_key(),
bob_app.identity_key.public_key().clone(),
Secret::default(),
None,
(),

View file

@ -84,6 +84,8 @@ pub struct Session<Application: ApplicationLayer> {
/// An arbitrary application defined object associated with each session
pub application_data: Application::Data,
pub static_public_key: P384PublicKey,
send_counter: AtomicU64,
receive_window: [AtomicU64; COUNTER_WINDOW_MAX_OOO],
header_protection_cipher: Aes,
@ -287,7 +289,7 @@ impl<Application: ApplicationLayer> Context<Application> {
mut send: SendFunction,
mtu: usize,
remote_s_public_blob: &[u8],
remote_s_public_p384: &P384PublicKey,
remote_s_public_p384: P384PublicKey,
psk: Secret<BASE_KEY_SIZE>,
metadata: Option<Vec<u8>>,
application_data: Application::Data,
@ -317,6 +319,7 @@ impl<Application: ApplicationLayer> Context<Application> {
let session = Arc::new(Session {
id: local_session_id,
application_data,
static_public_key: remote_s_public_p384,
send_counter: AtomicU64::new(3), // 1 and 2 are reserved for init and final ack
receive_window: std::array::from_fn(|_| AtomicU64::new(0)),
header_protection_cipher: Aes::new(&header_protection_key),
@ -1080,6 +1083,7 @@ impl<Application: ApplicationLayer> Context<Application> {
let session = Arc::new(Session {
id: incoming.bob_session_id,
application_data,
static_public_key: alice_noise_s,
send_counter: AtomicU64::new(2), // 1 was already used during negotiation
receive_window: std::array::from_fn(|_| AtomicU64::new(incoming_counter)),
header_protection_cipher: Aes::new(&incoming.header_protection_key),
@ -1129,70 +1133,86 @@ impl<Application: ApplicationLayer> Context<Application> {
if let Some(session) = session {
let state = session.state.read().unwrap();
if let (Some(remote_session_id), Some(key)) = (state.remote_session_id, state.keys[key_index].as_ref()) {
if !key.my_turn_to_rekey && {
if !key.my_turn_to_rekey {
let mut c = key.get_receive_cipher(incoming_counter);
c.reset_init_gcm(&incoming_message_nonce);
c.crypt_in_place(&mut pkt_assembled[RekeyInit::ENC_START..RekeyInit::AUTH_START]);
c.finish_decrypt(&pkt_assembled[RekeyInit::AUTH_START..])
} {
let pkt: &RekeyInit = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
if let Some(alice_e) = P384PublicKey::from_bytes(&pkt.alice_e) {
let bob_e_secret = P384KeyPair::generate();
let next_session_key = hmac_sha512_secret(
key.ratchet_key.as_bytes(),
bob_e_secret.agree(&alice_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
);
if c.finish_decrypt(&pkt_assembled[RekeyInit::AUTH_START..]) {
drop(c);
// Packet fully authenticated
if session.update_receive_window(incoming_counter) {
let mut reply_buf = [0u8; RekeyAck::SIZE];
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
reply.bob_e = *bob_e_secret.public_key_bytes();
reply.next_key_fingerprint = SHA384::hash(next_session_key.as_bytes());
let pkt: &RekeyInit = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
if let Some(alice_e) = P384PublicKey::from_bytes(&pkt.alice_e) {
let bob_e_secret = P384KeyPair::generate();
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
set_packet_header(
&mut reply_buf,
1,
0,
PACKET_TYPE_REKEY_ACK,
u64::from(remote_session_id),
state.current_key,
counter,
// Complete Noise_KKpsk0 on Bob's side.
// We do not mix noise_ss for two reasons:
// 1) A PACKET_TYPE_REKEY_INIT packet does not have any payload that would require noise_ss authenticated encryption.
// 2) noise_ss is a static value that will not change between rekeying events, thus not meaningfully contributing any ratchet entropy.
let noise_es = app.get_local_s_keypair().agree(&alice_e).ok_or(Error::FailedAuthentication)?;
let noise_ee = bob_e_secret.agree(&alice_e).ok_or(Error::FailedAuthentication)?;
let noise_se = bob_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
let noise_psk_se_ee_es = hmac_sha512_secret::<BASE_KEY_SIZE>(
hmac_sha512_secret::<BASE_KEY_SIZE>(
hmac_sha512_secret::<BASE_KEY_SIZE>(
key.ratchet_key.as_bytes(),
noise_es.as_bytes(),
).as_bytes(),
noise_ee.as_bytes(),
).as_bytes(),
noise_se.as_bytes(),
);
let mut c = key.get_send_cipher(counter)?;
c.reset_init_gcm(&create_message_nonce(PACKET_TYPE_REKEY_ACK, counter));
c.crypt_in_place(&mut reply_buf[RekeyAck::ENC_START..RekeyAck::AUTH_START]);
reply_buf[RekeyAck::AUTH_START..].copy_from_slice(&c.finish_encrypt());
drop(c);
// Packet fully authenticated
if session.update_receive_window(incoming_counter) {
let mut reply_buf = [0u8; RekeyAck::SIZE];
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
reply.bob_e = *bob_e_secret.public_key_bytes();
reply.next_key_fingerprint = SHA384::hash(noise_psk_se_ee_es.as_bytes());
session
.header_protection_cipher
.encrypt_block_in_place(&mut reply_buf[HEADER_PROTECT_ENCRYPT_START..HEADER_PROTECT_ENCRYPT_END]);
send(Some(&session), &mut reply_buf);
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
set_packet_header(
&mut reply_buf,
1,
0,
PACKET_TYPE_REKEY_ACK,
u64::from(remote_session_id),
state.current_key,
counter,
);
// 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);
let mut state = session.state.write().unwrap();
let _ = state.keys[key_index ^ 1].replace(SessionKey::new::<Application>(
next_session_key,
next_ratchet_count,
current_time,
counter,
false,
false,
));
let mut c = key.get_send_cipher(counter)?;
c.reset_init_gcm(&create_message_nonce(PACKET_TYPE_REKEY_ACK, counter));
c.crypt_in_place(&mut reply_buf[RekeyAck::ENC_START..RekeyAck::AUTH_START]);
reply_buf[RekeyAck::AUTH_START..].copy_from_slice(&c.finish_encrypt());
drop(c);
drop(state);
return Ok(ReceiveResult::Ok(Some(session)));
} else {
return Err(Error::OutOfSequence);
session
.header_protection_cipher
.encrypt_block_in_place(&mut reply_buf[HEADER_PROTECT_ENCRYPT_START..HEADER_PROTECT_ENCRYPT_END]);
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);
let mut state = session.state.write().unwrap();
let _ = state.keys[key_index ^ 1].replace(SessionKey::new::<Application>(
noise_psk_se_ee_es,
next_ratchet_count,
current_time,
counter,
false,
false,
));
drop(state);
return Ok(ReceiveResult::Ok(Some(session)));
} else {
return Err(Error::OutOfSequence);
}
}
}
return Err(Error::FailedAuthentication);
@ -1215,51 +1235,67 @@ impl<Application: ApplicationLayer> Context<Application> {
if let Some(session) = session {
let state = session.state.read().unwrap();
if let (Offer::RekeyInit(alice_e_secret, _), Some(key)) = (&state.outgoing_offer, state.keys[key_index].as_ref()) {
if key.my_turn_to_rekey && {
if key.my_turn_to_rekey {
let mut c = key.get_receive_cipher(incoming_counter);
c.reset_init_gcm(&incoming_message_nonce);
c.crypt_in_place(&mut pkt_assembled[RekeyAck::ENC_START..RekeyAck::AUTH_START]);
c.finish_decrypt(&pkt_assembled[RekeyAck::AUTH_START..])
} {
let pkt: &RekeyAck = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
if let Some(bob_e) = P384PublicKey::from_bytes(&pkt.bob_e) {
let next_session_key = hmac_sha512_secret(
key.ratchet_key.as_bytes(),
alice_e_secret.agree(&bob_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
);
if c.finish_decrypt(&pkt_assembled[RekeyAck::AUTH_START..]) {
drop(c);
if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(next_session_key.as_bytes())) {
if session.update_receive_window(incoming_counter) {
// 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);
let next_key_index = key_index ^ 1;
let mut state = session.state.write().unwrap();
let _ = state.keys[next_key_index].replace(SessionKey::new::<Application>(
next_session_key,
next_ratchet_count,
current_time,
session.send_counter.load(Ordering::Relaxed),
true,
true,
));
state.current_key = next_key_index; // this is an ACK so it's confirmed
state.outgoing_offer = Offer::None;
let pkt: &RekeyAck = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
if let Some(bob_e) = P384PublicKey::from_bytes(&pkt.bob_e) {
drop(state);
return Ok(ReceiveResult::Ok(Some(session)));
} else {
return Err(Error::OutOfSequence);
// Complete Noise_KKpsk0 on Alice's side.
// We do not mix noise_ss for two reasons:
// 1) PACKET_TYPE_REKEY_INIT does not have a payload that would require noise_ss authentication.
// 2) noise_ss is a static value that will not change between rekeying events, thus not meaningfully contributing any ratchet entropy.
let noise_es = alice_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
let noise_ee = alice_e_secret.agree(&bob_e).ok_or(Error::FailedAuthentication)?;
let noise_se = app.get_local_s_keypair().agree(&bob_e).ok_or(Error::FailedAuthentication)?;
let noise_psk_se_ee_es = hmac_sha512_secret::<BASE_KEY_SIZE>(
hmac_sha512_secret::<BASE_KEY_SIZE>(
hmac_sha512_secret::<BASE_KEY_SIZE>(
key.ratchet_key.as_bytes(),
noise_es.as_bytes(),
).as_bytes(),
noise_ee.as_bytes(),
).as_bytes(),
noise_se.as_bytes(),
);
// We need to check that the key Bob is acknowledging matches the latest sent offer.
// Because of OOO, it might not, in which case this rekey must be cancelled and retried.
if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(noise_psk_se_ee_es.as_bytes())) {
if session.update_receive_window(incoming_counter) {
// 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);
let next_key_index = key_index ^ 1;
let mut state = session.state.write().unwrap();
let _ = state.keys[next_key_index].replace(SessionKey::new::<Application>(
noise_psk_se_ee_es,
next_ratchet_count,
current_time,
session.send_counter.load(Ordering::Relaxed),
true,
true,
));
state.current_key = next_key_index; // this is an ACK so it's confirmed
state.outgoing_offer = Offer::None;
drop(state);
return Ok(ReceiveResult::Ok(Some(session)));
}
}
return Err(Error::OutOfSequence);
}
}
return Err(Error::FailedAuthentication);
}
return Err(Error::FailedAuthentication);
} else {
return Err(Error::OutOfSequence);
}
return Err(Error::OutOfSequence);
} else {
return Err(Error::UnknownLocalSessionId);
}