mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 21:43:44 +02:00
Prep for new controller, also reorg header fields in Noise session and move some functions around.
This commit is contained in:
parent
f153d43797
commit
a723403fe9
5 changed files with 384 additions and 414 deletions
|
@ -57,6 +57,9 @@ ignored when analyzing the "real" security of the protocol.
|
||||||
/// Minimum packet size / minimum size for work buffers.
|
/// Minimum packet size / minimum size for work buffers.
|
||||||
pub const MIN_BUFFER_SIZE: usize = 1400;
|
pub const MIN_BUFFER_SIZE: usize = 1400;
|
||||||
|
|
||||||
|
/// Minimum possible packet size.
|
||||||
|
pub const MIN_PACKET_SIZE: usize = HEADER_SIZE + 1 + AES_GCM_TAG_SIZE;
|
||||||
|
|
||||||
/// Start attempting to rekey after a key has been used to send packets this many times.
|
/// Start attempting to rekey after a key has been used to send packets this many times.
|
||||||
const REKEY_AFTER_USES: u64 = 536870912;
|
const REKEY_AFTER_USES: u64 = 536870912;
|
||||||
|
|
||||||
|
@ -86,9 +89,7 @@ const E1_TYPE_NONE: u8 = 0;
|
||||||
/// Secondary (hybrid) ephemeral key is Kyber512
|
/// Secondary (hybrid) ephemeral key is Kyber512
|
||||||
const E1_TYPE_KYBER512: u8 = 1;
|
const E1_TYPE_KYBER512: u8 = 1;
|
||||||
|
|
||||||
/// Header size; header is: [4] counter | [6] destination session ID | [1] type
|
|
||||||
const HEADER_SIZE: usize = 11;
|
const HEADER_SIZE: usize = 11;
|
||||||
|
|
||||||
const AES_GCM_TAG_SIZE: usize = 16;
|
const AES_GCM_TAG_SIZE: usize = 16;
|
||||||
const HMAC_SIZE: usize = 48; // HMAC-SHA384
|
const HMAC_SIZE: usize = 48; // HMAC-SHA384
|
||||||
const SESSION_ID_SIZE: usize = 6;
|
const SESSION_ID_SIZE: usize = 6;
|
||||||
|
@ -128,6 +129,9 @@ pub enum Error {
|
||||||
|
|
||||||
/// Attempt to send using session without established key.
|
/// Attempt to send using session without established key.
|
||||||
SessionNotEstablished,
|
SessionNotEstablished,
|
||||||
|
|
||||||
|
/// Packet ignored by rate limiter.
|
||||||
|
RateLimited,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
|
@ -140,6 +144,7 @@ impl std::fmt::Display for Error {
|
||||||
Self::NewSessionRejected => f.write_str("NewSessionRejected"),
|
Self::NewSessionRejected => f.write_str("NewSessionRejected"),
|
||||||
Self::MaxKeyLifetimeExceeded => f.write_str("MaxKeyLifetimeExceeded"),
|
Self::MaxKeyLifetimeExceeded => f.write_str("MaxKeyLifetimeExceeded"),
|
||||||
Self::SessionNotEstablished => f.write_str("SessionNotEstablished"),
|
Self::SessionNotEstablished => f.write_str("SessionNotEstablished"),
|
||||||
|
Self::RateLimited => f.write_str("RateLimited"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,21 +247,61 @@ pub struct Session<O> {
|
||||||
psk: Secret<64>,
|
psk: Secret<64>,
|
||||||
ss: Secret<48>,
|
ss: Secret<48>,
|
||||||
outgoing_obfuscator: Obfuscator,
|
outgoing_obfuscator: Obfuscator,
|
||||||
state: RwLock<State>,
|
state: RwLock<MutableState>,
|
||||||
remote_s_public_p384: [u8; P384_PUBLIC_KEY_SIZE],
|
remote_s_public_p384: [u8; P384_PUBLIC_KEY_SIZE],
|
||||||
|
|
||||||
/// Arbitrary object associated with this session
|
/// Arbitrary object associated with this session
|
||||||
pub associated_object: O,
|
pub associated_object: O,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable inner state of Session (except counter, which is atomic to avoid locks on packet sends)
|
struct MutableState {
|
||||||
struct State {
|
|
||||||
remote_session_id: Option<SessionId>,
|
remote_session_id: Option<SessionId>,
|
||||||
keys: [Option<SessionKey>; 2], // current, next
|
keys: [Option<SessionKey>; 2], // current, next
|
||||||
offer: Option<EphemeralOffer>,
|
offer: Option<EphemeralOffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O> Session<O> {
|
impl<O> Session<O> {
|
||||||
|
/// Create a new session and return this plus an outgoing packet to send to the other end.
|
||||||
|
pub fn new<'a, const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE: usize>(
|
||||||
|
buffer: &'a mut [u8; MAX_PACKET_SIZE],
|
||||||
|
local_session_id: SessionId,
|
||||||
|
local_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
||||||
|
local_s_keypair_p384: &P384KeyPair,
|
||||||
|
remote_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
||||||
|
remote_s_public_p384: &P384PublicKey,
|
||||||
|
psk: &Secret<64>,
|
||||||
|
associated_object: O,
|
||||||
|
current_time: i64,
|
||||||
|
jedi: bool,
|
||||||
|
) -> Result<(Self, &'a [u8]), Error> {
|
||||||
|
debug_assert!(MAX_PACKET_SIZE >= MIN_BUFFER_SIZE);
|
||||||
|
let counter = Counter::new();
|
||||||
|
if let Some(ss) = local_s_keypair_p384.agree(remote_s_public_p384) {
|
||||||
|
let outgoing_obfuscator = Obfuscator::new(remote_s_public);
|
||||||
|
if let Some((offer, psize)) = EphemeralOffer::create_alice_offer(buffer, counter.next(), local_session_id, None, local_s_public, remote_s_public_p384, &ss, &outgoing_obfuscator, current_time, jedi) {
|
||||||
|
return Ok((
|
||||||
|
Session::<O> {
|
||||||
|
id: local_session_id,
|
||||||
|
send_counter: counter,
|
||||||
|
remote_s_public_hash: SHA384::hash(remote_s_public),
|
||||||
|
psk: psk.clone(),
|
||||||
|
ss,
|
||||||
|
outgoing_obfuscator,
|
||||||
|
state: RwLock::new(MutableState {
|
||||||
|
remote_session_id: None,
|
||||||
|
keys: [None, None],
|
||||||
|
offer: Some(offer),
|
||||||
|
}),
|
||||||
|
remote_s_public_p384: remote_s_public_p384.as_bytes().clone(),
|
||||||
|
associated_object,
|
||||||
|
},
|
||||||
|
&buffer[..psize],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(Error::InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether this session should initiate a re-key, returning a packet to send if true.
|
/// Check whether this session should initiate a re-key, returning a packet to send if true.
|
||||||
///
|
///
|
||||||
/// This must be checked often enough to ensure that the hard key usage limit is not reached, which in the
|
/// This must be checked often enough to ensure that the hard key usage limit is not reached, which in the
|
||||||
|
@ -304,62 +349,19 @@ impl<O> Session<O> {
|
||||||
Err(Error::SessionNotEstablished)
|
Err(Error::SessionNotEstablished)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new session and return this plus an outgoing packet to send to the other end.
|
/// Receive a packet from the network and take the appropriate action.
|
||||||
pub fn new_session<'a, O, const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE: usize>(
|
///
|
||||||
buffer: &'a mut [u8; MAX_PACKET_SIZE],
|
/// Check ReceiveResult to see if it includes data or a reply packet.
|
||||||
local_session_id: SessionId,
|
pub fn receive<
|
||||||
local_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
|
||||||
local_s_keypair_p384: &P384KeyPair,
|
|
||||||
remote_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
|
||||||
remote_s_public_p384: &P384PublicKey,
|
|
||||||
psk: &Secret<64>,
|
|
||||||
associated_object: O,
|
|
||||||
current_time: i64,
|
|
||||||
jedi: bool,
|
|
||||||
) -> Result<(Session<O>, &'a [u8]), Error> {
|
|
||||||
debug_assert!(MAX_PACKET_SIZE >= MIN_BUFFER_SIZE);
|
|
||||||
let counter = Counter::new();
|
|
||||||
if let Some(ss) = local_s_keypair_p384.agree(remote_s_public_p384) {
|
|
||||||
let outgoing_obfuscator = Obfuscator::new(remote_s_public);
|
|
||||||
if let Some((offer, psize)) = EphemeralOffer::create_alice_offer(buffer, counter.next(), local_session_id, None, local_s_public, remote_s_public_p384, &ss, &outgoing_obfuscator, current_time, jedi) {
|
|
||||||
return Ok((
|
|
||||||
Session::<O> {
|
|
||||||
id: local_session_id,
|
|
||||||
send_counter: counter,
|
|
||||||
remote_s_public_hash: SHA384::hash(remote_s_public),
|
|
||||||
psk: psk.clone(),
|
|
||||||
ss,
|
|
||||||
outgoing_obfuscator,
|
|
||||||
state: RwLock::new(State {
|
|
||||||
remote_session_id: None,
|
|
||||||
keys: [None, None],
|
|
||||||
offer: Some(offer),
|
|
||||||
}),
|
|
||||||
remote_s_public_p384: remote_s_public_p384.as_bytes().clone(),
|
|
||||||
associated_object,
|
|
||||||
},
|
|
||||||
&buffer[..psize],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(Error::InvalidParameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive a packet from the network and take the appropriate action.
|
|
||||||
///
|
|
||||||
/// Check ReceiveResult to see if it includes data or a reply packet.
|
|
||||||
pub fn receive<
|
|
||||||
'a,
|
'a,
|
||||||
ExtractP384PublicKeyFunction: FnOnce(&[u8; STATIC_PUBLIC_SIZE]) -> Option<P384PublicKey>,
|
ExtractP384PublicKeyFunction: FnOnce(&[u8; STATIC_PUBLIC_SIZE]) -> Option<P384PublicKey>,
|
||||||
SessionLookupFunction: FnOnce(SessionId) -> Option<S>,
|
SessionLookupFunction: FnOnce(SessionId) -> Option<S>,
|
||||||
NewSessionAuthenticatorFunction: FnOnce(&[u8; STATIC_PUBLIC_SIZE]) -> Option<(SessionId, Secret<64>, O)>,
|
NewSessionAuthenticatorFunction: FnOnce(&[u8; STATIC_PUBLIC_SIZE]) -> Option<(SessionId, Secret<64>, O)>,
|
||||||
S: std::ops::Deref<Target = Session<O>>,
|
S: std::ops::Deref<Target = Session<O>>,
|
||||||
O,
|
|
||||||
const MAX_PACKET_SIZE: usize,
|
const MAX_PACKET_SIZE: usize,
|
||||||
const STATIC_PUBLIC_SIZE: usize,
|
const STATIC_PUBLIC_SIZE: usize,
|
||||||
>(
|
>(
|
||||||
incoming_packet: &[u8],
|
incoming_packet: &[u8],
|
||||||
buffer: &'a mut [u8; MAX_PACKET_SIZE],
|
buffer: &'a mut [u8; MAX_PACKET_SIZE],
|
||||||
local_s_keypair_p384: &P384KeyPair,
|
local_s_keypair_p384: &P384KeyPair,
|
||||||
|
@ -369,16 +371,16 @@ pub fn receive<
|
||||||
new_session_auth: NewSessionAuthenticatorFunction,
|
new_session_auth: NewSessionAuthenticatorFunction,
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
jedi: bool,
|
jedi: bool,
|
||||||
) -> Result<ReceiveResult<'a, O>, Error> {
|
) -> Result<ReceiveResult<'a, O>, Error> {
|
||||||
debug_assert!(MAX_PACKET_SIZE >= MIN_BUFFER_SIZE);
|
debug_assert!(MAX_PACKET_SIZE >= MIN_BUFFER_SIZE);
|
||||||
if incoming_packet.len() > MAX_PACKET_SIZE || incoming_packet.len() <= 16 {
|
if incoming_packet.len() > MAX_PACKET_SIZE || incoming_packet.len() <= MIN_PACKET_SIZE {
|
||||||
unlikely_branch();
|
unlikely_branch();
|
||||||
return Err(Error::InvalidPacket);
|
return Err(Error::InvalidPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
incoming_obfuscator.0.decrypt_block(&incoming_packet[0..16], &mut buffer[0..16]);
|
incoming_obfuscator.0.decrypt_block(&incoming_packet[..16], &mut buffer[..16]);
|
||||||
let local_session_id = SessionId::new_from_bytes(&buffer[4..10]);
|
let packet_type = buffer[0];
|
||||||
let packet_type = buffer[10];
|
let local_session_id = SessionId::new_from_bytes(&buffer[1..7]);
|
||||||
|
|
||||||
debug_assert_eq!(PACKET_TYPE_DATA, 0);
|
debug_assert_eq!(PACKET_TYPE_DATA, 0);
|
||||||
debug_assert_eq!(PACKET_TYPE_NOP, 1);
|
debug_assert_eq!(PACKET_TYPE_NOP, 1);
|
||||||
|
@ -407,7 +409,7 @@ pub fn receive<
|
||||||
}
|
}
|
||||||
|
|
||||||
if packet_type == PACKET_TYPE_DATA {
|
if packet_type == PACKET_TYPE_DATA {
|
||||||
return Ok(ReceiveResult::OkData(&buffer[HEADER_SIZE..data_len], u32::from_le_bytes(nonce[..4].try_into().unwrap())));
|
return Ok(ReceiveResult::OkData(&buffer[HEADER_SIZE..data_len], u32::from_le_bytes(nonce[7..11].try_into().unwrap())));
|
||||||
} else {
|
} else {
|
||||||
return Ok(ReceiveResult::Ok);
|
return Ok(ReceiveResult::Ok);
|
||||||
}
|
}
|
||||||
|
@ -451,6 +453,15 @@ pub fn receive<
|
||||||
PACKET_TYPE_KEY_OFFER => {
|
PACKET_TYPE_KEY_OFFER => {
|
||||||
// alice (remote) -> bob (local)
|
// alice (remote) -> bob (local)
|
||||||
|
|
||||||
|
// Check rate limit if this session is known.
|
||||||
|
if let Some(session) = session.as_ref() {
|
||||||
|
if let Some(offer) = session.state.read().offer.as_ref() {
|
||||||
|
if (current_time - offer.creation_time) < OFFER_RATE_LIMIT_MS {
|
||||||
|
return Err(Error::RateLimited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (alice_e0_public, e0s) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..HEADER_SIZE + P384_PUBLIC_KEY_SIZE])
|
let (alice_e0_public, e0s) = P384PublicKey::from_bytes(&buffer[HEADER_SIZE..HEADER_SIZE + P384_PUBLIC_KEY_SIZE])
|
||||||
.and_then(|pk| local_s_keypair_p384.agree(&pk).map(move |s| (pk, s)))
|
.and_then(|pk| local_s_keypair_p384.agree(&pk).map(move |s| (pk, s)))
|
||||||
.ok_or(Error::FailedAuthentication)?;
|
.ok_or(Error::FailedAuthentication)?;
|
||||||
|
@ -468,8 +479,8 @@ pub fn receive<
|
||||||
|
|
||||||
let (alice_session_id, alice_s_public, alice_e1_public) = parse_KEY_OFFER_after_header(&buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..payload_end])?;
|
let (alice_session_id, alice_s_public, alice_e1_public) = parse_KEY_OFFER_after_header(&buffer[(HEADER_SIZE + P384_PUBLIC_KEY_SIZE)..payload_end])?;
|
||||||
|
|
||||||
|
// Important! Check to make sure the caller's public identity matches the one for this session.
|
||||||
if let Some(session) = session.as_ref() {
|
if let Some(session) = session.as_ref() {
|
||||||
// If we already have a session for this session ID, make sure this is the same node calling.
|
|
||||||
if !session.remote_s_public_hash.eq(&SHA384::hash(&alice_s_public)) {
|
if !session.remote_s_public_hash.eq(&SHA384::hash(&alice_s_public)) {
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
}
|
}
|
||||||
|
@ -501,7 +512,7 @@ pub fn receive<
|
||||||
psk,
|
psk,
|
||||||
ss,
|
ss,
|
||||||
outgoing_obfuscator: Obfuscator::new(&alice_s_public),
|
outgoing_obfuscator: Obfuscator::new(&alice_s_public),
|
||||||
state: RwLock::new(State {
|
state: RwLock::new(MutableState {
|
||||||
remote_session_id: Some(alice_session_id),
|
remote_session_id: Some(alice_session_id),
|
||||||
keys: [None, None],
|
keys: [None, None],
|
||||||
offer: None,
|
offer: None,
|
||||||
|
@ -652,6 +663,7 @@ pub fn receive<
|
||||||
_ => return Err(Error::InvalidPacket),
|
_ => return Err(Error::InvalidPacket),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -845,10 +857,10 @@ impl SessionKey {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn assemble_and_armor_DATA<const MAX_PACKET_SIZE: usize>(buffer: &mut [u8; MAX_PACKET_SIZE], data: &[u8], packet_type: u8, remote_session_id: u64, counter: CounterValue, key: &SessionKey, outgoing_obfuscator: &Obfuscator) -> Result<usize, Error> {
|
fn assemble_and_armor_DATA<const MAX_PACKET_SIZE: usize>(buffer: &mut [u8; MAX_PACKET_SIZE], data: &[u8], packet_type: u8, remote_session_id: u64, counter: CounterValue, key: &SessionKey, outgoing_obfuscator: &Obfuscator) -> Result<usize, Error> {
|
||||||
buffer[0..4].copy_from_slice(&counter.to_bytes());
|
|
||||||
buffer[4..10].copy_from_slice(&remote_session_id.to_le_bytes()[..SESSION_ID_SIZE]);
|
|
||||||
debug_assert!(packet_type == PACKET_TYPE_DATA || packet_type == PACKET_TYPE_NOP);
|
debug_assert!(packet_type == PACKET_TYPE_DATA || packet_type == PACKET_TYPE_NOP);
|
||||||
buffer[10] = packet_type;
|
buffer[0] = packet_type;
|
||||||
|
buffer[1..7].copy_from_slice(&remote_session_id.to_le_bytes()[..SESSION_ID_SIZE]);
|
||||||
|
buffer[7..11].copy_from_slice(&counter.to_bytes());
|
||||||
|
|
||||||
let payload_end = HEADER_SIZE + data.len();
|
let payload_end = HEADER_SIZE + data.len();
|
||||||
let tag_end = payload_end + AES_GCM_TAG_SIZE;
|
let tag_end = payload_end + AES_GCM_TAG_SIZE;
|
||||||
|
@ -889,9 +901,9 @@ fn assemble_KEY_OFFER<const MAX_PACKET_SIZE: usize, const STATIC_PUBLIC_SIZE: us
|
||||||
alice_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
alice_s_public: &[u8; STATIC_PUBLIC_SIZE],
|
||||||
alice_e1_public: Option<&[u8; pqc_kyber::KYBER_PUBLICKEYBYTES]>,
|
alice_e1_public: Option<&[u8; pqc_kyber::KYBER_PUBLICKEYBYTES]>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
buffer[0..4].copy_from_slice(&counter.to_bytes());
|
buffer[0] = PACKET_TYPE_KEY_OFFER;
|
||||||
buffer[4..10].copy_from_slice(&bob_session_id.map_or(0_u64, |i| i.into()).to_le_bytes()[..SESSION_ID_SIZE]);
|
buffer[1..7].copy_from_slice(&bob_session_id.map_or(0_u64, |i| i.into()).to_le_bytes()[..SESSION_ID_SIZE]);
|
||||||
buffer[10] = PACKET_TYPE_KEY_OFFER;
|
buffer[7..11].copy_from_slice(&counter.to_bytes());
|
||||||
let mut b = &mut buffer[HEADER_SIZE..];
|
let mut b = &mut buffer[HEADER_SIZE..];
|
||||||
|
|
||||||
b[..P384_PUBLIC_KEY_SIZE].copy_from_slice(alice_e0_public.as_bytes());
|
b[..P384_PUBLIC_KEY_SIZE].copy_from_slice(alice_e0_public.as_bytes());
|
||||||
|
@ -961,9 +973,9 @@ fn assemble_KEY_COUNTER_OFFER<const MAX_PACKET_SIZE: usize>(
|
||||||
bob_session_id: SessionId,
|
bob_session_id: SessionId,
|
||||||
bob_e1_public: Option<&[u8; pqc_kyber::KYBER_CIPHERTEXTBYTES]>,
|
bob_e1_public: Option<&[u8; pqc_kyber::KYBER_CIPHERTEXTBYTES]>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
buffer[0..4].copy_from_slice(&counter.to_bytes());
|
buffer[0] = PACKET_TYPE_KEY_COUNTER_OFFER;
|
||||||
alice_session_id.copy_to(&mut buffer[4..10]);
|
alice_session_id.copy_to(&mut buffer[1..7]);
|
||||||
buffer[10] = PACKET_TYPE_KEY_COUNTER_OFFER;
|
buffer[7..11].copy_from_slice(&counter.to_bytes());
|
||||||
let mut b = &mut buffer[HEADER_SIZE..];
|
let mut b = &mut buffer[HEADER_SIZE..];
|
||||||
|
|
||||||
b[..P384_PUBLIC_KEY_SIZE].copy_from_slice(bob_e0_public.as_bytes());
|
b[..P384_PUBLIC_KEY_SIZE].copy_from_slice(bob_e0_public.as_bytes());
|
||||||
|
@ -1053,7 +1065,7 @@ mod tests {
|
||||||
let mut from_bob: Vec<Vec<u8>> = Vec::new();
|
let mut from_bob: Vec<Vec<u8>> = Vec::new();
|
||||||
|
|
||||||
// Session TO Bob, on Alice's side.
|
// Session TO Bob, on Alice's side.
|
||||||
let (alice, packet) = new_session(
|
let (alice, packet) = Session::new(
|
||||||
&mut a_buffer,
|
&mut a_buffer,
|
||||||
SessionId::new_random(),
|
SessionId::new_random(),
|
||||||
alice_static_keypair.public_key_bytes(),
|
alice_static_keypair.public_key_bytes(),
|
||||||
|
@ -1075,7 +1087,7 @@ mod tests {
|
||||||
for _ in 0..256 {
|
for _ in 0..256 {
|
||||||
while !from_alice.is_empty() || !from_bob.is_empty() {
|
while !from_alice.is_empty() || !from_bob.is_empty() {
|
||||||
if let Some(packet) = from_alice.pop() {
|
if let Some(packet) = from_alice.pop() {
|
||||||
let r = receive(
|
let r = Session::receive(
|
||||||
packet.as_slice(),
|
packet.as_slice(),
|
||||||
&mut b_buffer,
|
&mut b_buffer,
|
||||||
&bob_static_keypair,
|
&bob_static_keypair,
|
||||||
|
@ -1138,7 +1150,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(packet) = from_bob.pop() {
|
if let Some(packet) = from_bob.pop() {
|
||||||
let r = receive(
|
let r = Session::receive(
|
||||||
packet.as_slice(),
|
packet.as_slice(),
|
||||||
&mut b_buffer,
|
&mut b_buffer,
|
||||||
&alice_static_keypair,
|
&alice_static_keypair,
|
||||||
|
|
|
@ -102,11 +102,8 @@ pub trait InnerProtocolInterface: Sync + Send + 'static {
|
||||||
/// Handle an OK, returing true if the OK was recognized.
|
/// Handle an OK, returing true if the OK was recognized.
|
||||||
async fn handle_ok<SI: SystemInterface>(&self, source: &Peer<SI>, source_path: &Path<SI>, in_re_verb: u8, in_re_message_id: u64, payload: &PacketBuffer, cursor: &mut usize) -> bool;
|
async fn handle_ok<SI: SystemInterface>(&self, source: &Peer<SI>, source_path: &Path<SI>, in_re_verb: u8, in_re_message_id: u64, payload: &PacketBuffer, cursor: &mut usize) -> bool;
|
||||||
|
|
||||||
/// Check if this remote peer has a trust relationship with this node.
|
/// Check if this peer should communicate with another at all.
|
||||||
///
|
fn should_communicate_with(&self, id: &Identity) -> bool;
|
||||||
/// This is checked to determine if we should do things like make direct links or respond to
|
|
||||||
/// various other VL1 messages.
|
|
||||||
fn has_trust_relationship(&self, id: &Identity) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How often to check the root cluster definitions against the root list and update.
|
/// How often to check the root cluster definitions against the root list and update.
|
||||||
|
|
|
@ -75,13 +75,7 @@ pub struct Peer<SI: SystemInterface> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt AEAD packet encryption and MAC validation. Returns message ID on success.
|
/// Attempt AEAD packet encryption and MAC validation. Returns message ID on success.
|
||||||
fn try_aead_decrypt(
|
fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8], packet_header: &PacketHeader, fragments: &[Option<PooledPacketBuffer>], payload: &mut PacketBuffer) -> Option<MessageId> {
|
||||||
secret: &SymmetricSecret,
|
|
||||||
packet_frag0_payload_bytes: &[u8],
|
|
||||||
packet_header: &PacketHeader,
|
|
||||||
fragments: &[Option<PooledPacketBuffer>],
|
|
||||||
payload: &mut PacketBuffer,
|
|
||||||
) -> Option<MessageId> {
|
|
||||||
let cipher = packet_header.cipher();
|
let cipher = packet_header.cipher();
|
||||||
match cipher {
|
match cipher {
|
||||||
security_constants::CIPHER_NOCRYPT_POLY1305 | security_constants::CIPHER_SALSA2012_POLY1305 => {
|
security_constants::CIPHER_NOCRYPT_POLY1305 | security_constants::CIPHER_SALSA2012_POLY1305 => {
|
||||||
|
@ -279,13 +273,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
match &p.endpoint {
|
match &p.endpoint {
|
||||||
Endpoint::IpUdp(existing_ip) => {
|
Endpoint::IpUdp(existing_ip) => {
|
||||||
if existing_ip.ip_bytes().eq(new_ip.ip_bytes()) {
|
if existing_ip.ip_bytes().eq(new_ip.ip_bytes()) {
|
||||||
debug_event!(
|
debug_event!(si, "[vl1] {} replacing path {} with {} (same IP, different port)", self.identity.address.to_string(), p.endpoint.to_string(), new_path.endpoint.to_string());
|
||||||
si,
|
|
||||||
"[vl1] {} replacing path {} with {} (same IP, different port)",
|
|
||||||
self.identity.address.to_string(),
|
|
||||||
p.endpoint.to_string(),
|
|
||||||
new_path.endpoint.to_string()
|
|
||||||
);
|
|
||||||
pi.path = Arc::downgrade(new_path);
|
pi.path = Arc::downgrade(new_path);
|
||||||
pi.canonical_instance_id = new_path.canonical.canonical_instance_id();
|
pi.canonical_instance_id = new_path.canonical.canonical_instance_id();
|
||||||
pi.last_receive_time_ticks = time_ticks;
|
pi.last_receive_time_ticks = time_ticks;
|
||||||
|
@ -332,15 +320,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
///
|
///
|
||||||
/// This does not set the fragmentation field in the packet header, MAC, or encrypt the packet. The sender
|
/// This does not set the fragmentation field in the packet header, MAC, or encrypt the packet. The sender
|
||||||
/// must do that while building the packet. The fragmentation flag must be set if fragmentation will be needed.
|
/// must do that while building the packet. The fragmentation flag must be set if fragmentation will be needed.
|
||||||
async fn internal_send(
|
async fn internal_send(&self, si: &SI, endpoint: &Endpoint, local_socket: Option<&SI::LocalSocket>, local_interface: Option<&SI::LocalInterface>, max_fragment_size: usize, packet: &PacketBuffer) -> bool {
|
||||||
&self,
|
|
||||||
si: &SI,
|
|
||||||
endpoint: &Endpoint,
|
|
||||||
local_socket: Option<&SI::LocalSocket>,
|
|
||||||
local_interface: Option<&SI::LocalInterface>,
|
|
||||||
max_fragment_size: usize,
|
|
||||||
packet: &PacketBuffer,
|
|
||||||
) -> bool {
|
|
||||||
let packet_size = packet.len();
|
let packet_size = packet.len();
|
||||||
if packet_size > max_fragment_size {
|
if packet_size > max_fragment_size {
|
||||||
let bytes = packet.as_bytes();
|
let bytes = packet.as_bytes();
|
||||||
|
@ -350,8 +330,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
let mut pos = UDP_DEFAULT_MTU;
|
let mut pos = UDP_DEFAULT_MTU;
|
||||||
|
|
||||||
let overrun_size = (packet_size - UDP_DEFAULT_MTU) as u32;
|
let overrun_size = (packet_size - UDP_DEFAULT_MTU) as u32;
|
||||||
let fragment_count =
|
let fragment_count = (overrun_size / (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) + (((overrun_size % (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) != 0) as u32);
|
||||||
(overrun_size / (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) + (((overrun_size % (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) != 0) as u32);
|
|
||||||
debug_assert!(fragment_count <= packet_constants::FRAGMENT_COUNT_MAX as u32);
|
debug_assert!(fragment_count <= packet_constants::FRAGMENT_COUNT_MAX as u32);
|
||||||
|
|
||||||
let mut header = FragmentHeader {
|
let mut header = FragmentHeader {
|
||||||
|
@ -400,9 +379,16 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_fragment_size = if path.endpoint.requires_fragmentation() { UDP_DEFAULT_MTU } else { usize::MAX };
|
let max_fragment_size = if path.endpoint.requires_fragmentation() {
|
||||||
let flags_cipher_hops =
|
UDP_DEFAULT_MTU
|
||||||
if packet.len() > max_fragment_size { packet_constants::HEADER_FLAG_FRAGMENTED | security_constants::CIPHER_AES_GMAC_SIV } else { security_constants::CIPHER_AES_GMAC_SIV };
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
|
let flags_cipher_hops = if packet.len() > max_fragment_size {
|
||||||
|
packet_constants::HEADER_FLAG_FRAGMENTED | security_constants::CIPHER_AES_GMAC_SIV
|
||||||
|
} else {
|
||||||
|
security_constants::CIPHER_AES_GMAC_SIV
|
||||||
|
};
|
||||||
|
|
||||||
let mut aes_gmac_siv = self.identity_symmetric_key.aes_gmac_siv.get();
|
let mut aes_gmac_siv = self.identity_symmetric_key.aes_gmac_siv.get();
|
||||||
aes_gmac_siv.encrypt_init(&self.next_message_id().to_ne_bytes());
|
aes_gmac_siv.encrypt_init(&self.next_message_id().to_ne_bytes());
|
||||||
|
@ -481,7 +467,11 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_fragment_size = if destination.requires_fragmentation() { UDP_DEFAULT_MTU } else { usize::MAX };
|
let max_fragment_size = if destination.requires_fragmentation() {
|
||||||
|
UDP_DEFAULT_MTU
|
||||||
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
let time_ticks = si.time_ticks();
|
let time_ticks = si.time_ticks();
|
||||||
|
|
||||||
let mut packet = PacketBuffer::new();
|
let mut packet = PacketBuffer::new();
|
||||||
|
@ -532,17 +522,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
/// those fragments after the main packet header and first chunk.
|
/// those fragments after the main packet header and first chunk.
|
||||||
///
|
///
|
||||||
/// This returns true if the packet decrypted and passed authentication.
|
/// This returns true if the packet decrypted and passed authentication.
|
||||||
pub(crate) async fn receive<PH: InnerProtocolInterface>(
|
pub(crate) async fn receive<PH: InnerProtocolInterface>(&self, node: &Node<SI>, si: &SI, ph: &PH, time_ticks: i64, source_path: &Arc<Path<SI>>, packet_header: &PacketHeader, frag0: &PacketBuffer, fragments: &[Option<PooledPacketBuffer>]) -> bool {
|
||||||
&self,
|
|
||||||
node: &Node<SI>,
|
|
||||||
si: &SI,
|
|
||||||
ph: &PH,
|
|
||||||
time_ticks: i64,
|
|
||||||
source_path: &Arc<Path<SI>>,
|
|
||||||
packet_header: &PacketHeader,
|
|
||||||
frag0: &PacketBuffer,
|
|
||||||
fragments: &[Option<PooledPacketBuffer>],
|
|
||||||
) -> bool {
|
|
||||||
if let Ok(packet_frag0_payload_bytes) = frag0.as_bytes_starting_at(packet_constants::VERB_INDEX) {
|
if let Ok(packet_frag0_payload_bytes) = frag0.as_bytes_starting_at(packet_constants::VERB_INDEX) {
|
||||||
let mut payload = PacketBuffer::new();
|
let mut payload = PacketBuffer::new();
|
||||||
|
|
||||||
|
@ -606,18 +586,8 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_incoming_hello<PH: InnerProtocolInterface>(
|
async fn handle_incoming_hello<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, source_path: &Arc<Path<SI>>, hops: u8, payload: &PacketBuffer) -> bool {
|
||||||
&self,
|
if !(ph.should_communicate_with(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
|
||||||
si: &SI,
|
|
||||||
ph: &PH,
|
|
||||||
node: &Node<SI>,
|
|
||||||
time_ticks: i64,
|
|
||||||
message_id: MessageId,
|
|
||||||
source_path: &Arc<Path<SI>>,
|
|
||||||
hops: u8,
|
|
||||||
payload: &PacketBuffer,
|
|
||||||
) -> bool {
|
|
||||||
if !(ph.has_trust_relationship(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
|
|
||||||
debug_event!(si, "[vl1] dropping HELLO from {} due to lack of trust relationship", self.identity.address.to_string());
|
debug_event!(si, "[vl1] dropping HELLO from {} due to lack of trust relationship", self.identity.address.to_string());
|
||||||
return true; // packet wasn't invalid, just ignored
|
return true; // packet wasn't invalid, just ignored
|
||||||
}
|
}
|
||||||
|
@ -629,9 +599,8 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
{
|
{
|
||||||
let mut remote_node_info = self.remote_node_info.write();
|
let mut remote_node_info = self.remote_node_info.write();
|
||||||
remote_node_info.remote_protocol_version = hello_fixed_headers.version_proto;
|
remote_node_info.remote_protocol_version = hello_fixed_headers.version_proto;
|
||||||
remote_node_info.remote_version = (hello_fixed_headers.version_major as u64).wrapping_shl(48)
|
remote_node_info.remote_version =
|
||||||
| (hello_fixed_headers.version_minor as u64).wrapping_shl(32)
|
(hello_fixed_headers.version_major as u64).wrapping_shl(48) | (hello_fixed_headers.version_minor as u64).wrapping_shl(32) | (u16::from_be_bytes(hello_fixed_headers.version_revision) as u64).wrapping_shl(16);
|
||||||
| (u16::from_be_bytes(hello_fixed_headers.version_revision) as u64).wrapping_shl(16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut packet = PacketBuffer::new();
|
let mut packet = PacketBuffer::new();
|
||||||
|
@ -648,7 +617,14 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
f.1.version_revision = VERSION_REVISION.to_be_bytes();
|
f.1.version_revision = VERSION_REVISION.to_be_bytes();
|
||||||
}
|
}
|
||||||
if hello_fixed_headers.version_proto >= 20 {
|
if hello_fixed_headers.version_proto >= 20 {
|
||||||
let session_metadata = self.create_session_metadata(node, if hops == 0 { Some(&source_path.endpoint) } else { None });
|
let session_metadata = self.create_session_metadata(
|
||||||
|
node,
|
||||||
|
if hops == 0 {
|
||||||
|
Some(&source_path.endpoint)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
);
|
||||||
assert!(session_metadata.len() <= 0xffff); // sanity check, should be impossible
|
assert!(session_metadata.len() <= 0xffff); // sanity check, should be impossible
|
||||||
assert!(packet.append_u16(session_metadata.len() as u16).is_ok());
|
assert!(packet.append_u16(session_metadata.len() as u16).is_ok());
|
||||||
assert!(packet.append_bytes(session_metadata.as_slice()).is_ok());
|
assert!(packet.append_bytes(session_metadata.as_slice()).is_ok());
|
||||||
|
@ -676,17 +652,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_incoming_ok<PH: InnerProtocolInterface>(
|
async fn handle_incoming_ok<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, hops: u8, path_is_known: bool, payload: &PacketBuffer) -> bool {
|
||||||
&self,
|
|
||||||
si: &SI,
|
|
||||||
ph: &PH,
|
|
||||||
node: &Node<SI>,
|
|
||||||
time_ticks: i64,
|
|
||||||
source_path: &Arc<Path<SI>>,
|
|
||||||
hops: u8,
|
|
||||||
path_is_known: bool,
|
|
||||||
payload: &PacketBuffer,
|
|
||||||
) -> bool {
|
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
if let Ok(ok_header) = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor) {
|
if let Ok(ok_header) = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor) {
|
||||||
let in_re_message_id: MessageId = u64::from_ne_bytes(ok_header.in_re_message_id);
|
let in_re_message_id: MessageId = u64::from_ne_bytes(ok_header.in_re_message_id);
|
||||||
|
@ -709,12 +675,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
let reported_endpoint2 = reported_endpoint.clone();
|
let reported_endpoint2 = reported_endpoint.clone();
|
||||||
if remote_node_info.reported_local_endpoints.insert(reported_endpoint, time_ticks).is_none() {
|
if remote_node_info.reported_local_endpoints.insert(reported_endpoint, time_ticks).is_none() {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
debug_event!(
|
debug_event!(si, "[vl1] {} reported new remote perspective, local endpoint: {}", self.identity.address.to_string(), reported_endpoint2.to_string());
|
||||||
si,
|
|
||||||
"[vl1] {} reported new remote perspective, local endpoint: {}",
|
|
||||||
self.identity.address.to_string(),
|
|
||||||
reported_endpoint2.to_string()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -797,7 +758,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_incoming_whois<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
|
async fn handle_incoming_whois<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
|
||||||
if node.this_node_is_root() || ph.has_trust_relationship(&self.identity) {
|
if node.this_node_is_root() || ph.should_communicate_with(&self.identity) {
|
||||||
let mut packet = PacketBuffer::new();
|
let mut packet = PacketBuffer::new();
|
||||||
packet.set_size(packet_constants::HEADER_SIZE);
|
packet.set_size(packet_constants::HEADER_SIZE);
|
||||||
{
|
{
|
||||||
|
@ -832,7 +793,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_incoming_echo<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
|
async fn handle_incoming_echo<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
|
||||||
if ph.has_trust_relationship(&self.identity) || node.is_peer_root(self) {
|
if ph.should_communicate_with(&self.identity) || node.is_peer_root(self) {
|
||||||
let mut packet = PacketBuffer::new();
|
let mut packet = PacketBuffer::new();
|
||||||
packet.set_size(packet_constants::HEADER_SIZE);
|
packet.set_size(packet_constants::HEADER_SIZE);
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,10 +39,19 @@ impl NetworkId {
|
||||||
pub fn to_bytes(&self) -> [u8; 8] {
|
pub fn to_bytes(&self) -> [u8; 8] {
|
||||||
self.0.get().to_be_bytes()
|
self.0.get().to_be_bytes()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NetworkId> for u64 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u64(&self) -> u64 {
|
fn from(v: NetworkId) -> Self {
|
||||||
self.0.get()
|
v.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&NetworkId> for u64 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(v: &NetworkId) -> Self {
|
||||||
|
v.0.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,7 @@ impl InnerProtocolInterface for Switch {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
async fn handle_error<SI: SystemInterface>(
|
async fn handle_error<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, in_re_verb: u8, in_re_message_id: u64, error_code: u8, payload: &PacketBuffer, cursor: &mut usize) -> bool {
|
||||||
&self,
|
|
||||||
peer: &Peer<SI>,
|
|
||||||
source_path: &Path<SI>,
|
|
||||||
in_re_verb: u8,
|
|
||||||
in_re_message_id: u64,
|
|
||||||
error_code: u8,
|
|
||||||
payload: &PacketBuffer,
|
|
||||||
cursor: &mut usize,
|
|
||||||
) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +28,7 @@ impl InnerProtocolInterface for Switch {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn has_trust_relationship(&self, id: &Identity) -> bool {
|
fn should_communicate_with(&self, id: &Identity) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue