Finish huge network hypervisor refactor.

This commit is contained in:
Adam Ierymenko 2023-03-24 18:38:33 -04:00
parent 86652ec969
commit ec7fae71b4
17 changed files with 523 additions and 256 deletions

View file

@ -12,10 +12,18 @@ use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::hex; use zerotier_utils::hex;
use zerotier_utils::memory; use zerotier_utils::memory;
/// A full (V2) ZeroTier address.
///
/// The first 40 bits (5 bytes) of the address are the legacy 40-bit short ZeroTier address computed from
/// a hash of the identity's X25519 keys. The remaining bits are a SHA384 hash of that short address and
/// all key types and key material. See identity.rs for details.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)] #[repr(transparent)]
pub struct Address(pub(super) [u8; Self::SIZE_BYTES]); pub struct Address(pub(super) [u8; Self::SIZE_BYTES]);
/// A partial address, which is bytes and the number of bytes of specificity (similar to a CIDR IP address).
///
/// Partial addresses are looked up to get full addresses (and identities) via roots using WHOIS messages.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PartialAddress(pub(super) Address, pub(super) u16); pub struct PartialAddress(pub(super) Address, pub(super) u16);
@ -39,18 +47,36 @@ impl Address {
} }
} }
/// Get the first 40 bits of this address (for legacy use) /// Get the first 40 bits of this address (a legacy V1 ZeroTier address)
#[inline(always)] #[inline(always)]
pub(crate) fn legacy_bytes(&self) -> &[u8; 5] { pub fn legacy_bytes(&self) -> &[u8; 5] {
memory::array_range::<u8, { Address::SIZE_BYTES }, 0, { PartialAddress::LEGACY_SIZE_BYTES }>(&self.0) memory::array_range::<u8, { Address::SIZE_BYTES }, 0, { PartialAddress::LEGACY_SIZE_BYTES }>(&self.0)
} }
/// Get the legacy address in the least significant bits of a u64.
#[inline(always)]
pub(crate) fn legacy_u64(&self) -> u64 {
u64::from_be(memory::load_raw(&self.0)).wrapping_shr(24)
}
/// Get a partial address object (with full specificity) for this address /// Get a partial address object (with full specificity) for this address
#[inline(always)] #[inline(always)]
pub fn to_partial(&self) -> PartialAddress { pub fn to_partial(&self) -> PartialAddress {
PartialAddress(Address(self.0), Self::SIZE_BYTES as u16) PartialAddress(Address(self.0), Self::SIZE_BYTES as u16)
} }
/// Get a partial address covering the 40-bit legacy address.
pub fn to_legacy_partial(&self) -> PartialAddress {
PartialAddress(
Address({
let mut tmp = [0u8; PartialAddress::MAX_SIZE_BYTES];
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&self.0[..PartialAddress::LEGACY_SIZE_BYTES]);
tmp
}),
PartialAddress::LEGACY_SIZE_BYTES as u16,
)
}
#[inline(always)] #[inline(always)]
pub fn as_bytes(&self) -> &[u8; Self::SIZE_BYTES] { pub fn as_bytes(&self) -> &[u8; Self::SIZE_BYTES] {
&self.0 &self.0
@ -65,20 +91,18 @@ impl Borrow<[u8; Self::SIZE_BYTES]> for Address {
} }
impl ToString for Address { impl ToString for Address {
#[inline(always)]
fn to_string(&self) -> String { fn to_string(&self) -> String {
let mut tmp = String::with_capacity(Self::SIZE_BYTES * 2); base24::encode(&self.0)
base24::encode_into(&self.0, &mut tmp);
tmp
} }
} }
impl FromStr for Address { impl FromStr for Address {
type Err = InvalidParameterError; type Err = InvalidParameterError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut tmp = Vec::with_capacity(Self::SIZE_BYTES); base24::decode(s.as_bytes()).and_then(|b| Self::from_bytes(b.as_slice()))
base24::decode_into(s, &mut tmp);
Self::from_bytes(tmp.as_slice())
} }
} }
@ -163,11 +187,6 @@ impl PartialAddress {
pub const MIN_SIZE_BYTES: usize = Self::LEGACY_SIZE_BYTES; pub const MIN_SIZE_BYTES: usize = Self::LEGACY_SIZE_BYTES;
pub const MAX_SIZE_BYTES: usize = Address::SIZE_BYTES; pub const MAX_SIZE_BYTES: usize = Address::SIZE_BYTES;
/// Create an invalid uninitialized address (used when generating Identity)
pub(super) fn new_uninitialized() -> Self {
Self(Address([0u8; Self::MAX_SIZE_BYTES]), 0)
}
/// Construct an address from a byte slice with its length determining specificity. /// Construct an address from a byte slice with its length determining specificity.
#[inline] #[inline]
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidParameterError> { pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidParameterError> {
@ -229,6 +248,11 @@ impl PartialAddress {
memory::array_range::<u8, { Address::SIZE_BYTES }, 0, { PartialAddress::LEGACY_SIZE_BYTES }>(&self.0 .0) memory::array_range::<u8, { Address::SIZE_BYTES }, 0, { PartialAddress::LEGACY_SIZE_BYTES }>(&self.0 .0)
} }
#[inline(always)]
pub(crate) fn legacy_u64(&self) -> u64 {
u64::from_be(memory::load_raw(&self.0 .0)).wrapping_shr(24)
}
#[inline(always)] #[inline(always)]
pub(super) fn matches(&self, k: &Address) -> bool { pub(super) fn matches(&self, k: &Address) -> bool {
debug_assert!(self.1 >= Self::MIN_SIZE_BYTES as u16); debug_assert!(self.1 >= Self::MIN_SIZE_BYTES as u16);
@ -270,9 +294,7 @@ impl ToString for PartialAddress {
if self.is_legacy() { if self.is_legacy() {
hex::to_string(&self.0 .0[..Self::LEGACY_SIZE_BYTES]) hex::to_string(&self.0 .0[..Self::LEGACY_SIZE_BYTES])
} else { } else {
let mut tmp = String::with_capacity(Self::MAX_SIZE_BYTES * 2); base24::encode(self.as_bytes())
base24::encode_into(&self.0 .0[..self.1 as usize], &mut tmp);
tmp
} }
} }
} }
@ -282,11 +304,9 @@ impl FromStr for PartialAddress {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 10 { if s.len() == 10 {
return Self::from_bytes(hex::from_string(s).as_slice()); Self::from_bytes(hex::from_string(s).as_slice())
} else { } else {
let mut tmp = Vec::with_capacity(Self::MAX_SIZE_BYTES); base24::decode(s.as_bytes()).and_then(|b| Self::from_bytes(b.as_slice()))
base24::decode_into(s, &mut tmp)?;
return Self::from_bytes(tmp.as_slice());
} }
} }
} }

View file

@ -37,7 +37,6 @@ pub enum Endpoint {
Nil, Nil,
/// Via another node using unencapsulated relaying (e.g. via a root) /// Via another node using unencapsulated relaying (e.g. via a root)
/// This is the address and the full identity fingerprint.
ZeroTier(Address), ZeroTier(Address),
/// Direct L2 Ethernet /// Direct L2 Ethernet
@ -65,7 +64,6 @@ pub enum Endpoint {
WebRTC(Vec<u8>), WebRTC(Vec<u8>),
/// Via another node using inner encapsulation via VERB_ENCAP. /// Via another node using inner encapsulation via VERB_ENCAP.
/// This is the address and the full identity fingerprint.
ZeroTierEncap(Address), ZeroTierEncap(Address),
} }

View file

@ -1,8 +1,10 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. // (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
use std::array::TryFromSliceError;
use std::io::Write; use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::address::{Address, PartialAddress}; use super::address::{Address, PartialAddress};
@ -14,6 +16,7 @@ use zerotier_crypto::secret::Secret;
use zerotier_crypto::typestate::Valid; use zerotier_crypto::typestate::Valid;
use zerotier_crypto::x25519::*; use zerotier_crypto::x25519::*;
use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::base24;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError}; use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::error::InvalidFormatError; use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError}; use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
@ -65,7 +68,7 @@ impl Identity {
const ALGORITHM_X25519: u8 = 0; const ALGORITHM_X25519: u8 = 0;
const ALGORITHM_P384: u8 = 1; const ALGORITHM_P384: u8 = 1;
const V0_IDENTITY_POW_THRESHOLD: u8 = 17; const LEGACY_ADDRESS_POW_THRESHOLD: u8 = 17;
/// Generate a new ZeroTier identity. /// Generate a new ZeroTier identity.
/// If x25519_only is true a legacy identity without NIST P-384 key pairs will be generated. /// If x25519_only is true a legacy identity without NIST P-384 key pairs will be generated.
@ -85,23 +88,27 @@ impl Identity {
x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: ed25519_eddsa }, x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: ed25519_eddsa },
p384: None, p384: None,
}; };
let mut legacy_address_derivation_hasher = SHA512::new();
loop { loop {
let mut legacy_address_derivation_hash = SHA512::new(); legacy_address_derivation_hasher.update(&secret.public.x25519.ecdh);
legacy_address_derivation_hash.update(&secret.public.x25519.ecdh); legacy_address_derivation_hasher.update(&secret.public.x25519.eddsa);
legacy_address_derivation_hash.update(&secret.public.x25519.eddsa); let mut legacy_address_derivation_hash = legacy_address_derivation_hasher.finish();
let mut legacy_address_derivation_hash = legacy_address_derivation_hash.finish();
legacy_address_derivation_work_function(&mut legacy_address_derivation_hash); legacy_address_derivation_work_function(&mut legacy_address_derivation_hash);
if legacy_address_derivation_hash[0] < Self::V0_IDENTITY_POW_THRESHOLD && legacy_address_derivation_hash[59] != Address::RESERVED_PREFIX { if legacy_address_derivation_hash[0] < Self::LEGACY_ADDRESS_POW_THRESHOLD
&& legacy_address_derivation_hash[59] != Address::RESERVED_PREFIX
&& legacy_address_derivation_hash[59..64].iter().any(|i| *i != 0)
{
secret.public.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&legacy_address_derivation_hash[59..64]); secret.public.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&legacy_address_derivation_hash[59..64]);
break; break;
} else { } else {
// Regenerate one of the two keys until we meet the legacy address work function criteria. // Regenerate one of the two keys until we meet the legacy address work function criteria.
secret.x25519.ecdh = X25519KeyPair::generate(); secret.x25519.ecdh = X25519KeyPair::generate();
secret.public.x25519.ecdh = secret.x25519.ecdh.public_bytes(); secret.public.x25519.ecdh = secret.x25519.ecdh.public_bytes();
legacy_address_derivation_hasher.reset();
} }
} }
// Generate NIST P-384 key pairs unless this is disabled. // Generate NIST P-384 key pairs unless we just want a legacy identity.
if !x25519_only { if !x25519_only {
secret.p384 = Some(P384Secret { secret.p384 = Some(P384Secret {
ecdh: P384KeyPair::generate(), ecdh: P384KeyPair::generate(),
@ -116,7 +123,9 @@ impl Identity {
} }
// Bits 40-384 of the address are filled from a SHA384 hash of all keys for a full length V2 address. // Bits 40-384 of the address are filled from a SHA384 hash of all keys for a full length V2 address.
secret.public.populate_extended_address_bits(); let mut address = secret.public.address.clone();
secret.public.populate_extended_address_bits(&mut address);
secret.public.address = address;
// For V2 identities we include two self signatures to ensure that all these different key pairs // For V2 identities we include two self signatures to ensure that all these different key pairs
// are properly bound together and can't be changed independently. // are properly bound together and can't be changed independently.
@ -133,9 +142,43 @@ impl Identity {
} }
/// Locally validate this identity. /// Locally validate this identity.
/// This checks address derivation, any self-signatures, etc. /// This checks address derivation, any self-signatures, etc. It's a little time consuming so results should be cached
pub fn validate(&self) -> Option<Valid<Self>> { /// if possible. Returns a marked typestate on success, which does sort of cache the results and prevents misuse.
todo!() pub fn validate(self) -> Option<Valid<Self>> {
// First, check that the full SHA384 (bits 40-384) in the address is correct. Check first since this is fast.
let mut test_address = Address::new_uninitialized();
test_address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]);
self.populate_extended_address_bits(&mut test_address);
if self.address != test_address {
return None;
}
// Second, check self-signatures if we have them. Check second since this is somewhat slower.
if let Some(p384) = self.p384.as_ref() {
let mut for_self_signing =
[0u8; Address::SIZE_BYTES + 1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE];
self.encode_for_self_signing(&mut for_self_signing);
if !ed25519_verify(&self.x25519.eddsa, &p384.ed25519_self_signature, &for_self_signing)
|| !p384.ecdsa.verify(&for_self_signing, &p384.p384_self_signature)
{
return None;
}
}
// Finally, check the legacy address as this is the most costly check. If we deprecate V1 this could go away
// in the future since the full SHA384 (checked above) includes the first 40 bits of the address in the hash.
let mut legacy_address_derivation_hasher = SHA512::new();
legacy_address_derivation_hasher.update(&self.x25519.ecdh);
legacy_address_derivation_hasher.update(&self.x25519.eddsa);
let mut legacy_address_derivation_hash = legacy_address_derivation_hasher.finish();
legacy_address_derivation_work_function(&mut legacy_address_derivation_hash);
if legacy_address_derivation_hash[0] >= Self::LEGACY_ADDRESS_POW_THRESHOLD
|| !legacy_address_derivation_hash[59..64].eq(self.address.legacy_bytes())
{
return None;
}
return Some(Valid::mark_valid(self));
} }
/// Verify a signature with this identity. /// Verify a signature with this identity.
@ -148,7 +191,7 @@ impl Identity {
} }
/// Populate bits 40-384 of the address with a hash of everything else. /// Populate bits 40-384 of the address with a hash of everything else.
fn populate_extended_address_bits(&mut self) { fn populate_extended_address_bits(&self, address: &mut Address) {
let mut sha = SHA384::new(); let mut sha = SHA384::new();
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
sha.update(&[Self::ALGORITHM_X25519 sha.update(&[Self::ALGORITHM_X25519
@ -164,7 +207,7 @@ impl Identity {
sha.update(p384.ecdsa.as_bytes()); sha.update(p384.ecdsa.as_bytes());
} }
let sha = sha.finish(); let sha = sha.finish();
self.address.0[PartialAddress::LEGACY_SIZE_BYTES..].copy_from_slice(&sha[..Address::SIZE_BYTES - PartialAddress::LEGACY_SIZE_BYTES]); address.0[PartialAddress::LEGACY_SIZE_BYTES..].copy_from_slice(&sha[..Address::SIZE_BYTES - PartialAddress::LEGACY_SIZE_BYTES]);
} }
/// Encode for self-signing, used only with p384 keys enabled and panics otherwise. /// Encode for self-signing, used only with p384 keys enabled and panics otherwise.
@ -178,10 +221,13 @@ impl Identity {
let _ = buf.write_all(p384.ecdsa.as_bytes()); let _ = buf.write_all(p384.ecdsa.as_bytes());
} }
/// Decode a byte serialized identity.
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> { pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> {
let mut id;
let mut address = Address::new_uninitialized();
if b.len() == packed::V2_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == (Self::ALGORITHM_X25519 | Self::ALGORITHM_P384) { if b.len() == packed::V2_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == (Self::ALGORITHM_X25519 | Self::ALGORITHM_P384) {
let p: &packed::V2Public = memory::cast_to_struct(b); let p: &packed::V2Public = memory::cast_to_struct(b);
let mut id = Self { id = Self {
address: Address::new_uninitialized(), address: Address::new_uninitialized(),
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 }, x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
p384: Some(P384 { p384: Some(P384 {
@ -191,24 +237,24 @@ impl Identity {
p384_self_signature: p.p384_self_signature, p384_self_signature: p.p384_self_signature,
}), }),
}; };
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address); address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
id.populate_extended_address_bits();
return Ok(id);
} else if b.len() == packed::V1_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == Self::ALGORITHM_X25519 { } else if b.len() == packed::V1_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == Self::ALGORITHM_X25519 {
let p: &packed::V1Public = memory::cast_to_struct(b); let p: &packed::V1Public = memory::cast_to_struct(b);
let mut id = Self { id = Self {
address: Address::new_uninitialized(), address: Address::new_uninitialized(),
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 }, x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
p384: None, p384: None,
}; };
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address); address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
id.populate_extended_address_bits();
return Ok(id);
} else { } else {
return Err(InvalidFormatError); return Err(InvalidFormatError);
} }
id.populate_extended_address_bits(&mut address);
id.address = address;
return Ok(id);
} }
/// Write as a byte serialized identity to a writer.
pub fn write_bytes<W: Write>(&self, w: &mut W, x25519_only: bool) -> Result<(), std::io::Error> { pub fn write_bytes<W: Write>(&self, w: &mut W, x25519_only: bool) -> Result<(), std::io::Error> {
if let (false, Some(p384)) = (x25519_only, self.p384.as_ref()) { if let (false, Some(p384)) = (x25519_only, self.p384.as_ref()) {
w.write_all(memory::as_byte_array::<packed::V2Public, { packed::V2_PUBLIC_SIZE }>(&packed::V2Public { w.write_all(memory::as_byte_array::<packed::V2Public, { packed::V2_PUBLIC_SIZE }>(&packed::V2Public {
@ -236,16 +282,21 @@ impl Identity {
impl ToString for Identity { impl ToString for Identity {
fn to_string(&self) -> String { fn to_string(&self) -> String {
if let Some(p384) = self.p384.as_ref() { if let Some(p384) = self.p384.as_ref() {
format!( let mut s = String::with_capacity(1024);
"{}:1:{}:{}:{}:{}:{}:{}", base24::encode_into(self.address.as_bytes(), &mut s);
self.address.to_string(), s.push_str(":1:");
hex::to_string(&self.x25519.ecdh), base24::encode_into(&self.x25519.ecdh, &mut s);
hex::to_string(&self.x25519.eddsa), s.push(':');
hex::to_string(p384.ecdh.as_bytes()), base24::encode_into(&self.x25519.eddsa, &mut s);
hex::to_string(p384.ecdsa.as_bytes()), s.push(':');
hex::to_string(&p384.ed25519_self_signature), base24::encode_into(p384.ecdh.as_bytes(), &mut s);
hex::to_string(&p384.p384_self_signature) s.push(':');
) base24::encode_into(p384.ecdsa.as_bytes(), &mut s);
s.push(':');
base24::encode_into(&p384.ed25519_self_signature, &mut s);
s.push(':');
base24::encode_into(&p384.p384_self_signature, &mut s);
s
} else { } else {
format!( format!(
"{}:0:{}:{}", "{}:0:{}:{}",
@ -264,9 +315,54 @@ impl FromStr for Identity {
let ss: Vec<&str> = s.split(':').collect(); let ss: Vec<&str> = s.split(':').collect();
if ss.len() >= 2 { if ss.len() >= 2 {
if ss[1] == "1" && ss.len() == 8 { if ss[1] == "1" && ss.len() == 8 {
todo!() return Ok(Self {
address: Address::from_str(ss[0]).map_err(|_| InvalidFormatError)?,
x25519: X25519 {
ecdh: base24::decode(ss[2].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
eddsa: base24::decode(ss[3].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
},
p384: Some(P384 {
ecdh: P384PublicKey::from_bytes(base24::decode(ss[4].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
.ok_or(InvalidFormatError)?,
ecdsa: P384PublicKey::from_bytes(base24::decode(ss[5].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
.ok_or(InvalidFormatError)?,
ed25519_self_signature: base24::decode(ss[6].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
p384_self_signature: base24::decode(ss[7].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
}),
});
} else if ss[1] == "0" && ss.len() == 4 { } else if ss[1] == "0" && ss.len() == 4 {
todo!() let mut address = {
let legacy_address = hex::from_string(ss[0]);
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
return Err(InvalidFormatError);
}
let mut tmp = [0u8; Address::SIZE_BYTES];
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
Address(tmp)
};
let mut a = Self {
address: Address::new_uninitialized(),
x25519: X25519 {
ecdh: hex::from_string(ss[2]).try_into().map_err(|_| InvalidFormatError)?,
eddsa: hex::from_string(ss[3]).try_into().map_err(|_| InvalidFormatError)?,
},
p384: None,
};
a.populate_extended_address_bits(&mut address);
a.address = address;
return Ok(a);
} }
} }
return Err(InvalidFormatError); return Err(InvalidFormatError);
@ -409,6 +505,110 @@ impl<'de> Deserialize<'de> for Identity {
} }
} }
/// The actual serialization format for secret identities.
#[derive(Serialize, Deserialize)]
struct IdentitySecretForSerialization<'a> {
pub address: &'a [u8],
pub ed25519_self_signature: Option<&'a [u8]>,
pub p384_self_signature: Option<&'a [u8]>,
pub x25519_ecdh_public: Option<&'a [u8]>,
pub x25519_ecdh_secret: Option<&'a [u8]>,
pub x25519_eddsa_public: Option<&'a [u8]>,
pub x25519_eddsa_secret: Option<&'a [u8]>,
pub p384_ecdh_public: Option<&'a [u8]>,
pub p384_ecdh_secret: Option<&'a [u8]>,
pub p384_ecdsa_public: Option<&'a [u8]>,
pub p384_ecdsa_secret: Option<&'a [u8]>,
}
impl Serialize for IdentitySecret {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let p384_ecdh_secret = self.p384.as_ref().map(|p384| p384.ecdh.secret_key_bytes());
let p384_ecdsa_secret = self.p384.as_ref().map(|p384| p384.ecdsa.secret_key_bytes());
IdentitySecretForSerialization {
address: self.public.address.as_bytes(),
ed25519_self_signature: self.public.p384.as_ref().map(|p384| &p384.ed25519_self_signature[..]),
p384_self_signature: self.public.p384.as_ref().map(|p384| &p384.p384_self_signature[..]),
x25519_ecdh_public: Some(&self.public.x25519.ecdh),
x25519_ecdh_secret: Some(&self.x25519.ecdh.secret_bytes().as_bytes()[..]),
x25519_eddsa_public: Some(&self.public.x25519.eddsa),
x25519_eddsa_secret: Some(&self.x25519.eddsa.secret_bytes().as_bytes()[..]),
p384_ecdh_public: self.p384.as_ref().map(|p384| &p384.ecdh.public_key_bytes()[..]),
p384_ecdh_secret: p384_ecdh_secret.as_ref().map(|s| &s.as_bytes()[..]),
p384_ecdsa_public: self.p384.as_ref().map(|p384| &p384.ecdsa.public_key_bytes()[..]),
p384_ecdsa_secret: p384_ecdsa_secret.as_ref().map(|s| &s.as_bytes()[..]),
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for IdentitySecret {
fn deserialize<D>(deserializer: D) -> Result<IdentitySecret, D::Error>
where
D: Deserializer<'de>,
{
IdentitySecretForSerialization::deserialize(deserializer).and_then(|tmp| {
if let (Some(x25519_ecdh_public), Some(x25519_ecdh_secret), Some(x25519_eddsa_public), Some(x25519_eddsa_secret)) = (
tmp.x25519_ecdh_public,
tmp.x25519_ecdh_secret,
tmp.x25519_eddsa_public,
tmp.x25519_eddsa_secret,
) {
let tmp_p384 = if let (Some(a), Some(b), Some(c), Some(d), Some(e), Some(f)) = (
tmp.ed25519_self_signature,
tmp.p384_self_signature,
tmp.p384_ecdh_public,
tmp.p384_ecdh_secret,
tmp.p384_ecdsa_public,
tmp.p384_ecdsa_secret,
) {
Some((a, b, c, d, e, f))
} else {
None
};
let e2 = || D::Error::custom("invalid key");
let e = |_e: TryFromSliceError| e2();
Ok(IdentitySecret {
public: Valid::mark_valid(Identity {
address: Address::from_bytes(tmp.address).map_err(|_| e2())?,
x25519: X25519 {
ecdh: x25519_ecdh_public.try_into().map_err(e)?,
eddsa: x25519_eddsa_public.try_into().map_err(e)?,
},
p384: if let Some(tmp_p384) = tmp_p384.as_ref() {
Some(P384 {
ecdh: P384PublicKey::from_bytes(tmp_p384.2).ok_or_else(e2)?,
ecdsa: P384PublicKey::from_bytes(tmp_p384.3).ok_or_else(e2)?,
ed25519_self_signature: tmp_p384.0.try_into().map_err(e)?,
p384_self_signature: tmp_p384.1.try_into().map_err(e)?,
})
} else {
None
},
}),
x25519: X25519Secret {
ecdh: X25519KeyPair::from_bytes(x25519_ecdh_public, x25519_ecdh_secret).ok_or_else(e2)?,
eddsa: Ed25519KeyPair::from_bytes(x25519_eddsa_public, x25519_eddsa_secret).ok_or_else(e2)?,
},
p384: if let Some(tmp_p384) = tmp_p384.as_ref() {
Some(P384Secret {
ecdh: P384KeyPair::from_bytes(tmp_p384.2, tmp_p384.3).ok_or_else(e2)?,
ecdsa: P384KeyPair::from_bytes(tmp_p384.4, tmp_p384.5).ok_or_else(e2)?,
})
} else {
None
},
})
} else {
Err(D::Error::custom("missing required fields"))
}
})
}
}
mod packed { mod packed {
use super::*; use super::*;

View file

@ -14,7 +14,7 @@ mod whois;
pub mod identity; pub mod identity;
pub mod inetaddress; pub mod inetaddress;
pub use address::Address; pub use address::{Address, PartialAddress};
pub use endpoint::Endpoint; pub use endpoint::Endpoint;
pub use event::Event; pub use event::Event;
pub use inetaddress::InetAddress; pub use inetaddress::InetAddress;

View file

@ -50,6 +50,7 @@ pub trait ApplicationLayer: Sync + Send + 'static {
/// ///
/// The default implementation always returns true. Typically this is what you want for a /// The default implementation always returns true. Typically this is what you want for a
/// controller or a root but not a regular node (unless required for backward compatibility). /// controller or a root but not a regular node (unless required for backward compatibility).
#[allow(unused)]
fn should_respond_to(&self, id: &Valid<Identity>) -> bool { fn should_respond_to(&self, id: &Valid<Identity>) -> bool {
true true
} }

View file

@ -45,7 +45,7 @@ impl<Application: ApplicationLayer + ?Sized> PeerMap<Application> {
for m in matches { for m in matches {
if address.matches(m.0) { if address.matches(m.0) {
if r.is_none() { if r.is_none() {
r.insert(m.1); let _ = r.insert(m.1);
} else { } else {
return None; return None;
} }
@ -58,8 +58,8 @@ impl<Application: ApplicationLayer + ?Sized> PeerMap<Application> {
/// Insert the supplied peer if it is in fact new, otherwise return the existing peer with the same address. /// Insert the supplied peer if it is in fact new, otherwise return the existing peer with the same address.
pub fn add(&self, peer: Arc<Peer<Application>>) -> (Arc<Peer<Application>>, bool) { pub fn add(&self, peer: Arc<Peer<Application>>) -> (Arc<Peer<Application>>, bool) {
let mm = self.maps[peer.identity.address.0[0] as usize].write().unwrap(); let mut mm = self.maps[peer.identity.address.0[0] as usize].write().unwrap();
let p = mm.entry(peer.identity.address).or_insert(peer.clone()); let p = mm.entry(peer.identity.address.clone()).or_insert(peer.clone());
if Arc::ptr_eq(p, &peer) { if Arc::ptr_eq(p, &peer) {
(peer, true) (peer, true)
} else { } else {
@ -69,13 +69,18 @@ impl<Application: ApplicationLayer + ?Sized> PeerMap<Application> {
/// Get a peer or create one if not found. /// Get a peer or create one if not found.
/// This should be used when the peer will almost always be new, such as on OK(WHOIS). /// This should be used when the peer will almost always be new, such as on OK(WHOIS).
pub fn get_or_add(&self, this_node_identity: &IdentitySecret, peer_identity: Valid<Identity>, time_ticks: i64) -> Option<Arc<Peer<Application>>> { pub fn get_or_add(
let peer = Arc::new(Peer::new(this_node_identity, peer_identity, time_ticks)?); &self,
this_node_identity: &IdentitySecret,
peer_identity: &Valid<Identity>,
time_ticks: i64,
) -> Option<Arc<Peer<Application>>> {
let peer = Arc::new(Peer::new(this_node_identity, peer_identity.clone(), time_ticks)?);
Some( Some(
self.maps[peer_identity.address.0[0] as usize] self.maps[peer_identity.address.0[0] as usize]
.write() .write()
.unwrap() .unwrap()
.entry(peer.identity.address) .entry(peer_identity.address.clone())
.or_insert(peer) .or_insert(peer)
.clone(), .clone(),
) )

View file

@ -13,7 +13,7 @@ use crate::protocol;
use zerotier_crypto::typestate::Valid; use zerotier_crypto::typestate::Valid;
use zerotier_utils::ringbuffer::RingBuffer; use zerotier_utils::ringbuffer::RingBuffer;
pub struct Whois<Application: ApplicationLayer + ?Sized> { pub(super) struct Whois<Application: ApplicationLayer + ?Sized> {
whois_queue: Mutex<BTreeMap<PartialAddress, WhoisQueueItem<Application>>>, whois_queue: Mutex<BTreeMap<PartialAddress, WhoisQueueItem<Application>>>,
} }
@ -54,18 +54,18 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
let mut to_delete = Vec::with_capacity(2); let mut to_delete = Vec::with_capacity(2);
for qi in q.range((Bound::Unbounded, Bound::Included(identity.address.to_partial()))).rev() { for qi in q.range((Bound::Unbounded, Bound::Included(identity.address.to_partial()))).rev() {
if qi.0.matches(&identity.address) { if qi.0.matches(&identity.address) {
to_delete.push(qi.0); to_delete.push(qi.0.clone());
// TODO // TODO
} else { } else {
break; break;
} }
} }
for a in to_delete { for a in to_delete {
queued_items.push(q.remove(a).unwrap()); queued_items.push(q.remove(&a).unwrap());
} }
} }
if let Some(peer) = node.peers.get_or_add(&node.identity_secret, identity, time_ticks) { if let Some(peer) = node.peers.get_or_add(&node.identity_secret, &identity, time_ticks) {
for qi in queued_items.iter() { for qi in queued_items.iter() {
for pkt in qi.pending_v1_packets.iter() { for pkt in qi.pending_v1_packets.iter() {
if let Some(source_path) = pkt.0.upgrade() { if let Some(source_path) = pkt.0.upgrade() {
@ -80,7 +80,7 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
pub fn retry_queued(&self) {} pub fn retry_queued(&self) {}
fn send_whois(&self, app: &Application, node: &Node<Application>, addresses: &[PartialAddress], time_ticks: i64) { fn send_whois(&self, app: &Application, node: &Node<Application>, mut addresses: &[PartialAddress], time_ticks: i64) {
debug_assert!(!addresses.is_empty()); debug_assert!(!addresses.is_empty());
debug_event!(app, "[vl1] [v1] sending WHOIS for {}", { debug_event!(app, "[vl1] [v1] sending WHOIS for {}", {
let mut tmp = String::new(); let mut tmp = String::new();

View file

@ -42,7 +42,7 @@ impl MulticastAuthority {
} }
/// Call for VL2_MULTICAST_LIKE packets. /// Call for VL2_MULTICAST_LIKE packets.
pub fn handle_vl2_multicast_like<Application: ApplicationLayer + ?Sized, Authenticator: Fn(NetworkId, &Identity) -> bool>( pub fn handle_vl2_multicast_like<Application: ApplicationLayer + ?Sized, Authenticator: Fn(&NetworkId, &Identity) -> bool>(
&self, &self,
auth: Authenticator, auth: Authenticator,
time_ticks: i64, time_ticks: i64,
@ -53,12 +53,12 @@ impl MulticastAuthority {
let mut subscriptions = RMaybeWLockGuard::new_read(&self.subscriptions); let mut subscriptions = RMaybeWLockGuard::new_read(&self.subscriptions);
while (cursor + 8 + 6 + 4) <= payload.len() { while (cursor + 8 + 6 + 4) <= payload.len() {
let network_id = NetworkId::from_bytes_fixed(payload.read_bytes_fixed(&mut cursor).unwrap()); let network_id = NetworkId::from_bytes(payload.read_bytes_fixed::<8>(&mut cursor).unwrap());
if let Some(network_id) = network_id { if let Ok(network_id) = network_id {
let mac = MAC::from_bytes_fixed(payload.read_bytes_fixed(&mut cursor).unwrap()); let mac = MAC::from_bytes_fixed(payload.read_bytes_fixed(&mut cursor).unwrap());
if let Some(mac) = mac { if let Some(mac) = mac {
if auth(network_id, &source.identity) { if auth(&network_id, &source.identity) {
let sub_key = (network_id, MulticastGroup { mac, adi: payload.read_u32(&mut cursor).unwrap() }); let sub_key = (network_id.clone(), MulticastGroup { mac, adi: payload.read_u32(&mut cursor).unwrap() });
if let Some(sub) = subscriptions.read().get(&sub_key) { if let Some(sub) = subscriptions.read().get(&sub_key) {
let _ = sub.lock().unwrap().insert(source.identity.address.clone(), time_ticks); let _ = sub.lock().unwrap().insert(source.identity.address.clone(), time_ticks);
} else { } else {
@ -79,7 +79,7 @@ impl MulticastAuthority {
} }
/// Call for VL2_MULTICAST_GATHER packets. /// Call for VL2_MULTICAST_GATHER packets.
pub fn handle_vl2_multicast_gather<Application: ApplicationLayer + ?Sized, Authenticator: Fn(NetworkId, &Identity) -> bool>( pub fn handle_vl2_multicast_gather<Application: ApplicationLayer + ?Sized, Authenticator: Fn(&NetworkId, &Identity) -> bool>(
&self, &self,
auth: Authenticator, auth: Authenticator,
time_ticks: i64, time_ticks: i64,
@ -91,17 +91,17 @@ impl MulticastAuthority {
mut cursor: usize, mut cursor: usize,
) -> PacketHandlerResult { ) -> PacketHandlerResult {
if let Some(network_id) = payload if let Some(network_id) = payload
.read_bytes_fixed(&mut cursor) .read_bytes_fixed::<8>(&mut cursor)
.map_or(None, |network_id| NetworkId::from_bytes_fixed(network_id)) .map_or(None, |network_id| NetworkId::from_bytes(network_id).ok())
{ {
if auth(network_id, &source.identity) { if auth(&network_id, &source.identity) {
cursor += 1; // skip flags, currently unused cursor += 1; // skip flags, currently unused
if let Some(mac) = payload.read_bytes_fixed(&mut cursor).map_or(None, |mac| MAC::from_bytes_fixed(mac)) { if let Some(mac) = payload.read_bytes_fixed(&mut cursor).map_or(None, |mac| MAC::from_bytes_fixed(mac)) {
let mut gathered = Vec::new(); let mut gathered = Vec::new();
let adi = payload.read_u32(&mut cursor).unwrap_or(0); let adi = payload.read_u32(&mut cursor).unwrap_or(0);
let subscriptions = self.subscriptions.read().unwrap(); let subscriptions = self.subscriptions.read().unwrap();
if let Some(sub) = subscriptions.get(&(network_id, MulticastGroup { mac, adi })) { if let Some(sub) = subscriptions.get(&(network_id.clone(), MulticastGroup { mac, adi })) {
let sub = sub.lock().unwrap(); let sub = sub.lock().unwrap();
for a in sub.keys() { for a in sub.keys() {
gathered.push(a.clone()); gathered.push(a.clone());
@ -115,7 +115,7 @@ impl MulticastAuthority {
ok_header.in_re_verb = protocol::message_type::VL2_MULTICAST_GATHER; ok_header.in_re_verb = protocol::message_type::VL2_MULTICAST_GATHER;
ok_header.in_re_message_id = message_id.to_be_bytes(); ok_header.in_re_message_id = message_id.to_be_bytes();
packet.append_bytes_fixed(&network_id.to_bytes())?; packet.append_bytes_fixed(&network_id.to_legacy_u64().to_be_bytes())?;
packet.append_bytes_fixed(&mac.to_bytes())?; packet.append_bytes_fixed(&mac.to_bytes())?;
packet.append_u32(adi)?; packet.append_u32(adi)?;
packet.append_u32(gathered.len() as u32)?; packet.append_u32(gathered.len() as u32)?;
@ -127,7 +127,7 @@ impl MulticastAuthority {
packet.append_u16(in_this_packet as u16)?; packet.append_u16(in_this_packet as u16)?;
for _ in 0..in_this_packet { for _ in 0..in_this_packet {
packet.append_bytes_fixed(gathered.pop().unwrap().legacy_address().as_bytes())?; packet.append_bytes_fixed(gathered.pop().unwrap().legacy_bytes())?;
} }
Ok(()) Ok(())

View file

@ -1,113 +1,113 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. // (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
pub type NetworkId = u64; use std::fmt::Debug;
use std::str::FromStr;
//pub struct NetworkId; use crate::vl1::{Address, PartialAddress};
/* use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)] use zerotier_utils::error::InvalidParameterError;
pub struct NetworkId(NonZeroU64); use zerotier_utils::hex;
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NetworkId {
// Legacy network ID consisting of 40-bit partial address and 24-bit network number.
Legacy(u64),
// Full length network ID consisting of 384-bit address and 24-bit network number.
Full(Address, u32),
}
impl NetworkId { impl NetworkId {
#[inline] pub fn to_bytes(&self) -> Vec<u8> {
pub fn from_u64(i: u64) -> Option<NetworkId> { match self {
// Note that we check both that 'i' is non-zero and that the address of the controller is valid. Self::Legacy(nwid) => nwid.to_be_bytes().to_vec(),
if let Some(ii) = NonZeroU64::new(i) { Self::Full(controller, nw) => {
if Address::from_legacy_u64(i).is_some() { let mut tmp = [0u8; Address::SIZE_BYTES + 4];
return Some(Self(ii)); tmp[..Address::SIZE_BYTES].copy_from_slice(controller.as_bytes());
tmp[Address::SIZE_BYTES..].copy_from_slice(&nw.to_be_bytes());
tmp.to_vec()
} }
} }
return None;
} }
#[inline] pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidParameterError> {
pub fn from_controller_and_network_no(controller: Address, network_no: u64) -> Option<NetworkId> { if b.len() == 8 {
Self::from_u64(controller.to_legacy_u64().wrapping_shl(24) | (network_no & 0xffffff)) Self::from_legacy_u64(u64::from_be_bytes(b.try_into().unwrap()))
} } else if b.len() == Address::SIZE_BYTES + 4 {
Ok(Self::Full(
#[inline] Address::from_bytes(&b[..Address::SIZE_BYTES])?,
pub fn from_bytes(b: &[u8]) -> Option<NetworkId> { u32::from_be_bytes(b[Address::SIZE_BYTES..].try_into().unwrap()),
if b.len() >= 8 { ))
Self::from_bytes_fixed(b[0..8].try_into().unwrap())
} else { } else {
None Err(InvalidParameterError("invalid network ID"))
} }
} }
#[inline] pub fn from_legacy_u64(nwid: u64) -> Result<Self, InvalidParameterError> {
pub fn from_bytes_fixed(b: &[u8; 8]) -> Option<NetworkId> { let _ = PartialAddress::from_legacy_address_u64(nwid)?; // check validity of address portion
Self::from_u64(u64::from_be_bytes(*b)) Ok(Self::Legacy(nwid))
} }
#[inline] /// Get the legacy 40-bit partial controller address from this network ID.
pub fn to_bytes(&self) -> [u8; 8] { pub(crate) fn legacy_controller_address(&self) -> PartialAddress {
self.0.get().to_be_bytes() match self {
Self::Legacy(nwid) => PartialAddress::from_legacy_address_u64(nwid.wrapping_shr(24)).unwrap(),
Self::Full(controller, _) => PartialAddress::from_bytes(&controller.as_bytes()[..PartialAddress::LEGACY_SIZE_BYTES]).unwrap(),
}
} }
/// Get the network controller ID for this network, which is the most significant 40 bits. /// Convert this into a legacy network ID in u64 form, or return itself if already a legacy ID.
#[inline] pub(crate) fn to_legacy_u64(&self) -> u64 {
pub fn network_controller(&self) -> Address { match self {
Address::from_legacy_u64(self.0.get()).unwrap() Self::Legacy(nwid) => *nwid,
} Self::Full(controller, nw) => controller.legacy_u64().wrapping_shl(24) | ((*nw & 0xffffff) as u64),
}
/// Consume this network ID and return one with the same network number but a different controller ID.
pub fn change_network_controller(self, new_controller: Address) -> NetworkId {
Self(NonZeroU64::new((self.network_no() as u64) | new_controller.to_legacy_u64().wrapping_shl(24)).unwrap())
}
/// Get the 24-bit local network identifier minus the 40-bit controller address portion.
#[inline]
pub fn network_no(&self) -> u32 {
(self.0.get() & 0xffffff) as u32
}
}
impl From<NetworkId> for u64 {
#[inline(always)]
fn from(v: NetworkId) -> Self {
v.0.get()
}
}
impl From<&NetworkId> for u64 {
#[inline(always)]
fn from(v: &NetworkId) -> Self {
v.0.get()
} }
} }
impl ToString for NetworkId { impl ToString for NetworkId {
fn to_string(&self) -> String { fn to_string(&self) -> String {
let mut v = self.0.get(); match self {
let mut s = String::with_capacity(16); Self::Legacy(nwid) => hex::to_string_u64(*nwid, false),
for _ in 0..16 { Self::Full(controller, nw) => format!("{:08x}@{}", *nw, controller.to_string()),
s.push(HEX_CHARS[(v >> 60) as usize] as char);
v <<= 4;
} }
s
}
}
impl Debug for NetworkId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
} }
} }
impl FromStr for NetworkId { impl FromStr for NetworkId {
type Err = InvalidFormatError; type Err = InvalidParameterError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
NetworkId::from_bytes(hex::from_string(s).as_slice()).map_or_else(|| Err(InvalidFormatError), |a| Ok(a)) if s.len() == 16 {
Self::from_legacy_u64(hex::from_string_u64(s))
} else {
let mut fno = 0;
let mut net_no = 0;
let mut controller = None;
for ss in s.split('@') {
if fno == 0 {
net_no = hex::from_string_u64(ss);
} else if fno == 1 {
controller = Some(Address::from_str(ss)?);
} else {
return Err(InvalidParameterError("invalid network ID"));
}
fno += 1;
}
if let Some(controller) = controller {
return Ok(Self::Full(controller, net_no as u32));
} else {
return Err(InvalidParameterError("invalid network ID"));
}
}
} }
} }
impl Hash for NetworkId { impl Debug for NetworkId {
#[inline(always)] #[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
state.write_u64(self.0.get()); f.write_str(self.to_string().as_str())
} }
} }
@ -119,7 +119,7 @@ impl Serialize for NetworkId {
if serializer.is_human_readable() { if serializer.is_human_readable() {
serializer.serialize_str(self.to_string().as_str()) serializer.serialize_str(self.to_string().as_str())
} else { } else {
serializer.serialize_bytes(&self.to_bytes()) serializer.serialize_bytes(self.to_bytes().as_slice())
} }
} }
} }
@ -130,25 +130,21 @@ impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
type Value = NetworkId; type Value = NetworkId;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a ZeroTier network ID") formatter.write_str("network ID")
} }
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where where
E: serde::de::Error, E: serde::de::Error,
{ {
if v.len() == 8 { NetworkId::from_bytes(v).map_err(|_| E::custom("invalid network ID"))
NetworkId::from_bytes(v).map_or_else(|| Err(E::custom("object too large")), |a| Ok(a))
} else {
Err(E::custom("object too large"))
}
} }
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where where
E: serde::de::Error, E: serde::de::Error,
{ {
NetworkId::from_str(v).map_err(|e| E::custom(e.to_string())) NetworkId::from_str(v).map_err(|_| E::custom("invalid network ID"))
} }
} }
@ -164,4 +160,3 @@ impl<'de> Deserialize<'de> for NetworkId {
} }
} }
} }
*/

View file

@ -10,7 +10,7 @@ use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError}; use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use crate::protocol; use crate::protocol;
use crate::vl1::{Address, InetAddress, MAC}; use crate::vl1::{Address, InetAddress, PartialAddress, MAC};
#[allow(unused)] #[allow(unused)]
pub const RULES_ENGINE_REVISION: u8 = 1; pub const RULES_ENGINE_REVISION: u8 = 1;
@ -174,16 +174,16 @@ impl Default for RuleValue {
pub trait RuleVisitor { pub trait RuleVisitor {
fn action_drop(&mut self) -> bool; fn action_drop(&mut self) -> bool;
fn action_accept(&mut self) -> bool; fn action_accept(&mut self) -> bool;
fn action_tee(&mut self, address: Address, flags: u32, length: u16) -> bool; fn action_tee(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool;
fn action_watch(&mut self, address: Address, flags: u32, length: u16) -> bool; fn action_watch(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool;
fn action_redirect(&mut self, address: Address, flags: u32, length: u16) -> bool; fn action_redirect(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool;
fn action_break(&mut self) -> bool; fn action_break(&mut self) -> bool;
fn action_priority(&mut self, qos_bucket: u8) -> bool; fn action_priority(&mut self, qos_bucket: u8) -> bool;
fn invalid_rule(&mut self) -> bool; fn invalid_rule(&mut self) -> bool;
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: Address); fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress);
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: Address); fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress);
fn match_vlan_id(&mut self, not: bool, or: bool, id: u16); fn match_vlan_id(&mut self, not: bool, or: bool, id: u16);
fn match_vlan_pcp(&mut self, not: bool, or: bool, pcp: u8); fn match_vlan_pcp(&mut self, not: bool, or: bool, pcp: u8);
fn match_vlan_dei(&mut self, not: bool, or: bool, dei: u8); fn match_vlan_dei(&mut self, not: bool, or: bool, dei: u8);
@ -239,7 +239,7 @@ impl Rule {
Self { Self {
t: action::TEE, t: action::TEE,
v: RuleValue { v: RuleValue {
forward: rule_value::Forward { address: address.legacy_address().to_u64(), flags, length }, forward: rule_value::Forward { address: address.legacy_u64(), flags, length },
}, },
} }
} }
@ -248,7 +248,7 @@ impl Rule {
Self { Self {
t: action::TEE, t: action::TEE,
v: RuleValue { v: RuleValue {
forward: rule_value::Forward { address: address.legacy_address().to_u64(), flags, length }, forward: rule_value::Forward { address: address.legacy_u64(), flags, length },
}, },
} }
} }
@ -257,7 +257,7 @@ impl Rule {
Self { Self {
t: action::TEE, t: action::TEE,
v: RuleValue { v: RuleValue {
forward: rule_value::Forward { address: address.legacy_address().to_u64(), flags, length }, forward: rule_value::Forward { address: address.legacy_u64(), flags, length },
}, },
} }
} }
@ -273,14 +273,14 @@ impl Rule {
pub fn match_source_zerotier_address(not: bool, or: bool, address: Address) -> Self { pub fn match_source_zerotier_address(not: bool, or: bool, address: Address) -> Self {
Self { Self {
t: t(not, or, match_cond::SOURCE_ZEROTIER_ADDRESS), t: t(not, or, match_cond::SOURCE_ZEROTIER_ADDRESS),
v: RuleValue { zt: address.legacy_address().to_u64() }, v: RuleValue { zt: address.legacy_u64() },
} }
} }
pub fn match_dest_zerotier_address(not: bool, or: bool, address: Address) -> Self { pub fn match_dest_zerotier_address(not: bool, or: bool, address: Address) -> Self {
Self { Self {
t: t(not, or, match_cond::DEST_ZEROTIER_ADDRESS), t: t(not, or, match_cond::DEST_ZEROTIER_ADDRESS),
v: RuleValue { zt: address.legacy_address().to_u64() }, v: RuleValue { zt: address.legacy_u64() },
} }
} }
@ -306,21 +306,21 @@ impl Rule {
return v.action_accept(); return v.action_accept();
} }
action::TEE => { action::TEE => {
if let Some(a) = LegacyAddress::from_u64(self.v.forward.address) { if let Ok(a) = PartialAddress::from_legacy_address_u64(self.v.forward.address) {
return v.action_tee(a, self.v.forward.flags, self.v.forward.length); return v.action_tee(a, self.v.forward.flags, self.v.forward.length);
} else { } else {
return v.invalid_rule(); return v.invalid_rule();
} }
} }
action::WATCH => { action::WATCH => {
if let Some(a) = LegacyAddress::from_u64(self.v.forward.address) { if let Ok(a) = PartialAddress::from_legacy_address_u64(self.v.forward.address) {
return v.action_watch(a, self.v.forward.flags, self.v.forward.length); return v.action_watch(a, self.v.forward.flags, self.v.forward.length);
} else { } else {
return v.invalid_rule(); return v.invalid_rule();
} }
} }
action::REDIRECT => { action::REDIRECT => {
if let Some(a) = LegacyAddress::from_u64(self.v.forward.address) { if let Ok(a) = PartialAddress::from_legacy_address_u64(self.v.forward.address) {
return v.action_redirect(a, self.v.forward.flags, self.v.forward.length); return v.action_redirect(a, self.v.forward.flags, self.v.forward.length);
} else { } else {
return v.invalid_rule(); return v.invalid_rule();
@ -333,14 +333,14 @@ impl Rule {
return v.action_priority(self.v.qos_bucket); return v.action_priority(self.v.qos_bucket);
} }
match_cond::SOURCE_ZEROTIER_ADDRESS => { match_cond::SOURCE_ZEROTIER_ADDRESS => {
if let Some(a) = LegacyAddress::from_u64(self.v.zt) { if let Ok(a) = PartialAddress::from_legacy_address_u64(self.v.zt) {
v.match_source_zerotier_address(not, or, a); v.match_source_zerotier_address(not, or, a);
} else { } else {
return v.invalid_rule(); return v.invalid_rule();
} }
} }
match_cond::DEST_ZEROTIER_ADDRESS => { match_cond::DEST_ZEROTIER_ADDRESS => {
if let Some(a) = LegacyAddress::from_u64(self.v.zt) { if let Ok(a) = PartialAddress::from_legacy_address_u64(self.v.zt) {
v.match_dest_zerotier_address(not, or, a); v.match_dest_zerotier_address(not, or, a);
} else { } else {
return v.invalid_rule(); return v.invalid_rule();
@ -775,13 +775,13 @@ static HR_NAME_TO_RULE_TYPE: phf::Map<&'static str, u8> = phf_map! {
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
struct HumanReadableRule<'a> { struct HumanReadableRule<'a> {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<LegacyAddress>, pub address: Option<PartialAddress>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub flags: Option<u32>, pub flags: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub length: Option<u16>, pub length: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub zt: Option<LegacyAddress>, pub zt: Option<PartialAddress>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub vlanId: Option<u16>, pub vlanId: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -837,7 +837,7 @@ impl<'a> HumanReadableRule<'a> {
unsafe { unsafe {
match *t { match *t {
action::TEE | action::WATCH | action::REDIRECT => { action::TEE | action::WATCH | action::REDIRECT => {
r.v.forward.address = self.address.as_ref()?.to_u64(); r.v.forward.address = self.address.as_ref()?.legacy_u64();
r.v.forward.flags = self.flags?; r.v.forward.flags = self.flags?;
r.v.forward.length = self.length?; r.v.forward.length = self.length?;
} }
@ -845,7 +845,7 @@ impl<'a> HumanReadableRule<'a> {
r.v.qos_bucket = self.qosBucket?; r.v.qos_bucket = self.qosBucket?;
} }
match_cond::SOURCE_ZEROTIER_ADDRESS | match_cond::DEST_ZEROTIER_ADDRESS => { match_cond::SOURCE_ZEROTIER_ADDRESS | match_cond::DEST_ZEROTIER_ADDRESS => {
r.v.zt = self.address.as_ref()?.to_u64(); r.v.zt = self.address.as_ref()?.legacy_u64();
} }
match_cond::VLAN_ID => { match_cond::VLAN_ID => {
r.v.vlan_id = self.vlanId?; r.v.vlan_id = self.vlanId?;
@ -982,7 +982,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
} }
#[inline(always)] #[inline(always)]
fn action_tee(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_tee(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_TEE"; self.0._type = "ACTION_TEE";
let _ = self.0.address.insert(address); let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags); let _ = self.0.flags.insert(flags);
@ -991,7 +991,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
} }
#[inline(always)] #[inline(always)]
fn action_watch(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_watch(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_WATCH"; self.0._type = "ACTION_WATCH";
let _ = self.0.address.insert(address); let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags); let _ = self.0.flags.insert(flags);
@ -1000,7 +1000,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
} }
#[inline(always)] #[inline(always)]
fn action_redirect(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_redirect(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_REDIRECT"; self.0._type = "ACTION_REDIRECT";
let _ = self.0.address.insert(address); let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags); let _ = self.0.flags.insert(flags);
@ -1027,13 +1027,13 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
} }
#[inline(always)] #[inline(always)]
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: LegacyAddress) { fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress) {
let _ = self.0.zt.insert(address); let _ = self.0.zt.insert(address);
self.do_cond("MATCH_SOURCE_ZEROTIER_ADDRESS", not, or); self.do_cond("MATCH_SOURCE_ZEROTIER_ADDRESS", not, or);
} }
#[inline(always)] #[inline(always)]
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: LegacyAddress) { fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress) {
let _ = self.0.zt.insert(address); let _ = self.0.zt.insert(address);
self.do_cond("MATCH_DEST_ZEROTIER_ADDRESS", not, or); self.do_cond("MATCH_DEST_ZEROTIER_ADDRESS", not, or);
} }
@ -1217,19 +1217,19 @@ impl RuleVisitor for RuleStringer {
} }
#[inline(always)] #[inline(always)]
fn action_tee(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_tee(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_TEE({}, {}, {})", address.to_string(), flags, length); self.0 = format!("ACTION_TEE({}, {}, {})", address.to_string(), flags, length);
true true
} }
#[inline(always)] #[inline(always)]
fn action_watch(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_watch(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_WATCH({}, {}, {})", address.to_string(), flags, length); self.0 = format!("ACTION_WATCH({}, {}, {})", address.to_string(), flags, length);
true true
} }
#[inline(always)] #[inline(always)]
fn action_redirect(&mut self, address: LegacyAddress, flags: u32, length: u16) -> bool { fn action_redirect(&mut self, address: PartialAddress, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_REDIRECT({}, {}, {})", address.to_string(), flags, length); self.0 = format!("ACTION_REDIRECT({}, {}, {})", address.to_string(), flags, length);
true true
} }
@ -1253,7 +1253,7 @@ impl RuleVisitor for RuleStringer {
} }
#[inline(always)] #[inline(always)]
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: LegacyAddress) { fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress) {
self.0 = format!( self.0 = format!(
"MATCH_SOURCE_ZEROTIER_ADDRESS({}{}{})", "MATCH_SOURCE_ZEROTIER_ADDRESS({}{}{})",
if or { if or {
@ -1271,7 +1271,7 @@ impl RuleVisitor for RuleStringer {
} }
#[inline(always)] #[inline(always)]
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: LegacyAddress) { fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: PartialAddress) {
self.0 = format!( self.0 = format!(
"MATCH_DEST_ZEROTIER_ADDRESS({}{}{})", "MATCH_DEST_ZEROTIER_ADDRESS({}{}{})",
if or { if or {

View file

@ -1,7 +1,7 @@
use std::io::Write; use std::io::Write;
use crate::vl1::identity::{Identity, IdentitySecret}; use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::LegacyAddress; use crate::vl1::PartialAddress;
use crate::vl2::NetworkId; use crate::vl2::NetworkId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -24,10 +24,10 @@ use zerotier_utils::memory;
/// certificate. /// certificate.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CertificateOfMembership { pub struct CertificateOfMembership {
pub network_id: NetworkId, pub network_id: u64, // 64-bit legacy network ID
pub timestamp: i64, pub timestamp: i64,
pub max_delta: u64, pub max_delta: u64,
pub issued_to: LegacyAddress, pub issued_to: u64, // 40-bit legacy address
pub issued_to_fingerprint: Blob<32>, pub issued_to_fingerprint: Blob<32>,
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>, pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
} }
@ -35,12 +35,12 @@ pub struct CertificateOfMembership {
impl CertificateOfMembership { impl CertificateOfMembership {
/// Create a new signed certificate of membership. /// Create a new signed certificate of membership.
/// None is returned if an error occurs, such as the issuer missing its secrets. /// None is returned if an error occurs, such as the issuer missing its secrets.
pub fn new(issuer: &IdentitySecret, network_id: NetworkId, issued_to: &Identity, timestamp: i64, max_delta: u64) -> Self { pub fn new(issuer: &IdentitySecret, network_id: &NetworkId, issued_to: &Identity, timestamp: i64, max_delta: u64) -> Self {
let mut com = CertificateOfMembership { let mut com = CertificateOfMembership {
network_id, network_id: network_id.to_legacy_u64(),
timestamp, timestamp,
max_delta, max_delta,
issued_to: issued_to.address.legacy_address(), issued_to: issued_to.address.legacy_u64(),
issued_to_fingerprint: Blob::default(), issued_to_fingerprint: Blob::default(),
signature: ArrayVec::new(), signature: ArrayVec::new(),
}; };
@ -59,7 +59,7 @@ impl CertificateOfMembership {
q[4] = u64::from(self.network_id).to_be(); q[4] = u64::from(self.network_id).to_be();
q[5] = 0; // no disagreement permitted q[5] = 0; // no disagreement permitted
q[6] = 2u64.to_be(); q[6] = 2u64.to_be();
q[7] = self.issued_to.to_u64().to_be(); q[7] = self.issued_to.to_be();
q[8] = u64::MAX; // no to_be needed for all-1s q[8] = u64::MAX; // no to_be needed for all-1s
// This is a fix for a security issue in V1 in which an attacker could (with much CPU use) // This is a fix for a security issue in V1 in which an attacker could (with much CPU use)
@ -86,20 +86,20 @@ impl CertificateOfMembership {
/// Get the identity fingerprint used in V1, which only covers the curve25519 keys. /// Get the identity fingerprint used in V1, which only covers the curve25519 keys.
fn v1_proto_issued_to_fingerprint(issued_to: &Identity) -> [u8; 32] { fn v1_proto_issued_to_fingerprint(issued_to: &Identity) -> [u8; 32] {
let mut v1_signee_hasher = SHA384::new(); let mut v1_signee_hasher = SHA384::new();
v1_signee_hasher.update(issued_to.address.legacy_address().as_bytes()); v1_signee_hasher.update(issued_to.address.legacy_bytes());
v1_signee_hasher.update(&issued_to.x25519.ecdh); v1_signee_hasher.update(&issued_to.x25519.ecdh);
v1_signee_hasher.update(&issued_to.x25519.eddsa); v1_signee_hasher.update(&issued_to.x25519.eddsa);
(&v1_signee_hasher.finish()[..32]).try_into().unwrap() (&v1_signee_hasher.finish()[..32]).try_into().unwrap()
} }
/// Get this certificate of membership in byte encoded format. /// Get this certificate of membership in byte encoded format.
pub fn to_bytes(&self, controller_address: LegacyAddress) -> ArrayVec<u8, 384> { pub fn to_bytes(&self, controller_address: PartialAddress) -> ArrayVec<u8, 384> {
let mut v = ArrayVec::new(); let mut v = ArrayVec::new();
v.push(1); // version byte from v1 protocol v.push(1); // version byte from v1 protocol
v.push(0); v.push(0);
v.push(7); // 7 qualifiers, big-endian 16-bit v.push(7); // 7 qualifiers, big-endian 16-bit
let _ = v.write_all(&self.v1_proto_get_qualifier_bytes()); let _ = v.write_all(&self.v1_proto_get_qualifier_bytes());
let _ = v.write_all(controller_address.as_bytes()); let _ = v.write_all(controller_address.legacy_bytes());
let _ = v.write_all(self.signature.as_bytes()); let _ = v.write_all(self.signature.as_bytes());
v v
} }
@ -152,10 +152,10 @@ impl CertificateOfMembership {
b = &b[5..]; // skip issuer address which is always the controller b = &b[5..]; // skip issuer address which is always the controller
Ok(Self { Ok(Self {
network_id: NetworkId::from_u64(network_id).ok_or(InvalidParameterError("invalid network ID"))?, network_id: NetworkId::from_legacy_u64(network_id)?.to_legacy_u64(),
timestamp, timestamp,
max_delta, max_delta,
issued_to: LegacyAddress::from_u64(issued_to).ok_or(InvalidParameterError("invalid issued to address"))?, issued_to,
issued_to_fingerprint: Blob::from(v1_fingerprint), issued_to_fingerprint: Blob::from(v1_fingerprint),
signature: { signature: {
let mut s = ArrayVec::new(); let mut s = ArrayVec::new();

View file

@ -2,7 +2,7 @@ use std::collections::HashSet;
use std::io::Write; use std::io::Write;
use crate::vl1::identity::{Identity, IdentitySecret}; use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::{InetAddress, LegacyAddress, MAC}; use crate::vl1::{Address, InetAddress, PartialAddress, MAC};
use crate::vl2::NetworkId; use crate::vl2::NetworkId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -30,21 +30,21 @@ impl Thing {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CertificateOfOwnership { pub struct CertificateOfOwnership {
pub network_id: NetworkId, pub network_id: u64, // legacy 64-bit network ID
pub timestamp: i64, pub timestamp: i64,
pub things: HashSet<Thing>, pub things: HashSet<Thing>,
pub issued_to: LegacyAddress, pub issued_to: u64, // legacy 40-bit address
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>, pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
} }
impl CertificateOfOwnership { impl CertificateOfOwnership {
/// Create a new empty and unsigned certificate. /// Create a new empty and unsigned certificate.
pub fn new(network_id: NetworkId, timestamp: i64, issued_to: LegacyAddress) -> Self { pub fn new(network_id: &NetworkId, timestamp: i64, issued_to: &Address) -> Self {
Self { Self {
network_id, network_id: network_id.to_legacy_u64(),
timestamp, timestamp,
things: HashSet::with_capacity(4), things: HashSet::with_capacity(4),
issued_to, issued_to: issued_to.legacy_u64(),
signature: ArrayVec::new(), signature: ArrayVec::new(),
} }
} }
@ -63,7 +63,7 @@ impl CertificateOfOwnership {
let _ = self.things.insert(Thing::Mac(mac)); let _ = self.things.insert(Thing::Mac(mac));
} }
fn internal_to_bytes(&self, for_sign: bool, signed_by: LegacyAddress) -> Option<Vec<u8>> { fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> Option<Vec<u8>> {
if self.things.len() > 0xffff { if self.things.len() > 0xffff {
return None; return None;
} }
@ -71,7 +71,7 @@ impl CertificateOfOwnership {
if for_sign { if for_sign {
let _ = v.write_all(&[0x7fu8; 8]); let _ = v.write_all(&[0x7fu8; 8]);
} }
let _ = v.write_all(&self.network_id.to_bytes()); let _ = v.write_all(&self.network_id.to_be_bytes());
let _ = v.write_all(&self.timestamp.to_be_bytes()); let _ = v.write_all(&self.timestamp.to_be_bytes());
let _ = v.write_all(&[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); // obsolete flags and ID fields let _ = v.write_all(&[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); // obsolete flags and ID fields
let _ = v.write_all(&(self.things.len() as u16).to_be_bytes()); let _ = v.write_all(&(self.things.len() as u16).to_be_bytes());
@ -94,8 +94,8 @@ impl CertificateOfOwnership {
} }
} }
} }
let _ = v.write_all(self.issued_to.as_bytes()); let _ = v.write_all(&self.issued_to.to_be_bytes()[3..8]);
let _ = v.write_all(signed_by.as_bytes()); let _ = v.write_all(signed_by.legacy_bytes());
if for_sign { if for_sign {
v.push(0); v.push(0);
v.push(0); v.push(0);
@ -112,7 +112,7 @@ impl CertificateOfOwnership {
} }
#[inline(always)] #[inline(always)]
pub fn to_bytes(&self, signed_by: LegacyAddress) -> Option<Vec<u8>> { pub fn to_bytes(&self, signed_by: &Address) -> Option<Vec<u8>> {
self.internal_to_bytes(false, signed_by) self.internal_to_bytes(false, signed_by)
} }
@ -153,10 +153,10 @@ impl CertificateOfOwnership {
} }
Ok(( Ok((
Self { Self {
network_id: NetworkId::from_u64(network_id).ok_or(InvalidParameterError("invalid network ID"))?, network_id: NetworkId::from_legacy_u64(network_id)?.to_legacy_u64(),
timestamp, timestamp,
things, things,
issued_to: LegacyAddress::from_bytes(&b[..5]).ok_or(InvalidParameterError("invalid address"))?, issued_to: PartialAddress::from_bytes(&b[..5])?.legacy_u64(),
signature: { signature: {
let mut s = ArrayVec::new(); let mut s = ArrayVec::new();
s.push_slice(&b[13..109]); s.push_slice(&b[13..109]);
@ -168,8 +168,8 @@ impl CertificateOfOwnership {
} }
/// Sign certificate of ownership for use by V1 nodes. /// Sign certificate of ownership for use by V1 nodes.
pub fn sign(&mut self, issuer_address: LegacyAddress, issuer: &IdentitySecret, issued_to: &Identity) -> bool { pub fn sign(&mut self, issuer_address: &Address, issuer: &IdentitySecret, issued_to: &Identity) -> bool {
self.issued_to = issued_to.address.legacy_address(); self.issued_to = issued_to.address.legacy_u64();
if let Some(to_sign) = self.internal_to_bytes(true, issuer_address) { if let Some(to_sign) = self.internal_to_bytes(true, issuer_address) {
self.signature = issuer.sign(&to_sign.as_slice()); self.signature = issuer.sign(&to_sign.as_slice());
return true; return true;

View file

@ -175,7 +175,7 @@ impl NetworkConfig {
proto_v1_field_name::network_config::CERTIFICATE_OF_MEMBERSHIP, proto_v1_field_name::network_config::CERTIFICATE_OF_MEMBERSHIP,
v1cred v1cred
.certificate_of_membership .certificate_of_membership
.to_bytes(self.network_id.network_controller().legacy_address()) .to_bytes(self.network_id.legacy_controller_address())
.as_bytes() .as_bytes()
.to_vec(), .to_vec(),
); );
@ -183,7 +183,7 @@ impl NetworkConfig {
if !v1cred.certificates_of_ownership.is_empty() { if !v1cred.certificates_of_ownership.is_empty() {
let mut certs = Vec::with_capacity(v1cred.certificates_of_ownership.len() * 256); let mut certs = Vec::with_capacity(v1cred.certificates_of_ownership.len() * 256);
for c in v1cred.certificates_of_ownership.iter() { for c in v1cred.certificates_of_ownership.iter() {
let _ = certs.write_all(c.to_bytes(controller_identity.address.legacy_address())?.as_slice()); let _ = certs.write_all(c.to_bytes(&controller_identity.address)?.as_slice());
} }
d.set_bytes(proto_v1_field_name::network_config::CERTIFICATES_OF_OWNERSHIP, certs); d.set_bytes(proto_v1_field_name::network_config::CERTIFICATES_OF_OWNERSHIP, certs);
} }
@ -191,7 +191,7 @@ impl NetworkConfig {
if !v1cred.tags.is_empty() { if !v1cred.tags.is_empty() {
let mut tags = Vec::with_capacity(v1cred.tags.len() * 256); let mut tags = Vec::with_capacity(v1cred.tags.len() * 256);
for (_, t) in v1cred.tags.iter() { for (_, t) in v1cred.tags.iter() {
let _ = tags.write_all(t.to_bytes(controller_identity.address.legacy_address()).as_ref()); let _ = tags.write_all(t.to_bytes(&controller_identity.address).as_ref());
} }
d.set_bytes(proto_v1_field_name::network_config::TAGS, tags); d.set_bytes(proto_v1_field_name::network_config::TAGS, tags);
} }

View file

@ -5,7 +5,7 @@ use zerotier_utils::arrayvec::ArrayVec;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::vl1::identity::IdentitySecret; use crate::vl1::identity::IdentitySecret;
use crate::vl1::LegacyAddress; use crate::vl1::Address;
use crate::vl2::v1::CredentialType; use crate::vl2::v1::CredentialType;
use crate::vl2::NetworkId; use crate::vl2::NetworkId;
@ -14,8 +14,8 @@ use crate::vl2::NetworkId;
pub struct Revocation { pub struct Revocation {
pub network_id: NetworkId, pub network_id: NetworkId,
pub threshold: i64, pub threshold: i64,
pub target: LegacyAddress, pub target: u64, // legacy 40-bit address
pub issued_to: LegacyAddress, pub issued_to: u64, // legacy 40-bit address
pub signature: ArrayVec<u8, 96>, pub signature: ArrayVec<u8, 96>,
pub fast_propagate: bool, pub fast_propagate: bool,
} }
@ -24,17 +24,17 @@ impl Revocation {
pub fn new( pub fn new(
network_id: NetworkId, network_id: NetworkId,
threshold: i64, threshold: i64,
target: LegacyAddress, target: &Address,
issued_to: LegacyAddress, issued_to: &Address,
signer_address: LegacyAddress, signer_address: &Address,
signer: &IdentitySecret, signer: &IdentitySecret,
fast_propagate: bool, fast_propagate: bool,
) -> Self { ) -> Self {
let mut r = Self { let mut r = Self {
network_id, network_id,
threshold, threshold,
target, target: target.legacy_u64(),
issued_to, issued_to: issued_to.legacy_u64(),
signature: ArrayVec::new(), signature: ArrayVec::new(),
fast_propagate, fast_propagate,
}; };
@ -42,19 +42,19 @@ impl Revocation {
r r
} }
fn internal_to_bytes(&self, for_sign: bool, signed_by: LegacyAddress) -> ArrayVec<u8, 256> { fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> ArrayVec<u8, 256> {
let mut v = ArrayVec::new(); let mut v = ArrayVec::new();
if for_sign { if for_sign {
let _ = v.write_all(&[0x7f; 8]); let _ = v.write_all(&[0x7f; 8]);
} }
let _ = v.write_all(&[0; 4]); let _ = v.write_all(&[0; 4]);
let _ = v.write_all(&((self.threshold as u32) ^ (self.target.to_u64() as u32)).to_be_bytes()); // ID only used in V1, arbitrary let _ = v.write_all(&((self.threshold as u32) ^ (self.target as u32)).to_be_bytes()); // ID is arbitrary
let _ = v.write_all(&self.network_id.to_bytes()); let _ = v.write_all(&self.network_id.to_bytes());
let _ = v.write_all(&[0; 8]); let _ = v.write_all(&[0; 8]);
let _ = v.write_all(&self.threshold.to_be_bytes()); let _ = v.write_all(&self.threshold.to_be_bytes());
let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this
let _ = v.write_all(self.target.as_bytes()); let _ = v.write_all(&self.target.to_be_bytes()[3..8]);
let _ = v.write_all(signed_by.as_bytes()); let _ = v.write_all(signed_by.as_bytes());
v.push(CredentialType::CertificateOfMembership as u8); v.push(CredentialType::CertificateOfMembership as u8);
@ -71,7 +71,7 @@ impl Revocation {
} }
#[inline(always)] #[inline(always)]
pub fn v1_proto_to_bytes(&self, controller_address: LegacyAddress) -> ArrayVec<u8, 256> { pub fn v1_proto_to_bytes(&self, controller_address: &Address) -> ArrayVec<u8, 256> {
self.internal_to_bytes(false, controller_address) self.internal_to_bytes(false, controller_address)
} }
} }

View file

@ -1,7 +1,7 @@
use std::io::Write; use std::io::Write;
use crate::vl1::identity::{Identity, IdentitySecret}; use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::LegacyAddress; use crate::vl1::{Address, PartialAddress};
use crate::vl2::NetworkId; use crate::vl2::NetworkId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -12,9 +12,9 @@ use zerotier_utils::error::InvalidParameterError;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Tag { pub struct Tag {
pub network_id: NetworkId, pub network_id: u64, // legacy 64-bit network ID
pub timestamp: i64, pub timestamp: i64,
pub issued_to: LegacyAddress, pub issued_to: u64, // legacy 40-bit address
pub id: u32, pub id: u32,
pub value: u32, pub value: u32,
pub signature: Blob<96>, pub signature: Blob<96>,
@ -24,16 +24,16 @@ impl Tag {
pub fn new( pub fn new(
id: u32, id: u32,
value: u32, value: u32,
issuer_address: LegacyAddress, issuer_address: &Address,
issuer: &IdentitySecret, issuer: &IdentitySecret,
network_id: NetworkId, network_id: &NetworkId,
issued_to: &Identity, issued_to: &Identity,
timestamp: i64, timestamp: i64,
) -> Self { ) -> Self {
let mut tag = Self { let mut tag = Self {
network_id, network_id: network_id.to_legacy_u64(),
timestamp, timestamp,
issued_to: issued_to.address.legacy_address(), issued_to: issued_to.address.legacy_u64(),
id, id,
value, value,
signature: Blob::default(), signature: Blob::default(),
@ -43,17 +43,17 @@ impl Tag {
tag tag
} }
fn internal_to_bytes(&self, for_sign: bool, signed_by: LegacyAddress) -> ArrayVec<u8, 256> { fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> ArrayVec<u8, 256> {
let mut v = ArrayVec::new(); let mut v = ArrayVec::new();
if for_sign { if for_sign {
let _ = v.write_all(&[0x7f; 8]); let _ = v.write_all(&[0x7f; 8]);
} }
let _ = v.write_all(&self.network_id.to_bytes()); let _ = v.write_all(&self.network_id.to_be_bytes());
let _ = v.write_all(&self.timestamp.to_be_bytes()); let _ = v.write_all(&self.timestamp.to_be_bytes());
let _ = v.write_all(&self.id.to_be_bytes()); let _ = v.write_all(&self.id.to_be_bytes());
let _ = v.write_all(&self.value.to_be_bytes()); let _ = v.write_all(&self.value.to_be_bytes());
let _ = v.write_all(self.issued_to.as_bytes()); let _ = v.write_all(&self.issued_to.to_be_bytes()[3..8]);
let _ = v.write_all(signed_by.as_bytes()); let _ = v.write_all(signed_by.legacy_bytes());
if !for_sign { if !for_sign {
v.push(1); v.push(1);
v.push(0); v.push(0);
@ -69,7 +69,7 @@ impl Tag {
} }
#[inline(always)] #[inline(always)]
pub fn to_bytes(&self, signed_by: LegacyAddress) -> ArrayVec<u8, 256> { pub fn to_bytes(&self, signed_by: &Address) -> ArrayVec<u8, 256> {
self.internal_to_bytes(false, signed_by) self.internal_to_bytes(false, signed_by)
} }
@ -80,9 +80,9 @@ impl Tag {
} }
Ok(( Ok((
Self { Self {
network_id: NetworkId::from_bytes(&b[0..8]).ok_or(InvalidParameterError("invalid network ID"))?, network_id: NetworkId::from_bytes(&b[0..8])?.to_legacy_u64(),
timestamp: i64::from_be_bytes(b[8..16].try_into().unwrap()), timestamp: i64::from_be_bytes(b[8..16].try_into().unwrap()),
issued_to: LegacyAddress::from_bytes(&b[24..29]).ok_or(InvalidParameterError("invalid address"))?, issued_to: PartialAddress::from_bytes(&b[24..29])?.legacy_u64(),
id: u32::from_be_bytes(b[16..20].try_into().unwrap()), id: u32::from_be_bytes(b[16..20].try_into().unwrap()),
value: u32::from_be_bytes(b[20..24].try_into().unwrap()), value: u32::from_be_bytes(b[20..24].try_into().unwrap()),
signature: { signature: {

View file

@ -10,10 +10,14 @@ use std::io::Write;
use crate::error::InvalidParameterError; use crate::error::InvalidParameterError;
// All unambiguous letters, thus easy to type on the alphabetic keyboards on phones without extra shift taps. /// All unambiguous letters, thus easy to type on the alphabetic keyboards on phones without extra shift taps.
const BASE24_ALPHABET: [u8; 24] = *(b"abcdefghjkmnopqrstuvwxyz"); // avoids 'i' and 'l' /// The letters 'l' and 'v' are skipped.
const BASE24_ALPHABET: [u8; 24] = [
b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'v', b'w', b'x', b'y', b'z',
];
/// Reverse table for BASE24 alphabet, indexed relative to 'a' or 'A'.
const BASE24_ALPHABET_INV: [u8; 26] = [ const BASE24_ALPHABET_INV: [u8; 26] = [
0, 1, 2, 3, 4, 5, 6, 7, 255, 8, 9, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 11, 12, 13, 14, 15, 16, 17, 18, 255, 19, 20, 21, 22, 23,
]; ];
/// Encode a byte slice into base24 ASCII format (no padding) /// Encode a byte slice into base24 ASCII format (no padding)
@ -62,8 +66,8 @@ fn decode_up_to_u32(s: &[u8]) -> Result<u32, InvalidParameterError> {
} }
/// Decode a base24 ASCII slice into bytes (no padding, length determines output length) /// Decode a base24 ASCII slice into bytes (no padding, length determines output length)
pub fn decode_into(s: &str, b: &mut Vec<u8>) -> Result<(), InvalidParameterError> { pub fn decode_into(s: &[u8], b: &mut Vec<u8>) -> Result<(), InvalidParameterError> {
let mut s = s.as_bytes(); let mut s = s.as_ref();
while s.len() >= 7 { while s.len() >= 7 {
let _ = b.write_all(&decode_up_to_u32(&s[..7])?.to_le_bytes()); let _ = b.write_all(&decode_up_to_u32(&s[..7])?.to_le_bytes());
@ -84,6 +88,18 @@ pub fn decode_into(s: &str, b: &mut Vec<u8>) -> Result<(), InvalidParameterError
return Ok(()); return Ok(());
} }
pub fn encode(b: &[u8]) -> String {
let mut tmp = String::with_capacity(((b.len() / 4) * 7) + 2);
encode_into(b, &mut tmp);
tmp
}
pub fn decode(s: &[u8]) -> Result<Vec<u8>, InvalidParameterError> {
let mut tmp = Vec::with_capacity(((s.len() / 7) * 4) + 2);
decode_into(s, &mut tmp)?;
Ok(tmp)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -99,7 +115,7 @@ mod tests {
encode_into(&tmp[..i], &mut s); encode_into(&tmp[..i], &mut s);
//println!("{}", s); //println!("{}", s);
v.clear(); v.clear();
decode_into(s.as_str(), &mut v).expect("decode error"); decode_into(s.as_str().as_bytes(), &mut v).expect("decode error");
assert!(v.as_slice().eq(&tmp[..i])); assert!(v.as_slice().eq(&tmp[..i]));
} }
for b in tmp.iter_mut() { for b in tmp.iter_mut() {

View file

@ -78,6 +78,38 @@ pub fn from_string(s: &str) -> Vec<u8> {
b b
} }
pub fn from_string_u64(s: &str) -> u64 {
let mut n = 0u64;
let mut byte = 0_u8;
let mut have_8: bool = false;
for cc in s.as_bytes() {
let c = *cc;
if c >= 48 && c <= 57 {
byte = (byte.wrapping_shl(4)) | (c - 48);
if have_8 {
n = n.wrapping_shl(8);
n |= byte as u64;
}
have_8 = !have_8;
} else if c >= 65 && c <= 70 {
byte = (byte.wrapping_shl(4)) | (c - 55);
if have_8 {
n = n.wrapping_shl(8);
n |= byte as u64;
}
have_8 = !have_8;
} else if c >= 97 && c <= 102 {
byte = (byte.wrapping_shl(4)) | (c - 87);
if have_8 {
n = n.wrapping_shl(8);
n |= byte as u64;
}
have_8 = !have_8;
}
}
n
}
/// Encode bytes from 'b' into hex characters in 'dest' and return the number of hex characters written. /// Encode bytes from 'b' into hex characters in 'dest' and return the number of hex characters written.
/// This will panic if the destination slice is smaller than twice the length of the source. /// This will panic if the destination slice is smaller than twice the length of the source.
pub fn to_hex_bytes(b: &[u8], dest: &mut [u8]) -> usize { pub fn to_hex_bytes(b: &[u8], dest: &mut [u8]) -> usize {