mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 05:23:44 +02:00
Add ICMP to endpoint, and some ZSSP revisions.
This commit is contained in:
parent
d10bffdee9
commit
98c0575a00
3 changed files with 217 additions and 126 deletions
|
@ -10,7 +10,7 @@ use std::ops::Deref;
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use crate::aes::{Aes, AesGcm};
|
||||
use crate::hash::{hmac_sha384, hmac_sha512, SHA384};
|
||||
use crate::hash::{hmac_sha512, HMACSHA384, SHA384};
|
||||
use crate::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE};
|
||||
use crate::random;
|
||||
use crate::secret::Secret;
|
||||
|
@ -86,18 +86,9 @@ const KEY_EXCHANGE_MAX_FRAGMENTS: usize = 2; // enough room for p384 + ZT identi
|
|||
/// Size of packet header
|
||||
const HEADER_SIZE: usize = 16;
|
||||
|
||||
/// Size of "check" field at start of header
|
||||
const HEADER_CHECK_SIZE: usize = 4;
|
||||
|
||||
/// Size of AES-GCM MAC tags
|
||||
const AES_GCM_TAG_SIZE: usize = 16;
|
||||
|
||||
/// Start of section of packet used as AES-GCM nonce.
|
||||
const AES_GCM_NONCE_START: usize = 4;
|
||||
|
||||
/// End of section of packet used as AES-GCM nonce (nonce is 12 bytes).
|
||||
const AES_GCM_NONCE_END: usize = 16;
|
||||
|
||||
/// Size of HMAC-SHA384
|
||||
const HMAC_SIZE: usize = 48;
|
||||
|
||||
|
@ -393,11 +384,16 @@ impl<H: Host> Session<H> {
|
|||
let counter = self.send_counter.next();
|
||||
|
||||
create_packet_header(mtu_buffer, packet_len, mtu_buffer.len(), PACKET_TYPE_DATA, remote_session_id.into(), counter)?;
|
||||
|
||||
let mut c = key.get_send_cipher(counter)?;
|
||||
c.init(&mtu_buffer[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(memory::as_byte_array::<Pseudoheader, 12>(&Pseudoheader::make(
|
||||
remote_session_id.into(),
|
||||
PACKET_TYPE_DATA,
|
||||
counter.to_u32(),
|
||||
)));
|
||||
|
||||
if packet_len > mtu_buffer.len() {
|
||||
let mut header: [u8; HEADER_SIZE - HEADER_CHECK_SIZE] = mtu_buffer[HEADER_CHECK_SIZE..HEADER_SIZE].try_into().unwrap();
|
||||
let mut header: [u8; 16] = mtu_buffer[..HEADER_SIZE].try_into().unwrap();
|
||||
let fragment_data_mtu = mtu_buffer.len() - HEADER_SIZE;
|
||||
let last_fragment_data_mtu = mtu_buffer.len() - (HEADER_SIZE + AES_GCM_TAG_SIZE);
|
||||
loop {
|
||||
|
@ -405,15 +401,12 @@ impl<H: Host> Session<H> {
|
|||
let fragment_size = fragment_data_size + HEADER_SIZE;
|
||||
c.crypt(&data[..fragment_data_size], &mut mtu_buffer[HEADER_SIZE..fragment_size]);
|
||||
data = &data[fragment_data_size..];
|
||||
|
||||
let hc = calc_header_check_code(mtu_buffer, &self.header_check_cipher);
|
||||
mtu_buffer[..HEADER_CHECK_SIZE].copy_from_slice(&hc.to_ne_bytes());
|
||||
|
||||
armor_header(mtu_buffer, &self.header_check_cipher);
|
||||
send(&mut mtu_buffer[..fragment_size]);
|
||||
|
||||
debug_assert!(header[7].wrapping_shr(2) < 63);
|
||||
header[7] += 0x04; // increment fragment number
|
||||
mtu_buffer[HEADER_CHECK_SIZE..HEADER_SIZE].copy_from_slice(&header);
|
||||
mtu_buffer[..HEADER_SIZE].copy_from_slice(&header);
|
||||
|
||||
if data.len() <= last_fragment_data_mtu {
|
||||
break;
|
||||
|
@ -426,9 +419,7 @@ impl<H: Host> Session<H> {
|
|||
c.crypt(data, &mut mtu_buffer[HEADER_SIZE..gcm_tag_idx]);
|
||||
mtu_buffer[gcm_tag_idx..packet_len].copy_from_slice(&c.finish_encrypt());
|
||||
|
||||
let hc = calc_header_check_code(mtu_buffer, &self.header_check_cipher);
|
||||
mtu_buffer[..HEADER_CHECK_SIZE].copy_from_slice(&hc.to_ne_bytes());
|
||||
|
||||
armor_header(mtu_buffer, &self.header_check_cipher);
|
||||
send(&mut mtu_buffer[..packet_len]);
|
||||
|
||||
key.return_send_cipher(c);
|
||||
|
@ -526,34 +517,49 @@ impl<H: Host> ReceiveContext<H> {
|
|||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
|
||||
let header_0_8 = memory::u64_from_le_bytes(&incoming_packet[HEADER_CHECK_SIZE..12]); // session ID, type, frag info
|
||||
let counter = memory::u32_from_le_bytes(&incoming_packet[12..16]);
|
||||
let local_session_id = SessionId::new_from_u64(header_0_8 & SessionId::MAX_BIT_MASK);
|
||||
let packet_type = (header_0_8.wrapping_shr(48) as u8) & 15;
|
||||
let fragment_count = ((header_0_8.wrapping_shr(52) as u8) & 63).wrapping_add(1);
|
||||
let fragment_no = (header_0_8.wrapping_shr(58) as u8) & 63;
|
||||
let local_session_id = SessionId::new_from_u64(memory::u64_from_le_bytes(incoming_packet) & SessionId::MAX_BIT_MASK);
|
||||
|
||||
if let Some(local_session_id) = local_session_id {
|
||||
if let Some(session) = host.session_lookup(local_session_id) {
|
||||
if memory::u32_from_ne_bytes(incoming_packet) != calc_header_check_code(incoming_packet, &session.header_check_cipher) {
|
||||
unlikely_branch();
|
||||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
|
||||
if fragment_count > 1 {
|
||||
if fragment_count <= (MAX_FRAGMENTS as u8) && fragment_no < fragment_count {
|
||||
let mut defrag = session.defrag.lock();
|
||||
let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count));
|
||||
if let Some(assembled_packet) = fragment_gather_array.add(fragment_no, incoming_packet_buf) {
|
||||
drop(defrag); // release lock
|
||||
return self.receive_complete(host, &mut send, data_buf, assembled_packet.as_ref(), packet_type, Some(session), mtu, current_time);
|
||||
if let Some((packet_type, fragment_count, fragment_no, counter)) = dearmor_header(incoming_packet, &session.header_check_cipher) {
|
||||
if fragment_count > 1 {
|
||||
if fragment_count <= (MAX_FRAGMENTS as u8) && fragment_no < fragment_count {
|
||||
let mut defrag = session.defrag.lock();
|
||||
let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count));
|
||||
if let Some(assembled_packet) = fragment_gather_array.add(fragment_no, incoming_packet_buf) {
|
||||
drop(defrag); // release lock
|
||||
return self.receive_complete(
|
||||
host,
|
||||
&mut send,
|
||||
data_buf,
|
||||
memory::as_byte_array(&Pseudoheader::make(u64::from(local_session_id), packet_type, counter)),
|
||||
assembled_packet.as_ref(),
|
||||
packet_type,
|
||||
Some(session),
|
||||
mtu,
|
||||
current_time,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unlikely_branch();
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
} else {
|
||||
unlikely_branch();
|
||||
return Err(Error::InvalidPacket);
|
||||
return self.receive_complete(
|
||||
host,
|
||||
&mut send,
|
||||
data_buf,
|
||||
memory::as_byte_array(&Pseudoheader::make(u64::from(local_session_id), packet_type, counter)),
|
||||
&[incoming_packet_buf],
|
||||
packet_type,
|
||||
Some(session),
|
||||
mtu,
|
||||
current_time,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return self.receive_complete(host, &mut send, data_buf, &[incoming_packet_buf], packet_type, Some(session), mtu, current_time);
|
||||
unlikely_branch();
|
||||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
} else {
|
||||
unlikely_branch();
|
||||
|
@ -561,18 +567,27 @@ impl<H: Host> ReceiveContext<H> {
|
|||
}
|
||||
} else {
|
||||
unlikely_branch();
|
||||
|
||||
if memory::u32_from_ne_bytes(incoming_packet) != calc_header_check_code(incoming_packet, &self.incoming_init_header_check_cipher) {
|
||||
if let Some((packet_type, fragment_count, fragment_no, counter)) = dearmor_header(incoming_packet, &self.incoming_init_header_check_cipher) {
|
||||
let mut defrag = self.initial_offer_defrag.lock();
|
||||
let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count));
|
||||
if let Some(assembled_packet) = fragment_gather_array.add(fragment_no, incoming_packet_buf) {
|
||||
drop(defrag); // release lock
|
||||
return self.receive_complete(
|
||||
host,
|
||||
&mut send,
|
||||
data_buf,
|
||||
memory::as_byte_array(&Pseudoheader::make(0, packet_type, counter)),
|
||||
assembled_packet.as_ref(),
|
||||
packet_type,
|
||||
None,
|
||||
mtu,
|
||||
current_time,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unlikely_branch();
|
||||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
|
||||
let mut defrag = self.initial_offer_defrag.lock();
|
||||
let fragment_gather_array = defrag.get_or_create_mut(&counter, || GatherArray::new(fragment_count));
|
||||
if let Some(assembled_packet) = fragment_gather_array.add(fragment_no, incoming_packet_buf) {
|
||||
drop(defrag); // release lock
|
||||
return self.receive_complete(host, &mut send, data_buf, assembled_packet.as_ref(), packet_type, None, mtu, current_time);
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(ReceiveResult::Ok);
|
||||
|
@ -583,6 +598,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
host: &H,
|
||||
send: &mut SendFunction,
|
||||
data_buf: &'a mut [u8],
|
||||
pseudoheader: &[u8; 12],
|
||||
fragments: &[H::IncomingPacketBuffer],
|
||||
packet_type: u8,
|
||||
session: Option<H::SessionRef>,
|
||||
|
@ -605,7 +621,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
}
|
||||
|
||||
let mut c = key.get_receive_cipher();
|
||||
c.init(&fragments.first().unwrap().as_ref()[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(pseudoheader);
|
||||
|
||||
let mut data_len = 0;
|
||||
|
||||
|
@ -662,7 +678,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
} else {
|
||||
unlikely_branch();
|
||||
|
||||
let mut incoming_packet_buf = [0_u8; MIN_MTU * KEY_EXCHANGE_MAX_FRAGMENTS];
|
||||
let mut incoming_packet_buf = [0_u8; 4096];
|
||||
let mut incoming_packet_len = 0;
|
||||
for i in 0..fragments.len() {
|
||||
let mut ff = fragments[i].as_ref();
|
||||
|
@ -680,9 +696,6 @@ impl<H: Host> ReceiveContext<H> {
|
|||
let original_ciphertext = incoming_packet_buf.clone();
|
||||
let incoming_packet = &mut incoming_packet_buf[..incoming_packet_len];
|
||||
|
||||
if incoming_packet_len <= HEADER_SIZE {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
if incoming_packet[HEADER_SIZE] != SESSION_PROTOCOL_VERSION {
|
||||
return Err(Error::UnknownProtocolVersion);
|
||||
}
|
||||
|
@ -699,11 +712,11 @@ impl<H: Host> ReceiveContext<H> {
|
|||
let hmac1_end = incoming_packet_len - HMAC_SIZE;
|
||||
|
||||
// Check that the sender knows this host's identity before doing anything else.
|
||||
if !hmac_sha384(host.get_local_s_public_hash(), &incoming_packet[HEADER_CHECK_SIZE..hmac1_end]).eq(&incoming_packet[hmac1_end..]) {
|
||||
if !hmac_sha384_2(host.get_local_s_public_hash(), pseudoheader, &incoming_packet[HEADER_SIZE..hmac1_end]).eq(&incoming_packet[hmac1_end..]) {
|
||||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
|
||||
// Check rate limit if this session is known.
|
||||
// Check rate limits.
|
||||
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 {
|
||||
|
@ -722,7 +735,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
|
||||
// Decrypt the encrypted part of the packet payload and authenticate the above key exchange via AES-GCM auth.
|
||||
let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<32>(), false);
|
||||
c.init(&incoming_packet[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(pseudoheader);
|
||||
c.crypt_in_place(&mut incoming_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..payload_end]);
|
||||
if !c.finish_decrypt(&incoming_packet[payload_end..aes_gcm_tag_end]) {
|
||||
return Err(Error::FailedAuthentication);
|
||||
|
@ -766,9 +779,10 @@ impl<H: Host> ReceiveContext<H> {
|
|||
key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes()));
|
||||
|
||||
// Authenticate entire packet with HMAC-SHA384, verifying alice's identity via 'ss' secret.
|
||||
if !hmac_sha384(
|
||||
if !hmac_sha384_2(
|
||||
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
|
||||
&original_ciphertext[HEADER_CHECK_SIZE..aes_gcm_tag_end],
|
||||
pseudoheader,
|
||||
&original_ciphertext[HEADER_SIZE..aes_gcm_tag_end],
|
||||
)
|
||||
.eq(&incoming_packet[aes_gcm_tag_end..hmac1_end])
|
||||
{
|
||||
|
@ -787,14 +801,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
let se0 = bob_e0_keypair.agree(&alice_s_public_p384).ok_or(Error::FailedAuthentication)?;
|
||||
|
||||
// Gate (via host) and then create new session object if this is a new session.
|
||||
let new_session = if let Some(session) = session.as_ref() {
|
||||
if let Some(current_remote_session_id) = session.state.read().remote_session_id {
|
||||
// We already checked the remote identity, but also check the remote session ID. If it's wrong
|
||||
// then something's expired or wrong.
|
||||
if current_remote_session_id != alice_session_id {
|
||||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
}
|
||||
let new_session = if session.is_some() {
|
||||
None
|
||||
} else {
|
||||
if let Some((new_session_id, psk, associated_object)) = host.accept_new_session(alice_s_public, alice_metadata) {
|
||||
|
@ -833,7 +840,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
&hmac_sha512(&hmac_sha512(&hmac_sha512(key.as_bytes(), bob_e0_keypair.public_key_bytes()), e0e0.as_bytes()), se0.as_bytes()),
|
||||
));
|
||||
|
||||
// At this point we've completed Noise_IK key derivation with NIST P-384 ECDH, but see final step below...
|
||||
// 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.
|
||||
let (bob_e1_public, e1e1) = if JEDI && alice_e1_public.len() > 0 {
|
||||
|
@ -843,7 +850,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
return Err(Error::FailedAuthentication);
|
||||
}
|
||||
} else {
|
||||
(None, None) // use all zero Kyber secret if disabled
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// Create reply packet.
|
||||
|
@ -856,7 +863,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
rp.write_all(&[SESSION_PROTOCOL_VERSION])?;
|
||||
rp.write_all(bob_e0_keypair.public_key_bytes())?;
|
||||
|
||||
rp.write_all(&offer_id.to_le_bytes())?;
|
||||
rp.write_all(&offer_id)?;
|
||||
rp.write_all(&session.id.0.get().to_le_bytes()[..SESSION_ID_SIZE])?;
|
||||
varint::write(&mut rp, 0)?; // they don't need our static public; they have it
|
||||
varint::write(&mut rp, 0)?; // no meta-data in counter-offers (could be used in the future)
|
||||
|
@ -876,18 +883,18 @@ impl<H: Host> ReceiveContext<H> {
|
|||
REPLY_BUF_LEN - rp.len()
|
||||
};
|
||||
create_packet_header(&mut reply_buf, reply_len, mtu, PACKET_TYPE_KEY_COUNTER_OFFER, alice_session_id.into(), reply_counter)?;
|
||||
let reply_pseudoheader = Pseudoheader::make(alice_session_id.into(), PACKET_TYPE_KEY_COUNTER_OFFER, reply_counter.to_u32());
|
||||
|
||||
// Encrypt reply packet using final Noise_IK key BEFORE mixing with the hybrid Kyber result, since the other
|
||||
// side will need to decrypt this to get the Kyber cyphertext.
|
||||
// Encrypt reply packet using final Noise_IK key BEFORE mixing hybrid or ratcheting, since the other side
|
||||
// must decrypt before doing these things.
|
||||
let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<32>(), true);
|
||||
c.init(&reply_buf[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(memory::as_byte_array::<Pseudoheader, 12>(&reply_pseudoheader));
|
||||
c.crypt_in_place(&mut reply_buf[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..reply_len]);
|
||||
let c = c.finish_encrypt();
|
||||
reply_buf[reply_len..(reply_len + AES_GCM_TAG_SIZE)].copy_from_slice(&c);
|
||||
reply_len += AES_GCM_TAG_SIZE;
|
||||
|
||||
// Normal Noise_IK is done. Now we mix in the ratchet key from the previous session key (if any) and the key
|
||||
// negotiated via Kyber1024 (if any).
|
||||
// Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any).
|
||||
if let Some(ratchet_key) = ratchet_key {
|
||||
key = Secret(hmac_sha512(ratchet_key.as_bytes(), key.as_bytes()));
|
||||
}
|
||||
|
@ -899,11 +906,16 @@ impl<H: Host> ReceiveContext<H> {
|
|||
// mixed in, this doesn't constitute session authentication with Kyber because there's no static Kyber key
|
||||
// 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.
|
||||
let hmac = hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &reply_buf[HEADER_CHECK_SIZE..reply_len]);
|
||||
let hmac = hmac_sha384_2(
|
||||
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
|
||||
memory::as_byte_array::<Pseudoheader, 12>(&reply_pseudoheader),
|
||||
&reply_buf[HEADER_SIZE..reply_len],
|
||||
);
|
||||
reply_buf[reply_len..(reply_len + HMAC_SIZE)].copy_from_slice(&hmac);
|
||||
reply_len += HMAC_SIZE;
|
||||
|
||||
let mut state = session.state.write();
|
||||
let _ = state.remote_session_id.replace(alice_session_id);
|
||||
add_session_key(&mut state.keys, SessionKey::new(key, Role::Bob, current_time, reply_counter, ratchet_count + 1, e1e1.is_some()));
|
||||
drop(state);
|
||||
|
||||
|
@ -941,7 +953,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
));
|
||||
|
||||
let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::<32>(), false);
|
||||
c.init(&incoming_packet[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(pseudoheader);
|
||||
c.crypt_in_place(&mut incoming_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..payload_end]);
|
||||
if !c.finish_decrypt(&incoming_packet[payload_end..aes_gcm_tag_end]) {
|
||||
return Err(Error::FailedAuthentication);
|
||||
|
@ -952,7 +964,7 @@ impl<H: Host> ReceiveContext<H> {
|
|||
let (offer_id, bob_session_id, _, _, bob_e1_public, bob_ratchet_key_id) =
|
||||
parse_key_offer_after_header(&incoming_packet[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..], packet_type)?;
|
||||
|
||||
if offer.id != offer_id {
|
||||
if !offer.id.eq(&offer_id) {
|
||||
return Ok(ReceiveResult::Ignored);
|
||||
}
|
||||
|
||||
|
@ -975,9 +987,10 @@ impl<H: Host> ReceiveContext<H> {
|
|||
key = Secret(hmac_sha512(e1e1.as_bytes(), key.as_bytes()));
|
||||
}
|
||||
|
||||
if !hmac_sha384(
|
||||
if !hmac_sha384_2(
|
||||
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
|
||||
&original_ciphertext[HEADER_CHECK_SIZE..aes_gcm_tag_end],
|
||||
pseudoheader,
|
||||
&original_ciphertext[HEADER_SIZE..aes_gcm_tag_end],
|
||||
)
|
||||
.eq(&incoming_packet[aes_gcm_tag_end..incoming_packet.len()])
|
||||
{
|
||||
|
@ -993,13 +1006,11 @@ impl<H: Host> ReceiveContext<H> {
|
|||
create_packet_header(&mut reply_buf, HEADER_SIZE + AES_GCM_TAG_SIZE, mtu, PACKET_TYPE_NOP, bob_session_id.into(), counter)?;
|
||||
|
||||
let mut c = key.get_send_cipher(counter)?;
|
||||
c.init(&reply_buf[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(memory::as_byte_array::<Pseudoheader, 12>(&Pseudoheader::make(bob_session_id.into(), PACKET_TYPE_NOP, counter.to_u32())));
|
||||
reply_buf[HEADER_SIZE..].copy_from_slice(&c.finish_encrypt());
|
||||
key.return_send_cipher(c);
|
||||
|
||||
let hc = calc_header_check_code(&reply_buf, &session.header_check_cipher);
|
||||
reply_buf[..HEADER_CHECK_SIZE].copy_from_slice(&hc.to_ne_bytes());
|
||||
|
||||
armor_header(&mut reply_buf, &session.header_check_cipher);
|
||||
send(&mut reply_buf);
|
||||
|
||||
let mut state = RwLockUpgradableReadGuard::upgrade(state);
|
||||
|
@ -1057,9 +1068,21 @@ impl CounterValue {
|
|||
}
|
||||
}
|
||||
|
||||
/// Temporary object to construct a "pseudo-header" for AES-GCM nonce and HMAC calculation.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
struct Pseudoheader(u64, u32);
|
||||
|
||||
impl Pseudoheader {
|
||||
#[inline(always)]
|
||||
pub fn make(session_id: u64, packet_type: u8, counter: u32) -> Self {
|
||||
Pseudoheader((session_id | (packet_type as u64)).to_le(), counter.to_le())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ephemeral offer sent with KEY_OFFER and rememebered so state can be reconstructed on COUNTER_OFFER.
|
||||
struct EphemeralOffer {
|
||||
id: u64,
|
||||
id: [u8; 16],
|
||||
creation_time: i64,
|
||||
ratchet_count: u64,
|
||||
ratchet_key: Option<Secret<64>>,
|
||||
|
@ -1101,7 +1124,7 @@ fn create_initial_offer<SendFunction: FnMut(&mut [u8])>(
|
|||
(None, 0)
|
||||
};
|
||||
|
||||
let id = random::next_u64_secure();
|
||||
let id: [u8; 16] = random::get_bytes_secure();
|
||||
|
||||
const PACKET_BUF_SIZE: usize = MIN_MTU * KEY_EXCHANGE_MAX_FRAGMENTS;
|
||||
let mut packet_buf = [0_u8; PACKET_BUF_SIZE];
|
||||
|
@ -1111,7 +1134,7 @@ fn create_initial_offer<SendFunction: FnMut(&mut [u8])>(
|
|||
p.write_all(&[SESSION_PROTOCOL_VERSION])?;
|
||||
p.write_all(alice_e0_keypair.public_key_bytes())?;
|
||||
|
||||
p.write_all(&id.to_le_bytes())?;
|
||||
p.write_all(&id)?;
|
||||
p.write_all(&alice_session_id.0.get().to_le_bytes()[..SESSION_ID_SIZE])?;
|
||||
varint::write(&mut p, alice_s_public.len() as u64)?;
|
||||
p.write_all(alice_s_public)?;
|
||||
|
@ -1133,13 +1156,15 @@ fn create_initial_offer<SendFunction: FnMut(&mut [u8])>(
|
|||
PACKET_BUF_SIZE - p.len()
|
||||
};
|
||||
|
||||
create_packet_header(&mut packet_buf, packet_len, mtu, PACKET_TYPE_KEY_OFFER, bob_session_id.map_or(0_u64, |i| i.into()), counter)?;
|
||||
let bob_session_id: u64 = bob_session_id.map_or(0_u64, |i| i.into());
|
||||
create_packet_header(&mut packet_buf, packet_len, mtu, PACKET_TYPE_KEY_OFFER, bob_session_id, counter)?;
|
||||
let pseudoheader = Pseudoheader::make(bob_session_id, PACKET_TYPE_KEY_OFFER, counter.to_u32());
|
||||
|
||||
let key = Secret(hmac_sha512(&hmac_sha512(&INITIAL_KEY, alice_e0_keypair.public_key_bytes()), e0s.unwrap().as_bytes()));
|
||||
|
||||
let gcm_tag = {
|
||||
let mut c = AesGcm::new(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::<32>(), true);
|
||||
c.init(&packet_buf[AES_GCM_NONCE_START..AES_GCM_NONCE_END]);
|
||||
c.init(memory::as_byte_array::<Pseudoheader, 12>(&pseudoheader));
|
||||
c.crypt_in_place(&mut packet_buf[(HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE)..packet_len]);
|
||||
c.finish_encrypt()
|
||||
};
|
||||
|
@ -1148,11 +1173,15 @@ fn create_initial_offer<SendFunction: FnMut(&mut [u8])>(
|
|||
|
||||
let key = Secret(hmac_sha512(key.as_bytes(), ss.as_bytes()));
|
||||
|
||||
let hmac = hmac_sha384(kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), &packet_buf[HEADER_CHECK_SIZE..packet_len]);
|
||||
let hmac = hmac_sha384_2(
|
||||
kbkdf512(key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(),
|
||||
memory::as_byte_array::<Pseudoheader, 12>(&pseudoheader),
|
||||
&packet_buf[HEADER_SIZE..packet_len],
|
||||
);
|
||||
packet_buf[packet_len..(packet_len + HMAC_SIZE)].copy_from_slice(&hmac);
|
||||
packet_len += HMAC_SIZE;
|
||||
|
||||
let hmac = hmac_sha384(bob_s_public_hash, &packet_buf[HEADER_CHECK_SIZE..packet_len]);
|
||||
let hmac = hmac_sha384_2(bob_s_public_hash, memory::as_byte_array::<Pseudoheader, 12>(&pseudoheader), &packet_buf[HEADER_SIZE..packet_len]);
|
||||
packet_buf[packet_len..(packet_len + HMAC_SIZE)].copy_from_slice(&hmac);
|
||||
packet_len += HMAC_SIZE;
|
||||
|
||||
|
@ -1173,6 +1202,7 @@ fn create_initial_offer<SendFunction: FnMut(&mut [u8])>(
|
|||
fn create_packet_header(header: &mut [u8], packet_len: usize, mtu: usize, packet_type: u8, recipient_session_id: u64, counter: CounterValue) -> Result<(), Error> {
|
||||
let fragment_count = ((packet_len as f32) / (mtu - HEADER_SIZE) as f32).ceil() as usize;
|
||||
|
||||
debug_assert!(header.len() >= HEADER_SIZE);
|
||||
debug_assert!(mtu >= MIN_MTU);
|
||||
debug_assert!(packet_len >= MIN_PACKET_SIZE);
|
||||
debug_assert!(fragment_count <= MAX_FRAGMENTS);
|
||||
|
@ -1181,8 +1211,9 @@ fn create_packet_header(header: &mut [u8], packet_len: usize, mtu: usize, packet
|
|||
debug_assert!(recipient_session_id <= 0xffffffffffff); // session ID is 48 bits
|
||||
|
||||
if fragment_count <= MAX_FRAGMENTS {
|
||||
header[HEADER_CHECK_SIZE..12].copy_from_slice(&(recipient_session_id | (packet_type as u64).wrapping_shl(48) | ((fragment_count - 1) as u64).wrapping_shl(52)).to_le_bytes());
|
||||
header[12..HEADER_SIZE].copy_from_slice(&counter.to_u32().to_le_bytes());
|
||||
header[0..8].copy_from_slice(&(recipient_session_id | (packet_type as u64).wrapping_shl(48) | ((fragment_count - 1) as u64).wrapping_shl(52)).to_le_bytes());
|
||||
header[8..12].copy_from_slice(&counter.to_u32().to_le_bytes());
|
||||
header[12..16].fill(0);
|
||||
Ok(())
|
||||
} else {
|
||||
unlikely_branch();
|
||||
|
@ -1194,17 +1225,17 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(send: &mut SendFuncti
|
|||
let packet_len = packet.len();
|
||||
let mut fragment_start = 0;
|
||||
let mut fragment_end = packet_len.min(mtu);
|
||||
let mut header: [u8; HEADER_SIZE - HEADER_CHECK_SIZE] = packet[HEADER_CHECK_SIZE..HEADER_SIZE].try_into().unwrap();
|
||||
let mut header: [u8; 16] = packet[..HEADER_SIZE].try_into().unwrap();
|
||||
loop {
|
||||
let hc = calc_header_check_code(&packet[fragment_start..], header_check_cipher);
|
||||
packet[fragment_start..(fragment_start + HEADER_CHECK_SIZE)].copy_from_slice(&hc.to_ne_bytes());
|
||||
send(&mut packet[fragment_start..fragment_end]);
|
||||
let fragment = &mut packet[fragment_start..fragment_end];
|
||||
armor_header(fragment, header_check_cipher);
|
||||
send(fragment);
|
||||
if fragment_end < packet_len {
|
||||
debug_assert!(header[7].wrapping_shr(2) < 63);
|
||||
header[7] += 0x04; // increment fragment number
|
||||
fragment_start = fragment_end - HEADER_SIZE;
|
||||
fragment_end = (fragment_start + mtu).min(packet_len);
|
||||
packet[(fragment_start + HEADER_CHECK_SIZE)..(fragment_start + HEADER_SIZE)].copy_from_slice(&header);
|
||||
packet[fragment_start..(fragment_start + HEADER_SIZE)].copy_from_slice(&header);
|
||||
} else {
|
||||
debug_assert_eq!(fragment_end, packet_len);
|
||||
break;
|
||||
|
@ -1212,12 +1243,42 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(send: &mut SendFuncti
|
|||
}
|
||||
}
|
||||
|
||||
/// Encrypt everything in header after session ID using AES-CTR and the second 16 bytes as a nonce.
|
||||
/// The last four bytes of the header must be zero, so this also embeds a small header MAC.
|
||||
#[inline(always)]
|
||||
fn calc_header_check_code(packet: &[u8], header_check_cipher: &Aes) -> u32 {
|
||||
fn armor_header(packet: &mut [u8], header_check_cipher: &Aes) {
|
||||
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
||||
let mut header_check = 0u128.to_ne_bytes();
|
||||
header_check_cipher.encrypt_block(&packet[8..24], &mut header_check);
|
||||
memory::u32_from_ne_bytes(&header_check)
|
||||
let mut header_pad = 0u128.to_ne_bytes();
|
||||
header_check_cipher.encrypt_block(&packet[16..32], &mut header_pad);
|
||||
packet[SESSION_ID_SIZE..HEADER_SIZE].iter_mut().zip(header_pad.iter()).for_each(|(x, y)| *x ^= *y);
|
||||
}
|
||||
|
||||
/// Dearmor the armored part of the header and return it if the 32-bit MAC matches.
|
||||
fn dearmor_header(packet: &[u8], header_check_cipher: &Aes) -> Option<(u8, u8, u8, u32)> {
|
||||
debug_assert!(packet.len() >= MIN_PACKET_SIZE);
|
||||
let mut header_pad = 0u128.to_ne_bytes();
|
||||
header_check_cipher.encrypt_block(&packet[16..32], &mut header_pad);
|
||||
let header_pad = u128::from_ne_bytes(header_pad);
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
let (header_0_8, header_8_16) = {
|
||||
let header = memory::u128_from_ne_bytes(packet) ^ header_pad.wrapping_shl(48);
|
||||
(header as u64, header.wrapping_shr(64) as u64)
|
||||
};
|
||||
#[cfg(target_endian = "big")]
|
||||
let (header_0_8, header_8_16) = {
|
||||
let header = memory::u128_from_ne_bytes(packet) ^ header_pad.wrapping_shr(48);
|
||||
((header.wrapping_shr(64) as u64).swap_bytes(), (header as u64).swap_bytes())
|
||||
};
|
||||
|
||||
if header_8_16.wrapping_shr(32) == 0 {
|
||||
let packet_type = (header_0_8.wrapping_shr(48) as u8) & 15;
|
||||
let fragment_count = ((header_0_8.wrapping_shr(52) as u8) & 63).wrapping_add(1);
|
||||
let fragment_no = (header_0_8.wrapping_shr(58) as u8) & 63;
|
||||
Some((packet_type, fragment_count, fragment_no, header_8_16 as u32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn add_session_key(keys: &mut LinkedList<SessionKey>, key: SessionKey) {
|
||||
|
@ -1237,11 +1298,10 @@ fn add_session_key(keys: &mut LinkedList<SessionKey>, key: SessionKey) {
|
|||
keys.push_back(key);
|
||||
}
|
||||
|
||||
fn parse_key_offer_after_header(incoming_packet: &[u8], packet_type: u8) -> Result<(u64, SessionId, &[u8], &[u8], &[u8], Option<[u8; 16]>), Error> {
|
||||
fn parse_key_offer_after_header(incoming_packet: &[u8], packet_type: u8) -> Result<([u8; 16], SessionId, &[u8], &[u8], &[u8], Option<[u8; 16]>), Error> {
|
||||
let mut p = &incoming_packet[..];
|
||||
let mut offer_id = [0_u8; 8];
|
||||
let mut offer_id = [0_u8; 16];
|
||||
p.read_exact(&mut offer_id)?;
|
||||
let offer_id = u64::from_le_bytes(offer_id);
|
||||
let alice_session_id = SessionId::new_from_reader(&mut p)?;
|
||||
if alice_session_id.is_none() {
|
||||
return Err(Error::InvalidPacket);
|
||||
|
@ -1387,6 +1447,14 @@ impl SessionKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Shortcut to HMAC data split into two slices.
|
||||
fn hmac_sha384_2(key: &[u8], a: &[u8], b: &[u8]) -> [u8; 48] {
|
||||
let mut hmac = HMACSHA384::new(key);
|
||||
hmac.update(a);
|
||||
hmac.update(b);
|
||||
hmac.finish()
|
||||
}
|
||||
|
||||
/// HMAC-SHA512 key derivation function modeled on: https://csrc.nist.gov/publications/detail/sp/800-108/final (page 12)
|
||||
fn kbkdf512(key: &[u8], label: u8) -> Secret<64> {
|
||||
Secret(hmac_sha512(key, &[0, 0, 0, 0, b'Z', b'T', label, 0, 0, 0, 0, 0x02, 0x00]))
|
||||
|
|
|
@ -18,7 +18,7 @@ pub const TYPE_ZEROTIER: u8 = 1;
|
|||
pub const TYPE_ETHERNET: u8 = 2;
|
||||
pub const TYPE_WIFIDIRECT: u8 = 3;
|
||||
pub const TYPE_BLUETOOTH: u8 = 4;
|
||||
pub const TYPE_IP: u8 = 5;
|
||||
pub const TYPE_ICMP: u8 = 5;
|
||||
pub const TYPE_IPUDP: u8 = 6;
|
||||
pub const TYPE_IPTCP: u8 = 7;
|
||||
pub const TYPE_HTTP: u8 = 8;
|
||||
|
@ -48,8 +48,8 @@ pub enum Endpoint {
|
|||
/// Local bluetooth
|
||||
Bluetooth(MAC),
|
||||
|
||||
/// Raw IP without a UDP or other header
|
||||
Ip(InetAddress),
|
||||
/// ICMP ECHO, which can be used to traverse some NATs
|
||||
Icmp(InetAddress),
|
||||
|
||||
/// Raw UDP, the default and usually preferred transport mode
|
||||
IpUdp(InetAddress),
|
||||
|
@ -80,7 +80,7 @@ impl Endpoint {
|
|||
#[inline(always)]
|
||||
pub fn ip(&self) -> Option<(&InetAddress, u8)> {
|
||||
match self {
|
||||
Endpoint::Ip(ip) => Some((&ip, TYPE_IP)),
|
||||
Endpoint::Icmp(ip) => Some((&ip, TYPE_ICMP)),
|
||||
Endpoint::IpUdp(ip) => Some((&ip, TYPE_IPUDP)),
|
||||
Endpoint::IpTcp(ip) => Some((&ip, TYPE_IPTCP)),
|
||||
_ => None,
|
||||
|
@ -94,7 +94,7 @@ impl Endpoint {
|
|||
Endpoint::Ethernet(_) => TYPE_ETHERNET,
|
||||
Endpoint::WifiDirect(_) => TYPE_WIFIDIRECT,
|
||||
Endpoint::Bluetooth(_) => TYPE_BLUETOOTH,
|
||||
Endpoint::Ip(_) => TYPE_IP,
|
||||
Endpoint::Icmp(_) => TYPE_ICMP,
|
||||
Endpoint::IpUdp(_) => TYPE_IPUDP,
|
||||
Endpoint::IpTcp(_) => TYPE_IPTCP,
|
||||
Endpoint::Http(_) => TYPE_HTTP,
|
||||
|
@ -120,7 +120,7 @@ impl Endpoint {
|
|||
/// Returns true if this is an endpoint type that requires that large packets be fragmented.
|
||||
pub fn requires_fragmentation(&self) -> bool {
|
||||
match self {
|
||||
Endpoint::Ip(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => true,
|
||||
Endpoint::Icmp(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -149,8 +149,8 @@ impl Marshalable for Endpoint {
|
|||
buf.append_u8(16 + TYPE_BLUETOOTH)?;
|
||||
buf.append_bytes_fixed(&m.to_bytes())
|
||||
}
|
||||
Endpoint::Ip(ip) => {
|
||||
buf.append_u8(16 + TYPE_IP)?;
|
||||
Endpoint::Icmp(ip) => {
|
||||
buf.append_u8(16 + TYPE_ICMP)?;
|
||||
ip.marshal(buf)
|
||||
}
|
||||
Endpoint::IpUdp(ip) => {
|
||||
|
@ -207,7 +207,7 @@ impl Marshalable for Endpoint {
|
|||
TYPE_ETHERNET => Ok(Endpoint::Ethernet(MAC::unmarshal(buf, cursor)?)),
|
||||
TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(MAC::unmarshal(buf, cursor)?)),
|
||||
TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(MAC::unmarshal(buf, cursor)?)),
|
||||
TYPE_IP => Ok(Endpoint::Ip(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_ICMP => Ok(Endpoint::Icmp(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_IPUDP => Ok(Endpoint::IpUdp(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_IPTCP => Ok(Endpoint::IpTcp(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_HTTP => Ok(Endpoint::Http(String::from_utf8_lossy(buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?).to_string())),
|
||||
|
@ -244,9 +244,9 @@ impl Hash for Endpoint {
|
|||
state.write_u8(TYPE_BLUETOOTH);
|
||||
state.write_u64(m.into())
|
||||
}
|
||||
Endpoint::Ip(ip) => {
|
||||
state.write_u8(TYPE_IP);
|
||||
ip.ip_bytes().hash(state);
|
||||
Endpoint::Icmp(ip) => {
|
||||
state.write_u8(TYPE_ICMP);
|
||||
ip.hash(state);
|
||||
}
|
||||
Endpoint::IpUdp(ip) => {
|
||||
state.write_u8(TYPE_IPUDP);
|
||||
|
@ -281,7 +281,7 @@ impl Ord for Endpoint {
|
|||
(Endpoint::Ethernet(a), Endpoint::Ethernet(b)) => a.cmp(b),
|
||||
(Endpoint::WifiDirect(a), Endpoint::WifiDirect(b)) => a.cmp(b),
|
||||
(Endpoint::Bluetooth(a), Endpoint::Bluetooth(b)) => a.cmp(b),
|
||||
(Endpoint::Ip(a), Endpoint::Ip(b)) => a.cmp(b),
|
||||
(Endpoint::Icmp(a), Endpoint::Icmp(b)) => a.cmp(b),
|
||||
(Endpoint::IpUdp(a), Endpoint::IpUdp(b)) => a.cmp(b),
|
||||
(Endpoint::IpTcp(a), Endpoint::IpTcp(b)) => a.cmp(b),
|
||||
(Endpoint::Http(a), Endpoint::Http(b)) => a.cmp(b),
|
||||
|
@ -307,7 +307,7 @@ impl ToString for Endpoint {
|
|||
Endpoint::Ethernet(m) => format!("eth:{}", m.to_string()),
|
||||
Endpoint::WifiDirect(m) => format!("wifip2p:{}", m.to_string()),
|
||||
Endpoint::Bluetooth(m) => format!("bt:{}", m.to_string()),
|
||||
Endpoint::Ip(ip) => format!("ip:{}", ip.to_ip_string()),
|
||||
Endpoint::Icmp(ip) => format!("icmp:{}", ip.to_string()),
|
||||
Endpoint::IpUdp(ip) => format!("udp:{}", ip.to_string()),
|
||||
Endpoint::IpTcp(ip) => format!("tcp:{}", ip.to_string()),
|
||||
Endpoint::Http(url) => format!("url:{}", url.clone()), // http or https
|
||||
|
@ -351,7 +351,7 @@ impl FromStr for Endpoint {
|
|||
"eth" => return Ok(Endpoint::Ethernet(MAC::from_str(endpoint_data)?)),
|
||||
"wifip2p" => return Ok(Endpoint::WifiDirect(MAC::from_str(endpoint_data)?)),
|
||||
"bt" => return Ok(Endpoint::Bluetooth(MAC::from_str(endpoint_data)?)),
|
||||
"ip" => return Ok(Endpoint::Ip(InetAddress::from_str(endpoint_data)?)),
|
||||
"icmp" => return Ok(Endpoint::Icmp(InetAddress::from_str(endpoint_data)?)),
|
||||
"udp" => return Ok(Endpoint::IpUdp(InetAddress::from_str(endpoint_data)?)),
|
||||
"tcp" => return Ok(Endpoint::IpTcp(InetAddress::from_str(endpoint_data)?)),
|
||||
"url" => return Ok(Endpoint::Http(endpoint_data.into())),
|
||||
|
@ -563,7 +563,7 @@ mod tests {
|
|||
|
||||
let inet = crate::vl1::InetAddress::from_ip_port(&v, 1234);
|
||||
|
||||
for e in [Endpoint::Ip(inet.clone()), Endpoint::IpTcp(inet.clone()), Endpoint::IpUdp(inet.clone())] {
|
||||
for e in [Endpoint::Icmp(inet.clone()), Endpoint::IpTcp(inet.clone()), Endpoint::IpUdp(inet.clone())] {
|
||||
let mut buf = Buffer::<20>::new();
|
||||
|
||||
let res = e.marshal(&mut buf);
|
||||
|
@ -646,9 +646,9 @@ mod tests {
|
|||
|
||||
let inet = crate::vl1::InetAddress::from_ip_port(&v, 0);
|
||||
|
||||
let ip = Endpoint::Ip(inet.clone());
|
||||
let ip = Endpoint::Icmp(inet.clone());
|
||||
assert_ne!(ip.to_string().len(), 0);
|
||||
assert!(ip.to_string().starts_with("ip"));
|
||||
assert!(ip.to_string().starts_with("icmp"));
|
||||
|
||||
let ip2 = Endpoint::from_str(&ip.to_string()).unwrap();
|
||||
assert_eq!(ip, ip2);
|
||||
|
@ -665,7 +665,11 @@ mod tests {
|
|||
|
||||
let mac = crate::vl1::MAC::from_u64(rand::random()).unwrap();
|
||||
|
||||
for e in [(Endpoint::Ethernet(mac.clone()), "eth"), (Endpoint::WifiDirect(mac.clone()), "wifip2p"), (Endpoint::Bluetooth(mac.clone()), "bt")] {
|
||||
for e in [
|
||||
(Endpoint::Ethernet(mac.clone()), "eth"),
|
||||
(Endpoint::WifiDirect(mac.clone()), "wifip2p"),
|
||||
(Endpoint::Bluetooth(mac.clone()), "bt"),
|
||||
] {
|
||||
assert_ne!(e.0.to_string().len(), 0);
|
||||
assert!(e.0.to_string().starts_with(e.1));
|
||||
|
||||
|
@ -684,7 +688,10 @@ mod tests {
|
|||
v[0] = rand::random()
|
||||
}
|
||||
|
||||
for e in [(Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash), "zt"), (Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash), "zte")] {
|
||||
for e in [
|
||||
(Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash), "zt"),
|
||||
(Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash), "zte"),
|
||||
] {
|
||||
assert_ne!(e.0.to_string().len(), 0);
|
||||
assert!(e.0.to_string().starts_with(e.1));
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
use std::mem::size_of;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||
#[allow(unused)]
|
||||
mod fast_int_memory_access {
|
||||
|
@ -39,6 +41,12 @@ mod fast_int_memory_access {
|
|||
unsafe { *b.as_ptr().cast() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn u128_from_ne_bytes(b: &[u8]) -> u128 {
|
||||
assert!(b.len() >= 16);
|
||||
unsafe { *b.as_ptr().cast() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn u64_from_ne_bytes(b: &[u8]) -> u64 {
|
||||
assert!(b.len() >= 8);
|
||||
|
@ -207,3 +215,11 @@ mod fast_int_memory_access {
|
|||
}
|
||||
|
||||
pub use fast_int_memory_access::*;
|
||||
|
||||
/// Get a reference to a raw object as a byte array.
|
||||
/// The template parameter S must equal the size of the object in bytes or this will panic.
|
||||
#[inline(always)]
|
||||
pub fn as_byte_array<T: Copy, const S: usize>(o: &T) -> &[u8; S] {
|
||||
assert_eq!(S, size_of::<T>());
|
||||
unsafe { &*(o as *const T).cast::<[u8; S]>() }
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue