mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 13:03:45 +02:00
Finish huge network hypervisor refactor.
This commit is contained in:
parent
86652ec969
commit
ec7fae71b4
17 changed files with 523 additions and 256 deletions
|
@ -12,10 +12,18 @@ use zerotier_utils::error::InvalidParameterError;
|
|||
use zerotier_utils::hex;
|
||||
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)]
|
||||
#[repr(transparent)]
|
||||
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)]
|
||||
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)]
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[inline(always)]
|
||||
pub fn to_partial(&self) -> PartialAddress {
|
||||
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)]
|
||||
pub fn as_bytes(&self) -> &[u8; Self::SIZE_BYTES] {
|
||||
&self.0
|
||||
|
@ -65,20 +91,18 @@ impl Borrow<[u8; Self::SIZE_BYTES]> for Address {
|
|||
}
|
||||
|
||||
impl ToString for Address {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
let mut tmp = String::with_capacity(Self::SIZE_BYTES * 2);
|
||||
base24::encode_into(&self.0, &mut tmp);
|
||||
tmp
|
||||
base24::encode(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Address {
|
||||
type Err = InvalidParameterError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut tmp = Vec::with_capacity(Self::SIZE_BYTES);
|
||||
base24::decode_into(s, &mut tmp);
|
||||
Self::from_bytes(tmp.as_slice())
|
||||
base24::decode(s.as_bytes()).and_then(|b| Self::from_bytes(b.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,11 +187,6 @@ impl PartialAddress {
|
|||
pub const MIN_SIZE_BYTES: usize = Self::LEGACY_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.
|
||||
#[inline]
|
||||
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)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn legacy_u64(&self) -> u64 {
|
||||
u64::from_be(memory::load_raw(&self.0 .0)).wrapping_shr(24)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn matches(&self, k: &Address) -> bool {
|
||||
debug_assert!(self.1 >= Self::MIN_SIZE_BYTES as u16);
|
||||
|
@ -270,9 +294,7 @@ impl ToString for PartialAddress {
|
|||
if self.is_legacy() {
|
||||
hex::to_string(&self.0 .0[..Self::LEGACY_SIZE_BYTES])
|
||||
} else {
|
||||
let mut tmp = String::with_capacity(Self::MAX_SIZE_BYTES * 2);
|
||||
base24::encode_into(&self.0 .0[..self.1 as usize], &mut tmp);
|
||||
tmp
|
||||
base24::encode(self.as_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,11 +304,9 @@ impl FromStr for PartialAddress {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.len() == 10 {
|
||||
return Self::from_bytes(hex::from_string(s).as_slice());
|
||||
Self::from_bytes(hex::from_string(s).as_slice())
|
||||
} else {
|
||||
let mut tmp = Vec::with_capacity(Self::MAX_SIZE_BYTES);
|
||||
base24::decode_into(s, &mut tmp)?;
|
||||
return Self::from_bytes(tmp.as_slice());
|
||||
base24::decode(s.as_bytes()).and_then(|b| Self::from_bytes(b.as_slice()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ pub enum Endpoint {
|
|||
Nil,
|
||||
|
||||
/// Via another node using unencapsulated relaying (e.g. via a root)
|
||||
/// This is the address and the full identity fingerprint.
|
||||
ZeroTier(Address),
|
||||
|
||||
/// Direct L2 Ethernet
|
||||
|
@ -65,7 +64,6 @@ pub enum Endpoint {
|
|||
WebRTC(Vec<u8>),
|
||||
|
||||
/// Via another node using inner encapsulation via VERB_ENCAP.
|
||||
/// This is the address and the full identity fingerprint.
|
||||
ZeroTierEncap(Address),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// (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::str::FromStr;
|
||||
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use super::address::{Address, PartialAddress};
|
||||
|
@ -14,6 +16,7 @@ use zerotier_crypto::secret::Secret;
|
|||
use zerotier_crypto::typestate::Valid;
|
||||
use zerotier_crypto::x25519::*;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::base24;
|
||||
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
|
||||
use zerotier_utils::error::InvalidFormatError;
|
||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
|
@ -65,7 +68,7 @@ impl Identity {
|
|||
const ALGORITHM_X25519: u8 = 0;
|
||||
const ALGORITHM_P384: u8 = 1;
|
||||
|
||||
const V0_IDENTITY_POW_THRESHOLD: u8 = 17;
|
||||
const LEGACY_ADDRESS_POW_THRESHOLD: u8 = 17;
|
||||
|
||||
/// Generate a new ZeroTier identity.
|
||||
/// 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 },
|
||||
p384: None,
|
||||
};
|
||||
let mut legacy_address_derivation_hasher = SHA512::new();
|
||||
loop {
|
||||
let mut legacy_address_derivation_hash = SHA512::new();
|
||||
legacy_address_derivation_hash.update(&secret.public.x25519.ecdh);
|
||||
legacy_address_derivation_hash.update(&secret.public.x25519.eddsa);
|
||||
let mut legacy_address_derivation_hash = legacy_address_derivation_hash.finish();
|
||||
legacy_address_derivation_hasher.update(&secret.public.x25519.ecdh);
|
||||
legacy_address_derivation_hasher.update(&secret.public.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::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]);
|
||||
break;
|
||||
} else {
|
||||
// Regenerate one of the two keys until we meet the legacy address work function criteria.
|
||||
secret.x25519.ecdh = X25519KeyPair::generate();
|
||||
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 {
|
||||
secret.p384 = Some(P384Secret {
|
||||
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.
|
||||
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
|
||||
// are properly bound together and can't be changed independently.
|
||||
|
@ -133,9 +142,43 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// Locally validate this identity.
|
||||
/// This checks address derivation, any self-signatures, etc.
|
||||
pub fn validate(&self) -> Option<Valid<Self>> {
|
||||
todo!()
|
||||
/// This checks address derivation, any self-signatures, etc. It's a little time consuming so results should be cached
|
||||
/// if possible. Returns a marked typestate on success, which does sort of cache the results and prevents misuse.
|
||||
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.
|
||||
|
@ -148,7 +191,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// 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();
|
||||
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
|
||||
sha.update(&[Self::ALGORITHM_X25519
|
||||
|
@ -164,7 +207,7 @@ impl Identity {
|
|||
sha.update(p384.ecdsa.as_bytes());
|
||||
}
|
||||
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.
|
||||
|
@ -178,10 +221,13 @@ impl Identity {
|
|||
let _ = buf.write_all(p384.ecdsa.as_bytes());
|
||||
}
|
||||
|
||||
/// Decode a byte serialized identity.
|
||||
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) {
|
||||
let p: &packed::V2Public = memory::cast_to_struct(b);
|
||||
let mut id = Self {
|
||||
id = Self {
|
||||
address: Address::new_uninitialized(),
|
||||
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
|
||||
p384: Some(P384 {
|
||||
|
@ -191,24 +237,24 @@ impl Identity {
|
|||
p384_self_signature: p.p384_self_signature,
|
||||
}),
|
||||
};
|
||||
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
id.populate_extended_address_bits();
|
||||
return Ok(id);
|
||||
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
} 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 mut id = Self {
|
||||
id = Self {
|
||||
address: Address::new_uninitialized(),
|
||||
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
|
||||
p384: None,
|
||||
};
|
||||
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
id.populate_extended_address_bits();
|
||||
return Ok(id);
|
||||
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
} else {
|
||||
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> {
|
||||
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 {
|
||||
|
@ -236,16 +282,21 @@ impl Identity {
|
|||
impl ToString for Identity {
|
||||
fn to_string(&self) -> String {
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
format!(
|
||||
"{}:1:{}:{}:{}:{}:{}:{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.x25519.ecdh),
|
||||
hex::to_string(&self.x25519.eddsa),
|
||||
hex::to_string(p384.ecdh.as_bytes()),
|
||||
hex::to_string(p384.ecdsa.as_bytes()),
|
||||
hex::to_string(&p384.ed25519_self_signature),
|
||||
hex::to_string(&p384.p384_self_signature)
|
||||
)
|
||||
let mut s = String::with_capacity(1024);
|
||||
base24::encode_into(self.address.as_bytes(), &mut s);
|
||||
s.push_str(":1:");
|
||||
base24::encode_into(&self.x25519.ecdh, &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(&self.x25519.eddsa, &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(p384.ecdh.as_bytes(), &mut s);
|
||||
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 {
|
||||
format!(
|
||||
"{}:0:{}:{}",
|
||||
|
@ -264,9 +315,54 @@ impl FromStr for Identity {
|
|||
let ss: Vec<&str> = s.split(':').collect();
|
||||
if ss.len() >= 2 {
|
||||
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 {
|
||||
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);
|
||||
|
@ -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 {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ mod whois;
|
|||
pub mod identity;
|
||||
pub mod inetaddress;
|
||||
|
||||
pub use address::Address;
|
||||
pub use address::{Address, PartialAddress};
|
||||
pub use endpoint::Endpoint;
|
||||
pub use event::Event;
|
||||
pub use inetaddress::InetAddress;
|
||||
|
|
|
@ -50,6 +50,7 @@ pub trait ApplicationLayer: Sync + Send + 'static {
|
|||
///
|
||||
/// 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).
|
||||
#[allow(unused)]
|
||||
fn should_respond_to(&self, id: &Valid<Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<Application: ApplicationLayer + ?Sized> PeerMap<Application> {
|
|||
for m in matches {
|
||||
if address.matches(m.0) {
|
||||
if r.is_none() {
|
||||
r.insert(m.1);
|
||||
let _ = r.insert(m.1);
|
||||
} else {
|
||||
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.
|
||||
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 p = mm.entry(peer.identity.address).or_insert(peer.clone());
|
||||
let mut mm = self.maps[peer.identity.address.0[0] as usize].write().unwrap();
|
||||
let p = mm.entry(peer.identity.address.clone()).or_insert(peer.clone());
|
||||
if Arc::ptr_eq(p, &peer) {
|
||||
(peer, true)
|
||||
} else {
|
||||
|
@ -69,13 +69,18 @@ impl<Application: ApplicationLayer + ?Sized> PeerMap<Application> {
|
|||
|
||||
/// 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).
|
||||
pub fn get_or_add(&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, time_ticks)?);
|
||||
pub fn get_or_add(
|
||||
&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(
|
||||
self.maps[peer_identity.address.0[0] as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(peer.identity.address)
|
||||
.entry(peer_identity.address.clone())
|
||||
.or_insert(peer)
|
||||
.clone(),
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::protocol;
|
|||
use zerotier_crypto::typestate::Valid;
|
||||
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>>>,
|
||||
}
|
||||
|
||||
|
@ -54,18 +54,18 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
|
|||
let mut to_delete = Vec::with_capacity(2);
|
||||
for qi in q.range((Bound::Unbounded, Bound::Included(identity.address.to_partial()))).rev() {
|
||||
if qi.0.matches(&identity.address) {
|
||||
to_delete.push(qi.0);
|
||||
to_delete.push(qi.0.clone());
|
||||
// TODO
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 pkt in qi.pending_v1_packets.iter() {
|
||||
if let Some(source_path) = pkt.0.upgrade() {
|
||||
|
@ -80,7 +80,7 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
|
|||
|
||||
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_event!(app, "[vl1] [v1] sending WHOIS for {}", {
|
||||
let mut tmp = String::new();
|
||||
|
|
|
@ -42,7 +42,7 @@ impl MulticastAuthority {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
auth: Authenticator,
|
||||
time_ticks: i64,
|
||||
|
@ -53,12 +53,12 @@ impl MulticastAuthority {
|
|||
let mut subscriptions = RMaybeWLockGuard::new_read(&self.subscriptions);
|
||||
|
||||
while (cursor + 8 + 6 + 4) <= payload.len() {
|
||||
let network_id = NetworkId::from_bytes_fixed(payload.read_bytes_fixed(&mut cursor).unwrap());
|
||||
if let Some(network_id) = network_id {
|
||||
let network_id = NetworkId::from_bytes(payload.read_bytes_fixed::<8>(&mut cursor).unwrap());
|
||||
if let Ok(network_id) = network_id {
|
||||
let mac = MAC::from_bytes_fixed(payload.read_bytes_fixed(&mut cursor).unwrap());
|
||||
if let Some(mac) = mac {
|
||||
if auth(network_id, &source.identity) {
|
||||
let sub_key = (network_id, MulticastGroup { mac, adi: payload.read_u32(&mut cursor).unwrap() });
|
||||
if auth(&network_id, &source.identity) {
|
||||
let sub_key = (network_id.clone(), MulticastGroup { mac, adi: payload.read_u32(&mut cursor).unwrap() });
|
||||
if let Some(sub) = subscriptions.read().get(&sub_key) {
|
||||
let _ = sub.lock().unwrap().insert(source.identity.address.clone(), time_ticks);
|
||||
} else {
|
||||
|
@ -79,7 +79,7 @@ impl MulticastAuthority {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
auth: Authenticator,
|
||||
time_ticks: i64,
|
||||
|
@ -91,17 +91,17 @@ impl MulticastAuthority {
|
|||
mut cursor: usize,
|
||||
) -> PacketHandlerResult {
|
||||
if let Some(network_id) = payload
|
||||
.read_bytes_fixed(&mut cursor)
|
||||
.map_or(None, |network_id| NetworkId::from_bytes_fixed(network_id))
|
||||
.read_bytes_fixed::<8>(&mut cursor)
|
||||
.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
|
||||
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 adi = payload.read_u32(&mut cursor).unwrap_or(0);
|
||||
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();
|
||||
for a in sub.keys() {
|
||||
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_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_u32(adi)?;
|
||||
packet.append_u32(gathered.len() as u32)?;
|
||||
|
@ -127,7 +127,7 @@ impl MulticastAuthority {
|
|||
|
||||
packet.append_u16(in_this_packet as u16)?;
|
||||
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(())
|
||||
|
|
|
@ -1,113 +1,113 @@
|
|||
// (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};
|
||||
|
||||
/*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct NetworkId(NonZeroU64);
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use zerotier_utils::error::InvalidParameterError;
|
||||
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 {
|
||||
#[inline]
|
||||
pub fn from_u64(i: u64) -> Option<NetworkId> {
|
||||
// Note that we check both that 'i' is non-zero and that the address of the controller is valid.
|
||||
if let Some(ii) = NonZeroU64::new(i) {
|
||||
if Address::from_legacy_u64(i).is_some() {
|
||||
return Some(Self(ii));
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Legacy(nwid) => nwid.to_be_bytes().to_vec(),
|
||||
Self::Full(controller, nw) => {
|
||||
let mut tmp = [0u8; Address::SIZE_BYTES + 4];
|
||||
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_controller_and_network_no(controller: Address, network_no: u64) -> Option<NetworkId> {
|
||||
Self::from_u64(controller.to_legacy_u64().wrapping_shl(24) | (network_no & 0xffffff))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes(b: &[u8]) -> Option<NetworkId> {
|
||||
if b.len() >= 8 {
|
||||
Self::from_bytes_fixed(b[0..8].try_into().unwrap())
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidParameterError> {
|
||||
if b.len() == 8 {
|
||||
Self::from_legacy_u64(u64::from_be_bytes(b.try_into().unwrap()))
|
||||
} else if b.len() == Address::SIZE_BYTES + 4 {
|
||||
Ok(Self::Full(
|
||||
Address::from_bytes(&b[..Address::SIZE_BYTES])?,
|
||||
u32::from_be_bytes(b[Address::SIZE_BYTES..].try_into().unwrap()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
Err(InvalidParameterError("invalid network ID"))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes_fixed(b: &[u8; 8]) -> Option<NetworkId> {
|
||||
Self::from_u64(u64::from_be_bytes(*b))
|
||||
pub fn from_legacy_u64(nwid: u64) -> Result<Self, InvalidParameterError> {
|
||||
let _ = PartialAddress::from_legacy_address_u64(nwid)?; // check validity of address portion
|
||||
Ok(Self::Legacy(nwid))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> [u8; 8] {
|
||||
self.0.get().to_be_bytes()
|
||||
/// Get the legacy 40-bit partial controller address from this network ID.
|
||||
pub(crate) fn legacy_controller_address(&self) -> PartialAddress {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn network_controller(&self) -> Address {
|
||||
Address::from_legacy_u64(self.0.get()).unwrap()
|
||||
/// Convert this into a legacy network ID in u64 form, or return itself if already a legacy ID.
|
||||
pub(crate) fn to_legacy_u64(&self) -> u64 {
|
||||
match self {
|
||||
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 {
|
||||
fn to_string(&self) -> String {
|
||||
let mut v = self.0.get();
|
||||
let mut s = String::with_capacity(16);
|
||||
for _ in 0..16 {
|
||||
s.push(HEX_CHARS[(v >> 60) as usize] as char);
|
||||
v <<= 4;
|
||||
match self {
|
||||
Self::Legacy(nwid) => hex::to_string_u64(*nwid, false),
|
||||
Self::Full(controller, nw) => format!("{:08x}@{}", *nw, controller.to_string()),
|
||||
}
|
||||
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 {
|
||||
type Err = InvalidFormatError;
|
||||
type Err = InvalidParameterError;
|
||||
|
||||
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)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0.get());
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ impl Serialize for NetworkId {
|
|||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} 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;
|
||||
|
||||
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>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.len() == 8 {
|
||||
NetworkId::from_bytes(v).map_or_else(|| Err(E::custom("object too large")), |a| Ok(a))
|
||||
} else {
|
||||
Err(E::custom("object too large"))
|
||||
}
|
||||
NetworkId::from_bytes(v).map_err(|_| E::custom("invalid network ID"))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,7 @@ use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
|
|||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
|
||||
use crate::protocol;
|
||||
use crate::vl1::{Address, InetAddress, MAC};
|
||||
use crate::vl1::{Address, InetAddress, PartialAddress, MAC};
|
||||
|
||||
#[allow(unused)]
|
||||
pub const RULES_ENGINE_REVISION: u8 = 1;
|
||||
|
@ -174,16 +174,16 @@ impl Default for RuleValue {
|
|||
pub trait RuleVisitor {
|
||||
fn action_drop(&mut self) -> bool;
|
||||
fn action_accept(&mut self) -> bool;
|
||||
fn action_tee(&mut self, address: Address, flags: u32, length: u16) -> bool;
|
||||
fn action_watch(&mut self, address: Address, flags: u32, length: u16) -> bool;
|
||||
fn action_redirect(&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: PartialAddress, 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_priority(&mut self, qos_bucket: u8) -> bool;
|
||||
|
||||
fn invalid_rule(&mut self) -> bool;
|
||||
|
||||
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: Address);
|
||||
fn match_dest_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: PartialAddress);
|
||||
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_dei(&mut self, not: bool, or: bool, dei: u8);
|
||||
|
@ -239,7 +239,7 @@ impl Rule {
|
|||
Self {
|
||||
t: action::TEE,
|
||||
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 {
|
||||
t: action::TEE,
|
||||
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 {
|
||||
t: action::TEE,
|
||||
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 {
|
||||
Self {
|
||||
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 {
|
||||
Self {
|
||||
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();
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
return v.invalid_rule();
|
||||
}
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
return v.invalid_rule();
|
||||
}
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
return v.invalid_rule();
|
||||
|
@ -333,14 +333,14 @@ impl Rule {
|
|||
return v.action_priority(self.v.qos_bucket);
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
return v.invalid_rule();
|
||||
}
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
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)]
|
||||
struct HumanReadableRule<'a> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub address: Option<LegacyAddress>,
|
||||
pub address: Option<PartialAddress>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub flags: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub length: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub zt: Option<LegacyAddress>,
|
||||
pub zt: Option<PartialAddress>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub vlanId: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -837,7 +837,7 @@ impl<'a> HumanReadableRule<'a> {
|
|||
unsafe {
|
||||
match *t {
|
||||
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.length = self.length?;
|
||||
}
|
||||
|
@ -845,7 +845,7 @@ impl<'a> HumanReadableRule<'a> {
|
|||
r.v.qos_bucket = self.qosBucket?;
|
||||
}
|
||||
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 => {
|
||||
r.v.vlan_id = self.vlanId?;
|
||||
|
@ -982,7 +982,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
|
|||
}
|
||||
|
||||
#[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";
|
||||
let _ = self.0.address.insert(address);
|
||||
let _ = self.0.flags.insert(flags);
|
||||
|
@ -991,7 +991,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
|
|||
}
|
||||
|
||||
#[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";
|
||||
let _ = self.0.address.insert(address);
|
||||
let _ = self.0.flags.insert(flags);
|
||||
|
@ -1000,7 +1000,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
|
|||
}
|
||||
|
||||
#[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";
|
||||
let _ = self.0.address.insert(address);
|
||||
let _ = self.0.flags.insert(flags);
|
||||
|
@ -1027,13 +1027,13 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
|
|||
}
|
||||
|
||||
#[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);
|
||||
self.do_cond("MATCH_SOURCE_ZEROTIER_ADDRESS", not, or);
|
||||
}
|
||||
|
||||
#[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);
|
||||
self.do_cond("MATCH_DEST_ZEROTIER_ADDRESS", not, or);
|
||||
}
|
||||
|
@ -1217,19 +1217,19 @@ impl RuleVisitor for RuleStringer {
|
|||
}
|
||||
|
||||
#[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);
|
||||
true
|
||||
}
|
||||
|
||||
#[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);
|
||||
true
|
||||
}
|
||||
|
||||
#[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);
|
||||
true
|
||||
}
|
||||
|
@ -1253,7 +1253,7 @@ impl RuleVisitor for RuleStringer {
|
|||
}
|
||||
|
||||
#[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!(
|
||||
"MATCH_SOURCE_ZEROTIER_ADDRESS({}{}{})",
|
||||
if or {
|
||||
|
@ -1271,7 +1271,7 @@ impl RuleVisitor for RuleStringer {
|
|||
}
|
||||
|
||||
#[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!(
|
||||
"MATCH_DEST_ZEROTIER_ADDRESS({}{}{})",
|
||||
if or {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::Write;
|
||||
|
||||
use crate::vl1::identity::{Identity, IdentitySecret};
|
||||
use crate::vl1::LegacyAddress;
|
||||
use crate::vl1::PartialAddress;
|
||||
use crate::vl2::NetworkId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -24,10 +24,10 @@ use zerotier_utils::memory;
|
|||
/// certificate.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CertificateOfMembership {
|
||||
pub network_id: NetworkId,
|
||||
pub network_id: u64, // 64-bit legacy network ID
|
||||
pub timestamp: i64,
|
||||
pub max_delta: u64,
|
||||
pub issued_to: LegacyAddress,
|
||||
pub issued_to: u64, // 40-bit legacy address
|
||||
pub issued_to_fingerprint: Blob<32>,
|
||||
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
|
||||
}
|
||||
|
@ -35,12 +35,12 @@ pub struct CertificateOfMembership {
|
|||
impl CertificateOfMembership {
|
||||
/// Create a new signed certificate of membership.
|
||||
/// 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 {
|
||||
network_id,
|
||||
network_id: network_id.to_legacy_u64(),
|
||||
timestamp,
|
||||
max_delta,
|
||||
issued_to: issued_to.address.legacy_address(),
|
||||
issued_to: issued_to.address.legacy_u64(),
|
||||
issued_to_fingerprint: Blob::default(),
|
||||
signature: ArrayVec::new(),
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ impl CertificateOfMembership {
|
|||
q[4] = u64::from(self.network_id).to_be();
|
||||
q[5] = 0; // no disagreement permitted
|
||||
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
|
||||
|
||||
// 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.
|
||||
fn v1_proto_issued_to_fingerprint(issued_to: &Identity) -> [u8; 32] {
|
||||
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.eddsa);
|
||||
(&v1_signee_hasher.finish()[..32]).try_into().unwrap()
|
||||
}
|
||||
|
||||
/// 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();
|
||||
v.push(1); // version byte from v1 protocol
|
||||
v.push(0);
|
||||
v.push(7); // 7 qualifiers, big-endian 16-bit
|
||||
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());
|
||||
v
|
||||
}
|
||||
|
@ -152,10 +152,10 @@ impl CertificateOfMembership {
|
|||
b = &b[5..]; // skip issuer address which is always the controller
|
||||
|
||||
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,
|
||||
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),
|
||||
signature: {
|
||||
let mut s = ArrayVec::new();
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashSet;
|
|||
use std::io::Write;
|
||||
|
||||
use crate::vl1::identity::{Identity, IdentitySecret};
|
||||
use crate::vl1::{InetAddress, LegacyAddress, MAC};
|
||||
use crate::vl1::{Address, InetAddress, PartialAddress, MAC};
|
||||
use crate::vl2::NetworkId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -30,21 +30,21 @@ impl Thing {
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CertificateOfOwnership {
|
||||
pub network_id: NetworkId,
|
||||
pub network_id: u64, // legacy 64-bit network ID
|
||||
pub timestamp: i64,
|
||||
pub things: HashSet<Thing>,
|
||||
pub issued_to: LegacyAddress,
|
||||
pub issued_to: u64, // legacy 40-bit address
|
||||
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
|
||||
}
|
||||
|
||||
impl CertificateOfOwnership {
|
||||
/// 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 {
|
||||
network_id,
|
||||
network_id: network_id.to_legacy_u64(),
|
||||
timestamp,
|
||||
things: HashSet::with_capacity(4),
|
||||
issued_to,
|
||||
issued_to: issued_to.legacy_u64(),
|
||||
signature: ArrayVec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl CertificateOfOwnership {
|
|||
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 {
|
||||
return None;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl CertificateOfOwnership {
|
|||
if for_sign {
|
||||
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(&[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());
|
||||
|
@ -94,8 +94,8 @@ impl CertificateOfOwnership {
|
|||
}
|
||||
}
|
||||
}
|
||||
let _ = v.write_all(self.issued_to.as_bytes());
|
||||
let _ = v.write_all(signed_by.as_bytes());
|
||||
let _ = v.write_all(&self.issued_to.to_be_bytes()[3..8]);
|
||||
let _ = v.write_all(signed_by.legacy_bytes());
|
||||
if for_sign {
|
||||
v.push(0);
|
||||
v.push(0);
|
||||
|
@ -112,7 +112,7 @@ impl CertificateOfOwnership {
|
|||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
|
@ -153,10 +153,10 @@ impl CertificateOfOwnership {
|
|||
}
|
||||
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,
|
||||
things,
|
||||
issued_to: LegacyAddress::from_bytes(&b[..5]).ok_or(InvalidParameterError("invalid address"))?,
|
||||
issued_to: PartialAddress::from_bytes(&b[..5])?.legacy_u64(),
|
||||
signature: {
|
||||
let mut s = ArrayVec::new();
|
||||
s.push_slice(&b[13..109]);
|
||||
|
@ -168,8 +168,8 @@ impl CertificateOfOwnership {
|
|||
}
|
||||
|
||||
/// Sign certificate of ownership for use by V1 nodes.
|
||||
pub fn sign(&mut self, issuer_address: LegacyAddress, issuer: &IdentitySecret, issued_to: &Identity) -> bool {
|
||||
self.issued_to = issued_to.address.legacy_address();
|
||||
pub fn sign(&mut self, issuer_address: &Address, issuer: &IdentitySecret, issued_to: &Identity) -> bool {
|
||||
self.issued_to = issued_to.address.legacy_u64();
|
||||
if let Some(to_sign) = self.internal_to_bytes(true, issuer_address) {
|
||||
self.signature = issuer.sign(&to_sign.as_slice());
|
||||
return true;
|
||||
|
|
|
@ -175,7 +175,7 @@ impl NetworkConfig {
|
|||
proto_v1_field_name::network_config::CERTIFICATE_OF_MEMBERSHIP,
|
||||
v1cred
|
||||
.certificate_of_membership
|
||||
.to_bytes(self.network_id.network_controller().legacy_address())
|
||||
.to_bytes(self.network_id.legacy_controller_address())
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
);
|
||||
|
@ -183,7 +183,7 @@ impl NetworkConfig {
|
|||
if !v1cred.certificates_of_ownership.is_empty() {
|
||||
let mut certs = Vec::with_capacity(v1cred.certificates_of_ownership.len() * 256);
|
||||
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);
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ impl NetworkConfig {
|
|||
if !v1cred.tags.is_empty() {
|
||||
let mut tags = Vec::with_capacity(v1cred.tags.len() * 256);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use zerotier_utils::arrayvec::ArrayVec;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::vl1::identity::IdentitySecret;
|
||||
use crate::vl1::LegacyAddress;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl2::v1::CredentialType;
|
||||
use crate::vl2::NetworkId;
|
||||
|
||||
|
@ -14,8 +14,8 @@ use crate::vl2::NetworkId;
|
|||
pub struct Revocation {
|
||||
pub network_id: NetworkId,
|
||||
pub threshold: i64,
|
||||
pub target: LegacyAddress,
|
||||
pub issued_to: LegacyAddress,
|
||||
pub target: u64, // legacy 40-bit address
|
||||
pub issued_to: u64, // legacy 40-bit address
|
||||
pub signature: ArrayVec<u8, 96>,
|
||||
pub fast_propagate: bool,
|
||||
}
|
||||
|
@ -24,17 +24,17 @@ impl Revocation {
|
|||
pub fn new(
|
||||
network_id: NetworkId,
|
||||
threshold: i64,
|
||||
target: LegacyAddress,
|
||||
issued_to: LegacyAddress,
|
||||
signer_address: LegacyAddress,
|
||||
target: &Address,
|
||||
issued_to: &Address,
|
||||
signer_address: &Address,
|
||||
signer: &IdentitySecret,
|
||||
fast_propagate: bool,
|
||||
) -> Self {
|
||||
let mut r = Self {
|
||||
network_id,
|
||||
threshold,
|
||||
target,
|
||||
issued_to,
|
||||
target: target.legacy_u64(),
|
||||
issued_to: issued_to.legacy_u64(),
|
||||
signature: ArrayVec::new(),
|
||||
fast_propagate,
|
||||
};
|
||||
|
@ -42,19 +42,19 @@ impl Revocation {
|
|||
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();
|
||||
if for_sign {
|
||||
let _ = v.write_all(&[0x7f; 8]);
|
||||
}
|
||||
|
||||
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(&[0; 8]);
|
||||
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.target.as_bytes());
|
||||
let _ = v.write_all(&self.target.to_be_bytes()[3..8]);
|
||||
let _ = v.write_all(signed_by.as_bytes());
|
||||
v.push(CredentialType::CertificateOfMembership as u8);
|
||||
|
||||
|
@ -71,7 +71,7 @@ impl Revocation {
|
|||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::Write;
|
||||
|
||||
use crate::vl1::identity::{Identity, IdentitySecret};
|
||||
use crate::vl1::LegacyAddress;
|
||||
use crate::vl1::{Address, PartialAddress};
|
||||
use crate::vl2::NetworkId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -12,9 +12,9 @@ use zerotier_utils::error::InvalidParameterError;
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Tag {
|
||||
pub network_id: NetworkId,
|
||||
pub network_id: u64, // legacy 64-bit network ID
|
||||
pub timestamp: i64,
|
||||
pub issued_to: LegacyAddress,
|
||||
pub issued_to: u64, // legacy 40-bit address
|
||||
pub id: u32,
|
||||
pub value: u32,
|
||||
pub signature: Blob<96>,
|
||||
|
@ -24,16 +24,16 @@ impl Tag {
|
|||
pub fn new(
|
||||
id: u32,
|
||||
value: u32,
|
||||
issuer_address: LegacyAddress,
|
||||
issuer_address: &Address,
|
||||
issuer: &IdentitySecret,
|
||||
network_id: NetworkId,
|
||||
network_id: &NetworkId,
|
||||
issued_to: &Identity,
|
||||
timestamp: i64,
|
||||
) -> Self {
|
||||
let mut tag = Self {
|
||||
network_id,
|
||||
network_id: network_id.to_legacy_u64(),
|
||||
timestamp,
|
||||
issued_to: issued_to.address.legacy_address(),
|
||||
issued_to: issued_to.address.legacy_u64(),
|
||||
id,
|
||||
value,
|
||||
signature: Blob::default(),
|
||||
|
@ -43,17 +43,17 @@ impl 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();
|
||||
if for_sign {
|
||||
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.id.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(signed_by.as_bytes());
|
||||
let _ = v.write_all(&self.issued_to.to_be_bytes()[3..8]);
|
||||
let _ = v.write_all(signed_by.legacy_bytes());
|
||||
if !for_sign {
|
||||
v.push(1);
|
||||
v.push(0);
|
||||
|
@ -69,7 +69,7 @@ impl Tag {
|
|||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,9 @@ impl Tag {
|
|||
}
|
||||
Ok((
|
||||
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()),
|
||||
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()),
|
||||
value: u32::from_be_bytes(b[20..24].try_into().unwrap()),
|
||||
signature: {
|
||||
|
|
|
@ -10,10 +10,14 @@ use std::io::Write;
|
|||
|
||||
use crate::error::InvalidParameterError;
|
||||
|
||||
// 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'
|
||||
/// All unambiguous letters, thus easy to type on the alphabetic keyboards on phones without extra shift taps.
|
||||
/// 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] = [
|
||||
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)
|
||||
|
@ -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)
|
||||
pub fn decode_into(s: &str, b: &mut Vec<u8>) -> Result<(), InvalidParameterError> {
|
||||
let mut s = s.as_bytes();
|
||||
pub fn decode_into(s: &[u8], b: &mut Vec<u8>) -> Result<(), InvalidParameterError> {
|
||||
let mut s = s.as_ref();
|
||||
|
||||
while s.len() >= 7 {
|
||||
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(());
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -99,7 +115,7 @@ mod tests {
|
|||
encode_into(&tmp[..i], &mut s);
|
||||
//println!("{}", s);
|
||||
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]));
|
||||
}
|
||||
for b in tmp.iter_mut() {
|
||||
|
|
|
@ -78,6 +78,38 @@ pub fn from_string(s: &str) -> Vec<u8> {
|
|||
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.
|
||||
/// 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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue