renamed under a new naming convention

This commit is contained in:
mamoniot 2022-12-14 11:06:10 -05:00
parent b3b0ef02b9
commit adc0674fb5

View file

@ -202,7 +202,7 @@ impl std::fmt::Debug for Error {
} }
/// Result generated by the packet receive function, with possible payloads. /// Result generated by the packet receive function, with possible payloads.
pub enum ReceiveResult<'a, H: Host> { pub enum ReceiveResult<'a, H: ApplicationLayer> {
/// Packet is valid, no action needs to be taken. /// Packet is valid, no action needs to be taken.
Ok, Ok,
@ -266,7 +266,7 @@ impl From<SessionId> for u64 {
/// This holds the data structures used to defragment incoming packets that are not associated with an /// This holds the data structures used to defragment incoming packets that are not associated with an
/// existing session, which would be new attempts to create sessions. Typically one of these is associated /// existing session, which would be new attempts to create sessions. Typically one of these is associated
/// with a single listen socket, local bound port, or other inbound endpoint. /// with a single listen socket, local bound port, or other inbound endpoint.
pub struct ReceiveContext<H: Host> { pub struct ReceiveContext<H: ApplicationLayer> {
initial_offer_defrag: Mutex<RingBufferMap<u32, GatherArray<H::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 128>>, initial_offer_defrag: Mutex<RingBufferMap<u32, GatherArray<H::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 128>>,
incoming_init_header_check_cipher: Aes, incoming_init_header_check_cipher: Aes,
} }
@ -275,9 +275,9 @@ pub struct ReceiveContext<H: Host> {
/// ///
/// Templating the session on this trait lets the code here be almost entirely transport, OS, /// Templating the session on this trait lets the code here be almost entirely transport, OS,
/// and use case independent. /// and use case independent.
pub trait Host: Sized { pub trait ApplicationLayer: Sized {
/// Arbitrary opaque object associated with a session, such as a connection state object. /// Arbitrary opaque object associated with a session, such as a connection state object.
type AssociatedObject; type SessionUserData;
/// Arbitrary object that dereferences to the session, such as Arc<Session<Self>>. /// Arbitrary object that dereferences to the session, such as Arc<Session<Self>>.
type SessionRef: Deref<Target = Session<Self>>; type SessionRef: Deref<Target = Session<Self>>;
@ -298,7 +298,7 @@ pub trait Host: Sized {
/// ///
/// This must contain a NIST P-384 public key but can contain other information. In ZeroTier this /// This must contain a NIST P-384 public key but can contain other information. In ZeroTier this
/// is a byte serialized identity. It could just be a naked NIST P-384 key if that's all you need. /// is a byte serialized identity. It could just be a naked NIST P-384 key if that's all you need.
fn get_local_s_public(&self) -> &[u8]; fn get_local_s_public_raw(&self) -> &[u8];
/// Get SHA384(this host's static public key blob). /// Get SHA384(this host's static public key blob).
/// ///
@ -308,20 +308,20 @@ pub trait Host: Sized {
/// Get a reference to this hosts' static public key's NIST P-384 secret key pair. /// Get a reference to this hosts' static public key's NIST P-384 secret key pair.
/// ///
/// This must return the NIST P-384 public key that is contained within the static public key blob. /// This must return the NIST P-384 public key that is contained within the static public key blob.
fn get_local_s_keypair_p384(&self) -> &P384KeyPair; fn get_local_s_keypair(&self) -> &P384KeyPair;
/// Extract the NIST P-384 ECC public key component from a static public key blob or return None on failure. /// Extract the NIST P-384 ECC public key component from a static public key blob or return None on failure.
/// ///
/// This is called to parse the static public key blob from the other end and extract its NIST P-384 public /// This is called to parse the static public key blob from the other end and extract its NIST P-384 public
/// key. SECURITY NOTE: the information supplied here is from the wire so care must be taken to parse it /// key. SECURITY NOTE: the information supplied here is from the wire so care must be taken to parse it
/// safely and fail on any error or corruption. /// safely and fail on any error or corruption.
fn extract_p384_static(static_public: &[u8]) -> Option<P384PublicKey>; fn extract_s_public_from_raw(static_public: &[u8]) -> Option<P384PublicKey>;
/// Look up a local session by local session ID or return None if not found. /// Look up a local session by local session ID or return None if not found.
fn session_lookup(&self, local_session_id: SessionId) -> Option<Self::SessionRef>; fn lookup_session(&self, local_session_id: SessionId) -> Option<Self::SessionRef>;
/// Rate limit and check an attempted new session (called before accept_new_session). /// Rate limit and check an attempted new session (called before accept_new_session).
fn check_new_session_attempt(&self, rc: &ReceiveContext<Self>, remote_address: &Self::RemoteAddress) -> bool; fn check_new_session(&self, rc: &ReceiveContext<Self>, remote_address: &Self::RemoteAddress) -> bool;
/// Check whether a new session should be accepted. /// Check whether a new session should be accepted.
/// ///
@ -334,74 +334,74 @@ pub trait Host: Sized {
remote_address: &Self::RemoteAddress, remote_address: &Self::RemoteAddress,
remote_static_public: &[u8], remote_static_public: &[u8],
remote_metadata: &[u8], remote_metadata: &[u8],
) -> Option<(SessionId, Secret<64>, Self::AssociatedObject)>; ) -> Option<(SessionId, Secret<64>, Self::SessionUserData)>;
} }
/// ZSSP bi-directional packet transport channel. /// ZSSP bi-directional packet transport channel.
pub struct Session<H: Host> { pub struct Session<Layer: ApplicationLayer> {
/// This side's session ID (unique on this side) /// This side's session ID (unique on this side)
pub id: SessionId, pub id: SessionId,
/// An arbitrary object associated with session (type defined in Host trait) /// An arbitrary object associated with session (type defined in Host trait)
pub associated_object: H::AssociatedObject, pub user_data: Layer::SessionUserData,
send_counter: Counter, // Outgoing packet counter and nonce state send_counter: Counter, // Outgoing packet counter and nonce state
psk: Secret<64>, // Arbitrary PSK provided by external code psk: Secret<64>, // Arbitrary PSK provided by external code
ss: Secret<48>, // Static raw shared ECDH NIST P-384 key noise_ss: Secret<48>, // Static raw shared ECDH NIST P-384 key
header_check_cipher: Aes, // Cipher used for header MAC (fragmentation) header_check_cipher: Aes, // Cipher used for header MAC (fragmentation)
state: RwLock<SessionMutableState>, // Mutable parts of state (other than defrag buffers) state: RwLock<SessionMutableState>, // Mutable parts of state (other than defrag buffers)
remote_s_public_hash: [u8; 48], // SHA384(remote static public key blob) remote_s_public_hash: [u8; 48], // SHA384(remote static public key blob)
remote_s_public_p384: [u8; P384_PUBLIC_KEY_SIZE], // Remote NIST P-384 static public key remote_s_public_raw: [u8; P384_PUBLIC_KEY_SIZE], // Remote NIST P-384 static public key
defrag: Mutex<RingBufferMap<u32, GatherArray<H::IncomingPacketBuffer, MAX_FRAGMENTS>, 8, 8>>, defrag: Mutex<RingBufferMap<u32, GatherArray<Layer::IncomingPacketBuffer, MAX_FRAGMENTS>, 8, 8>>,
} }
struct SessionMutableState { struct SessionMutableState {
remote_session_id: Option<SessionId>, // The other side's 48-bit session ID remote_session_id: Option<SessionId>, // The other side's 48-bit session ID
keys: [Option<SessionKey>; KEY_HISTORY_SIZE], // Buffers to store current, next, and last active key session_keys: [Option<SessionKey>; KEY_HISTORY_SIZE], // Buffers to store current, next, and last active key
key_ptr: usize, // Pointer used for keys[] circular buffer cur_session_key_idx: usize, // Pointer used for keys[] circular buffer
offer: Option<Box<EphemeralOffer>>, // Most recent ephemeral offer sent to remote offer: Option<Box<EphemeralOffer>>, // Most recent ephemeral offer sent to remote
last_remote_offer: i64, // Time of most recent ephemeral offer (ms) last_remote_offer: i64, // Time of most recent ephemeral offer (ms)
} }
impl<H: Host> Session<H> { impl<Layer: ApplicationLayer> Session<Layer> {
/// Create a new session and send an initial key offer message to the other end. /// Create a new session and send an initial key offer message to the other end.
/// ///
/// * `host` - Interface to application using ZSSP /// * `host` - Interface to application using ZSSP
/// * `local_session_id` - ID for this side of the session, must be locally unique /// * `local_session_id` - ID for this side (Alice) of the session, must be locally unique
/// * `remote_s_public` - Remote side's public key/identity /// * `remote_s_public_raw` - Remote side's (Bob's) public key/identity
/// * `offer_metadata` - Arbitrary meta-data to send with key offer (empty if none) /// * `offer_metadata` - Arbitrary meta-data to send with key offer (empty if none)
/// * `psk` - Arbitrary pre-shared key to include as initial key material (use all zero secret if none) /// * `psk` - Arbitrary pre-shared key to include as initial key material (use all zero secret if none)
/// * `associated_object` - Arbitrary object to put into session /// * `user_data` - Arbitrary object to put into session
/// * `mtu` - Physical wire MTU /// * `mtu` - Physical wire maximum transmition unit
/// * `current_time` - Current monotonic time in milliseconds /// * `current_time` - Current monotonic time in milliseconds
pub fn new<SendFunction: FnMut(&mut [u8])>( pub fn new<SendFunction: FnMut(&mut [u8])>(
host: &H, host: &Layer,
mut send: SendFunction, mut send: SendFunction,
local_session_id: SessionId, local_session_id: SessionId,
remote_s_public: &[u8], remote_s_public_raw: &[u8],
offer_metadata: &[u8], offer_metadata: &[u8],
psk: &Secret<64>, psk: &Secret<64>,
associated_object: H::AssociatedObject, user_data: Layer::SessionUserData,
mtu: usize, mtu: usize,
current_time: i64, current_time: i64,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
if let Some(remote_s_public_p384) = H::extract_p384_static(remote_s_public) { if let Some(remote_s_public_p384) = Layer::extract_s_public_from_raw(remote_s_public_raw) {
if let Some(ss) = host.get_local_s_keypair_p384().agree(&remote_s_public_p384) { if let Some(noise_ss) = host.get_local_s_keypair().agree(&remote_s_public_p384) {
let send_counter = Counter::new(); let send_counter = Counter::new();
let remote_s_public_hash = SHA384::hash(remote_s_public); let remote_s_public_hash = SHA384::hash(remote_s_public_raw);
let header_check_cipher = let header_check_cipher =
Aes::new(kbkdf512(ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::<HEADER_CHECK_AES_KEY_SIZE>()); Aes::new(kbkdf512(noise_ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::<HEADER_CHECK_AES_KEY_SIZE>());
if let Ok(offer) = send_ephemeral_offer( if let Ok(offer) = send_ephemeral_offer(
&mut send, &mut send,
send_counter.next(), send_counter.next(),
local_session_id, local_session_id,
None, None,
host.get_local_s_public(), host.get_local_s_public_raw(),
offer_metadata, offer_metadata,
&remote_s_public_p384, &remote_s_public_p384,
&remote_s_public_hash, &remote_s_public_hash,
&ss, &noise_ss,
None, None,
None, None,
mtu, mtu,
@ -409,20 +409,20 @@ impl<H: Host> Session<H> {
) { ) {
return Ok(Self { return Ok(Self {
id: local_session_id, id: local_session_id,
associated_object, user_data,
send_counter, send_counter,
psk: psk.clone(), psk: psk.clone(),
ss, noise_ss,
header_check_cipher, header_check_cipher,
state: RwLock::new(SessionMutableState { state: RwLock::new(SessionMutableState {
remote_session_id: None, remote_session_id: None,
keys: [None, None, None], session_keys: [None, None, None],
key_ptr: 0, cur_session_key_idx: 0,
offer: Some(offer), offer: Some(offer),
last_remote_offer: i64::MIN, last_remote_offer: i64::MIN,
}), }),
remote_s_public_hash, remote_s_public_hash,
remote_s_public_p384: remote_s_public_p384.as_bytes().clone(), remote_s_public_raw: remote_s_public_p384.as_bytes().clone(),
defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)), defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)),
}); });
} }
@ -446,7 +446,7 @@ impl<H: Host> Session<H> {
debug_assert!(mtu_buffer.len() >= MIN_TRANSPORT_MTU); debug_assert!(mtu_buffer.len() >= MIN_TRANSPORT_MTU);
let state = self.state.read().unwrap(); let state = self.state.read().unwrap();
if let Some(remote_session_id) = state.remote_session_id { if let Some(remote_session_id) = state.remote_session_id {
if let Some(key) = state.keys[state.key_ptr].as_ref() { if let Some(sym_key) = state.session_keys[state.cur_session_key_idx].as_ref() {
// Total size of the armored packet we are going to send (may end up being fragmented) // Total size of the armored packet we are going to send (may end up being fragmented)
let mut packet_len = data.len() + HEADER_SIZE + AES_GCM_TAG_SIZE; let mut packet_len = data.len() + HEADER_SIZE + AES_GCM_TAG_SIZE;
@ -465,7 +465,7 @@ impl<H: Host> Session<H> {
// Get an initialized AES-GCM cipher and re-initialize with a 96-bit IV built from remote session ID, // Get an initialized AES-GCM cipher and re-initialize with a 96-bit IV built from remote session ID,
// packet type, and counter. // packet type, and counter.
let mut c = key.get_send_cipher(counter)?; let mut c = sym_key.get_send_cipher(counter)?;
c.reset_init_gcm(CanonicalHeader::make(remote_session_id, PACKET_TYPE_DATA, counter.to_u32()).as_bytes()); c.reset_init_gcm(CanonicalHeader::make(remote_session_id, PACKET_TYPE_DATA, counter.to_u32()).as_bytes());
// Send first N-1 fragments of N total fragments. // Send first N-1 fragments of N total fragments.
@ -500,7 +500,7 @@ impl<H: Host> Session<H> {
send(&mut mtu_buffer[..packet_len]); send(&mut mtu_buffer[..packet_len]);
// Check reusable AES-GCM instance back into pool. // Check reusable AES-GCM instance back into pool.
key.return_send_cipher(c); sym_key.return_send_cipher(c);
return Ok(()); return Ok(());
} else { } else {
@ -515,7 +515,7 @@ impl<H: Host> Session<H> {
/// Check whether this session is established. /// Check whether this session is established.
pub fn established(&self) -> bool { pub fn established(&self) -> bool {
let state = self.state.read().unwrap(); let state = self.state.read().unwrap();
state.remote_session_id.is_some() && state.keys[state.key_ptr].is_some() state.remote_session_id.is_some() && state.session_keys[state.cur_session_key_idx].is_some()
} }
/// Get information about this session's security state. /// Get information about this session's security state.
@ -524,7 +524,7 @@ impl<H: Host> Session<H> {
/// and whether Kyber1024 was used. None is returned if the session isn't established. /// and whether Kyber1024 was used. None is returned if the session isn't established.
pub fn status(&self) -> Option<([u8; 16], i64, u64, bool)> { pub fn status(&self) -> Option<([u8; 16], i64, u64, bool)> {
let state = self.state.read().unwrap(); let state = self.state.read().unwrap();
if let Some(key) = state.keys[state.key_ptr].as_ref() { if let Some(key) = state.session_keys[state.cur_session_key_idx].as_ref() {
Some((key.secret_fingerprint, key.establish_time, key.ratchet_count, key.jedi)) Some((key.secret_fingerprint, key.establish_time, key.ratchet_count, key.jedi))
} else { } else {
None None
@ -541,7 +541,7 @@ impl<H: Host> Session<H> {
/// * `force_rekey` - Re-key the session now regardless of key aging (still subject to rate limiting) /// * `force_rekey` - Re-key the session now regardless of key aging (still subject to rate limiting)
pub fn service<SendFunction: FnMut(&mut [u8])>( pub fn service<SendFunction: FnMut(&mut [u8])>(
&self, &self,
host: &H, host: &Layer,
mut send: SendFunction, mut send: SendFunction,
offer_metadata: &[u8], offer_metadata: &[u8],
mtu: usize, mtu: usize,
@ -550,26 +550,26 @@ impl<H: Host> Session<H> {
) { ) {
let state = self.state.read().unwrap(); let state = self.state.read().unwrap();
if (force_rekey if (force_rekey
|| state.keys[state.key_ptr] || state.session_keys[state.cur_session_key_idx]
.as_ref() .as_ref()
.map_or(true, |key| key.lifetime.should_rekey(self.send_counter.previous(), current_time))) .map_or(true, |key| key.lifetime.should_rekey(self.send_counter.previous(), current_time)))
&& state && state
.offer .offer
.as_ref() .as_ref()
.map_or(true, |o| (current_time - o.creation_time) > H::REKEY_RATE_LIMIT_MS) .map_or(true, |o| (current_time - o.creation_time) > Layer::REKEY_RATE_LIMIT_MS)
{ {
if let Some(remote_s_public_p384) = P384PublicKey::from_bytes(&self.remote_s_public_p384) { if let Some(remote_s_public) = P384PublicKey::from_bytes(&self.remote_s_public_raw) {
if let Ok(offer) = send_ephemeral_offer( if let Ok(offer) = send_ephemeral_offer(
&mut send, &mut send,
self.send_counter.next(), self.send_counter.next(),
self.id, self.id,
state.remote_session_id, state.remote_session_id,
host.get_local_s_public(), host.get_local_s_public_raw(),
offer_metadata, offer_metadata,
&remote_s_public_p384, &remote_s_public,
&self.remote_s_public_hash, &self.remote_s_public_hash,
&self.ss, &self.noise_ss,
state.keys[state.key_ptr].as_ref(), state.session_keys[state.cur_session_key_idx].as_ref(),
if state.remote_session_id.is_some() { if state.remote_session_id.is_some() {
Some(&self.header_check_cipher) Some(&self.header_check_cipher)
} else { } else {
@ -586,8 +586,8 @@ impl<H: Host> Session<H> {
} }
} }
impl<H: Host> ReceiveContext<H> { impl<Layer: ApplicationLayer> ReceiveContext<Layer> {
pub fn new(host: &H) -> Self { pub fn new(host: &Layer) -> Self {
Self { Self {
initial_offer_defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)), initial_offer_defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)),
incoming_init_header_check_cipher: Aes::new( incoming_init_header_check_cipher: Aes::new(
@ -607,14 +607,14 @@ impl<H: Host> ReceiveContext<H> {
#[inline] #[inline]
pub fn receive<'a, SendFunction: FnMut(&mut [u8])>( pub fn receive<'a, SendFunction: FnMut(&mut [u8])>(
&self, &self,
host: &H, host: &Layer,
remote_address: &H::RemoteAddress, remote_address: &Layer::RemoteAddress,
mut send: SendFunction, mut send: SendFunction,
data_buf: &'a mut [u8], data_buf: &'a mut [u8],
incoming_packet_buf: H::IncomingPacketBuffer, incoming_packet_buf: Layer::IncomingPacketBuffer,
mtu: usize, mtu: usize,
current_time: i64, current_time: i64,
) -> Result<ReceiveResult<'a, H>, Error> { ) -> Result<ReceiveResult<'a, Layer>, Error> {
let incoming_packet = incoming_packet_buf.as_ref(); let incoming_packet = incoming_packet_buf.as_ref();
if incoming_packet.len() < MIN_PACKET_SIZE { if incoming_packet.len() < MIN_PACKET_SIZE {
unlikely_branch(); unlikely_branch();
@ -629,7 +629,7 @@ impl<H: Host> ReceiveContext<H> {
if let Some(local_session_id) = SessionId::new_from_u64(u64::from_le(memory::load_raw(&incoming_packet[8..16])) & 0xffffffffffffu64) if let Some(local_session_id) = SessionId::new_from_u64(u64::from_le(memory::load_raw(&incoming_packet[8..16])) & 0xffffffffffffu64)
{ {
if let Some(session) = host.session_lookup(local_session_id) { if let Some(session) = host.lookup_session(local_session_id) {
if verify_header_check_code(incoming_packet, &session.header_check_cipher) { if verify_header_check_code(incoming_packet, &session.header_check_cipher) {
let canonical_header = CanonicalHeader::make(local_session_id, packet_type, counter); let canonical_header = CanonicalHeader::make(local_session_id, packet_type, counter);
if fragment_count > 1 { if fragment_count > 1 {
@ -730,17 +730,17 @@ impl<H: Host> ReceiveContext<H> {
#[inline] #[inline]
fn receive_complete<'a, SendFunction: FnMut(&mut [u8])>( fn receive_complete<'a, SendFunction: FnMut(&mut [u8])>(
&self, &self,
host: &H, host: &Layer,
remote_address: &H::RemoteAddress, remote_address: &Layer::RemoteAddress,
send: &mut SendFunction, send: &mut SendFunction,
data_buf: &'a mut [u8], data_buf: &'a mut [u8],
canonical_header_bytes: &[u8; 12], canonical_header_bytes: &[u8; 12],
fragments: &[H::IncomingPacketBuffer], fragments: &[Layer::IncomingPacketBuffer],
packet_type: u8, packet_type: u8,
session: Option<H::SessionRef>, session: Option<Layer::SessionRef>,
mtu: usize, mtu: usize,
current_time: i64, current_time: i64,
) -> Result<ReceiveResult<'a, H>, Error> { ) -> Result<ReceiveResult<'a, Layer>, Error> {
debug_assert!(fragments.len() >= 1); debug_assert!(fragments.len() >= 1);
// The first 'if' below should capture both DATA and NOP but not other types. Sanity check this. // The first 'if' below should capture both DATA and NOP but not other types. Sanity check this.
@ -751,9 +751,9 @@ impl<H: Host> ReceiveContext<H> {
if let Some(session) = session { if let Some(session) = session {
let state = session.state.read().unwrap(); let state = session.state.read().unwrap();
for p in 0..KEY_HISTORY_SIZE { for p in 0..KEY_HISTORY_SIZE {
let key_ptr = (state.key_ptr + p) % KEY_HISTORY_SIZE; let key_idx = (state.cur_session_key_idx + p) % KEY_HISTORY_SIZE;
if let Some(key) = state.keys[key_ptr].as_ref() { if let Some(session_key) = state.session_keys[key_idx].as_ref() {
let mut c = key.get_receive_cipher(); let mut c = session_key.get_receive_cipher();
c.reset_init_gcm(canonical_header_bytes); c.reset_init_gcm(canonical_header_bytes);
let mut data_len = 0; let mut data_len = 0;
@ -766,7 +766,7 @@ impl<H: Host> ReceiveContext<H> {
data_len += f.len() - HEADER_SIZE; data_len += f.len() - HEADER_SIZE;
if data_len > data_buf.len() { if data_len > data_buf.len() {
unlikely_branch(); unlikely_branch();
key.return_receive_cipher(c); session_key.return_receive_cipher(c);
return Err(Error::DataBufferTooSmall); return Err(Error::DataBufferTooSmall);
} }
c.crypt(&f[HEADER_SIZE..], &mut data_buf[current_frag_data_start..data_len]); c.crypt(&f[HEADER_SIZE..], &mut data_buf[current_frag_data_start..data_len]);
@ -782,7 +782,7 @@ impl<H: Host> ReceiveContext<H> {
data_len += last_fragment.len() - (HEADER_SIZE + AES_GCM_TAG_SIZE); data_len += last_fragment.len() - (HEADER_SIZE + AES_GCM_TAG_SIZE);
if data_len > data_buf.len() { if data_len > data_buf.len() {
unlikely_branch(); unlikely_branch();
key.return_receive_cipher(c); session_key.return_receive_cipher(c);
return Err(Error::DataBufferTooSmall); return Err(Error::DataBufferTooSmall);
} }
c.crypt( c.crypt(
@ -791,21 +791,21 @@ impl<H: Host> ReceiveContext<H> {
); );
let aead_authentication_ok = c.finish_decrypt(&last_fragment[(last_fragment.len() - AES_GCM_TAG_SIZE)..]); let aead_authentication_ok = c.finish_decrypt(&last_fragment[(last_fragment.len() - AES_GCM_TAG_SIZE)..]);
key.return_receive_cipher(c); session_key.return_receive_cipher(c);
if aead_authentication_ok { if aead_authentication_ok {
// Select this key as the new default if it's newer than the current key. // Select this key as the new default if it's newer than the current key.
if p > 0 if p > 0
&& state.keys[state.key_ptr] && state.session_keys[state.cur_session_key_idx]
.as_ref() .as_ref()
.map_or(true, |old| old.establish_counter < key.establish_counter) .map_or(true, |old| old.establish_counter < session_key.establish_counter)
{ {
drop(state); drop(state);
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
state.key_ptr = key_ptr; state.cur_session_key_idx = key_idx;
for i in 0..KEY_HISTORY_SIZE { for i in 0..KEY_HISTORY_SIZE {
if i != key_ptr { if i != key_idx {
if let Some(old_key) = state.keys[key_ptr].as_ref() { if let Some(old_key) = state.session_keys[key_idx].as_ref() {
// Release pooled cipher memory from old keys. // Release pooled cipher memory from old keys.
old_key.receive_cipher_pool.lock().unwrap().clear(); old_key.receive_cipher_pool.lock().unwrap().clear();
old_key.send_cipher_pool.lock().unwrap().clear(); old_key.send_cipher_pool.lock().unwrap().clear();
@ -882,27 +882,27 @@ impl<H: Host> ReceiveContext<H> {
// Check rate limits. // Check rate limits.
if let Some(session) = session.as_ref() { if let Some(session) = session.as_ref() {
if (current_time - session.state.read().unwrap().last_remote_offer) < H::REKEY_RATE_LIMIT_MS { if (current_time - session.state.read().unwrap().last_remote_offer) < Layer::REKEY_RATE_LIMIT_MS {
return Err(Error::RateLimited); return Err(Error::RateLimited);
} }
} else { } else {
if !host.check_new_session_attempt(self, remote_address) { if !host.check_new_session(self, remote_address) {
return Err(Error::RateLimited); return Err(Error::RateLimited);
} }
} }
// Key agreement: alice (remote) ephemeral NIST P-384 <> local static NIST P-384 // Key agreement: alice (remote) ephemeral NIST P-384 <> local static NIST P-384
let (alice_e0_public, e0s) = let (alice_e_public, noise_es) =
P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)]) P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)])
.and_then(|pk| host.get_local_s_keypair_p384().agree(&pk).map(move |s| (pk, s))) .and_then(|pk| host.get_local_s_keypair().agree(&pk).map(move |s| (pk, s)))
.ok_or(Error::FailedAuthentication)?; .ok_or(Error::FailedAuthentication)?;
// Initial key derivation from starting point, mixing in alice's ephemeral public and the e0s. // Initial key derivation from starting point, mixing in alice's ephemeral public and the es.
let mut key = Secret(hmac_sha512(&hmac_sha512(&INITIAL_KEY, alice_e0_public.as_bytes()), e0s.as_bytes())); let es_key = Secret(hmac_sha512(&hmac_sha512(&INITIAL_KEY, alice_e_public.as_bytes()), noise_es.as_bytes()));
// Decrypt the encrypted part of the packet payload and authenticate the above key exchange via AES-GCM auth. // Decrypt the encrypted part of the packet payload and authenticate the above key exchange via AES-GCM auth.
let mut c = AesGcm::new( let mut c = AesGcm::new(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<AES_KEY_SIZE>(), kbkdf512(es_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<AES_KEY_SIZE>(),
false, false,
); );
c.reset_init_gcm(canonical_header_bytes); c.reset_init_gcm(canonical_header_bytes);
@ -912,7 +912,7 @@ impl<H: Host> ReceiveContext<H> {
} }
// Parse payload and get alice's session ID, alice's public blob, metadata, and (if present) Alice's Kyber1024 public. // Parse payload and get alice's session ID, alice's public blob, metadata, and (if present) Alice's Kyber1024 public.
let (offer_id, alice_session_id, alice_s_public, alice_metadata, alice_e1_public, alice_ratchet_key_fingerprint) = let (offer_id, alice_session_id, alice_s_public_raw, alice_metadata, alice_e1_public_raw, alice_ratchet_key_fingerprint) =
parse_key_offer_after_header(&kex_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..kex_packet_len], packet_type)?; parse_key_offer_after_header(&kex_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..kex_packet_len], packet_type)?;
// We either have a session, in which case they should have supplied a ratchet key fingerprint, or // We either have a session, in which case they should have supplied a ratchet key fingerprint, or
@ -922,21 +922,22 @@ impl<H: Host> ReceiveContext<H> {
} }
// Extract alice's static NIST P-384 public key from her public blob. // Extract alice's static NIST P-384 public key from her public blob.
let alice_s_public_p384 = H::extract_p384_static(alice_s_public).ok_or(Error::InvalidPacket)?; let alice_s_public = Layer::extract_s_public_from_raw(alice_s_public_raw).ok_or(Error::InvalidPacket)?;
// Key agreement: both sides' static P-384 keys. // Key agreement: both sides' static P-384 keys.
let ss = host let noise_ss = host
.get_local_s_keypair_p384() .get_local_s_keypair()
.agree(&alice_s_public_p384) .agree(&alice_s_public)
.ok_or(Error::FailedAuthentication)?; .ok_or(Error::FailedAuthentication)?;
// Mix result of 'ss' agreement into master key. // Mix result of 'ss' agreement into master key.
key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes())); let ss_key = Secret(hmac_sha512(es_key.as_bytes(), noise_ss.as_bytes()));
drop(es_key);
// Authenticate entire packet with HMAC-SHA384, verifying alice's identity via 'ss' secret that was // Authenticate entire packet with HMAC-SHA384, verifying alice's identity via 'ss' secret that was
// just mixed into the key. // just mixed into the key.
if !hmac_sha384_2( if !hmac_sha384_2(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), kbkdf512(ss_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
canonical_header_bytes, canonical_header_bytes,
&kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end], &kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end],
) )
@ -951,7 +952,7 @@ impl<H: Host> ReceiveContext<H> {
// then create new sessions. // then create new sessions.
let (new_session, ratchet_key, ratchet_count) = if let Some(session) = session.as_ref() { let (new_session, ratchet_key, ratchet_count) = if let Some(session) = session.as_ref() {
// Existing session identity must match the one in this offer. // Existing session identity must match the one in this offer.
if !session.remote_s_public_hash.eq(&SHA384::hash(&alice_s_public)) { if !session.remote_s_public_hash.eq(&SHA384::hash(&alice_s_public_raw)) {
return Err(Error::FailedAuthentication); return Err(Error::FailedAuthentication);
} }
@ -960,7 +961,7 @@ impl<H: Host> ReceiveContext<H> {
let mut ratchet_key = None; let mut ratchet_key = None;
let mut ratchet_count = 0; let mut ratchet_count = 0;
let state = session.state.read().unwrap(); let state = session.state.read().unwrap();
for k in state.keys.iter() { for k in state.session_keys.iter() {
if let Some(k) = k.as_ref() { if let Some(k) = k.as_ref() {
if secret_fingerprint(k.ratchet_key.as_bytes())[..16].eq(alice_ratchet_key_fingerprint) { if secret_fingerprint(k.ratchet_key.as_bytes())[..16].eq(alice_ratchet_key_fingerprint) {
ratchet_key = Some(k.ratchet_key.clone()); ratchet_key = Some(k.ratchet_key.clone());
@ -976,28 +977,28 @@ impl<H: Host> ReceiveContext<H> {
(None, ratchet_key, ratchet_count) (None, ratchet_key, ratchet_count)
} else { } else {
if let Some((new_session_id, psk, associated_object)) = if let Some((new_session_id, psk, associated_object)) =
host.accept_new_session(self, remote_address, alice_s_public, alice_metadata) host.accept_new_session(self, remote_address, alice_s_public_raw, alice_metadata)
{ {
let header_check_cipher = Aes::new( let header_check_cipher = Aes::new(
kbkdf512(ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::<HEADER_CHECK_AES_KEY_SIZE>(), kbkdf512(noise_ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::<HEADER_CHECK_AES_KEY_SIZE>(),
); );
( (
Some(Session::<H> { Some(Session::<Layer> {
id: new_session_id, id: new_session_id,
associated_object, user_data: associated_object,
send_counter: Counter::new(), send_counter: Counter::new(),
psk, psk,
ss, noise_ss,
header_check_cipher, header_check_cipher,
state: RwLock::new(SessionMutableState { state: RwLock::new(SessionMutableState {
remote_session_id: Some(alice_session_id), remote_session_id: Some(alice_session_id),
keys: [None, None, None], session_keys: [None, None, None],
key_ptr: 0, cur_session_key_idx: 0,
offer: None, offer: None,
last_remote_offer: current_time, last_remote_offer: current_time,
}), }),
remote_s_public_hash: SHA384::hash(&alice_s_public), remote_s_public_hash: SHA384::hash(&alice_s_public_raw),
remote_s_public_p384: alice_s_public_p384.as_bytes().clone(), remote_s_public_raw: alice_s_public.as_bytes().clone(),
defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)), defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)),
}), }),
None, None,
@ -1013,33 +1014,34 @@ impl<H: Host> ReceiveContext<H> {
let session = existing_session.as_ref().map_or_else(|| new_session.as_ref().unwrap(), |s| &*s); let session = existing_session.as_ref().map_or_else(|| new_session.as_ref().unwrap(), |s| &*s);
// Generate our ephemeral NIST P-384 key pair. // Generate our ephemeral NIST P-384 key pair.
let bob_e0_keypair = P384KeyPair::generate(); let bob_e_keypair = P384KeyPair::generate();
// Key agreement: both sides' ephemeral P-384 public keys. // Key agreement: both sides' ephemeral P-384 public keys.
let e0e0 = bob_e0_keypair.agree(&alice_e0_public).ok_or(Error::FailedAuthentication)?; let noise_ee = bob_e_keypair.agree(&alice_e_public).ok_or(Error::FailedAuthentication)?;
// Key agreement: bob (local) static NIST P-384, alice (remote) ephemeral P-384. // Key agreement: bob (local) static NIST P-384, alice (remote) ephemeral P-384.
let se0 = bob_e0_keypair.agree(&alice_s_public_p384).ok_or(Error::FailedAuthentication)?; let noise_se = bob_e_keypair.agree(&alice_s_public).ok_or(Error::FailedAuthentication)?;
// Mix in the psk, the key to this point, our ephemeral public, e0e0, and se0, completing Noise_IK. // Mix in the psk, the key to this point, our ephemeral public, ee, and se, completing Noise_IK.
// //
// FIPS note: the order of HMAC parameters are flipped here from the usual Noise HMAC(key, X). That's because // FIPS note: the order of HMAC parameters are flipped here from the usual Noise HMAC(key, X). That's because
// NIST/FIPS allows HKDF with HMAC(salt, key) and salt is allowed to be anything. This way if the PSK is not // NIST/FIPS allows HKDF with HMAC(salt, key) and salt is allowed to be anything. This way if the PSK is not
// FIPS compliant the compliance of the entire key derivation is not invalidated. Both inputs are secrets of // FIPS compliant the compliance of the entire key derivation is not invalidated. Both inputs are secrets of
// fixed size so this shouldn't matter cryptographically. // fixed size so this shouldn't matter cryptographically.
key = Secret(hmac_sha512( let noise_ik_key = Secret(hmac_sha512(
session.psk.as_bytes(), session.psk.as_bytes(),
&hmac_sha512( &hmac_sha512(
&hmac_sha512(&hmac_sha512(key.as_bytes(), bob_e0_keypair.public_key_bytes()), e0e0.as_bytes()), &hmac_sha512(&hmac_sha512(ss_key.as_bytes(), bob_e_keypair.public_key_bytes()), noise_ee.as_bytes()),
se0.as_bytes(), noise_se.as_bytes(),
), ),
)); ));
drop(ss_key);
// At this point we've completed Noise_IK key derivation with NIST P-384 ECDH, but now for hybrid and ratcheting... // At this point we've completed Noise_IK key derivation with NIST P-384 ECDH, but now for hybrid and ratcheting...
// Generate a Kyber encapsulated ciphertext if Kyber is enabled and the other side sent us a public key. // Generate a Kyber encapsulated ciphertext if Kyber is enabled and the other side sent us a public key.
let (bob_e1_public, e1e1) = if JEDI && alice_e1_public.len() > 0 { let (bob_e1_public, e1e1) = if JEDI && alice_e1_public_raw.len() > 0 {
if let Ok((bob_e1_public, e1e1)) = pqc_kyber::encapsulate(alice_e1_public, &mut random::SecureRandom::default()) { if let Ok((bob_e1_public, e1e1)) = pqc_kyber::encapsulate(alice_e1_public_raw, &mut random::SecureRandom::default()) {
(Some(bob_e1_public), Some(Secret(e1e1))) (Some(bob_e1_public), Some(Secret(e1e1)))
} else { } else {
return Err(Error::FailedAuthentication); return Err(Error::FailedAuthentication);
@ -1055,7 +1057,7 @@ impl<H: Host> ReceiveContext<H> {
let mut rp = &mut reply_buf[HEADER_SIZE..]; let mut rp = &mut reply_buf[HEADER_SIZE..];
rp.write_all(&[SESSION_PROTOCOL_VERSION])?; rp.write_all(&[SESSION_PROTOCOL_VERSION])?;
rp.write_all(bob_e0_keypair.public_key_bytes())?; rp.write_all(bob_e_keypair.public_key_bytes())?;
rp.write_all(&offer_id)?; rp.write_all(&offer_id)?;
rp.write_all(&session.id.0.to_le_bytes()[..SESSION_ID_SIZE])?; rp.write_all(&session.id.0.to_le_bytes()[..SESSION_ID_SIZE])?;
@ -1090,7 +1092,7 @@ impl<H: Host> ReceiveContext<H> {
// Encrypt reply packet using final Noise_IK key BEFORE mixing hybrid or ratcheting, since the other side // Encrypt reply packet using final Noise_IK key BEFORE mixing hybrid or ratcheting, since the other side
// must decrypt before doing these things. // must decrypt before doing these things.
let mut c = AesGcm::new( let mut c = AesGcm::new(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<AES_KEY_SIZE>(), kbkdf512(noise_ik_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<AES_KEY_SIZE>(),
true, true,
); );
c.reset_init_gcm(reply_canonical_header.as_bytes()); c.reset_init_gcm(reply_canonical_header.as_bytes());
@ -1100,11 +1102,12 @@ impl<H: Host> ReceiveContext<H> {
reply_len += AES_GCM_TAG_SIZE; reply_len += AES_GCM_TAG_SIZE;
// Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any). // Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any).
let mut session_key = noise_ik_key;
if let Some(ratchet_key) = ratchet_key { if let Some(ratchet_key) = ratchet_key {
key = Secret(hmac_sha512(ratchet_key.as_bytes(), key.as_bytes())); session_key = Secret(hmac_sha512(ratchet_key.as_bytes(), session_key.as_bytes()));
} }
if let Some(e1e1) = e1e1.as_ref() { if let Some(e1e1) = e1e1.as_ref() {
key = Secret(hmac_sha512(e1e1.as_bytes(), key.as_bytes())); session_key = Secret(hmac_sha512(e1e1.as_bytes(), session_key.as_bytes()));
} }
// Authenticate packet using HMAC-SHA384 with final key. Note that while the final key now has the Kyber secret // Authenticate packet using HMAC-SHA384 with final key. Note that while the final key now has the Kyber secret
@ -1112,19 +1115,19 @@ impl<H: Host> ReceiveContext<H> {
// associated with the remote identity. An attacker who can break NIST P-384 (and has the psk) could MITM the // associated with the remote identity. An attacker who can break NIST P-384 (and has the psk) could MITM the
// Kyber exchange, but you'd need a not-yet-existing quantum computer for that. // Kyber exchange, but you'd need a not-yet-existing quantum computer for that.
let hmac = hmac_sha384_2( let hmac = hmac_sha384_2(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), kbkdf512(session_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
reply_canonical_header.as_bytes(), reply_canonical_header.as_bytes(),
&reply_buf[HEADER_SIZE..reply_len], &reply_buf[HEADER_SIZE..reply_len],
); );
reply_buf[reply_len..(reply_len + HMAC_SIZE)].copy_from_slice(&hmac); reply_buf[reply_len..(reply_len + HMAC_SIZE)].copy_from_slice(&hmac);
reply_len += HMAC_SIZE; reply_len += HMAC_SIZE;
let key = SessionKey::new(key, Role::Bob, current_time, reply_counter, ratchet_count + 1, e1e1.is_some()); let session_key = SessionKey::new(session_key, Role::Bob, current_time, reply_counter, ratchet_count + 1, e1e1.is_some());
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
let _ = state.remote_session_id.replace(alice_session_id); let _ = state.remote_session_id.replace(alice_session_id);
let next_key_ptr = (state.key_ptr + 1) % KEY_HISTORY_SIZE; let next_key_ptr = (state.cur_session_key_idx + 1) % KEY_HISTORY_SIZE;
let _ = state.keys[next_key_ptr].replace(key); let _ = state.session_keys[next_key_ptr].replace(session_key);
drop(state); drop(state);
// Bob now has final key state for this exchange. Yay! Now reply to Alice so she can construct it. // Bob now has final key state for this exchange. Yay! Now reply to Alice so she can construct it.
@ -1150,25 +1153,25 @@ impl<H: Host> ReceiveContext<H> {
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(offer) = state.offer.as_ref() { if let Some(offer) = state.offer.as_ref() {
let (bob_e0_public, e0e0) = let (bob_e_public, noise_ee) =
P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)]) P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)])
.and_then(|pk| offer.alice_e0_keypair.agree(&pk).map(move |s| (pk, s))) .and_then(|pk| offer.alice_e_keypair.agree(&pk).map(move |s| (pk, s)))
.ok_or(Error::FailedAuthentication)?; .ok_or(Error::FailedAuthentication)?;
let se0 = host let noise_se = host
.get_local_s_keypair_p384() .get_local_s_keypair()
.agree(&bob_e0_public) .agree(&bob_e_public)
.ok_or(Error::FailedAuthentication)?; .ok_or(Error::FailedAuthentication)?;
let mut key = Secret(hmac_sha512( let mut noise_ik_key = Secret(hmac_sha512(
session.psk.as_bytes(), session.psk.as_bytes(),
&hmac_sha512( &hmac_sha512(
&hmac_sha512(&hmac_sha512(offer.key.as_bytes(), bob_e0_public.as_bytes()), e0e0.as_bytes()), &hmac_sha512(&hmac_sha512(offer.ss_key.as_bytes(), bob_e_public.as_bytes()), noise_ee.as_bytes()),
se0.as_bytes(), noise_se.as_bytes(),
), ),
)); ));
let mut c = AesGcm::new( let mut c = AesGcm::new(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<AES_KEY_SIZE>(), kbkdf512(noise_ik_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<AES_KEY_SIZE>(),
false, false,
); );
c.reset_init_gcm(canonical_header_bytes); c.reset_init_gcm(canonical_header_bytes);
@ -1179,7 +1182,7 @@ impl<H: Host> ReceiveContext<H> {
// Alice has now completed Noise_IK with NIST P-384 and verified with GCM auth, but now for hybrid... // Alice has now completed Noise_IK with NIST P-384 and verified with GCM auth, but now for hybrid...
let (offer_id, bob_session_id, _, _, bob_e1_public, bob_ratchet_key_id) = parse_key_offer_after_header( let (offer_id, bob_session_id, _, _, bob_e1_public_raw, bob_ratchet_key_id) = parse_key_offer_after_header(
&kex_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..kex_packet_len], &kex_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..kex_packet_len],
packet_type, packet_type,
)?; )?;
@ -1188,8 +1191,8 @@ impl<H: Host> ReceiveContext<H> {
return Ok(ReceiveResult::Ignored); return Ok(ReceiveResult::Ignored);
} }
let e1e1 = if JEDI && bob_e1_public.len() > 0 && offer.alice_e1_keypair.is_some() { let e1e1 = if JEDI && bob_e1_public_raw.len() > 0 && offer.alice_e1_keypair.is_some() {
if let Ok(e1e1) = pqc_kyber::decapsulate(bob_e1_public, &offer.alice_e1_keypair.as_ref().unwrap().secret) { if let Ok(e1e1) = pqc_kyber::decapsulate(bob_e1_public_raw, &offer.alice_e1_keypair.as_ref().unwrap().secret) {
Some(Secret(e1e1)) Some(Secret(e1e1))
} else { } else {
return Err(Error::FailedAuthentication); return Err(Error::FailedAuthentication);
@ -1199,16 +1202,17 @@ impl<H: Host> ReceiveContext<H> {
}; };
let mut ratchet_count = 0; let mut ratchet_count = 0;
let mut session_key = noise_ik_key;
if bob_ratchet_key_id.is_some() && offer.ratchet_key.is_some() { if bob_ratchet_key_id.is_some() && offer.ratchet_key.is_some() {
key = Secret(hmac_sha512(offer.ratchet_key.as_ref().unwrap().as_bytes(), key.as_bytes())); session_key = Secret(hmac_sha512(offer.ratchet_key.as_ref().unwrap().as_bytes(), session_key.as_bytes()));
ratchet_count = offer.ratchet_count; ratchet_count = offer.ratchet_count;
} }
if let Some(e1e1) = e1e1.as_ref() { if let Some(e1e1) = e1e1.as_ref() {
key = Secret(hmac_sha512(e1e1.as_bytes(), key.as_bytes())); session_key = Secret(hmac_sha512(e1e1.as_bytes(), session_key.as_bytes()));
} }
if !hmac_sha384_2( if !hmac_sha384_2(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), kbkdf512(session_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
canonical_header_bytes, canonical_header_bytes,
&kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end], &kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end],
) )
@ -1220,7 +1224,7 @@ impl<H: Host> ReceiveContext<H> {
// Alice has now completed and validated the full hybrid exchange. // Alice has now completed and validated the full hybrid exchange.
let counter = session.send_counter.next(); let counter = session.send_counter.next();
let key = SessionKey::new(key, Role::Alice, current_time, counter, ratchet_count + 1, e1e1.is_some()); let session_key = SessionKey::new(session_key, Role::Alice, current_time, counter, ratchet_count + 1, e1e1.is_some());
let mut reply_buf = [0_u8; HEADER_SIZE + AES_GCM_TAG_SIZE]; let mut reply_buf = [0_u8; HEADER_SIZE + AES_GCM_TAG_SIZE];
create_packet_header( create_packet_header(
@ -1232,10 +1236,10 @@ impl<H: Host> ReceiveContext<H> {
counter, counter,
)?; )?;
let mut c = key.get_send_cipher(counter)?; let mut c = session_key.get_send_cipher(counter)?;
c.reset_init_gcm(CanonicalHeader::make(bob_session_id.into(), PACKET_TYPE_NOP, counter.to_u32()).as_bytes()); c.reset_init_gcm(CanonicalHeader::make(bob_session_id.into(), PACKET_TYPE_NOP, counter.to_u32()).as_bytes());
reply_buf[HEADER_SIZE..].copy_from_slice(&c.finish_encrypt()); reply_buf[HEADER_SIZE..].copy_from_slice(&c.finish_encrypt());
key.return_send_cipher(c); session_key.return_send_cipher(c);
set_header_check_code(&mut reply_buf, &session.header_check_cipher); set_header_check_code(&mut reply_buf, &session.header_check_cipher);
send(&mut reply_buf); send(&mut reply_buf);
@ -1243,8 +1247,8 @@ impl<H: Host> ReceiveContext<H> {
drop(state); drop(state);
let mut state = session.state.write().unwrap(); let mut state = session.state.write().unwrap();
let _ = state.remote_session_id.replace(bob_session_id); let _ = state.remote_session_id.replace(bob_session_id);
let next_key_ptr = (state.key_ptr + 1) % KEY_HISTORY_SIZE; let next_key_idx = (state.cur_session_key_idx + 1) % KEY_HISTORY_SIZE;
let _ = state.keys[next_key_ptr].replace(key); let _ = state.session_keys[next_key_idx].replace(session_key);
let _ = state.offer.take(); let _ = state.offer.take();
return Ok(ReceiveResult::Ok); return Ok(ReceiveResult::Ok);
@ -1336,8 +1340,8 @@ struct EphemeralOffer {
creation_time: i64, // Local time when offer was created creation_time: i64, // Local time when offer was created
ratchet_count: u64, // Ratchet count starting at zero for initial offer ratchet_count: u64, // Ratchet count starting at zero for initial offer
ratchet_key: Option<Secret<64>>, // Ratchet key from previous offer ratchet_key: Option<Secret<64>>, // Ratchet key from previous offer
key: Secret<64>, // Shared secret in-progress, at state after offer sent ss_key: Secret<64>, // Shared secret in-progress, at state after offer sent
alice_e0_keypair: P384KeyPair, // NIST P-384 key pair (Noise ephemeral key for Alice) alice_e_keypair: P384KeyPair, // NIST P-384 key pair (Noise ephemeral key for Alice)
alice_e1_keypair: Option<pqc_kyber::Keypair>, // Kyber1024 key pair (agreement result mixed post-Noise) alice_e1_keypair: Option<pqc_kyber::Keypair>, // Kyber1024 key pair (agreement result mixed post-Noise)
} }
@ -1358,10 +1362,10 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
current_time: i64, current_time: i64,
) -> Result<Box<EphemeralOffer>, Error> { ) -> Result<Box<EphemeralOffer>, Error> {
// Generate a NIST P-384 pair. // Generate a NIST P-384 pair.
let alice_e0_keypair = P384KeyPair::generate(); let alice_e_keypair = P384KeyPair::generate();
// Perform key agreement with the other side's static P-384 public key. // Perform key agreement with the other side's static P-384 public key.
let e0s = alice_e0_keypair.agree(bob_s_public_p384).ok_or(Error::InvalidPacket)?; let noise_es = alice_e_keypair.agree(bob_s_public_p384).ok_or(Error::InvalidPacket)?;
// Generate a Kyber1024 pair if enabled. // Generate a Kyber1024 pair if enabled.
let alice_e1_keypair = if JEDI { let alice_e1_keypair = if JEDI {
@ -1387,7 +1391,7 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
let mut p = &mut packet_buf[HEADER_SIZE..]; let mut p = &mut packet_buf[HEADER_SIZE..];
p.write_all(&[SESSION_PROTOCOL_VERSION])?; p.write_all(&[SESSION_PROTOCOL_VERSION])?;
p.write_all(alice_e0_keypair.public_key_bytes())?; p.write_all(alice_e_keypair.public_key_bytes())?;
p.write_all(&id)?; p.write_all(&id)?;
p.write_all(&alice_session_id.0.to_le_bytes()[..SESSION_ID_SIZE])?; p.write_all(&alice_session_id.0.to_le_bytes()[..SESSION_ID_SIZE])?;
@ -1412,9 +1416,9 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
}; };
// Create ephemeral agreement secret. // Create ephemeral agreement secret.
let key = Secret(hmac_sha512( let es_key = Secret(hmac_sha512(
&hmac_sha512(&INITIAL_KEY, alice_e0_keypair.public_key_bytes()), &hmac_sha512(&INITIAL_KEY, alice_e_keypair.public_key_bytes()),
e0s.as_bytes(), noise_es.as_bytes(),
)); ));
let bob_session_id = bob_session_id.unwrap_or(SessionId::NIL); let bob_session_id = bob_session_id.unwrap_or(SessionId::NIL);
@ -1425,7 +1429,7 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
// Encrypt packet and attach AES-GCM tag. // Encrypt packet and attach AES-GCM tag.
let gcm_tag = { let gcm_tag = {
let mut c = AesGcm::new( let mut c = AesGcm::new(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<AES_KEY_SIZE>(), kbkdf512(es_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<AES_KEY_SIZE>(),
true, true,
); );
c.reset_init_gcm(canonical_header.as_bytes()); c.reset_init_gcm(canonical_header.as_bytes());
@ -1436,11 +1440,12 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
packet_len += AES_GCM_TAG_SIZE; packet_len += AES_GCM_TAG_SIZE;
// Mix in static secret. // Mix in static secret.
let key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes())); let ss_key = Secret(hmac_sha512(es_key.as_bytes(), ss.as_bytes()));
drop(es_key);
// HMAC packet using static + ephemeral key. // HMAC packet using static + ephemeral key.
let hmac = hmac_sha384_2( let hmac = hmac_sha384_2(
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), kbkdf512(ss_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
canonical_header.as_bytes(), canonical_header.as_bytes(),
&packet_buf[HEADER_SIZE..packet_len], &packet_buf[HEADER_SIZE..packet_len],
); );
@ -1468,8 +1473,8 @@ fn send_ephemeral_offer<SendFunction: FnMut(&mut [u8])>(
creation_time: current_time, creation_time: current_time,
ratchet_count, ratchet_count,
ratchet_key, ratchet_key,
key, ss_key,
alice_e0_keypair, alice_e_keypair,
alice_e1_keypair, alice_e1_keypair,
})) }))
} }
@ -1807,15 +1812,15 @@ mod tests {
} }
} }
impl Host for Box<TestHost> { impl ApplicationLayer for Box<TestHost> {
type AssociatedObject = u32; type SessionUserData = u32;
type SessionRef = Arc<Session<Box<TestHost>>>; type SessionRef = Arc<Session<Box<TestHost>>>;
type IncomingPacketBuffer = Vec<u8>; type IncomingPacketBuffer = Vec<u8>;
type RemoteAddress = u32; type RemoteAddress = u32;
const REKEY_RATE_LIMIT_MS: i64 = 0; const REKEY_RATE_LIMIT_MS: i64 = 0;
fn get_local_s_public(&self) -> &[u8] { fn get_local_s_public_raw(&self) -> &[u8] {
self.local_s.public_key_bytes() self.local_s.public_key_bytes()
} }
@ -1823,15 +1828,15 @@ mod tests {
&self.local_s_hash &self.local_s_hash
} }
fn get_local_s_keypair_p384(&self) -> &P384KeyPair { fn get_local_s_keypair(&self) -> &P384KeyPair {
&self.local_s &self.local_s
} }
fn extract_p384_static(static_public: &[u8]) -> Option<P384PublicKey> { fn extract_s_public_from_raw(static_public: &[u8]) -> Option<P384PublicKey> {
P384PublicKey::from_bytes(static_public) P384PublicKey::from_bytes(static_public)
} }
fn session_lookup(&self, local_session_id: SessionId) -> Option<Self::SessionRef> { fn lookup_session(&self, local_session_id: SessionId) -> Option<Self::SessionRef> {
self.session.lock().unwrap().as_ref().and_then(|s| { self.session.lock().unwrap().as_ref().and_then(|s| {
if s.id == local_session_id { if s.id == local_session_id {
Some(s.clone()) Some(s.clone())
@ -1841,7 +1846,7 @@ mod tests {
}) })
} }
fn check_new_session_attempt(&self, _: &ReceiveContext<Self>, _: &Self::RemoteAddress) -> bool { fn check_new_session(&self, _: &ReceiveContext<Self>, _: &Self::RemoteAddress) -> bool {
true true
} }
@ -1851,7 +1856,7 @@ mod tests {
_: &u32, _: &u32,
_: &[u8], _: &[u8],
_: &[u8], _: &[u8],
) -> Option<(SessionId, Secret<64>, Self::AssociatedObject)> { ) -> Option<(SessionId, Secret<64>, Self::SessionUserData)> {
loop { loop {
let mut new_id = self.session_id_counter.lock().unwrap(); let mut new_id = self.session_id_counter.lock().unwrap();
*new_id += 1; *new_id += 1;