A bunch of reorg to consolidate utils, controller work, and rework identity binary marshaling for better backward compability.

This commit is contained in:
Adam Ierymenko 2022-09-27 15:46:18 -04:00
parent 051292f461
commit 38f8ee808e
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
20 changed files with 446 additions and 748 deletions

View file

@ -6,10 +6,10 @@ use tokio::time::{Duration, Instant};
use zerotier_utils::tokio; use zerotier_utils::tokio;
use zerotier_network_hypervisor::protocol::{verbs, PacketBuffer}; use zerotier_network_hypervisor::protocol::{verbs, PacketBuffer};
use zerotier_network_hypervisor::util::dictionary::Dictionary;
use zerotier_network_hypervisor::vl1::{HostSystem, Identity, InnerProtocol, PacketHandlerResult, Path, Peer}; use zerotier_network_hypervisor::vl1::{HostSystem, Identity, InnerProtocol, PacketHandlerResult, Path, Peer};
use zerotier_network_hypervisor::vl2::NetworkId; use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::dictionary::Dictionary;
use zerotier_utils::reaper::Reaper; use zerotier_utils::reaper::Reaper;
use crate::database::Database; use crate::database::Database;

View file

@ -4,9 +4,7 @@ pub const VERSION_MAJOR: u8 = 1;
pub const VERSION_MINOR: u8 = 99; pub const VERSION_MINOR: u8 = 99;
pub const VERSION_REVISION: u16 = 1; pub const VERSION_REVISION: u16 = 1;
pub mod error;
#[allow(unused)] #[allow(unused)]
pub mod protocol; pub mod protocol;
pub mod util;
pub mod vl1; pub mod vl1;
pub mod vl2; pub mod vl2;

View file

@ -1,8 +0,0 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
pub mod dictionary;
pub(crate) mod gate;
pub mod marshalable;
/// A value for ticks that indicates that something never happened, and is thus very long before zero ticks.
pub(crate) const NEVER_HAPPENED_TICKS: i64 = -2147483648;

View file

@ -6,9 +6,9 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::InvalidFormatError;
use crate::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE}; use crate::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex; use zerotier_utils::hex;
/// A unique address on the global ZeroTier VL1 network. /// A unique address on the global ZeroTier VL1 network.

View file

@ -6,13 +6,13 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::InvalidFormatError;
use crate::util::marshalable::*;
use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE; use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
use crate::vl1::inetaddress::InetAddress; use crate::vl1::inetaddress::InetAddress;
use crate::vl1::{Address, MAC}; use crate::vl1::{Address, MAC};
use zerotier_utils::buffer::Buffer; use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
pub const TYPE_NIL: u8 = 0; pub const TYPE_NIL: u8 = 0;
pub const TYPE_ZEROTIER: u8 = 1; pub const TYPE_ZEROTIER: u8 = 1;
@ -118,11 +118,14 @@ impl Endpoint {
} }
} }
/// Returns true if this is an endpoint type that requires that large packets be fragmented. /// Get the maximum fragment size for this endpoint or usize::MAX if there is no hard limit.
pub fn requires_fragmentation(&self) -> bool { #[inline(always)]
pub fn max_fragment_size(&self) -> usize {
match self { match self {
Endpoint::Icmp(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => true, Endpoint::Icmp(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => {
_ => false, crate::protocol::UDP_DEFAULT_MTU
}
_ => usize::MAX,
} }
} }
} }
@ -130,7 +133,7 @@ impl Endpoint {
impl Marshalable for Endpoint { impl Marshalable for Endpoint {
const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE; const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError> { fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
match self { match self {
Endpoint::Nil => { Endpoint::Nil => {
buf.append_u8(16 + TYPE_NIL)?; buf.append_u8(16 + TYPE_NIL)?;
@ -189,7 +192,7 @@ impl Marshalable for Endpoint {
Ok(()) Ok(())
} }
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Endpoint, MarshalUnmarshalError> { fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Endpoint, UnmarshalError> {
let type_byte = buf.read_u8(cursor)?; let type_byte = buf.read_u8(cursor)?;
if type_byte < 16 { if type_byte < 16 {
if type_byte == 4 { if type_byte == 4 {
@ -205,13 +208,13 @@ impl Marshalable for Endpoint {
u16::from_be_bytes(b[16..18].try_into().unwrap()), u16::from_be_bytes(b[16..18].try_into().unwrap()),
))) )))
} else { } else {
Err(MarshalUnmarshalError::InvalidData) Err(UnmarshalError::InvalidData)
} }
} else { } else {
match type_byte - 16 { match type_byte - 16 {
TYPE_NIL => Ok(Endpoint::Nil), TYPE_NIL => Ok(Endpoint::Nil),
TYPE_ZEROTIER => { TYPE_ZEROTIER => {
let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(MarshalUnmarshalError::InvalidData)?; let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?;
Ok(Endpoint::ZeroTier( Ok(Endpoint::ZeroTier(
zt, zt,
buf.read_bytes_fixed::<IDENTITY_FINGERPRINT_SIZE>(cursor)?.clone(), buf.read_bytes_fixed::<IDENTITY_FINGERPRINT_SIZE>(cursor)?.clone(),
@ -230,10 +233,10 @@ impl Marshalable for Endpoint {
buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?.to_vec(), buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?.to_vec(),
)), )),
TYPE_ZEROTIER_ENCAP => { TYPE_ZEROTIER_ENCAP => {
let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(MarshalUnmarshalError::InvalidData)?; let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?;
Ok(Endpoint::ZeroTierEncap(zt, buf.read_bytes_fixed(cursor)?.clone())) Ok(Endpoint::ZeroTierEncap(zt, buf.read_bytes_fixed(cursor)?.clone()))
} }
_ => Err(MarshalUnmarshalError::InvalidData), _ => Err(UnmarshalError::InvalidData),
} }
} }
} }

View file

@ -3,7 +3,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert::TryInto; use std::convert::TryInto;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io::{Read, Write}; use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -15,10 +15,11 @@ use zerotier_crypto::secret::Secret;
use zerotier_crypto::x25519::*; use zerotier_crypto::x25519::*;
use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
use zerotier_utils::hex; use zerotier_utils::hex;
use zerotier_utils::memory::{as_byte_array, as_flat_object}; use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use crate::error::{InvalidFormatError, InvalidParameterError};
use crate::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD}; use crate::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
use crate::vl1::Address; use crate::vl1::Address;
@ -142,30 +143,22 @@ fn zt_address_derivation_work_function(digest: &mut [u8; 64]) {
} }
impl Identity { impl Identity {
/// Length of an x25519-only public identity in byte array form. pub const BYTE_LENGTH_MAX: usize = ADDRESS_SIZE
pub const BYTE_LENGTH_X25519_PUBLIC: usize = ADDRESS_SIZE + 1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + 1 + 1 + 2;
/// Length of an x25519-only secret identity in byte array form.
pub const BYTE_LENGTH_X25519_SECRET: usize = Self::BYTE_LENGTH_X25519_PUBLIC + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE;
/// Length of a new dual-key public identity in byte array form.
pub const BYTE_LENGTH_X25519P384_PUBLIC: usize = Self::BYTE_LENGTH_X25519_PUBLIC
+ 1 + 1
+ P384_PUBLIC_KEY_SIZE + C25519_PUBLIC_KEY_SIZE
+ P384_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE
+ P384_ECDSA_SIGNATURE_SIZE
+ ED25519_SIGNATURE_SIZE;
/// Length of a new dual-key secret identity in byte array form.
pub const BYTE_LENGTH_X25519P384_SECRET: usize = Self::BYTE_LENGTH_X25519P384_PUBLIC
+ C25519_SECRET_KEY_SIZE + C25519_SECRET_KEY_SIZE
+ ED25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE
+ P384_PUBLIC_KEY_SIZE
+ P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE
+ P384_SECRET_KEY_SIZE; + P384_PUBLIC_KEY_SIZE
+ P384_SECRET_KEY_SIZE
+ P384_ECDSA_SIGNATURE_SIZE
+ P384_ECDSA_SIGNATURE_SIZE;
const ALGORITHM_X25519: u8 = 0x01; const ALGORITHM_X25519: u8 = 0x01;
const ALGORITHM_EC_NIST_P384: u8 = 0x02; const ALGORITHM_EC_NIST_P384: u8 = 0x02;
const FLAG_INCLUDES_SECRET: u8 = 0x80; const FLAG_INCLUDES_SECRETS: u8 = 0x80;
/// Generate a new identity. /// Generate a new identity.
pub fn generate() -> Self { pub fn generate() -> Self {
@ -270,7 +263,7 @@ impl Identity {
.p384 .p384
.insert(IdentityP384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa }); .insert(IdentityP384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa });
self.fingerprint = SHA384::hash(self.to_public_bytes().as_bytes()); self.fill_in_fingerprint();
return Ok(true); return Ok(true);
} }
@ -398,222 +391,67 @@ impl Identity {
return false; return false;
} }
pub fn to_public_bytes(&self) -> IdentityBytes { pub fn write_public<W: Write>(&self, w: &mut W, legacy_v0: bool) -> std::io::Result<()> {
if let Some(p384) = self.p384.as_ref() { w.write_all(&self.address.to_bytes())?;
IdentityBytes::X25519P384Public( if !legacy_v0 && self.p384.is_some() {
as_byte_array(&packed::V1 { let p384 = self.p384.as_ref().unwrap();
v0: packed::V0 { w.write_all(&[Self::ALGORITHM_X25519 | Self::ALGORITHM_EC_NIST_P384])?;
address: self.address.to_bytes(), w.write_all(&self.x25519)?;
key_type: 0, w.write_all(&self.ed25519)?;
x25519: self.x25519, w.write_all(p384.ecdh.as_bytes())?;
ed25519: self.ed25519, w.write_all(p384.ecdsa.as_bytes())?;
secret_length: 0, w.write_all(&p384.ecdsa_self_signature)?;
reserved: 0x03, w.write_all(&p384.ed25519_self_signature)?;
ext_len: ((Self::BYTE_LENGTH_X25519P384_PUBLIC - Self::BYTE_LENGTH_X25519_PUBLIC) as u16).to_be_bytes(),
},
key_type_flags: Self::ALGORITHM_EC_NIST_P384,
ecdh: p384.ecdh.as_bytes().clone(),
ecdsa: p384.ecdsa.as_bytes().clone(),
ecdsa_self_signature: p384.ecdsa_self_signature,
ed25519_self_signature: p384.ed25519_self_signature,
})
.clone(),
)
} else { } else {
IdentityBytes::X25519Public( w.write_all(&[0])?;
as_byte_array(&packed::V0 { w.write_all(&self.x25519)?;
address: self.address.to_bytes(), w.write_all(&self.ed25519)?;
key_type: 0, w.write_all(&[0])?;
x25519: self.x25519, }
ed25519: self.ed25519, Ok(())
secret_length: 0, }
reserved: 0x03,
ext_len: [0; 2], pub fn write_secret<W: Write>(&self, w: &mut W, legacy_v0: bool) -> std::io::Result<()> {
}) if let Some(s) = self.secret.as_ref() {
.clone(), w.write_all(&self.address.to_bytes());
) if !legacy_v0 && self.p384.is_some() && s.p384.is_some() {
let p384 = self.p384.as_ref().unwrap();
let p384s = s.p384.as_ref().unwrap();
w.write_all(&[Self::ALGORITHM_X25519 | Self::ALGORITHM_EC_NIST_P384 | Self::FLAG_INCLUDES_SECRETS]);
w.write_all(&self.x25519);
w.write_all(&self.ed25519);
w.write_all(s.x25519.secret_bytes().as_bytes());
w.write_all(s.ed25519.secret_bytes().as_bytes());
w.write_all(p384.ecdh.as_bytes());
w.write_all(p384.ecdsa.as_bytes());
w.write_all(p384s.ecdh.secret_key_bytes().as_bytes());
w.write_all(p384s.ecdsa.secret_key_bytes().as_bytes());
w.write_all(&p384.ecdsa_self_signature);
w.write_all(&p384.ed25519_self_signature);
} else {
w.write_all(&[0])?;
w.write_all(&self.x25519)?;
w.write_all(&self.ed25519)?;
w.write_all(&[(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8])?;
w.write_all(s.x25519.secret_bytes().as_bytes())?;
w.write_all(s.ed25519.secret_bytes().as_bytes())?;
}
return Ok(());
} else {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "no secret"));
} }
} }
pub fn to_secret_bytes(&self) -> Option<IdentityBytes> { pub fn to_public_bytes(&self, legacy_v8: bool) -> std::io::Result<Buffer<{ Self::BYTE_LENGTH_MAX }>> {
self.secret.as_ref().map(|s| { let mut buf = Buffer::<{ Self::BYTE_LENGTH_MAX }>::new();
if let Some(p384) = s.p384.as_ref() { self.write_public(&mut buf, false)?;
IdentityBytes::X25519P384Secret( Ok(buf)
as_byte_array(&packed::V1S {
v0s: packed::V0S {
address: self.address.to_bytes(),
key_type: 0,
x25519: self.x25519,
ed25519: self.ed25519,
secret_length: (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8,
x25519_secret: s.x25519.secret_bytes().0.clone(),
ed25519_secret: s.ed25519.secret_bytes().0.clone(),
reserved: 0x03,
ext_len: ((Self::BYTE_LENGTH_X25519P384_SECRET - Self::BYTE_LENGTH_X25519_SECRET) as u16).to_be_bytes(),
},
key_type_flags: Self::ALGORITHM_EC_NIST_P384 | Self::FLAG_INCLUDES_SECRET,
ecdh: p384.ecdh.public_key_bytes().clone(),
ecdsa: p384.ecdsa.public_key_bytes().clone(),
ecdsa_self_signature: self.p384.as_ref().unwrap().ecdsa_self_signature,
ed25519_self_signature: self.p384.as_ref().unwrap().ed25519_self_signature,
ecdh_secret: p384.ecdh.secret_key_bytes().0.clone(),
ecdsa_secret: p384.ecdsa.secret_key_bytes().0.clone(),
})
.clone(),
)
} else {
IdentityBytes::X25519Secret(
as_byte_array(&packed::V0S {
address: self.address.to_bytes(),
key_type: 0,
x25519: self.x25519,
ed25519: self.ed25519,
secret_length: (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8,
x25519_secret: s.x25519.secret_bytes().0.clone(),
ed25519_secret: s.ed25519.secret_bytes().0.clone(),
reserved: 0x03,
ext_len: [0; 2],
})
.clone(),
)
}
})
} }
/// Convert a byte respresentation into an identity. pub fn to_secret_bytes(&self, legacy_v8: bool) -> std::io::Result<Buffer<{ Self::BYTE_LENGTH_MAX }>> {
/// let mut buf = Buffer::<{ Self::BYTE_LENGTH_MAX }>::new();
/// WARNING: this performs basic sanity checking but does NOT perform a full validation of address derivation or self-signatures. self.write_secret(&mut buf, false)?;
pub fn from_bytes(bytes: &IdentityBytes) -> Option<Self> { Ok(buf)
let mut id = match bytes {
IdentityBytes::X25519Public(b) => {
let b: &packed::V0 = as_flat_object(b);
if b.key_type == 0 && b.secret_length == 0 && b.reserved == 0x03 && u16::from_be_bytes(b.ext_len) == 0 {
Some(Self {
address: Address::from_bytes_fixed(&b.address)?,
x25519: b.x25519,
ed25519: b.ed25519,
p384: None,
secret: None,
fingerprint: [0; 48],
})
} else {
None
}
}
IdentityBytes::X25519Secret(b) => {
let b: &packed::V0S = as_flat_object(b);
if b.key_type == 0
&& b.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
&& b.reserved == 0x03
&& u16::from_be_bytes(b.ext_len) == 0
{
Some(Self {
address: Address::from_bytes_fixed(&b.address)?,
x25519: b.x25519,
ed25519: b.ed25519,
p384: None,
secret: Some(IdentitySecret {
x25519: X25519KeyPair::from_bytes(&b.x25519, &b.x25519_secret)?,
ed25519: Ed25519KeyPair::from_bytes(&b.ed25519, &b.ed25519_secret)?,
p384: None,
}),
fingerprint: [0; 48],
})
} else {
None
}
}
IdentityBytes::X25519P384Public(b) => {
let b: &packed::V1 = as_flat_object(b);
if b.v0.key_type == 0
&& b.v0.secret_length == 0
&& b.v0.reserved == 0x03
&& u16::from_be_bytes(b.v0.ext_len) == (Self::BYTE_LENGTH_X25519P384_PUBLIC - Self::BYTE_LENGTH_X25519_PUBLIC) as u16
&& b.key_type_flags == Self::ALGORITHM_EC_NIST_P384
{
Some(Self {
address: Address::from_bytes_fixed(&b.v0.address)?,
x25519: b.v0.x25519,
ed25519: b.v0.ed25519,
p384: Some(IdentityP384Public {
ecdh: P384PublicKey::from_bytes(&b.ecdh)?,
ecdsa: P384PublicKey::from_bytes(&b.ecdsa)?,
ecdsa_self_signature: b.ecdsa_self_signature,
ed25519_self_signature: b.ed25519_self_signature,
}),
secret: None,
fingerprint: [0; 48],
})
} else {
None
}
}
IdentityBytes::X25519P384Secret(b) => {
let b: &packed::V1S = as_flat_object(b);
if b.v0s.key_type == 0
&& b.v0s.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
&& b.v0s.reserved == 0x03
&& u16::from_be_bytes(b.v0s.ext_len) == (Self::BYTE_LENGTH_X25519P384_SECRET - Self::BYTE_LENGTH_X25519_SECRET) as u16
&& b.key_type_flags == (Self::ALGORITHM_EC_NIST_P384 | Self::FLAG_INCLUDES_SECRET)
{
Some(Self {
address: Address::from_bytes_fixed(&b.v0s.address)?,
x25519: b.v0s.x25519,
ed25519: b.v0s.ed25519,
p384: Some(IdentityP384Public {
ecdh: P384PublicKey::from_bytes(&b.ecdh)?,
ecdsa: P384PublicKey::from_bytes(&b.ecdsa)?,
ecdsa_self_signature: b.ecdsa_self_signature,
ed25519_self_signature: b.ed25519_self_signature,
}),
secret: Some(IdentitySecret {
x25519: X25519KeyPair::from_bytes(&b.v0s.x25519, &b.v0s.x25519_secret)?,
ed25519: Ed25519KeyPair::from_bytes(&b.v0s.ed25519, &b.v0s.ed25519_secret)?,
p384: Some(IdentityP384Secret {
ecdh: P384KeyPair::from_bytes(&b.ecdh, &b.ecdh_secret)?,
ecdsa: P384KeyPair::from_bytes(&b.ecdsa, &b.ecdsa_secret)?,
}),
}),
fingerprint: [0; 48],
})
} else {
None
}
}
};
let fingerprint = SHA384::hash(id.as_ref().unwrap().to_public_bytes().as_bytes());
id.as_mut().unwrap().fingerprint = fingerprint;
id
}
/// Read an identity from a reader, inferring its total length from the stream.
pub fn read_bytes<R: Read>(r: &mut R) -> std::io::Result<Self> {
let mut buf = [0_u8; 512];
r.read_exact(&mut buf[..Self::BYTE_LENGTH_X25519_PUBLIC])?;
let x25519_public: &packed::V0 = as_flat_object(&buf);
let ext_len = u16::from_be_bytes(x25519_public.ext_len) as usize;
let obj_len = if x25519_public.secret_length == 0 {
let obj_len = ext_len + Self::BYTE_LENGTH_X25519_PUBLIC;
if ext_len > 0 {
r.read_exact(&mut buf[Self::BYTE_LENGTH_X25519_PUBLIC..obj_len])?;
}
obj_len
} else {
let obj_len = ext_len + Self::BYTE_LENGTH_X25519_SECRET;
if ext_len > 0 {
r.read_exact(&mut buf[Self::BYTE_LENGTH_X25519_PUBLIC..obj_len])?;
}
obj_len
};
IdentityBytes::try_from(&buf[..obj_len]).map_or_else(
|_| Err(std::io::Error::new(std::io::ErrorKind::Other, "invalid identity")),
|b| {
Identity::from_bytes(&b).map_or_else(
|| Err(std::io::Error::new(std::io::ErrorKind::Other, "invalid identity")),
|id| Ok(id),
)
},
)
} }
fn to_string_internal(&self, include_private: bool) -> String { fn to_string_internal(&self, include_private: bool) -> String {
@ -660,6 +498,12 @@ impl Identity {
s s
} }
fn fill_in_fingerprint(&mut self) {
let mut h = SHA384::new();
self.write_public(&mut h, false);
self.fingerprint = h.finish();
}
#[inline(always)] #[inline(always)]
pub fn to_public_string(&self) -> String { pub fn to_public_string(&self) -> String {
self.to_string_internal(false) self.to_string_internal(false)
@ -821,10 +665,108 @@ impl FromStr for Identity {
}, },
fingerprint: [0; 48], fingerprint: [0; 48],
}); });
id.as_mut().unwrap().fill_in_fingerprint();
id
}
}
let fingerprint = SHA384::hash(id.as_ref().unwrap().to_public_bytes().as_bytes()); impl Marshalable for Identity {
id.as_mut().unwrap().fingerprint = fingerprint; const MAX_MARSHAL_SIZE: usize = Self::BYTE_LENGTH_MAX;
#[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
self.write_public(buf, false).map_err(|e| e.into())
}
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError> {
let address = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?;
let type_flags = buf.read_u8(cursor)?;
let x25519 = buf.read_bytes_fixed::<C25519_PUBLIC_KEY_SIZE>(cursor)?;
let ed25519 = buf.read_bytes_fixed::<ED25519_PUBLIC_KEY_SIZE>(cursor)?;
let (
mut ecdh,
mut ecdsa,
mut ecdsa_self_signature,
mut ed25519_self_signature,
mut x25519_s,
mut ed25519_s,
mut ecdh_s,
mut ecdsa_s,
) = (None, None, None, None, None, None, None, None);
if type_flags == 0 {
const C25519_SECRETS_SIZE: u8 = (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8;
match buf.read_u8(cursor)? {
0 => {
x25519_s = None;
ed25519_s = None;
}
C25519_SECRETS_SIZE => {
x25519_s = Some(buf.read_bytes_fixed::<C25519_SECRET_KEY_SIZE>(cursor)?);
ed25519_s = Some(buf.read_bytes_fixed::<ED25519_SECRET_KEY_SIZE>(cursor)?);
}
_ => return Err(UnmarshalError::InvalidData),
}
} else {
if (type_flags & (Self::ALGORITHM_X25519 | Self::FLAG_INCLUDES_SECRETS))
== (Self::ALGORITHM_X25519 | Self::FLAG_INCLUDES_SECRETS)
{
x25519_s = Some(buf.read_bytes_fixed::<C25519_SECRET_KEY_SIZE>(cursor)?);
ed25519_s = Some(buf.read_bytes_fixed::<ED25519_SECRET_KEY_SIZE>(cursor)?);
}
if (type_flags & Self::ALGORITHM_EC_NIST_P384) != 0 {
ecdh = Some(buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?);
ecdsa = Some(buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?);
if (type_flags & Self::FLAG_INCLUDES_SECRETS) != 0 {
ecdh_s = Some(buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?);
ecdsa_s = Some(buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?);
}
ecdsa_self_signature = Some(buf.read_bytes_fixed::<P384_ECDSA_SIGNATURE_SIZE>(cursor)?);
ed25519_self_signature = Some(buf.read_bytes_fixed::<ED25519_SIGNATURE_SIZE>(cursor)?);
}
}
let mut id = Ok(Identity {
address,
x25519: x25519.clone(),
ed25519: ed25519.clone(),
p384: if let Some(ecdh) = ecdh {
Some(IdentityP384Public {
ecdh: P384PublicKey::from_bytes(ecdh).ok_or(UnmarshalError::InvalidData)?,
ecdsa: P384PublicKey::from_bytes(ecdsa.ok_or(UnmarshalError::InvalidData)?).ok_or(UnmarshalError::InvalidData)?,
ecdsa_self_signature: ecdsa_self_signature.ok_or(UnmarshalError::InvalidData)?.clone(),
ed25519_self_signature: ed25519_self_signature.ok_or(UnmarshalError::InvalidData)?.clone(),
})
} else {
None
},
secret: if let Some(x25519_s) = x25519_s {
Some(IdentitySecret {
x25519: X25519KeyPair::from_bytes(x25519, x25519_s).ok_or(UnmarshalError::InvalidData)?,
ed25519: Ed25519KeyPair::from_bytes(ed25519, ed25519_s.ok_or(UnmarshalError::InvalidData)?)
.ok_or(UnmarshalError::InvalidData)?,
p384: if let Some(ecdh_s) = ecdh_s {
Some(IdentityP384Secret {
ecdh: P384KeyPair::from_bytes(ecdh.ok_or(UnmarshalError::InvalidData)?, ecdh_s)
.ok_or(UnmarshalError::InvalidData)?,
ecdsa: P384KeyPair::from_bytes(
ecdsa.ok_or(UnmarshalError::InvalidData)?,
ecdsa_s.ok_or(UnmarshalError::InvalidData)?,
)
.ok_or(UnmarshalError::InvalidData)?,
})
} else {
None
},
})
} else {
None
},
fingerprint: [0u8; IDENTITY_FINGERPRINT_SIZE],
});
id.as_mut().unwrap().fill_in_fingerprint();
id id
} }
} }
@ -832,7 +774,7 @@ impl FromStr for Identity {
impl PartialEq for Identity { impl PartialEq for Identity {
#[inline(always)] #[inline(always)]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.fingerprint == other.fingerprint self.fingerprint.eq(&other.fingerprint)
} }
} }
@ -861,114 +803,6 @@ impl Hash for Identity {
} }
} }
mod packed {
use super::*;
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub(super) struct V0 {
pub address: [u8; ADDRESS_SIZE],
pub key_type: u8,
pub x25519: [u8; C25519_PUBLIC_KEY_SIZE],
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
pub secret_length: u8,
pub reserved: u8,
pub ext_len: [u8; 2],
}
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub(super) struct V0S {
pub address: [u8; ADDRESS_SIZE],
pub key_type: u8,
pub x25519: [u8; C25519_PUBLIC_KEY_SIZE],
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
pub secret_length: u8,
pub x25519_secret: [u8; C25519_SECRET_KEY_SIZE],
pub ed25519_secret: [u8; ED25519_SECRET_KEY_SIZE],
pub reserved: u8,
pub ext_len: [u8; 2],
}
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub(super) struct V1 {
pub v0: V0,
pub key_type_flags: u8,
pub ecdh: [u8; P384_PUBLIC_KEY_SIZE],
pub ecdsa: [u8; P384_PUBLIC_KEY_SIZE],
pub ecdsa_self_signature: [u8; P384_ECDSA_SIGNATURE_SIZE],
pub ed25519_self_signature: [u8; ED25519_SIGNATURE_SIZE],
}
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub(super) struct V1S {
pub v0s: V0S,
pub key_type_flags: u8,
pub ecdh: [u8; P384_PUBLIC_KEY_SIZE],
pub ecdsa: [u8; P384_PUBLIC_KEY_SIZE],
pub ecdsa_self_signature: [u8; P384_ECDSA_SIGNATURE_SIZE],
pub ed25519_self_signature: [u8; ED25519_SIGNATURE_SIZE],
pub ecdh_secret: [u8; P384_SECRET_KEY_SIZE],
pub ecdsa_secret: [u8; P384_SECRET_KEY_SIZE],
}
}
/// Identity rendered as a flat byte array.
///
/// Note that try_from() and into() here perform a straight conversion or cast between this and
/// byte slices. They don't check the actual format of the contained identity data.
#[derive(Clone, PartialEq, Eq)]
pub enum IdentityBytes {
X25519Public([u8; Identity::BYTE_LENGTH_X25519_PUBLIC]),
X25519Secret([u8; Identity::BYTE_LENGTH_X25519_SECRET]),
X25519P384Public([u8; Identity::BYTE_LENGTH_X25519P384_PUBLIC]),
X25519P384Secret([u8; Identity::BYTE_LENGTH_X25519P384_SECRET]),
}
impl IdentityBytes {
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
self.into()
}
}
impl<'a> From<&'a IdentityBytes> for &'a [u8] {
fn from(b: &'a IdentityBytes) -> Self {
match b {
IdentityBytes::X25519Public(b) => b,
IdentityBytes::X25519Secret(b) => b,
IdentityBytes::X25519P384Public(b) => b,
IdentityBytes::X25519P384Secret(b) => b,
}
}
}
impl TryFrom<&[u8]> for IdentityBytes {
type Error = std::array::TryFromSliceError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
match b.len() {
Identity::BYTE_LENGTH_X25519_PUBLIC => Ok(Self::X25519Public(b.try_into()?)),
Identity::BYTE_LENGTH_X25519_SECRET => Ok(Self::X25519Secret(b.try_into()?)),
Identity::BYTE_LENGTH_X25519P384_PUBLIC => Ok(Self::X25519P384Public(b.try_into()?)),
_ => Ok(Self::X25519P384Secret(b.try_into()?)),
}
}
}
impl Drop for IdentityBytes {
fn drop(&mut self) {
// This can contain secrets, so zero it like Secret<> in the crypto core.
unsafe {
for i in 0..std::mem::size_of::<Self>() {
std::ptr::write_volatile((self as *mut Self).cast::<u8>().add(i), 0);
}
}
}
}
impl Serialize for Identity { impl Serialize for Identity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
@ -977,7 +811,7 @@ impl Serialize for Identity {
if serializer.is_human_readable() { if serializer.is_human_readable() {
serializer.serialize_str(self.to_public_string().as_str()) serializer.serialize_str(self.to_public_string().as_str())
} else { } else {
serializer.serialize_bytes((&self.to_public_bytes()).into()) serializer.serialize_bytes(self.to_bytes().as_slice())
} }
} }
} }
@ -995,10 +829,7 @@ impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
where where
E: serde::de::Error, E: serde::de::Error,
{ {
IdentityBytes::try_from(v).map_or_else( todo!()
|e| Err(E::custom(e.to_string())),
|b| Identity::from_bytes(&b).map_or_else(|| Err(E::custom("invalid identity")), |id| Ok(id)),
)
} }
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
@ -1071,11 +902,11 @@ mod tests {
let gen = Identity::generate(); let gen = Identity::generate();
assert!(gen.agree(&gen).is_some()); assert!(gen.agree(&gen).is_some());
assert!(gen.validate_identity()); assert!(gen.validate_identity());
let bytes = gen.to_secret_bytes().unwrap(); let bytes = gen.to_secret_bytes(false).unwrap();
let string = gen.to_secret_string(); let string = gen.to_secret_string();
assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen)); assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen));
let gen_unmarshaled = Identity::from_bytes(&bytes).unwrap(); let gen_unmarshaled = Identity::from_bytes(bytes.as_bytes()).unwrap();
assert!(gen_unmarshaled.secret.is_some()); assert!(gen_unmarshaled.secret.is_some());
if !gen_unmarshaled.eq(&gen) { if !gen_unmarshaled.eq(&gen) {
println!( println!(
@ -1098,12 +929,12 @@ mod tests {
assert!(id.validate_identity()); assert!(id.validate_identity());
assert!(id.p384.is_none()); assert!(id.p384.is_none());
let idb = id.to_secret_bytes().unwrap(); let idb = id.to_secret_bytes(false).unwrap();
let id_unmarshal = Identity::from_bytes(&idb).unwrap(); let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
assert!(id == id_unmarshal); assert!(id == id_unmarshal);
assert!(id_unmarshal.secret.is_some()); assert!(id_unmarshal.secret.is_some());
let idb2 = id_unmarshal.to_public_bytes(); let idb2 = id_unmarshal.to_bytes();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap(); let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap();
assert!(id_unmarshal2 == id_unmarshal); assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id); assert!(id_unmarshal2 == id);
@ -1128,11 +959,11 @@ mod tests {
assert!(id.p384.is_some()); assert!(id.p384.is_some());
assert!(id.secret.as_ref().unwrap().p384.is_some()); assert!(id.secret.as_ref().unwrap().p384.is_some());
let idb = id.to_secret_bytes().unwrap(); let idb = id.to_secret_bytes(false).unwrap();
let id_unmarshal = Identity::from_bytes(&idb).unwrap(); let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
assert!(id == id_unmarshal); assert!(id == id_unmarshal);
let idb2 = id_unmarshal.to_public_bytes(); let idb2 = id_unmarshal.to_bytes();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap(); let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap();
assert!(id_unmarshal2 == id_unmarshal); assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id); assert!(id_unmarshal2 == id);

View file

@ -9,11 +9,9 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::util::marshalable::*;
use crate::error::InvalidFormatError;
use zerotier_utils::buffer::Buffer; use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
#[cfg(windows)] #[cfg(windows)]
use winapi::um::winsock2; use winapi::um::winsock2;
@ -91,7 +89,7 @@ impl ToSocketAddrs for InetAddress {
} }
impl TryInto<IpAddr> for InetAddress { impl TryInto<IpAddr> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<IpAddr, Self::Error> { fn try_into(self) -> Result<IpAddr, Self::Error> {
@ -100,20 +98,20 @@ impl TryInto<IpAddr> for InetAddress {
} }
impl TryInto<IpAddr> for &InetAddress { impl TryInto<IpAddr> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<IpAddr, Self::Error> { fn try_into(self) -> Result<IpAddr, Self::Error> {
match unsafe { self.sa.sa_family } { match unsafe { self.sa.sa_family } {
AF_INET => Ok(IpAddr::V4(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() }))), AF_INET => Ok(IpAddr::V4(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() }))),
AF_INET6 => Ok(IpAddr::V6(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr }))), AF_INET6 => Ok(IpAddr::V6(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr }))),
_ => Err(crate::error::InvalidParameterError("not an IP address")), _ => Err(InvalidParameterError("not an IP address")),
} }
} }
} }
impl TryInto<Ipv4Addr> for InetAddress { impl TryInto<Ipv4Addr> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<Ipv4Addr, Self::Error> { fn try_into(self) -> Result<Ipv4Addr, Self::Error> {
@ -122,19 +120,19 @@ impl TryInto<Ipv4Addr> for InetAddress {
} }
impl TryInto<Ipv4Addr> for &InetAddress { impl TryInto<Ipv4Addr> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<Ipv4Addr, Self::Error> { fn try_into(self) -> Result<Ipv4Addr, Self::Error> {
match unsafe { self.sa.sa_family } { match unsafe { self.sa.sa_family } {
AF_INET => Ok(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() })), AF_INET => Ok(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() })),
_ => Err(crate::error::InvalidParameterError("not an IPv4 address")), _ => Err(InvalidParameterError("not an IPv4 address")),
} }
} }
} }
impl TryInto<Ipv6Addr> for InetAddress { impl TryInto<Ipv6Addr> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<Ipv6Addr, Self::Error> { fn try_into(self) -> Result<Ipv6Addr, Self::Error> {
@ -143,19 +141,19 @@ impl TryInto<Ipv6Addr> for InetAddress {
} }
impl TryInto<Ipv6Addr> for &InetAddress { impl TryInto<Ipv6Addr> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<Ipv6Addr, Self::Error> { fn try_into(self) -> Result<Ipv6Addr, Self::Error> {
match unsafe { self.sa.sa_family } { match unsafe { self.sa.sa_family } {
AF_INET6 => Ok(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr })), AF_INET6 => Ok(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr })),
_ => Err(crate::error::InvalidParameterError("not an IPv6 address")), _ => Err(InvalidParameterError("not an IPv6 address")),
} }
} }
} }
impl TryInto<SocketAddr> for InetAddress { impl TryInto<SocketAddr> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddr, Self::Error> { fn try_into(self) -> Result<SocketAddr, Self::Error> {
@ -164,7 +162,7 @@ impl TryInto<SocketAddr> for InetAddress {
} }
impl TryInto<SocketAddr> for &InetAddress { impl TryInto<SocketAddr> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddr, Self::Error> { fn try_into(self) -> Result<SocketAddr, Self::Error> {
@ -180,14 +178,14 @@ impl TryInto<SocketAddr> for &InetAddress {
0, 0,
0, 0,
))), ))),
_ => Err(crate::error::InvalidParameterError("not an IP address")), _ => Err(InvalidParameterError("not an IP address")),
} }
} }
} }
} }
impl TryInto<SocketAddrV4> for InetAddress { impl TryInto<SocketAddrV4> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddrV4, Self::Error> { fn try_into(self) -> Result<SocketAddrV4, Self::Error> {
@ -196,7 +194,7 @@ impl TryInto<SocketAddrV4> for InetAddress {
} }
impl TryInto<SocketAddrV4> for &InetAddress { impl TryInto<SocketAddrV4> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddrV4, Self::Error> { fn try_into(self) -> Result<SocketAddrV4, Self::Error> {
@ -206,14 +204,14 @@ impl TryInto<SocketAddrV4> for &InetAddress {
Ipv4Addr::from(self.sin.sin_addr.s_addr.to_ne_bytes()), Ipv4Addr::from(self.sin.sin_addr.s_addr.to_ne_bytes()),
u16::from_be(self.sin.sin_port as u16), u16::from_be(self.sin.sin_port as u16),
)), )),
_ => Err(crate::error::InvalidParameterError("not an IPv4 address")), _ => Err(InvalidParameterError("not an IPv4 address")),
} }
} }
} }
} }
impl TryInto<SocketAddrV6> for InetAddress { impl TryInto<SocketAddrV6> for InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddrV6, Self::Error> { fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
@ -222,7 +220,7 @@ impl TryInto<SocketAddrV6> for InetAddress {
} }
impl TryInto<SocketAddrV6> for &InetAddress { impl TryInto<SocketAddrV6> for &InetAddress {
type Error = crate::error::InvalidParameterError; type Error = InvalidParameterError;
#[inline(always)] #[inline(always)]
fn try_into(self) -> Result<SocketAddrV6, Self::Error> { fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
@ -234,7 +232,7 @@ impl TryInto<SocketAddrV6> for &InetAddress {
0, 0,
0, 0,
)), )),
_ => Err(crate::error::InvalidParameterError("not an IPv6 address")), _ => Err(InvalidParameterError("not an IPv6 address")),
} }
} }
} }
@ -783,7 +781,7 @@ impl InetAddress {
impl Marshalable for InetAddress { impl Marshalable for InetAddress {
const MAX_MARSHAL_SIZE: usize = 19; const MAX_MARSHAL_SIZE: usize = 19;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError> { fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
unsafe { unsafe {
match self.sa.sa_family as AddressFamilyType { match self.sa.sa_family as AddressFamilyType {
AF_INET => { AF_INET => {
@ -810,7 +808,7 @@ impl Marshalable for InetAddress {
} }
} }
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<InetAddress, MarshalUnmarshalError> { fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<InetAddress, UnmarshalError> {
let t = buf.read_u8(cursor)?; let t = buf.read_u8(cursor)?;
if t == 4 { if t == 4 {
let b: &[u8; 6] = buf.read_bytes_fixed(cursor)?; let b: &[u8; 6] = buf.read_bytes_fixed(cursor)?;

View file

@ -7,11 +7,10 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::InvalidFormatError;
use crate::util::marshalable::*;
use zerotier_utils::buffer::Buffer; use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex; use zerotier_utils::hex;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
/// An Ethernet MAC address. /// An Ethernet MAC address.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -87,14 +86,14 @@ impl Marshalable for MAC {
const MAX_MARSHAL_SIZE: usize = 6; const MAX_MARSHAL_SIZE: usize = 6;
#[inline(always)] #[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError> { fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
buf.append_bytes(&self.0.get().to_be_bytes()[2..]) buf.append_bytes(&self.0.get().to_be_bytes()[2..])
.map_err(|_| MarshalUnmarshalError::OutOfBounds) .map_err(|_| UnmarshalError::OutOfBounds)
} }
#[inline(always)] #[inline(always)]
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, MarshalUnmarshalError> { fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError> {
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(MarshalUnmarshalError::InvalidData) Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)
} }
} }

View file

@ -9,10 +9,7 @@ use std::time::Duration;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use crate::error::InvalidParameterError;
use crate::protocol::*; use crate::protocol::*;
use crate::util::gate::IntervalGate;
use crate::util::marshalable::Marshalable;
use crate::vl1::address::Address; use crate::vl1::address::Address;
use crate::vl1::debug_event; use crate::vl1::debug_event;
use crate::vl1::endpoint::Endpoint; use crate::vl1::endpoint::Endpoint;
@ -24,7 +21,10 @@ use crate::vl1::rootset::RootSet;
use zerotier_crypto::random; use zerotier_crypto::random;
use zerotier_crypto::verified::Verified; use zerotier_crypto::verified::Verified;
use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::gate::IntervalGate;
use zerotier_utils::hex; use zerotier_utils::hex;
use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::ringbuffer::RingBuffer; use zerotier_utils::ringbuffer::RingBuffer;
/// Trait implemented by external code to handle events and provide an interface to the system or application. /// Trait implemented by external code to handle events and provide an interface to the system or application.
@ -820,14 +820,15 @@ impl<HostSystemImpl: HostSystem> Node<HostSystemImpl> {
packet.clear(); packet.clear();
packet.set_size(v1::HEADER_SIZE); packet.set_size(v1::HEADER_SIZE);
let _ = packet.append_u8(verbs::VL1_WHOIS); let _ = packet.append_u8(verbs::VL1_WHOIS);
} else { }
let _ = packet.append_bytes_fixed(&a.to_bytes()); let _ = packet.append_bytes_fixed(&a.to_bytes());
} }
} if packet.len() > (v1::HEADER_SIZE + 1) {
root.send(host_system, None, self, time_ticks, &mut packet); root.send(host_system, None, self, time_ticks, &mut packet);
} }
} }
} }
}
/// Called by Peer when an identity is received from another node, e.g. via OK(WHOIS). /// Called by Peer when an identity is received from another node, e.g. via OK(WHOIS).
pub(crate) fn handle_incoming_identity<InnerProtocolImpl: InnerProtocol>( pub(crate) fn handle_incoming_identity<InnerProtocolImpl: InnerProtocol>(

View file

@ -12,6 +12,7 @@ use crate::vl1::fragmentedpacket::FragmentedPacket;
use crate::vl1::node::*; use crate::vl1::node::*;
use zerotier_crypto::random; use zerotier_crypto::random;
use zerotier_utils::NEVER_HAPPENED_TICKS;
pub(crate) const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL; pub(crate) const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
@ -46,8 +47,8 @@ impl<HostSystemImpl: HostSystem> Path<HostSystemImpl> {
endpoint, endpoint,
local_socket, local_socket,
local_interface, local_interface,
last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_send_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_receive_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
create_time_ticks: time_ticks, create_time_ticks: time_ticks,
fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(4, PacketIdHasher(random::xorshift64_random()))), fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(4, PacketIdHasher(random::xorshift64_random()))),
} }

