mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-22 22:33:58 -07:00
Some more ZSSP work and a VDF we may use.
This commit is contained in:
parent
df39130082
commit
0b74ef6f3c
4 changed files with 189 additions and 77 deletions
|
@ -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
148
crypto/src/mimcvdf.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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,12 +1072,13 @@ 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)
|
||||||
|
@ -1124,11 +1096,6 @@ fn set_packet_header(
|
||||||
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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue