mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 12:33:44 +02:00
implemented noise_KKpsk0
This commit is contained in:
parent
f66a2a7ef9
commit
16c35a5d07
2 changed files with 127 additions and 91 deletions
|
@ -62,7 +62,7 @@ fn alice_main(
|
||||||
},
|
},
|
||||||
TEST_MTU,
|
TEST_MTU,
|
||||||
bob_app.identity_key.public_key_bytes(),
|
bob_app.identity_key.public_key_bytes(),
|
||||||
bob_app.identity_key.public_key(),
|
bob_app.identity_key.public_key().clone(),
|
||||||
Secret::default(),
|
Secret::default(),
|
||||||
None,
|
None,
|
||||||
(),
|
(),
|
||||||
|
|
|
@ -84,6 +84,8 @@ pub struct Session<Application: ApplicationLayer> {
|
||||||
/// An arbitrary application defined object associated with each session
|
/// An arbitrary application defined object associated with each session
|
||||||
pub application_data: Application::Data,
|
pub application_data: Application::Data,
|
||||||
|
|
||||||
|
pub static_public_key: P384PublicKey,
|
||||||
|
|
||||||
send_counter: AtomicU64,
|
send_counter: AtomicU64,
|
||||||
receive_window: [AtomicU64; COUNTER_WINDOW_MAX_OOO],
|
receive_window: [AtomicU64; COUNTER_WINDOW_MAX_OOO],
|
||||||
header_protection_cipher: Aes,
|
header_protection_cipher: Aes,
|
||||||
|
@ -287,7 +289,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
mut send: SendFunction,
|
mut send: SendFunction,
|
||||||
mtu: usize,
|
mtu: usize,
|
||||||
remote_s_public_blob: &[u8],
|
remote_s_public_blob: &[u8],
|
||||||
remote_s_public_p384: &P384PublicKey,
|
remote_s_public_p384: P384PublicKey,
|
||||||
psk: Secret<BASE_KEY_SIZE>,
|
psk: Secret<BASE_KEY_SIZE>,
|
||||||
metadata: Option<Vec<u8>>,
|
metadata: Option<Vec<u8>>,
|
||||||
application_data: Application::Data,
|
application_data: Application::Data,
|
||||||
|
@ -317,6 +319,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let session = Arc::new(Session {
|
let session = Arc::new(Session {
|
||||||
id: local_session_id,
|
id: local_session_id,
|
||||||
application_data,
|
application_data,
|
||||||
|
static_public_key: remote_s_public_p384,
|
||||||
send_counter: AtomicU64::new(3), // 1 and 2 are reserved for init and final ack
|
send_counter: AtomicU64::new(3), // 1 and 2 are reserved for init and final ack
|
||||||
receive_window: std::array::from_fn(|_| AtomicU64::new(0)),
|
receive_window: std::array::from_fn(|_| AtomicU64::new(0)),
|
||||||
header_protection_cipher: Aes::new(&header_protection_key),
|
header_protection_cipher: Aes::new(&header_protection_key),
|
||||||
|
@ -1080,6 +1083,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let session = Arc::new(Session {
|
let session = Arc::new(Session {
|
||||||
id: incoming.bob_session_id,
|
id: incoming.bob_session_id,
|
||||||
application_data,
|
application_data,
|
||||||
|
static_public_key: alice_noise_s,
|
||||||
send_counter: AtomicU64::new(2), // 1 was already used during negotiation
|
send_counter: AtomicU64::new(2), // 1 was already used during negotiation
|
||||||
receive_window: std::array::from_fn(|_| AtomicU64::new(incoming_counter)),
|
receive_window: std::array::from_fn(|_| AtomicU64::new(incoming_counter)),
|
||||||
header_protection_cipher: Aes::new(&incoming.header_protection_key),
|
header_protection_cipher: Aes::new(&incoming.header_protection_key),
|
||||||
|
@ -1129,18 +1133,33 @@ 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(remote_session_id), Some(key)) = (state.remote_session_id, state.keys[key_index].as_ref()) {
|
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);
|
let mut c = key.get_receive_cipher(incoming_counter);
|
||||||
c.reset_init_gcm(&incoming_message_nonce);
|
c.reset_init_gcm(&incoming_message_nonce);
|
||||||
c.crypt_in_place(&mut pkt_assembled[RekeyInit::ENC_START..RekeyInit::AUTH_START]);
|
c.crypt_in_place(&mut pkt_assembled[RekeyInit::ENC_START..RekeyInit::AUTH_START]);
|
||||||
c.finish_decrypt(&pkt_assembled[RekeyInit::AUTH_START..])
|
if c.finish_decrypt(&pkt_assembled[RekeyInit::AUTH_START..]) {
|
||||||
} {
|
drop(c);
|
||||||
|
|
||||||
let pkt: &RekeyInit = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
|
let pkt: &RekeyInit = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
|
||||||
if let Some(alice_e) = P384PublicKey::from_bytes(&pkt.alice_e) {
|
if let Some(alice_e) = P384PublicKey::from_bytes(&pkt.alice_e) {
|
||||||
let bob_e_secret = P384KeyPair::generate();
|
let bob_e_secret = P384KeyPair::generate();
|
||||||
let next_session_key = hmac_sha512_secret(
|
|
||||||
|
// 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(),
|
key.ratchet_key.as_bytes(),
|
||||||
bob_e_secret.agree(&alice_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
|
noise_es.as_bytes(),
|
||||||
|
).as_bytes(),
|
||||||
|
noise_ee.as_bytes(),
|
||||||
|
).as_bytes(),
|
||||||
|
noise_se.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Packet fully authenticated
|
// Packet fully authenticated
|
||||||
|
@ -1149,7 +1168,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
|
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
|
||||||
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
||||||
reply.bob_e = *bob_e_secret.public_key_bytes();
|
reply.bob_e = *bob_e_secret.public_key_bytes();
|
||||||
reply.next_key_fingerprint = SHA384::hash(next_session_key.as_bytes());
|
reply.next_key_fingerprint = SHA384::hash(noise_psk_se_ee_es.as_bytes());
|
||||||
|
|
||||||
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
|
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
|
||||||
set_packet_header(
|
set_packet_header(
|
||||||
|
@ -1181,7 +1200,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
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,
|
noise_psk_se_ee_es,
|
||||||
next_ratchet_count,
|
next_ratchet_count,
|
||||||
current_time,
|
current_time,
|
||||||
counter,
|
counter,
|
||||||
|
@ -1195,6 +1214,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return Err(Error::OutOfSequence);
|
return Err(Error::OutOfSequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1215,20 +1235,37 @@ 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 (Offer::RekeyInit(alice_e_secret, _), Some(key)) = (&state.outgoing_offer, state.keys[key_index].as_ref()) {
|
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);
|
let mut c = key.get_receive_cipher(incoming_counter);
|
||||||
c.reset_init_gcm(&incoming_message_nonce);
|
c.reset_init_gcm(&incoming_message_nonce);
|
||||||
c.crypt_in_place(&mut pkt_assembled[RekeyAck::ENC_START..RekeyAck::AUTH_START]);
|
c.crypt_in_place(&mut pkt_assembled[RekeyAck::ENC_START..RekeyAck::AUTH_START]);
|
||||||
c.finish_decrypt(&pkt_assembled[RekeyAck::AUTH_START..])
|
if c.finish_decrypt(&pkt_assembled[RekeyAck::AUTH_START..]) {
|
||||||
} {
|
drop(c);
|
||||||
|
|
||||||
let pkt: &RekeyAck = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
|
let pkt: &RekeyAck = byte_array_as_proto_buffer(&pkt_assembled).unwrap();
|
||||||
if let Some(bob_e) = P384PublicKey::from_bytes(&pkt.bob_e) {
|
if let Some(bob_e) = P384PublicKey::from_bytes(&pkt.bob_e) {
|
||||||
let next_session_key = hmac_sha512_secret(
|
|
||||||
|
// 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(),
|
key.ratchet_key.as_bytes(),
|
||||||
alice_e_secret.agree(&bob_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
|
noise_es.as_bytes(),
|
||||||
|
).as_bytes(),
|
||||||
|
noise_ee.as_bytes(),
|
||||||
|
).as_bytes(),
|
||||||
|
noise_se.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(next_session_key.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) {
|
if session.update_receive_window(incoming_counter) {
|
||||||
// The new "Alice" knows Bob has the key since this is an ACK, so she can go
|
// 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
|
// ahead and set current_key to the new key. Then when she sends something
|
||||||
|
@ -1238,7 +1275,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
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,
|
noise_psk_se_ee_es,
|
||||||
next_ratchet_count,
|
next_ratchet_count,
|
||||||
current_time,
|
current_time,
|
||||||
session.send_counter.load(Ordering::Relaxed),
|
session.send_counter.load(Ordering::Relaxed),
|
||||||
|
@ -1250,16 +1287,15 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
drop(state);
|
drop(state);
|
||||||
return Ok(ReceiveResult::Ok(Some(session)));
|
return Ok(ReceiveResult::Ok(Some(session)));
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
return Err(Error::OutOfSequence);
|
return Err(Error::OutOfSequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
} else {
|
|
||||||
return Err(Error::OutOfSequence);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return Err(Error::OutOfSequence);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::UnknownLocalSessionId);
|
return Err(Error::UnknownLocalSessionId);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue