Some more ZSSP work and a VDF we may use.

This commit is contained in:
Adam Ierymenko 2023-02-23 18:03:44 -05:00
commit 0b74ef6f3c
4 changed files with 189 additions and 77 deletions

View file

@ -3,6 +3,7 @@
pub mod aes; pub mod aes;
pub mod aes_gmac_siv; pub mod aes_gmac_siv;
pub mod hash; pub mod hash;
pub mod mimcvdf;
pub mod p384; pub mod p384;
pub mod poly1305; pub mod poly1305;
pub mod random; pub mod random;

148
crypto/src/mimcvdf.rs Normal file
View file

@ -0,0 +1,148 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c) ZeroTier, Inc.
* https://www.zerotier.com/
*/
/*
* https://eprint.iacr.org/2016/492.pdf
* https://vitalik.ca/general/2018/07/21/starks_part_3.html
*/
// 2^127 - 39
const PRIME: u128 = 170141183460469231731687303715884105689;
// 2p-1/3
const PRIME_2P_MINUS_1_DIV_3: u128 = 113427455640312821154458202477256070459;
const K_COUNT_MASK: usize = 63;
const K: [u64; 64] = [
0x921cdfd99022340f,
0xe7c65f78c70afaa8,
0x72793744494c4fda,
0x67759e2688bc9c0a,
0x7681a224661f0ac0,
0xa7b81b099925a2bf,
0x16d43792e66b030a,
0x841bd90742d26ee9,
0xb1346ec08db97053,
0xd044229c1173d972,
0xf4813498dfdead0e,
0xe46dca4c237d2c28,
0xac64872778089599,
0x67be75af74416e74,
0xb9dec3aefd3ae012,
0xf0497147953c4276,
0xf6ac07fd3944177d,
0xccf1c28813eb589b,
0x49abb5e2b0bff5bd,
0xd5c15eeb39587d69,
0x9c6ff50ee6898649,
0x763f3b25524a0fbf,
0xa6029c37f715c02c,
0xe458a5902b2b5629,
0x8e4d6be6a1ba32c5,
0x052aba0b61738f20,
0xc18a6901fa026b12,
0x137df11cf1dbe811,
0x5da0310e419be602,
0xc66ddec578f52891,
0xe4eae4efc0f0d54f,
0xf9d488269f118012,
0xcf9b5108f66e77d1,
0x443ba29939f5a657,
0xa4e4b7d28c51e5c2,
0xe030d1772f112c01,
0xe136f0cf8da5e172,
0x3e9ee638f9663dc2,
0xbc5c1db73e639dfd,
0xa9fbbaa873fedf73,
0xffb2a5247d10ab8f,
0x06e6f3b5ae4b67ac,
0x475e7d427d331282,
0xcac6237c40a9d653,
0xe9a15c1d177beefa,
0xa14ef2111c2175a3,
0x8427d4b68982fc21,
0x12171e2a55d43343,
0x37715fdea87a0a60,
0x24bc5d28cff8ecad,
0x92276e4118304e62,
0x824b66792f58dd45,
0xe43973cf253b6947,
0xd0db2c5a2a4f064d,
0x734cdb241520ad04,
0xcec4f2ce5013069e,
0x2741c83c07bbf9e0,
0x284be707dcbda1a4,
0xd602f3d8545799b2,
0xea3977f56573b4d2,
0x0723fda64d57d0c6,
0x04dc344d0dde863a,
0x7584143462914be4,
0x111307f7823dfcc6,
];
fn mulmod<const M: u128>(mut a: u128, mut b: u128) -> u128 {
let mut res: u128 = 0;
a %= M;
loop {
if (b & 1) != 0 {
res = res.wrapping_add(a) % M;
}
b = b.wrapping_shr(1);
if b != 0 {
a = a.wrapping_shl(1) % M;
} else {
return res;
}
}
}
#[inline(always)]
fn powmod<const M: u128>(mut base: u128, mut exp: u128) -> u128 {
let mut res: u128 = 1;
loop {
if (exp & 1) != 0 {
res = mulmod::<M>(base, res);
}
exp = exp.wrapping_shr(1);
if exp != 0 {
base = mulmod::<M>(base, base);
} else {
return res;
}
}
}
pub fn delay(mut input: u128, rounds: usize) -> u128 {
debug_assert!(rounds > 0);
input %= PRIME;
for r in 1..(rounds + 1) {
input = powmod::<PRIME>(input ^ (K[(rounds - r) & K_COUNT_MASK] as u128), PRIME_2P_MINUS_1_DIV_3);
}
input
}
pub fn verify(mut proof: u128, expected: u128, rounds: usize) -> bool {
debug_assert!(rounds > 0);
for r in 0..rounds {
proof = mulmod::<PRIME>(proof, mulmod::<PRIME>(proof, proof)) ^ (K[r & K_COUNT_MASK] as u128);
}
proof == (expected % PRIME)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn delay_and_verify() {
for i in 1..5 {
let input = (crate::random::xorshift64_random() as u128).wrapping_mul(crate::random::xorshift64_random() as u128);
let proof = delay(input, i * 3);
assert!(verify(proof, input, i * 3));
}
}
}

View file

@ -22,8 +22,6 @@ pub(crate) const SESSION_PROTOCOL_VERSION: u8 = 0x00;
pub(crate) const COUNTER_WINDOW_MAX_OOO: usize = 16; pub(crate) const COUNTER_WINDOW_MAX_OOO: usize = 16;
pub(crate) const COUNTER_WINDOW_MAX_SKIP_AHEAD: u64 = 16777216; pub(crate) const COUNTER_WINDOW_MAX_SKIP_AHEAD: u64 = 16777216;
pub(crate) const NOISE_MAX_HANDSHAKE_PACKET_SIZE: usize = 2048;
pub(crate) const PACKET_TYPE_DATA: u8 = 0; pub(crate) const PACKET_TYPE_DATA: u8 = 0;
pub(crate) const PACKET_TYPE_ALICE_NOISE_XK_INIT: u8 = 1; pub(crate) const PACKET_TYPE_ALICE_NOISE_XK_INIT: u8 = 1;
pub(crate) const PACKET_TYPE_BOB_NOISE_XK_ACK: u8 = 2; pub(crate) const PACKET_TYPE_BOB_NOISE_XK_ACK: u8 = 2;
@ -41,7 +39,8 @@ pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB: u8 = b'A'; // AES-G
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction
pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63 pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63
pub(crate) const KEY_EXCHANGE_MAX_FRAGMENTS: usize = 8; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc. pub(crate) const MAX_NOISE_HANDSHAKE_FRAGMENTS: usize = 16; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc.
pub(crate) const MAX_NOISE_HANDSHAKE_SIZE: usize = MAX_NOISE_HANDSHAKE_FRAGMENTS * MIN_TRANSPORT_MTU;
pub(crate) const AES_KEY_SIZE: usize = 32; pub(crate) const AES_KEY_SIZE: usize = 32;
pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16; pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16;
@ -80,13 +79,12 @@ pub(crate) struct BobNoiseXKAck {
pub bob_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES], pub bob_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES],
// -- end encrypted sectiion // -- end encrypted sectiion
pub hmac_es_ee: [u8; HMAC_SHA384_SIZE], pub hmac_es_ee: [u8; HMAC_SHA384_SIZE],
pub hmac_es_ee_se_hk_psk: [u8; HMAC_SHA384_SIZE],
} }
impl BobNoiseXKAck { impl BobNoiseXKAck {
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE; pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_CIPHERTEXTBYTES; pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_CIPHERTEXTBYTES;
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE + HMAC_SHA384_SIZE; pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
} }
/* /*

View file

@ -23,7 +23,6 @@ use zerotier_crypto::{random, secure_eq};
use zerotier_utils::gatherarray::GatherArray; use zerotier_utils::gatherarray::GatherArray;
use zerotier_utils::memory; use zerotier_utils::memory;
use zerotier_utils::ringbuffermap::RingBufferMap; use zerotier_utils::ringbuffermap::RingBufferMap;
use zerotier_utils::unlikely_branch;
use pqc_kyber::{KYBER_SECRETKEYBYTES, KYBER_SSBYTES}; use pqc_kyber::{KYBER_SECRETKEYBYTES, KYBER_SSBYTES};
@ -38,7 +37,8 @@ use crate::sessionid::SessionId;
/// Each application using ZSSP must create an instance of this to own sessions and /// Each application using ZSSP must create an instance of this to own sessions and
/// defragment incoming packets that are not yet associated with a session. /// defragment incoming packets that are not yet associated with a session.
pub struct Context<Application: ApplicationLayer> { pub struct Context<Application: ApplicationLayer> {
initial_offer_defrag: Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 1024>>, initial_offer_defrag:
Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, MAX_NOISE_HANDSHAKE_FRAGMENTS>, 1024, 1024>>,
sessions: RwLock<SessionMaps<Application>>, sessions: RwLock<SessionMaps<Application>>,
} }
@ -87,6 +87,7 @@ struct SessionMaps<Application: ApplicationLayer> {
incomplete: HashMap<SessionId, Arc<NoiseXKIncoming>>, incomplete: HashMap<SessionId, Arc<NoiseXKIncoming>>,
} }
/// State for an incoming incomplete Noise_XK session that isn't fully negotiated yet.
struct NoiseXKIncoming { struct NoiseXKIncoming {
timestamp: i64, timestamp: i64,
alice_session_id: SessionId, alice_session_id: SessionId,
@ -308,7 +309,6 @@ impl<Application: ApplicationLayer> Context<Application> {
) -> Result<ReceiveResult<'b, Application>, Error> { ) -> Result<ReceiveResult<'b, Application>, Error> {
let incoming_packet: &mut [u8] = incoming_packet_buf.as_mut(); let incoming_packet: &mut [u8] = incoming_packet_buf.as_mut();
if incoming_packet.len() < MIN_PACKET_SIZE { if incoming_packet.len() < MIN_PACKET_SIZE {
unlikely_branch();
return Err(Error::InvalidPacket); return Err(Error::InvalidPacket);
} }
@ -345,7 +345,6 @@ impl<Application: ApplicationLayer> Context<Application> {
return Ok(ReceiveResult::Ok); return Ok(ReceiveResult::Ok);
} }
} else { } else {
unlikely_branch();
return Err(Error::InvalidPacket); return Err(Error::InvalidPacket);
} }
} else { } else {
@ -365,11 +364,9 @@ impl<Application: ApplicationLayer> Context<Application> {
); );
} }
} else { } else {
unlikely_branch();
return Ok(ReceiveResult::Ignored); return Ok(ReceiveResult::Ignored);
} }
} else { } else {
unlikely_branch();
if let Some(p) = self.sessions.read().unwrap().incomplete.get(&local_session_id).cloned() { if let Some(p) = self.sessions.read().unwrap().incomplete.get(&local_session_id).cloned() {
Aes::new(p.header_check_cipher_key.as_bytes()) Aes::new(p.header_check_cipher_key.as_bytes())
.decrypt_block_in_place(&mut incoming_packet[HEADER_CHECK_ENCRYPT_START..HEADER_CHECK_ENCRYPT_END]); .decrypt_block_in_place(&mut incoming_packet[HEADER_CHECK_ENCRYPT_START..HEADER_CHECK_ENCRYPT_END]);
@ -378,12 +375,12 @@ impl<Application: ApplicationLayer> Context<Application> {
return Err(Error::UnknownLocalSessionId(local_session_id)); return Err(Error::UnknownLocalSessionId(local_session_id));
} }
} }
} else {
unlikely_branch();
} }
let (key_index, packet_type, fragment_count, fragment_no, counter) = parse_packet_header(&incoming_packet); // If we make it here the packet is not associated with a session or is associated with an
// incomplete session (Noise_XK mid-negotiation).
let (key_index, packet_type, fragment_count, fragment_no, counter) = parse_packet_header(&incoming_packet);
if fragment_count > 1 { if fragment_count > 1 {
let mut defrag = self.initial_offer_defrag.lock().unwrap(); let mut defrag = self.initial_offer_defrag.lock().unwrap();
let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count)); let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count));
@ -424,10 +421,6 @@ impl<Application: ApplicationLayer> Context<Application> {
return Ok(ReceiveResult::Ok); return Ok(ReceiveResult::Ok);
} }
/// Called internally when all fragments of a packet are received.
///
/// NOTE: header check codes will already have been validated on receipt of each fragment. AEAD authentication
/// and decryption has NOT yet been performed, and is done here.
fn receive_complete< fn receive_complete<
'b, 'b,
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]), SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
@ -466,7 +459,6 @@ impl<Application: ApplicationLayer> Context<Application> {
let current_frag_data_start = data_len; let current_frag_data_start = data_len;
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();
session_key.return_receive_cipher(c); session_key.return_receive_cipher(c);
return Err(Error::DataBufferTooSmall); return Err(Error::DataBufferTooSmall);
} }
@ -477,12 +469,10 @@ impl<Application: ApplicationLayer> Context<Application> {
let current_frag_data_start = data_len; let current_frag_data_start = data_len;
let last_fragment = fragments.last().unwrap().as_ref(); let last_fragment = fragments.last().unwrap().as_ref();
if last_fragment.len() < (HEADER_SIZE + AES_GCM_TAG_SIZE) { if last_fragment.len() < (HEADER_SIZE + AES_GCM_TAG_SIZE) {
unlikely_branch();
return Err(Error::InvalidPacket); return Err(Error::InvalidPacket);
} }
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();
session_key.return_receive_cipher(c); session_key.return_receive_cipher(c);
return Err(Error::DataBufferTooSmall); return Err(Error::DataBufferTooSmall);
} }
@ -492,8 +482,7 @@ impl<Application: ApplicationLayer> Context<Application> {
&mut data_buf[current_frag_data_start..data_len], &mut data_buf[current_frag_data_start..data_len],
); );
let gcm_tag = &last_fragment[payload_end..]; let aead_authentication_ok = c.finish_decrypt(&last_fragment[payload_end..]);
let aead_authentication_ok = c.finish_decrypt(gcm_tag);
session_key.return_receive_cipher(c); session_key.return_receive_cipher(c);
if aead_authentication_ok { if aead_authentication_ok {
@ -504,7 +493,6 @@ impl<Application: ApplicationLayer> Context<Application> {
if session_key.confirmed { if session_key.confirmed {
drop(state); drop(state);
} else { } else {
unlikely_branch();
let key_created_at_counter = session_key.created_at_counter; let key_created_at_counter = session_key.created_at_counter;
drop(state); drop(state);
@ -523,21 +511,18 @@ impl<Application: ApplicationLayer> Context<Application> {
return Ok(ReceiveResult::OkData(session, &mut data_buf[..data_len])); return Ok(ReceiveResult::OkData(session, &mut data_buf[..data_len]));
} else { } else {
unlikely_branch();
return Ok(ReceiveResult::Ignored); return Ok(ReceiveResult::Ignored);
} }
} }
} }
return Err(Error::FailedAuthentication); return Err(Error::FailedAuthentication);
} else { } else {
unlikely_branch();
return Err(Error::SessionNotEstablished); return Err(Error::SessionNotEstablished);
} }
} else { } else {
unlikely_branch(); // For Noise setup/KEX packets go ahead and pre-assemble all fragments to simplify the code below.
let mut pkt_assembly_buffer = [0u8; MAX_NOISE_HANDSHAKE_SIZE];
// For KEX packets go ahead and pre-assemble all fragments to simplify the code below.
let mut pkt_assembly_buffer = [0u8; NOISE_MAX_HANDSHAKE_PACKET_SIZE];
let pkt_assembled_size = assemble_fragments_into::<Application>(fragments, &mut pkt_assembly_buffer)?; let pkt_assembled_size = assemble_fragments_into::<Application>(fragments, &mut pkt_assembly_buffer)?;
if pkt_assembled_size < MIN_PACKET_SIZE { if pkt_assembled_size < MIN_PACKET_SIZE {
return Err(Error::InvalidPacket); return Err(Error::InvalidPacket);
@ -572,7 +557,6 @@ impl<Application: ApplicationLayer> Context<Application> {
} }
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?; let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
let alice_noise_e = P384PublicKey::from_bytes(&pkt.alice_noise_e).ok_or(Error::FailedAuthentication)?; let alice_noise_e = P384PublicKey::from_bytes(&pkt.alice_noise_e).ok_or(Error::FailedAuthentication)?;
let noise_es = app.get_local_s_keypair().agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?; let noise_es = app.get_local_s_keypair().agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?;
@ -596,8 +580,8 @@ impl<Application: ApplicationLayer> Context<Application> {
let mut ctr = AesCtr::new(kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION>(noise_es.as_bytes()).as_bytes()); let mut ctr = AesCtr::new(kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION>(noise_es.as_bytes()).as_bytes());
ctr.reset_set_iv(&SHA384::hash(&pkt.alice_noise_e)[..AES_CTR_NONCE_SIZE]); ctr.reset_set_iv(&SHA384::hash(&pkt.alice_noise_e)[..AES_CTR_NONCE_SIZE]);
ctr.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]); ctr.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
let alice_session_id = SessionId::new_from_bytes(&pkt.alice_session_id).ok_or(Error::InvalidPacket)?; let alice_session_id = SessionId::new_from_bytes(&pkt.alice_session_id).ok_or(Error::InvalidPacket)?;
// Create Bob's ephemeral keys and derive noise_es_ee by agreeing with Alice's. Also create // Create Bob's ephemeral keys and derive noise_es_ee by agreeing with Alice's. Also create
@ -639,7 +623,7 @@ impl<Application: ApplicationLayer> Context<Application> {
newest_id = Some(*id); newest_id = Some(*id);
} }
} }
sessions.incomplete.remove(newest_id.as_ref().unwrap()); let _ = sessions.incomplete.remove(newest_id.as_ref().unwrap());
} }
sessions.incomplete.insert( sessions.incomplete.insert(
@ -672,7 +656,7 @@ impl<Application: ApplicationLayer> Context<Application> {
ctr.reset_set_iv(&bob_noise_e[P384_PUBLIC_KEY_SIZE - AES_CTR_NONCE_SIZE..]); ctr.reset_set_iv(&bob_noise_e[P384_PUBLIC_KEY_SIZE - AES_CTR_NONCE_SIZE..]);
ctr.crypt_in_place(&mut reply_buffer[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]); ctr.crypt_in_place(&mut reply_buffer[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
// Add HMAC-SHA384 to reply packet, allowing Alice to derive noise_es_ee and authenticate. // Add HMAC-SHA384 to reply packet.
let reply_hmac = hmac_sha384_2( let reply_hmac = hmac_sha384_2(
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION>(noise_es_ee.as_bytes()).as_bytes(), kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION>(noise_es_ee.as_bytes()).as_bytes(),
&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1), &create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1),
@ -763,28 +747,17 @@ impl<Application: ApplicationLayer> Context<Application> {
noise_es_ee_se_hk_psk.as_bytes(), noise_es_ee_se_hk_psk.as_bytes(),
); );
// Authenticate entire key exchange.
if !secure_eq(
&pkt.hmac_es_ee_se_hk_psk,
&hmac_sha384_2(
noise_es_ee_se_hk_psk_hmac_key.as_bytes(),
&message_nonce,
&pkt_assembled[HEADER_SIZE..BobNoiseXKAck::AUTH_START + HMAC_SHA384_SIZE],
),
) {
return Err(Error::FailedAuthentication);
}
let reply_counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?; let reply_counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?;
let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, reply_counter.get()); let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, reply_counter.get());
// Create reply informing Bob of our static identity now that we've verified Bob and set // Create reply informing Bob of our static identity now that we've verified Bob and set
// up forward secrecy. Also return Bob's opaque note. // up forward secrecy. Also return Bob's opaque note.
let mut reply_buffer = [0u8; NOISE_MAX_HANDSHAKE_PACKET_SIZE]; let mut reply_buffer = [0u8; MAX_NOISE_HANDSHAKE_SIZE];
reply_buffer[HEADER_SIZE] = SESSION_PROTOCOL_VERSION; reply_buffer[HEADER_SIZE] = SESSION_PROTOCOL_VERSION;
let mut reply_len = HEADER_SIZE + 1; let mut reply_len = HEADER_SIZE + 1;
let mut reply_buffer_append = |b: &[u8]| { let mut reply_buffer_append = |b: &[u8]| {
let reply_len_new = reply_len + b.len(); let reply_len_new = reply_len + b.len();
debug_assert!(reply_len_new <= MAX_NOISE_HANDSHAKE_SIZE);
reply_buffer[reply_len..reply_len_new].copy_from_slice(b); reply_buffer[reply_len..reply_len_new].copy_from_slice(b);
reply_len = reply_len_new; reply_len = reply_len_new;
}; };
@ -1040,7 +1013,7 @@ impl<Application: ApplicationLayer> Session<Application> {
u64::from(remote_session_id), u64::from(remote_session_id),
state.current_key, state.current_key,
counter, counter,
)?; );
c.crypt(&data[..chunk_size], &mut mtu_sized_buffer[HEADER_SIZE..fragment_size]); c.crypt(&data[..chunk_size], &mut mtu_sized_buffer[HEADER_SIZE..fragment_size]);
data = &data[chunk_size..]; data = &data[chunk_size..];
if fragment_no == last_fragment_no { if fragment_no == last_fragment_no {
@ -1058,8 +1031,6 @@ impl<Application: ApplicationLayer> Session<Application> {
session_key.return_send_cipher(c); session_key.return_send_cipher(c);
return Ok(()); return Ok(());
} else {
unlikely_branch();
} }
} }
return Err(Error::SessionNotEstablished); return Err(Error::SessionNotEstablished);
@ -1101,34 +1072,30 @@ fn set_packet_header(
recipient_session_id: u64, recipient_session_id: u64,
key_index: usize, key_index: usize,
counter: u64, counter: u64,
) -> Result<(), Error> { ) {
debug_assert!(packet.len() >= MIN_PACKET_SIZE); debug_assert!(packet.len() >= MIN_PACKET_SIZE);
debug_assert!(fragment_count > 0); debug_assert!(fragment_count > 0);
debug_assert!(fragment_count <= MAX_FRAGMENTS);
debug_assert!(fragment_no < MAX_FRAGMENTS); debug_assert!(fragment_no < MAX_FRAGMENTS);
debug_assert!(packet_type <= 0x0f); // packet type is 4 bits debug_assert!(packet_type <= 0x0f); // packet type is 4 bits
if fragment_count <= MAX_FRAGMENTS {
// [0-47] recipient session ID // [0-47] recipient session ID
// -- start of header check cipher single block encrypt -- // -- start of header check cipher single block encrypt --
// [48-48] key index (least significant bit) // [48-48] key index (least significant bit)
// [49-51] packet type (0-15) // [49-51] packet type (0-15)
// [52-57] fragment count (1..64 - 1, so 0 means 1 fragment) // [52-57] fragment count (1..64 - 1, so 0 means 1 fragment)
// [58-63] fragment number (0..63) // [58-63] fragment number (0..63)
// [64-127] 64-bit counter // [64-127] 64-bit counter
memory::store_raw( memory::store_raw(
(u64::from(recipient_session_id) (u64::from(recipient_session_id)
| ((key_index & 1) as u64).wrapping_shl(48) | ((key_index & 1) as u64).wrapping_shl(48)
| (packet_type as u64).wrapping_shl(49) | (packet_type as u64).wrapping_shl(49)
| ((fragment_count - 1) as u64).wrapping_shl(52) | ((fragment_count - 1) as u64).wrapping_shl(52)
| (fragment_no as u64).wrapping_shl(58)) | (fragment_no as u64).wrapping_shl(58))
.to_le(), .to_le(),
packet, packet,
); );
memory::store_raw(counter.to_le(), &mut packet[8..]); memory::store_raw(counter.to_le(), &mut packet[8..]);
Ok(())
} else {
unlikely_branch();
Err(Error::DataTooLarge)
}
} }
#[inline(always)] #[inline(always)]
@ -1187,7 +1154,7 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(
recipient_session_id, recipient_session_id,
key_index, key_index,
counter, counter,
)?; );
if let Some(hcc) = header_check_cipher { if let Some(hcc) = header_check_cipher {
hcc.encrypt_block_in_place(&mut fragment[6..22]); hcc.encrypt_block_in_place(&mut fragment[6..22]);
} }
@ -1232,8 +1199,6 @@ impl SessionKey {
.pop() .pop()
.unwrap_or_else(|| Box::new(AesGcm::new(self.send_key.as_bytes(), true)))) .unwrap_or_else(|| Box::new(AesGcm::new(self.send_key.as_bytes(), true))))
} else { } else {
unlikely_branch();
// Not only do we return an error, but we also destroy the key. // Not only do we return an error, but we also destroy the key.
let mut scp = self.send_cipher_pool.lock().unwrap(); let mut scp = self.send_cipher_pool.lock().unwrap();
scp.clear(); scp.clear();