mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 08:27:39 +02:00
Merge pull request #3 from zerotier/fix-tests-on-linux
Fix tests on linux
This commit is contained in:
commit
17ea66f255
5 changed files with 202 additions and 156 deletions
5
Makefile
5
Makefile
|
@ -3,4 +3,9 @@ all:
|
|||
clean: FORCE
|
||||
rm -rf zerotier-core-crypto/target zerotier-network-hypervisor/target zerotier-system-service/target syncwhole/target aes-gmac-siv/target iblt/target
|
||||
|
||||
test:
|
||||
for i in */Cargo.toml; do cd $$(dirname $$i); cargo test || exit 1; cd ..; done
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: test
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
// AES-GMAC-SIV implemented using OpenSSL.
|
||||
|
||||
use openssl::symm::{Crypter, Cipher, Mode};
|
||||
use openssl::symm::{Cipher, Crypter, Mode};
|
||||
|
||||
fn aes_ctr_by_key_size(ks: usize) -> Cipher {
|
||||
match ks {
|
||||
|
@ -145,7 +145,8 @@ impl AesGmacSiv {
|
|||
let gmac = self.gmac.as_mut().unwrap();
|
||||
let _ = gmac.finalize(&mut self.tmp);
|
||||
let _ = gmac.get_tag(&mut self.tmp);
|
||||
unsafe { // tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
||||
unsafe {
|
||||
// tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) = *tmp ^ *tmp.offset(1);
|
||||
}
|
||||
|
@ -185,7 +186,8 @@ impl AesGmacSiv {
|
|||
let mut tag_tmp = [0_u8; 32];
|
||||
let _ = Crypter::new(aes_ecb_by_key_size(self.k1.len()), Mode::Decrypt, self.k1.as_slice(), None).unwrap().update(&self.tag, &mut tag_tmp);
|
||||
self.tag.copy_from_slice(&tag_tmp[0..16]);
|
||||
unsafe { // tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
||||
unsafe {
|
||||
// tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||
*tmp = *self.tag.as_mut_ptr().cast::<u64>();
|
||||
*tmp.offset(1) = 0;
|
||||
|
@ -227,13 +229,18 @@ impl AesGmacSiv {
|
|||
/// Finish decryption and return true if authentication appears valid.
|
||||
/// If this returns false the message should be dropped.
|
||||
#[inline(always)]
|
||||
pub fn decrypt_finish(&mut self) -> bool {
|
||||
pub fn decrypt_finish(&mut self) -> Option<&[u8; 16]> {
|
||||
let gmac = self.gmac.as_mut().unwrap();
|
||||
let _ = gmac.finalize(&mut self.tmp);
|
||||
let _ = gmac.get_tag(&mut self.tmp);
|
||||
unsafe { // tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
||||
unsafe {
|
||||
// tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1)
|
||||
if *self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1) {
|
||||
Some(&self.tag)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@ use zerotier_core_crypto::secret::Secret;
|
|||
|
||||
use crate::error::{InvalidFormatError, InvalidParameterError};
|
||||
use crate::util::buffer::Buffer;
|
||||
use crate::util::pool::{Pool, Pooled, PoolFactory};
|
||||
use crate::vl1::Address;
|
||||
use crate::util::pool::{Pool, PoolFactory, Pooled};
|
||||
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
|
||||
use crate::vl1::Address;
|
||||
|
||||
/// Curve25519 and Ed25519
|
||||
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
||||
|
@ -40,7 +40,8 @@ pub const IDENTITY_ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
|||
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
||||
|
||||
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
||||
pub const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
||||
pub const MAX_MARSHAL_SIZE: usize =
|
||||
ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IdentityP384Secret {
|
||||
|
@ -70,7 +71,7 @@ pub struct Identity {
|
|||
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||
pub p384: Option<IdentityP384Public>,
|
||||
pub secret: Option<IdentitySecret>,
|
||||
pub fingerprint: [u8; SHA512_HASH_SIZE]
|
||||
pub fingerprint: [u8; SHA512_HASH_SIZE],
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -129,12 +130,8 @@ impl Identity {
|
|||
c25519: c25519_pub,
|
||||
ed25519: ed25519_pub,
|
||||
p384: None,
|
||||
secret: Some(IdentitySecret {
|
||||
c25519,
|
||||
ed25519,
|
||||
p384: None,
|
||||
}),
|
||||
fingerprint: [0_u8; 64] // replaced in upgrade()
|
||||
secret: Some(IdentitySecret { c25519, ed25519, p384: None }),
|
||||
fingerprint: [0_u8; 64], // replaced in upgrade()
|
||||
};
|
||||
assert!(id.upgrade().is_ok());
|
||||
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
|
||||
|
@ -176,10 +173,7 @@ impl Identity {
|
|||
ecdsa_self_signature,
|
||||
ed25519_self_signature,
|
||||
});
|
||||
let _ = self.secret.as_mut().unwrap().p384.insert(IdentityP384Secret {
|
||||
ecdh: p384_ecdh,
|
||||
ecdsa: p384_ecdsa,
|
||||
});
|
||||
let _ = self.secret.as_mut().unwrap().p384.insert(IdentityP384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa });
|
||||
self.fingerprint = SHA512::hash(self_sign_buf.as_slice());
|
||||
|
||||
return Ok(true);
|
||||
|
@ -251,9 +245,7 @@ impl Identity {
|
|||
// for the final result to be technically FIPS compliant. Non-FIPS algorithm secrets are considered
|
||||
// a salt in the HMAC(salt, key) HKDF construction.
|
||||
if secret.p384.is_some() && other.p384.is_some() {
|
||||
secret.p384.as_ref().unwrap().ecdh.agree(&other.p384.as_ref().unwrap().ecdh).map(|p384_secret| {
|
||||
Secret(hmac_sha512(&c25519_secret.0, &p384_secret.0))
|
||||
})
|
||||
secret.p384.as_ref().unwrap().ecdh.agree(&other.p384.as_ref().unwrap().ecdh).map(|p384_secret| Secret(hmac_sha512(&c25519_secret.0, &p384_secret.0)))
|
||||
} else {
|
||||
Some(c25519_secret)
|
||||
}
|
||||
|
@ -291,7 +283,8 @@ impl Identity {
|
|||
|
||||
/// Verify a signature against this identity.
|
||||
pub fn verify(&self, msg: &[u8], mut signature: &[u8]) -> bool {
|
||||
if signature.len() == 96 { // legacy ed25519-only signature with hash included
|
||||
if signature.len() == 96 {
|
||||
// legacy ed25519-only signature with hash included
|
||||
ed25519_verify(&self.ed25519, signature, msg)
|
||||
} else if signature.len() > 1 {
|
||||
let algorithms = signature[0];
|
||||
|
@ -519,7 +512,7 @@ impl Identity {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
fingerprint: sha.finish()
|
||||
fingerprint: sha.finish(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -532,46 +525,42 @@ impl Identity {
|
|||
let p384 = self.p384.as_ref().unwrap();
|
||||
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
format!("{}:0:{}{}:{}{}:2:{}:{}",
|
||||
format!(
|
||||
"{}:0:{}{}:{}{}:2:{}:{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.c25519),
|
||||
hex::to_string(&self.ed25519),
|
||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||
hex::to_string(&secret.ed25519.secret_bytes().0),
|
||||
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD),
|
||||
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD))
|
||||
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD)
|
||||
)
|
||||
} else {
|
||||
format!("{}:0:{}{}:{}{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.c25519),
|
||||
hex::to_string(&self.ed25519),
|
||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||
hex::to_string(&secret.ed25519.secret_bytes().0))
|
||||
format!("{}:0:{}{}:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), hex::to_string(&secret.c25519.secret_bytes().0), hex::to_string(&secret.ed25519.secret_bytes().0))
|
||||
}
|
||||
} else {
|
||||
self.p384.as_ref().map_or_else(|| {
|
||||
format!("{}:0:{}{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.c25519),hex::to_string(&self.ed25519))
|
||||
}, |p384| {
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
format!("{}:0:{}{}::2:{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.c25519),
|
||||
hex::to_string(&self.ed25519),
|
||||
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
self.p384.as_ref().map_or_else(
|
||||
|| format!("{}:0:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519)),
|
||||
|p384| {
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
format!("{}:0:{}{}::2:{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this identity in string form with all ciphers and with secrets (if present)
|
||||
pub fn to_secret_string(&self) -> String { self.to_string_with_options(IDENTITY_ALGORITHM_ALL, true) }
|
||||
pub fn to_secret_string(&self) -> String {
|
||||
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Identity {
|
||||
/// Get only the public portion of this identity as a string, including all cipher suites.
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String { self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false) }
|
||||
fn to_string(&self) -> String {
|
||||
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Identity {
|
||||
|
@ -616,7 +605,8 @@ impl FromStr for Identity {
|
|||
ptr += 1;
|
||||
}
|
||||
|
||||
let keys = [hex::from_string(keys[0].unwrap_or("")), hex::from_string(keys[1].unwrap_or("")), base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()), base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new())];
|
||||
let keys =
|
||||
[hex::from_string(keys[0].unwrap_or("")), hex::from_string(keys[1].unwrap_or("")), base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()), base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new())];
|
||||
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
|
@ -697,30 +687,38 @@ impl FromStr for Identity {
|
|||
},
|
||||
})
|
||||
},
|
||||
fingerprint: sha.finish()
|
||||
fingerprint: sha.finish(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Identity {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool { self.fingerprint == other.fingerprint }
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.fingerprint == other.fingerprint
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Identity {}
|
||||
|
||||
impl Ord for Identity {
|
||||
fn cmp(&self, other: &Self) -> Ordering { self.address.cmp(&other.address).then_with(|| self.fingerprint.cmp(&other.fingerprint)) }
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.address.cmp(&other.address).then_with(|| self.fingerprint.cmp(&other.fingerprint))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Identity {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Identity {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { state.write_u64(self.address.to_u64()) }
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.address.to_u64())
|
||||
}
|
||||
}
|
||||
|
||||
const ADDRESS_DERIVATION_HASH_MEMORY_SIZE: usize = 2097152;
|
||||
|
@ -767,19 +765,25 @@ struct AddressDerivationMemory(*mut u8);
|
|||
|
||||
impl AddressDerivationMemory {
|
||||
#[inline(always)]
|
||||
fn get_memory(&mut self) -> *mut u8 { self.0 }
|
||||
fn get_memory(&mut self) -> *mut u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AddressDerivationMemory {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) { unsafe { dealloc(self.0, Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) }; }
|
||||
fn drop(&mut self) {
|
||||
unsafe { dealloc(self.0, Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) };
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressDerivationMemoryFactory;
|
||||
|
||||
impl PoolFactory<AddressDerivationMemory> for AddressDerivationMemoryFactory {
|
||||
#[inline(always)]
|
||||
fn create(&self) -> AddressDerivationMemory { AddressDerivationMemory(unsafe { alloc(Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) }) }
|
||||
fn create(&self) -> AddressDerivationMemory {
|
||||
AddressDerivationMemory(unsafe { alloc(Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reset(&self, _: &mut AddressDerivationMemory) {}
|
||||
|
@ -799,10 +803,10 @@ pub(crate) fn purge_verification_memory_pool() {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use zerotier_core_crypto::hex;
|
||||
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
||||
|
||||
const GOOD_V0_IDENTITIES: [&'static str; 10] = [
|
||||
"51ef313c3a:0:79fee239cf79833be3a9068565661dc33e04759fa0f7e2218d10f1a51d441f1bf71332eba26dfc3755ce60e14650fe68dede66cf145e429972a7f51e026374de:6d12b1c5e0eae3983a5ee5872fa9061963d9e2f8cdd85adab54bdec4bd67f538cafc91b8b5b93fca658a630aab030ec10d66235f2443ccf362c55c41ae01b46e",
|
||||
|
@ -814,7 +818,7 @@ mod tests {
|
|||
"c5bdb4a6a6:0:e0a8575bc0277ecf59aaa4a2724acc55554151fff510c8211b0b863398a04224ed918c16405552336ad4c4da3b98eb6224574f1cacaa69e19cdfde184fd9292d:0d45f17d73337cc1898f7be6aae54a050b39ed0259b608b80619c3f898caf8a3a48ae56e51c3d7d8426ef295c0628d81b1a99616a3ed28da49bf8f81e1bec863",
|
||||
"c622dedbe4:0:0cfec354be26b4b2fa9ea29166b4acaf9476d169d51fd741d7e4cd9de93f321c6b80628c50da566d0a6b07d58d651eba8af63e0edc36202c05c3f97c828788ad:31a75d2b46c1b0f33228d3869bc807b42b371bbcef4c96f7232a27c62f56397568558f115d9cff3d6f7b8efb726a1ea49a591662d9aacd1049e295cbb0cf3197",
|
||||
"e28829ab3c:0:8e36c4f6cb524cae6bbea5f26dadb601a76f2a3793961779317365effb17ac6cde4ff4149a1b3480fbdbdbabfe62e1f264e764f95540b63158d1ea8b1eb0df5b:957508a7546df18784cd285da2e6216e4265906c6c7fba9a895f29a724d63a2e0268128c0c9c2cc304c8c3304863cdfe437a7b93b12dc778c0372a116088e9cd",
|
||||
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626"
|
||||
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626",
|
||||
];
|
||||
const GOOD_V1_IDENTITIES: [&'static str; 10] = [
|
||||
"a8f6e0566e:0:a13a6394de205384eb75eb62179ef11423295c5ecdccfeed7f2eff6c7a74f8059c99eed164c5dfaf2a4cf395ec7b72b68ee1c3c31916de4bc57c07abfe77f9c2::2:A1Cj2O0hKLlhDQ6guCCv5H1UgzbegZwse0iqTaaZov9LpKifyKH0e1VzmHrPmoKcvgJyzI-BAqRQzBiUjScXIjojneNKOywc0Gvq-zeDCYPcXN393xi3q25mB3ud9iEN-GN6wiXPWFjHy-CBD9tGDJzr-G3ZJZvrdiLGT5rZ5W2cZtx8ORYnp9L9HJJOeb8qgdfVr67B5pT9jPsxSsw8P4qlzFFOlX2WN9Hvvu0TO6S_N4yq173deyr-f-ehcBFiBXsSG96p44oU4uRRBEDZWhzHDuD22Vw8PhsB8mko9IRqVXCGbvlaKJ0vyAZ_PyVRM9n_Z-HAEvLveAT-f61mh4YP",
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::{MaybeUninit, size_of, transmute_copy, zeroed};
|
||||
use std::mem::{size_of, transmute_copy, zeroed, MaybeUninit};
|
||||
use std::net::{IpAddr, Ipv6Addr};
|
||||
use std::ptr::{copy_nonoverlapping, null, slice_from_raw_parts, write_bytes};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winsock2 as winsock2;
|
||||
use winapi::um::winsock2;
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::buffer::Buffer;
|
||||
|
@ -74,18 +74,24 @@ pub union InetAddress {
|
|||
|
||||
impl Clone for InetAddress {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self { unsafe { transmute_copy(self) } }
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { transmute_copy(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InetAddress {
|
||||
#[inline(always)]
|
||||
fn default() -> InetAddress { unsafe { zeroed() } }
|
||||
fn default() -> InetAddress {
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
impl InetAddress {
|
||||
/// Get a new zero/nil InetAddress.
|
||||
#[inline(always)]
|
||||
pub fn new() -> InetAddress { unsafe { zeroed() } }
|
||||
pub fn new() -> InetAddress {
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
|
||||
/// Construct from IP and port.
|
||||
/// If the IP is not either 4 or 16 bytes in length, a nil/0 InetAddress is returned.
|
||||
|
@ -100,7 +106,9 @@ impl InetAddress {
|
|||
|
||||
/// Zero the contents of this InetAddress.
|
||||
#[inline(always)]
|
||||
pub fn zero(&mut self) { unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) }; }
|
||||
pub fn zero(&mut self) {
|
||||
unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) };
|
||||
}
|
||||
|
||||
/// Get an instance of 127.0.0.1/port
|
||||
pub fn ipv4_loopback(port: u16) -> InetAddress {
|
||||
|
@ -138,19 +146,35 @@ impl InetAddress {
|
|||
|
||||
/// Returns true if this InetAddress is the nil value (zero).
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool { unsafe { self.sa.sa_family == 0 } }
|
||||
pub fn is_nil(&self) -> bool {
|
||||
unsafe { self.sa.sa_family == 0 }
|
||||
}
|
||||
|
||||
/// Check if this is an IPv4 address.
|
||||
#[inline(always)]
|
||||
pub fn is_ipv4(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET } }
|
||||
pub fn is_ipv4(&self) -> bool {
|
||||
unsafe { self.sa.sa_family as u8 == AF_INET }
|
||||
}
|
||||
|
||||
/// Check if this is an IPv6 address.
|
||||
#[inline(always)]
|
||||
pub fn is_ipv6(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET6 } }
|
||||
pub fn is_ipv6(&self) -> bool {
|
||||
unsafe { self.sa.sa_family as u8 == AF_INET6 }
|
||||
}
|
||||
|
||||
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
||||
#[inline(always)]
|
||||
pub fn family(&self) -> u8 { unsafe { self.sa.sa_family } }
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn family(&self) -> u8 {
|
||||
unsafe { self.sa.sa_family }
|
||||
}
|
||||
|
||||
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
||||
#[inline(always)]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn family(&self) -> u16 {
|
||||
unsafe { self.sa.sa_family }
|
||||
}
|
||||
|
||||
/// Get a pointer to the C "sockaddr" structure and the size of the returned structure in bytes.
|
||||
/// This is useful for interacting with C-level socket APIs. This returns a null pointer if
|
||||
|
@ -161,7 +185,7 @@ impl InetAddress {
|
|||
match self.sa.sa_family as u8 {
|
||||
AF_INET => ((&self.sin as *const sockaddr_in).cast(), size_of::<sockaddr_in>()),
|
||||
AF_INET6 => ((&self.sin6 as *const sockaddr_in6).cast(), size_of::<sockaddr_in6>()),
|
||||
_ => (null(), 0)
|
||||
_ => (null(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +247,7 @@ impl InetAddress {
|
|||
u16::from_be(match self.sa.sa_family as u8 {
|
||||
AF_INET => self.sin.sin_port as u16,
|
||||
AF_INET6 => self.sin6.sin6_port as u16,
|
||||
_ => 0
|
||||
_ => 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -256,42 +280,48 @@ impl InetAddress {
|
|||
0x0a => IpScope::Private, // 10.0.0.0/8
|
||||
0x7f => IpScope::Loopback, // 127.0.0.0/8
|
||||
0x64 => {
|
||||
if (ip & 0xffc00000) == 0x64400000 { // 100.64.0.0/10
|
||||
if (ip & 0xffc00000) == 0x64400000 {
|
||||
// 100.64.0.0/10
|
||||
IpScope::Private
|
||||
} else {
|
||||
IpScope::Global
|
||||
}
|
||||
}
|
||||
0xa9 => {
|
||||
if (ip & 0xffff0000) == 0xa9fe0000 { // 169.254.0.0/16
|
||||
if (ip & 0xffff0000) == 0xa9fe0000 {
|
||||
// 169.254.0.0/16
|
||||
IpScope::LinkLocal
|
||||
} else {
|
||||
IpScope::Global
|
||||
}
|
||||
}
|
||||
0xac => {
|
||||
if (ip & 0xfff00000) == 0xac100000 { // 172.16.0.0/12
|
||||
if (ip & 0xfff00000) == 0xac100000 {
|
||||
// 172.16.0.0/12
|
||||
IpScope::Private
|
||||
} else {
|
||||
IpScope::Global
|
||||
}
|
||||
}
|
||||
0xc0 => {
|
||||
if (ip & 0xffff0000) == 0xc0a80000 || (ip & 0xffffff00) == 0xc0000200 { // 192.168.0.0/16 and 192.0.2.0/24
|
||||
if (ip & 0xffff0000) == 0xc0a80000 || (ip & 0xffffff00) == 0xc0000200 {
|
||||
// 192.168.0.0/16 and 192.0.2.0/24
|
||||
IpScope::Private
|
||||
} else {
|
||||
IpScope::Global
|
||||
}
|
||||
}
|
||||
0xc6 => {
|
||||
if (ip & 0xfffe0000) == 0xc6120000 || (ip & 0xffffff00) == 0xc6336400 { // 198.18.0.0/15 and 198.51.100.0/24
|
||||
if (ip & 0xfffe0000) == 0xc6120000 || (ip & 0xffffff00) == 0xc6336400 {
|
||||
// 198.18.0.0/15 and 198.51.100.0/24
|
||||
IpScope::Private
|
||||
} else {
|
||||
IpScope::Global
|
||||
}
|
||||
}
|
||||
0xcb => {
|
||||
if (ip & 0xffffff00) == 0xcb007100 { // 203.0.113.0/24
|
||||
if (ip & 0xffffff00) == 0xcb007100 {
|
||||
// 203.0.113.0/24
|
||||
IpScope::Private
|
||||
} else {
|
||||
IpScope::Global
|
||||
|
@ -310,13 +340,15 @@ impl InetAddress {
|
|||
0x33_u8, // 51.0.0.0/8 (UK Department of Social Security)
|
||||
0x37_u8, // 55.0.0.0/8 (US DoD)
|
||||
0x38_u8, // 56.0.0.0/8 (US Postal Service)
|
||||
].contains(&class_a) {
|
||||
]
|
||||
.contains(&class_a)
|
||||
{
|
||||
IpScope::PseudoPrivate
|
||||
} else {
|
||||
match ip >> 28 {
|
||||
0xe => IpScope::Multicast, // 224.0.0.0/4
|
||||
0xf => IpScope::Private, // 240.0.0.0/4 ("reserved," usually unusable)
|
||||
_ => IpScope::Global
|
||||
_ => IpScope::Global,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +388,7 @@ impl InetAddress {
|
|||
}
|
||||
IpScope::Global
|
||||
}
|
||||
_ => IpScope::None
|
||||
_ => IpScope::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +402,7 @@ impl InetAddress {
|
|||
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
||||
}
|
||||
AF_INET6 => Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string(),
|
||||
_ => String::from("(null)")
|
||||
_ => String::from("(null)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +426,7 @@ impl InetAddress {
|
|||
b[18] = *(&self.sin6.sin6_port as *const u16).cast::<u8>().offset(1);
|
||||
Ok(())
|
||||
}
|
||||
_ => buf.append_u8(0)
|
||||
_ => buf.append_u8(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -438,33 +470,37 @@ impl FromStr for InetAddress {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut addr = InetAddress::new();
|
||||
let (ip_str, port) = s.find('/').map_or_else(|| {
|
||||
(s, 0)
|
||||
}, |pos| {
|
||||
let ss = s.split_at(pos);
|
||||
let mut port_str = ss.1;
|
||||
if port_str.starts_with('/') {
|
||||
port_str = &port_str[1..];
|
||||
}
|
||||
(ss.0, u16::from_str_radix(port_str, 10).unwrap_or(0).to_be())
|
||||
});
|
||||
IpAddr::from_str(ip_str).map_or_else(|_| Err(InvalidFormatError), |ip| {
|
||||
unsafe {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => {
|
||||
addr.sin.sin_family = AF_INET.into();
|
||||
addr.sin.sin_port = port.into();
|
||||
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
|
||||
}
|
||||
IpAddr::V6(v6) => {
|
||||
addr.sin6.sin6_family = AF_INET6.into();
|
||||
addr.sin6.sin6_port = port.into();
|
||||
copy_nonoverlapping(v6.octets().as_ptr(), (&mut (addr.sin6.sin6_addr) as *mut in6_addr).cast(), 16);
|
||||
let (ip_str, port) = s.find('/').map_or_else(
|
||||
|| (s, 0),
|
||||
|pos| {
|
||||
let ss = s.split_at(pos);
|
||||
let mut port_str = ss.1;
|
||||
if port_str.starts_with('/') {
|
||||
port_str = &port_str[1..];
|
||||
}
|
||||
(ss.0, u16::from_str_radix(port_str, 10).unwrap_or(0).to_be())
|
||||
},
|
||||
);
|
||||
IpAddr::from_str(ip_str).map_or_else(
|
||||
|_| Err(InvalidFormatError),
|
||||
|ip| {
|
||||
unsafe {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => {
|
||||
addr.sin.sin_family = AF_INET.into();
|
||||
addr.sin.sin_port = port.into();
|
||||
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
|
||||
}
|
||||
IpAddr::V6(v6) => {
|
||||
addr.sin6.sin6_family = AF_INET6.into();
|
||||
addr.sin6.sin6_port = port.into();
|
||||
copy_nonoverlapping(v6.octets().as_ptr(), (&mut (addr.sin6.sin6_addr) as *mut in6_addr).cast(), 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(addr)
|
||||
})
|
||||
Ok(addr)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +509,7 @@ impl PartialEq for InetAddress {
|
|||
unsafe {
|
||||
if self.sa.sa_family == other.sa.sa_family {
|
||||
match self.sa.sa_family as u8 {
|
||||
AF_INET => { self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr }
|
||||
AF_INET => self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr,
|
||||
AF_INET6 => {
|
||||
if self.sin6.sin6_port == other.sin6.sin6_port {
|
||||
(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).eq(&*(&(other.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>())
|
||||
|
@ -481,7 +517,7 @@ impl PartialEq for InetAddress {
|
|||
false
|
||||
}
|
||||
}
|
||||
_ => true
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -494,7 +530,9 @@ impl Eq for InetAddress {}
|
|||
|
||||
impl PartialOrd for InetAddress {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
// Manually implement Ord to ensure consistent sort order across platforms, since we don't know exactly
|
||||
|
@ -504,9 +542,7 @@ impl Ord for InetAddress {
|
|||
unsafe {
|
||||
if self.sa.sa_family == other.sa.sa_family {
|
||||
match self.sa.sa_family as u8 {
|
||||
0 => {
|
||||
Ordering::Equal
|
||||
}
|
||||
0 => Ordering::Equal,
|
||||
AF_INET => {
|
||||
let ip_ordering = u32::from_be(self.sin.sin_addr.s_addr as u32).cmp(&u32::from_be(other.sin.sin_addr.s_addr as u32));
|
||||
if ip_ordering == Ordering::Equal {
|
||||
|
@ -532,9 +568,7 @@ impl Ord for InetAddress {
|
|||
}
|
||||
} else {
|
||||
match self.sa.sa_family as u8 {
|
||||
0 => {
|
||||
Ordering::Less
|
||||
}
|
||||
0 => Ordering::Less,
|
||||
AF_INET => {
|
||||
if other.sa.sa_family as u8 == AF_INET6 {
|
||||
Ordering::Less
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
use std::convert::TryInto;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::num::NonZeroI64;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
|
@ -22,15 +22,15 @@ use zerotier_core_crypto::random::{fill_bytes_secure, get_bytes_secure, next_u64
|
|||
use zerotier_core_crypto::salsa::Salsa;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
|
||||
use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
||||
use crate::util::{array_range, u64_as_bytes};
|
||||
use crate::util::buffer::Buffer;
|
||||
use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path};
|
||||
use crate::util::{array_range, u64_as_bytes};
|
||||
use crate::vl1::hybridkey::{HybridKeyPair, HybridPublicKey};
|
||||
use crate::vl1::identity::{IDENTITY_ALGORITHM_ALL, IDENTITY_ALGORITHM_X25519};
|
||||
use crate::vl1::node::*;
|
||||
use crate::vl1::protocol::*;
|
||||
use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret};
|
||||
use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path};
|
||||
use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
||||
|
||||
/// A remote peer known to this node.
|
||||
/// Sending-related and receiving-related fields are locked separately since concurrent
|
||||
|
@ -140,10 +140,12 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
|||
poly.update(packet_frag0_payload_bytes);
|
||||
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| salsa.crypt(packet_frag0_payload_bytes, b));
|
||||
for f in fragments.iter() {
|
||||
let _ = f.as_ref().map(|f| f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
||||
poly.update(f);
|
||||
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
||||
}));
|
||||
let _ = f.as_ref().map(|f| {
|
||||
f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
||||
poly.update(f);
|
||||
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
||||
})
|
||||
});
|
||||
}
|
||||
if poly.finish()[0..8].eq(&header.mac) {
|
||||
Some(u64::from_ne_bytes(header.id))
|
||||
|
@ -211,24 +213,15 @@ impl Peer {
|
|||
|
||||
/// Get the next message ID for sending a message to this peer.
|
||||
#[inline(always)]
|
||||
pub(crate) fn next_message_id(&self) -> u64 { self.message_id_counter.fetch_add(1, Ordering::Relaxed) }
|
||||
pub(crate) fn next_message_id(&self) -> u64 {
|
||||
self.message_id_counter.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Receive, decrypt, authenticate, and process an incoming packet from this peer.
|
||||
///
|
||||
/// If the packet comes in multiple fragments, the fragments slice should contain all
|
||||
/// those fragments after the main packet header and first chunk.
|
||||
pub(crate) fn receive<SI: SystemInterface, VI: VL1VirtualInterface>(
|
||||
&self,
|
||||
node: &Node,
|
||||
si: &SI,
|
||||
vi: &VI,
|
||||
time_ticks: i64,
|
||||
source_endpoint: &Endpoint,
|
||||
source_path: &Arc<Path>,
|
||||
header: &PacketHeader,
|
||||
frag0: &Buffer<{ PACKET_SIZE_MAX }>,
|
||||
fragments: &[Option<PacketBuffer>])
|
||||
{
|
||||
pub(crate) fn receive<SI: SystemInterface, VI: VL1VirtualInterface>(&self, node: &Node, si: &SI, vi: &VI, time_ticks: i64, source_endpoint: &Endpoint, source_path: &Arc<Path>, header: &PacketHeader, frag0: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option<PacketBuffer>]) {
|
||||
let _ = frag0.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| {
|
||||
let mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
|
||||
|
||||
|
@ -313,10 +306,11 @@ impl Peer {
|
|||
}
|
||||
} else {
|
||||
// In debug build check to make sure the next layer (VL2) is complying with the API contract.
|
||||
#[cfg(debug)] {
|
||||
#[cfg(debug)]
|
||||
{
|
||||
if match verb {
|
||||
VERB_VL1_NOP | VERB_VL1_HELLO | VERB_VL1_ERROR | VERB_VL1_OK | VERB_VL1_WHOIS | VERB_VL1_RENDEZVOUS | VERB_VL1_ECHO | VERB_VL1_PUSH_DIRECT_PATHS | VERB_VL1_USER_MESSAGE => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
} {
|
||||
panic!("The next layer handled a VL1 packet! It should not do this.");
|
||||
}
|
||||
|
@ -419,14 +413,15 @@ impl Peer {
|
|||
/// static identity key.
|
||||
pub(crate) fn send_hello<SI: SystemInterface>(&self, si: &SI, node: &Node, explicit_endpoint: Option<&Endpoint>) -> bool {
|
||||
let mut path = None;
|
||||
let destination = explicit_endpoint.map_or_else(|| {
|
||||
self.path(node).map_or(None, |p| {
|
||||
path = Some(p.clone());
|
||||
Some(p.endpoint().as_ref().clone())
|
||||
})
|
||||
}, |endpoint| {
|
||||
Some(endpoint.clone())
|
||||
});
|
||||
let destination = explicit_endpoint.map_or_else(
|
||||
|| {
|
||||
self.path(node).map_or(None, |p| {
|
||||
path = Some(p.clone());
|
||||
Some(p.endpoint().as_ref().clone())
|
||||
})
|
||||
},
|
||||
|endpoint| Some(endpoint.clone()),
|
||||
);
|
||||
if destination.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
@ -519,16 +514,17 @@ impl Peer {
|
|||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
self.total_bytes_sent.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
||||
|
||||
path.map_or_else(|| {
|
||||
self.send_to_endpoint(si, &destination, None, None, &packet)
|
||||
}, |p| {
|
||||
if self.send_to_endpoint(si, &destination, p.local_socket(), p.local_interface(), &packet) {
|
||||
p.log_send_anything(time_ticks);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
path.map_or_else(
|
||||
|| self.send_to_endpoint(si, &destination, None, None, &packet),
|
||||
|p| {
|
||||
if self.send_to_endpoint(si, &destination, p.local_socket(), p.local_interface(), &packet) {
|
||||
p.log_send_anything(time_ticks);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
|
||||
|
|
Loading…
Add table
Reference in a new issue