View file

@ -11,11 +11,11 @@ use zerotier_crypto::poly1305;
use zerotier_crypto::random; use zerotier_crypto::random;
use zerotier_crypto::salsa::Salsa; use zerotier_crypto::salsa::Salsa;
use zerotier_crypto::secret::Secret; use zerotier_crypto::secret::Secret;
use zerotier_utils::buffer::BufferReader; use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::memory::array_range; use zerotier_utils::memory::array_range;
use zerotier_utils::NEVER_HAPPENED_TICKS;
use crate::protocol::*; use crate::protocol::*;
use crate::util::marshalable::Marshalable;
use crate::vl1::address::Address; use crate::vl1::address::Address;
use crate::vl1::debug_event; use crate::vl1::debug_event;
use crate::vl1::node::*; use crate::vl1::node::*;
@ -69,10 +69,10 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
identity: id, identity: id,
static_symmetric_key: SymmetricSecret::new(static_secret), static_symmetric_key: SymmetricSecret::new(static_secret),
paths: Mutex::new(Vec::with_capacity(4)), paths: Mutex::new(Vec::with_capacity(4)),
last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_send_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_receive_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_forward_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_forward_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_hello_reply_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_hello_reply_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
create_time_ticks: time_ticks, create_time_ticks: time_ticks,
random_ticks_offset: random::xorshift64_random() as u32, random_ticks_offset: random::xorshift64_random() as u32,
message_id_counter: AtomicU64::new(random::xorshift64_random()), message_id_counter: AtomicU64::new(random::xorshift64_random()),
@ -279,11 +279,8 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
} }
}; };
let max_fragment_size = if path.endpoint.requires_fragmentation() { let max_fragment_size = path.endpoint.max_fragment_size();
UDP_DEFAULT_MTU if self.remote_node_info.read().remote_protocol_version >= 12 {
} else {
usize::MAX
};
let flags_cipher_hops = if packet.len() > max_fragment_size { let flags_cipher_hops = if packet.len() > max_fragment_size {
v1::HEADER_FLAG_FRAGMENTED | v1::CIPHER_AES_GMAC_SIV v1::HEADER_FLAG_FRAGMENTED | v1::CIPHER_AES_GMAC_SIV
} else { } else {
@ -297,19 +294,49 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
node.identity.address, node.identity.address,
flags_cipher_hops, flags_cipher_hops,
)); ));
if let Ok(payload) = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE) { let tag = if let Ok(payload) = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE) {
aes_gmac_siv.encrypt_first_pass(payload); aes_gmac_siv.encrypt_first_pass(payload);
aes_gmac_siv.encrypt_first_pass_finish(); aes_gmac_siv.encrypt_first_pass_finish();
aes_gmac_siv.encrypt_second_pass_in_place(payload); aes_gmac_siv.encrypt_second_pass_in_place(payload);
let tag = aes_gmac_siv.encrypt_second_pass_finish(); aes_gmac_siv.encrypt_second_pass_finish()
} else {
return false;
};
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap(); let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id = *array_range::<u8, 16, 0, 8>(tag); header.id.copy_from_slice(&tag[0..8]);
header.dest = self.identity.address.to_bytes(); header.dest = self.identity.address.to_bytes();
header.src = node.identity.address.to_bytes(); header.src = node.identity.address.to_bytes();
header.flags_cipher_hops = flags_cipher_hops; header.flags_cipher_hops = flags_cipher_hops;
header.mac = *array_range::<u8, 16, 8, 8>(tag); header.mac.copy_from_slice(&tag[8..16]);
} else {
let packet_len = packet.len();
let flags_cipher_hops = if packet.len() > max_fragment_size {
v1::HEADER_FLAG_FRAGMENTED | v1::CIPHER_SALSA2012_POLY1305
} else {
v1::CIPHER_SALSA2012_POLY1305
};
let (mut salsa, poly1305_otk) = salsa_poly_create(
&self.static_symmetric_key,
{
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id = self.next_message_id().to_ne_bytes();
header.dest = self.identity.address.to_bytes();
header.src = node.identity.address.to_bytes();
header.flags_cipher_hops = flags_cipher_hops;
header
},
packet_len,
);
let tag = if let Ok(payload) = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE) {
salsa.crypt_in_place(payload);
poly1305::compute(&poly1305_otk, payload)
} else { } else {
return false; return false;
};
packet.as_bytes_mut()[v1::MAC_FIELD_INDEX..(v1::MAC_FIELD_INDEX + 8)].copy_from_slice(&tag[0..8]);
} }
self.internal_send( self.internal_send(
@ -374,11 +401,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
} }
}; };
let max_fragment_size = if destination.requires_fragmentation() { let max_fragment_size = destination.max_fragment_size();
UDP_DEFAULT_MTU
} else {
usize::MAX
};
let time_ticks = host_system.time_ticks(); let time_ticks = host_system.time_ticks();
let mut packet = PacketBuffer::new(); let mut packet = PacketBuffer::new();
@ -401,7 +424,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
} }
debug_assert_eq!(packet.len(), 41); debug_assert_eq!(packet.len(), 41);
assert!(packet.append_bytes((&node.identity.to_public_bytes()).into()).is_ok()); assert!(node.identity.write_public(&mut packet, self.identity.p384.is_none()).is_ok());
let (_, poly1305_key) = salsa_poly_create( let (_, poly1305_key) = salsa_poly_create(
&self.static_symmetric_key, &self.static_symmetric_key,
@ -575,7 +598,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
let mut cursor = 0; let mut cursor = 0;
if let Ok(hello_fixed_headers) = payload.read_struct::<v1::message_component_structs::HelloFixedHeaderFields>(&mut cursor) { if let Ok(hello_fixed_headers) = payload.read_struct::<v1::message_component_structs::HelloFixedHeaderFields>(&mut cursor) {
if let Ok(identity) = Identity::read_bytes(&mut BufferReader::new(payload, &mut cursor)) { if let Ok(identity) = Identity::unmarshal(payload, &mut cursor) {
if identity.eq(&self.identity) { if identity.eq(&self.identity) {
{ {
let mut remote_node_info = self.remote_node_info.write(); let mut remote_node_info = self.remote_node_info.write();
@ -698,15 +721,22 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
verbs::VL1_WHOIS => { verbs::VL1_WHOIS => {
if node.is_peer_root(self) { if node.is_peer_root(self) {
while cursor < payload.len() { while cursor < payload.len() {
if let Ok(received_identity) = Identity::read_bytes(&mut BufferReader::new(payload, &mut cursor)) { let r = Identity::unmarshal(payload, &mut cursor);
if let Ok(received_identity) = r {
debug_event!( debug_event!(
host_system, host_system,
"[vl1] {} OK(WHOIS): {}", "[vl1] {} OK(WHOIS): new identity: {}",
self.identity.address.to_string(), self.identity.address.to_string(),
received_identity.to_string() received_identity.to_string()
); );
node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true); node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true);
} else { } else {
debug_event!(
host_system,
"[vl1] {} OK(WHOIS): bad identity: {}",
self.identity.address.to_string(),
r.err().unwrap().to_string()
);
break; break;
} }
} }
@ -750,14 +780,14 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
if addresses.len() >= ADDRESS_SIZE { if addresses.len() >= ADDRESS_SIZE {
if let Some(zt_address) = Address::from_bytes(&addresses[..ADDRESS_SIZE]) { if let Some(zt_address) = Address::from_bytes(&addresses[..ADDRESS_SIZE]) {
if let Some(peer) = node.peer(zt_address) { if let Some(peer) = node.peer(zt_address) {
let id_bytes_tmp = peer.identity.to_public_bytes(); if let Ok(id_bytes) = peer.identity.to_public_bytes(self.identity.p384.is_none()) {
let id_bytes = id_bytes_tmp.as_bytes();
if (packet.capacity() - packet.len()) < id_bytes.len() { if (packet.capacity() - packet.len()) < id_bytes.len() {
self.send(host_system, None, node, time_ticks, &mut packet); self.send(host_system, None, node, time_ticks, &mut packet);
packet.clear(); packet.clear();
init_packet(&mut packet); init_packet(&mut packet);
} }
let _ = packet.append_bytes(id_bytes); let _ = packet.append_bytes(id_bytes.as_bytes());
}
} }
} }
addresses = &addresses[ADDRESS_SIZE..]; addresses = &addresses[ADDRESS_SIZE..];

View file

@ -3,12 +3,12 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::io::Write; use std::io::Write;
use crate::util::marshalable::*;
use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE}; use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE};
use crate::vl1::Endpoint; use crate::vl1::Endpoint;
use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::buffer::{Buffer, BufferReader}; use zerotier_utils::buffer::Buffer;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use zerotier_crypto::verified::Verified; use zerotier_crypto::verified::Verified;
@ -100,43 +100,6 @@ impl RootSet {
rs.verify().unwrap() rs.verify().unwrap()
} }
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_signatures: bool) -> Result<(), MarshalUnmarshalError> {
buf.append_u8(0)?; // version byte for future use
buf.append_varint(self.name.as_bytes().len() as u64)?;
buf.append_bytes(self.name.as_bytes())?;
if self.url.is_some() {
let url = self.url.as_ref().unwrap().as_bytes();
buf.append_varint(url.len() as u64)?;
buf.append_bytes(url)?;
} else {
buf.append_varint(0)?;
}
buf.append_varint(self.revision)?;
buf.append_varint(self.members.len() as u64)?;
for m in self.members.iter() {
buf.append_bytes((&m.identity.to_public_bytes()).into())?;
if m.endpoints.is_some() {
let endpoints = m.endpoints.as_ref().unwrap();
buf.append_varint(endpoints.len() as u64)?;
for a in endpoints.iter() {
a.marshal(buf)?;
}
} else {
buf.append_varint(0)?;
}
if include_signatures {
buf.append_varint(m.signature.len() as u64)?;
buf.append_bytes(m.signature.as_ref())?;
}
buf.append_varint(0)?; // flags, currently always 0
buf.append_u8(m.priority)?;
buf.append_u8(m.protocol_version)?;
buf.append_varint(0)?; // size of additional fields for future use
}
buf.append_varint(0)?; // size of additional fields for future use
Ok(())
}
/// Internal method to marshal without signatures for use during sign and verify. /// Internal method to marshal without signatures for use during sign and verify.
fn marshal_for_signing(&self) -> Buffer<{ Self::MAX_MARSHAL_SIZE }> { fn marshal_for_signing(&self) -> Buffer<{ Self::MAX_MARSHAL_SIZE }> {
let mut tmp = Buffer::<{ Self::MAX_MARSHAL_SIZE }>::new(); let mut tmp = Buffer::<{ Self::MAX_MARSHAL_SIZE }>::new();
@ -254,20 +217,57 @@ impl RootSet {
false false
} }
} }
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_signatures: bool) -> Result<(), UnmarshalError> {
buf.append_u8(0)?; // version byte for future use
buf.append_varint(self.name.as_bytes().len() as u64)?;
buf.append_bytes(self.name.as_bytes())?;
if self.url.is_some() {
let url = self.url.as_ref().unwrap().as_bytes();
buf.append_varint(url.len() as u64)?;
buf.append_bytes(url)?;
} else {
buf.append_varint(0)?;
}
buf.append_varint(self.revision)?;
buf.append_varint(self.members.len() as u64)?;
for m in self.members.iter() {
m.identity.marshal(buf)?;
if m.endpoints.is_some() {
let endpoints = m.endpoints.as_ref().unwrap();
buf.append_varint(endpoints.len() as u64)?;
for a in endpoints.iter() {
a.marshal(buf)?;
}
} else {
buf.append_varint(0)?;
}
if include_signatures {
buf.append_varint(m.signature.len() as u64)?;
buf.append_bytes(m.signature.as_ref())?;
}
buf.append_varint(0)?; // flags, currently always 0
buf.append_u8(m.priority)?;
buf.append_u8(m.protocol_version)?;
buf.append_varint(0)?; // size of additional fields for future use
}
buf.append_varint(0)?; // size of additional fields for future use
Ok(())
}
} }
impl Marshalable for RootSet { impl Marshalable for RootSet {
const MAX_MARSHAL_SIZE: usize = crate::protocol::v1::SIZE_MAX; const MAX_MARSHAL_SIZE: usize = crate::protocol::v1::SIZE_MAX;
#[inline(always)] #[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError> { fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
self.marshal_internal(buf, true) self.marshal_internal(buf, true)
} }
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, MarshalUnmarshalError> { fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError> {
let mut rc = Self::new(String::new(), None, 0); let mut rc = Self::new(String::new(), None, 0);
if buf.read_u8(cursor)? != 0 { if buf.read_u8(cursor)? != 0 {
return Err(MarshalUnmarshalError::UnsupportedVersion); return Err(UnmarshalError::UnsupportedVersion);
} }
let name_len = buf.read_varint(cursor)?; let name_len = buf.read_varint(cursor)?;
@ -283,7 +283,7 @@ impl Marshalable for RootSet {
let member_count = buf.read_varint(cursor)?; let member_count = buf.read_varint(cursor)?;
for _ in 0..member_count { for _ in 0..member_count {
let mut m = Root { let mut m = Root {
identity: Identity::read_bytes(&mut BufferReader::new(buf, cursor)).map_err(|e| MarshalUnmarshalError::IoError(e))?, identity: Identity::unmarshal(buf, cursor)?,
endpoints: None, endpoints: None,
signature: ArrayVec::new(), signature: ArrayVec::new(),
priority: 0, priority: 0,
@ -313,7 +313,7 @@ impl Marshalable for RootSet {
*cursor += buf.read_varint(cursor)? as usize; *cursor += buf.read_varint(cursor)? as usize;
if *cursor > buf.len() { if *cursor > buf.len() {
return Err(MarshalUnmarshalError::OutOfBounds); return Err(UnmarshalError::OutOfBounds);
} }
rc.members.sort(); rc.members.sort();

View file

@ -6,12 +6,11 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::InvalidFormatError;
use crate::util::marshalable::*;
use zerotier_utils::buffer::Buffer; use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex; use zerotier_utils::hex;
use zerotier_utils::hex::HEX_CHARS; use zerotier_utils::hex::HEX_CHARS;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)] #[repr(transparent)]
@ -61,13 +60,13 @@ impl Marshalable for NetworkId {
const MAX_MARSHAL_SIZE: usize = 8; const MAX_MARSHAL_SIZE: usize = 8;
#[inline(always)] #[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError> { fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
buf.append_u64(self.0.get()).map_err(|_| MarshalUnmarshalError::OutOfBounds) buf.append_u64(self.0.get()).map_err(|_| UnmarshalError::OutOfBounds)
} }
#[inline(always)] #[inline(always)]
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, MarshalUnmarshalError> { fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError> {
Self::from_u64(buf.read_u64(cursor)?).ok_or(MarshalUnmarshalError::InvalidData) Self::from_u64(buf.read_u64(cursor)?).ok_or(UnmarshalError::InvalidData)
} }
} }

View file

@ -6,9 +6,10 @@ use clap::ArgMatches;
use crate::{exitcode, Flags}; use crate::{exitcode, Flags};
use zerotier_network_hypervisor::util::marshalable::Marshalable;
use zerotier_network_hypervisor::vl1::RootSet; use zerotier_network_hypervisor::vl1::RootSet;
use zerotier_utils::json::to_json_pretty; use zerotier_utils::json::to_json_pretty;
use zerotier_utils::marshalable::Marshalable;
pub fn cmd(_: Flags, cmd_args: &ArgMatches) -> i32 { pub fn cmd(_: Flags, cmd_args: &ArgMatches) -> i32 {
match cmd_args.subcommand() { match cmd_args.subcommand() {

View file

@ -191,6 +191,22 @@ impl<T, const C: usize> ArrayVec<T, C> {
} }
} }
impl<T: Copy, const C: usize> ArrayVec<T, C> {
/// Push a slice of copyable objects, panic if capacity exceeded.
pub fn push_slice(&mut self, v: &[T]) {
let start = self.s;
let end = self.s + v.len();
if end <= C {
for i in start..end {
unsafe { self.a.get_unchecked_mut(i).write(*v.get_unchecked(i - start)) };
}
self.s = end;
} else {
panic!();
}
}
}
impl<T, const C: usize> Drop for ArrayVec<T, C> { impl<T, const C: usize> Drop for ArrayVec<T, C> {
#[inline(always)] #[inline(always)]
fn drop(&mut self) { fn drop(&mut self) {

View file

@ -3,8 +3,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io::Write; use std::io::Write;
use zerotier_utils::hex; use crate::hex;
use zerotier_utils::hex::HEX_CHARS;
const BOOL_TRUTH: &str = "1tTyY"; const BOOL_TRUTH: &str = "1tTyY";
@ -55,8 +54,8 @@ fn append_printable(s: &mut String, b: &[u8]) {
} else { } else {
s.push('\\'); s.push('\\');
s.push('x'); s.push('x');
s.push(HEX_CHARS[((c as u8) >> 4) as usize] as char); s.push(hex::HEX_CHARS[((c as u8) >> 4) as usize] as char);
s.push(HEX_CHARS[((c as u8) & 0xf) as usize] as char); s.push(hex::HEX_CHARS[((c as u8) & 0xf) as usize] as char);
} }
} }
} }
@ -212,163 +211,3 @@ impl ToString for Dictionary {
s s
} }
} }
#[cfg(test)]
mod tests {
#[derive(PartialEq, Eq, Clone, Debug)]
enum Type {
String,
Bytes,
U64,
Bool,
}
type TypeMap = HashMap<String, Type>;
use super::{Dictionary, BOOL_TRUTH};
use std::collections::HashMap;
fn randstring(len: u8) -> String {
(0..len)
.map(|_| (rand::random::<u8>() % 26) + 'a' as u8)
.map(|c| {
if rand::random::<bool>() {
(c as char).to_ascii_uppercase()
} else {
c as char
}
})
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join("")
}
fn make_dictionary() -> (Dictionary, TypeMap) {
let mut d = Dictionary::new();
let mut tm = TypeMap::new();
for _ in 0..(rand::random::<usize>() % 20) + 1 {
// NOTE: just doing this twice because I want to keep the code a little cleaner.
let selection = rand::random::<usize>() % 4;
let key = randstring(10);
// set the key
match selection {
0 => d.set_str(&key, &randstring(10)),
1 => d.set_u64(&key, rand::random()),
2 => d.set_bytes(
&key,
(0..((rand::random::<usize>() % 10) + 1))
.into_iter()
.map(|_| rand::random())
.collect::<Vec<u8>>(),
),
3 => d.set_bool(&key, rand::random::<bool>()),
_ => unreachable!(),
}
match selection {
0 => tm.insert(key, Type::String),
1 => tm.insert(key, Type::U64),
2 => tm.insert(key, Type::Bytes),
3 => tm.insert(key, Type::Bool),
_ => unreachable!(),
};
}
(d, tm)
}
#[test]
fn dictionary_basic() {
let mut d = Dictionary::new();
d.set_str("foo", "bar");
d.set_u64("bar", 0xfeedcafebabebeef);
d.set_bytes("baz", vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
d.set_bool("lala", true);
d.set_bool("haha", false);
let bytes = d.to_bytes();
let d2 = Dictionary::from_bytes(bytes.as_slice()).unwrap();
assert!(d.eq(&d2));
}
#[test]
fn dictionary_to_string() {
for _ in 0..1000 {
let (d, _) = make_dictionary();
assert_ne!(d.to_string().len(), 0)
}
}
#[test]
fn dictionary_clear() {
for _ in 0..1000 {
let (mut d, _) = make_dictionary();
assert_ne!(d.len(), 0);
assert!(!d.is_empty());
d.clear();
assert!(d.is_empty());
assert_eq!(d.len(), 0);
}
}
#[test]
fn dictionary_io() {
for _ in 0..1000 {
let (d, _) = make_dictionary();
assert_ne!(d.len(), 0);
assert!(!d.is_empty());
let mut v = Vec::new();
let mut cursor = std::io::Cursor::new(&mut v);
assert!(d.write_to(&mut cursor).is_ok());
drop(cursor);
assert!(!v.is_empty());
let d2 = super::Dictionary::from_bytes(v.as_slice());
assert!(d2.is_some());
let d2 = d2.unwrap();
assert_eq!(d, d2);
let d2 = super::Dictionary::from_bytes(&d.to_bytes());
assert!(d2.is_some());
let d2 = d2.unwrap();
assert_eq!(d, d2);
}
}
#[test]
fn dictionary_accessors() {
for _ in 0..1000 {
let (d, tm) = make_dictionary();
for (k, v) in d.iter() {
match tm.get(k).unwrap() {
Type::String => {
let v2 = d.get_str(k);
assert!(v2.is_some());
assert_eq!(String::from_utf8(v.to_vec()).unwrap(), String::from(v2.unwrap()));
}
Type::Bytes => {
let v2 = d.get_bytes(k);
assert!(v2.is_some());
assert_eq!(v, v2.unwrap());
}
Type::Bool => {
let v2 = d.get_bool(k);
assert!(v2.is_some());
// FIXME move this lettering to a constant
assert_eq!(BOOL_TRUTH.contains(*v.iter().nth(0).unwrap() as char), v2.unwrap());
}
Type::U64 => {
let v2 = d.get_u64(k);
assert!(v2.is_some());
assert_eq!(u64::from_str_radix(d.get_str(k).unwrap(), 16).unwrap(), v2.unwrap());
}
}
}
}
}
}

View file

@ -37,7 +37,7 @@ impl Debug for InvalidFormatError {
impl Error for InvalidFormatError {} impl Error for InvalidFormatError {}
pub struct InvalidParameterError(pub(crate) &'static str); pub struct InvalidParameterError(pub &'static str);
impl Display for InvalidParameterError { impl Display for InvalidParameterError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -53,20 +53,3 @@ impl Debug for InvalidParameterError {
} }
impl Error for InvalidParameterError {} impl Error for InvalidParameterError {}
pub struct MalformedRecordError(pub(crate) &'static str);
impl Display for MalformedRecordError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "InvalidParameterError: {}", self.0)
}
}
impl Debug for MalformedRecordError {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as Display>::fmt(self, f)
}
}
impl Error for MalformedRecordError {}

View file

@ -9,7 +9,7 @@ pub struct IntervalGate<const FREQ: i64>(i64);
impl<const FREQ: i64> Default for IntervalGate<FREQ> { impl<const FREQ: i64> Default for IntervalGate<FREQ> {
#[inline(always)] #[inline(always)]
fn default() -> Self { fn default() -> Self {
Self(crate::util::NEVER_HAPPENED_TICKS) Self(crate::NEVER_HAPPENED_TICKS)
} }
} }

View file

@ -3,11 +3,15 @@
pub mod arrayvec; pub mod arrayvec;
pub mod blob; pub mod blob;
pub mod buffer; pub mod buffer;
pub mod dictionary;
pub mod error;
#[allow(unused)] #[allow(unused)]
pub mod exitcode; pub mod exitcode;
pub mod gate;
pub mod gatherarray; pub mod gatherarray;
pub mod hex; pub mod hex;
pub mod json; pub mod json;
pub mod marshalable;
pub mod memory; pub mod memory;
pub mod pool; pub mod pool;
pub mod ringbuffer; pub mod ringbuffer;
@ -20,6 +24,9 @@ pub mod reaper;
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
pub use tokio; pub use tokio;
/// A monotonic ticks value for "never happened" that should be lower than any initial value.
pub const NEVER_HAPPENED_TICKS: i64 = i64::MIN / 2;
/// Get milliseconds since unix epoch. /// Get milliseconds since unix epoch.
pub fn ms_since_epoch() -> i64 { pub fn ms_since_epoch() -> i64 {
std::time::SystemTime::now() std::time::SystemTime::now()

View file

@ -3,72 +3,31 @@
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use zerotier_utils::buffer::{Buffer, OutOfBoundsError}; use crate::buffer::Buffer;
/// Must be larger than any object we want to use with to_bytes() or from_bytes(). /// Must be larger than any object we want to use with to_bytes() or from_bytes().
/// This hack can go away once Rust allows us to reference trait consts as generics. /// This hack can go away once Rust allows us to reference trait consts as generics.
const TEMP_BUF_SIZE: usize = 8192; const TEMP_BUF_SIZE: usize = 8192;
pub enum MarshalUnmarshalError {
OutOfBounds,
InvalidData,
UnsupportedVersion,
IoError(std::io::Error),
}
impl Display for MarshalUnmarshalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OutOfBounds => f.write_str("out of bounds"),
Self::InvalidData => f.write_str("invalid data"),
Self::UnsupportedVersion => f.write_str("unsupported version"),
Self::IoError(e) => f.write_str(e.to_string().as_str()),
}
}
}
impl Debug for MarshalUnmarshalError {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Error for MarshalUnmarshalError {}
impl From<OutOfBoundsError> for MarshalUnmarshalError {
#[inline(always)]
fn from(_: OutOfBoundsError) -> Self {
Self::OutOfBounds
}
}
impl From<std::io::Error> for MarshalUnmarshalError {
#[inline(always)]
fn from(e: std::io::Error) -> Self {
Self::IoError(e)
}
}
/// A super-lightweight zero-allocation serialization interface. /// A super-lightweight zero-allocation serialization interface.
pub trait Marshalable: Sized { pub trait Marshalable: Sized {
const MAX_MARSHAL_SIZE: usize; const MAX_MARSHAL_SIZE: usize;
/// Write this object into a buffer. /// Write this object into a buffer.
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), MarshalUnmarshalError>; fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError>;
/// Read this object from a buffer. /// Read this object from a buffer.
/// ///
/// The supplied cursor is advanced by the number of bytes read. If an Err is returned /// The supplied cursor is advanced by the number of bytes read. If an Err is returned
/// the value of the cursor is undefined but likely points to about where the error /// the value of the cursor is undefined but likely points to about where the error
/// occurred. It may also point beyond the buffer, which would indicate an overrun error. /// occurred. It may also point beyond the buffer, which would indicate an overrun error.
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, MarshalUnmarshalError>; fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError>;
/// Write this marshalable entity into a buffer of the given size. /// Write this marshalable entity into a buffer of the given size.
/// ///
/// This will return an Err if the buffer is too small or some other error occurs. It's just /// This will return an Err if the buffer is too small or some other error occurs. It's just
/// a shortcut to creating a buffer and marshaling into it. /// a shortcut to creating a buffer and marshaling into it.
fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, MarshalUnmarshalError> { fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, UnmarshalError> {
let mut tmp = Buffer::new(); let mut tmp = Buffer::new();
self.marshal(&mut tmp)?; self.marshal(&mut tmp)?;
Ok(tmp) Ok(tmp)
@ -77,7 +36,7 @@ pub trait Marshalable: Sized {
/// Unmarshal this object from a buffer. /// Unmarshal this object from a buffer.
/// ///
/// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor. /// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor.
fn from_buffer<const BL: usize>(buf: &Buffer<BL>) -> Result<Self, MarshalUnmarshalError> { fn from_buffer<const BL: usize>(buf: &Buffer<BL>) -> Result<Self, UnmarshalError> {
let mut tmp = 0; let mut tmp = 0;
Self::unmarshal(buf, &mut tmp) Self::unmarshal(buf, &mut tmp)
} }
@ -90,14 +49,55 @@ pub trait Marshalable: Sized {
} }
/// Unmarshal from a raw slice. /// Unmarshal from a raw slice.
fn from_bytes(b: &[u8]) -> Result<Self, MarshalUnmarshalError> { fn from_bytes(b: &[u8]) -> Result<Self, UnmarshalError> {
if b.len() <= TEMP_BUF_SIZE { if b.len() <= TEMP_BUF_SIZE {
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed(); let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
assert!(tmp.append_bytes(b).is_ok()); assert!(tmp.append_bytes(b).is_ok());
let mut cursor = 0; let mut cursor = 0;
Self::unmarshal(&tmp, &mut cursor) Self::unmarshal(&tmp, &mut cursor)
} else { } else {
Err(MarshalUnmarshalError::OutOfBounds) Err(UnmarshalError::OutOfBounds)
} }
} }
} }
pub enum UnmarshalError {
OutOfBounds,
InvalidData,
UnsupportedVersion,
IoError(std::io::Error),
}
impl Display for UnmarshalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OutOfBounds => f.write_str("out of bounds"),
Self::InvalidData => f.write_str("invalid data"),
Self::UnsupportedVersion => f.write_str("unsupported version"),
Self::IoError(e) => f.write_str(e.to_string().as_str()),
}
}
}
impl Debug for UnmarshalError {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Error for UnmarshalError {}
impl From<crate::buffer::OutOfBoundsError> for UnmarshalError {
#[inline(always)]
fn from(_: crate::buffer::OutOfBoundsError) -> Self {
Self::OutOfBounds
}
}
impl From<std::io::Error> for UnmarshalError {
#[inline(always)]
fn from(e: std::io::Error) -> Self {
Self::IoError(e)
}
}