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_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::vl2::NetworkId;
use zerotier_utils::dictionary::Dictionary;
use zerotier_utils::reaper::Reaper;
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_REVISION: u16 = 1;
pub mod error;
#[allow(unused)]
pub mod protocol;
pub mod util;
pub mod vl1;
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 crate::error::InvalidFormatError;
use crate::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
/// 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 crate::error::InvalidFormatError;
use crate::util::marshalable::*;
use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
use crate::vl1::inetaddress::InetAddress;
use crate::vl1::{Address, MAC};
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_ZEROTIER: u8 = 1;
@ -118,11 +118,14 @@ impl Endpoint {
}
}
/// Returns true if this is an endpoint type that requires that large packets be fragmented.
pub fn requires_fragmentation(&self) -> bool {
/// Get the maximum fragment size for this endpoint or usize::MAX if there is no hard limit.
#[inline(always)]
pub fn max_fragment_size(&self) -> usize {
match self {
Endpoint::Icmp(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => true,
_ => false,
Endpoint::Icmp(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => {
crate::protocol::UDP_DEFAULT_MTU
}
_ => usize::MAX,
}
}
}
@ -130,7 +133,7 @@ impl Endpoint {
impl Marshalable for Endpoint {
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 {
Endpoint::Nil => {
buf.append_u8(16 + TYPE_NIL)?;
@ -189,7 +192,7 @@ impl Marshalable for Endpoint {
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)?;
if type_byte < 16 {
if type_byte == 4 {
@ -205,13 +208,13 @@ impl Marshalable for Endpoint {
u16::from_be_bytes(b[16..18].try_into().unwrap()),
)))
} else {
Err(MarshalUnmarshalError::InvalidData)
Err(UnmarshalError::InvalidData)
}
} else {
match type_byte - 16 {
TYPE_NIL => Ok(Endpoint::Nil),
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(
zt,
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(),
)),
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()))
}
_ => Err(MarshalUnmarshalError::InvalidData),
_ => Err(UnmarshalError::InvalidData),
}
}
}

View file

@ -3,7 +3,7 @@
use std::cmp::Ordering;
use std::convert::TryInto;
use std::hash::{Hash, Hasher};
use std::io::{Read, Write};
use std::io::Write;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -15,10 +15,11 @@ use zerotier_crypto::secret::Secret;
use zerotier_crypto::x25519::*;
use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
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::vl1::Address;
@ -142,30 +143,22 @@ fn zt_address_derivation_work_function(digest: &mut [u8; 64]) {
}
impl Identity {
/// Length of an x25519-only public identity in byte array form.
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
pub const BYTE_LENGTH_MAX: usize = ADDRESS_SIZE
+ 1
+ P384_PUBLIC_KEY_SIZE
+ P384_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_PUBLIC_KEY_SIZE
+ ED25519_PUBLIC_KEY_SIZE
+ C25519_SECRET_KEY_SIZE
+ ED25519_SECRET_KEY_SIZE
+ P384_PUBLIC_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_EC_NIST_P384: u8 = 0x02;
const FLAG_INCLUDES_SECRET: u8 = 0x80;
const FLAG_INCLUDES_SECRETS: u8 = 0x80;
/// Generate a new identity.
pub fn generate() -> Self {
@ -270,7 +263,7 @@ impl Identity {
.p384
.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);
}
@ -398,222 +391,67 @@ impl Identity {
return false;
}
pub fn to_public_bytes(&self) -> IdentityBytes {
if let Some(p384) = self.p384.as_ref() {
IdentityBytes::X25519P384Public(
as_byte_array(&packed::V1 {
v0: packed::V0 {
address: self.address.to_bytes(),
key_type: 0,
x25519: self.x25519,
ed25519: self.ed25519,
secret_length: 0,
reserved: 0x03,
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(),
)
pub fn write_public<W: Write>(&self, w: &mut W, legacy_v0: bool) -> std::io::Result<()> {
w.write_all(&self.address.to_bytes())?;
if !legacy_v0 && self.p384.is_some() {
let p384 = self.p384.as_ref().unwrap();
w.write_all(&[Self::ALGORITHM_X25519 | Self::ALGORITHM_EC_NIST_P384])?;
w.write_all(&self.x25519)?;
w.write_all(&self.ed25519)?;
w.write_all(p384.ecdh.as_bytes())?;
w.write_all(p384.ecdsa.as_bytes())?;
w.write_all(&p384.ecdsa_self_signature)?;
w.write_all(&p384.ed25519_self_signature)?;
} else {
IdentityBytes::X25519Public(
as_byte_array(&packed::V0 {
address: self.address.to_bytes(),
key_type: 0,
x25519: self.x25519,
ed25519: self.ed25519,
secret_length: 0,
reserved: 0x03,
ext_len: [0; 2],
})
.clone(),
)
w.write_all(&[0])?;
w.write_all(&self.x25519)?;
w.write_all(&self.ed25519)?;
w.write_all(&[0])?;
}
Ok(())
}
pub fn write_secret<W: Write>(&self, w: &mut W, legacy_v0: bool) -> std::io::Result<()> {
if let Some(s) = self.secret.as_ref() {
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> {
self.secret.as_ref().map(|s| {
if let Some(p384) = s.p384.as_ref() {
IdentityBytes::X25519P384Secret(
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(),
)
}
})
pub fn to_public_bytes(&self, legacy_v8: bool) -> std::io::Result<Buffer<{ Self::BYTE_LENGTH_MAX }>> {
let mut buf = Buffer::<{ Self::BYTE_LENGTH_MAX }>::new();
self.write_public(&mut buf, false)?;
Ok(buf)
}
/// Convert a byte respresentation into an identity.
///
/// WARNING: this performs basic sanity checking but does NOT perform a full validation of address derivation or self-signatures.
pub fn from_bytes(bytes: &IdentityBytes) -> Option<Self> {
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),
)
},
)
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();
self.write_secret(&mut buf, false)?;
Ok(buf)
}
fn to_string_internal(&self, include_private: bool) -> String {
@ -660,6 +498,12 @@ impl Identity {
s
}
fn fill_in_fingerprint(&mut self) {
let mut h = SHA384::new();
self.write_public(&mut h, false);
self.fingerprint = h.finish();
}
#[inline(always)]
pub fn to_public_string(&self) -> String {
self.to_string_internal(false)
@ -821,10 +665,108 @@ impl FromStr for Identity {
},
fingerprint: [0; 48],
});
id.as_mut().unwrap().fill_in_fingerprint();
id
}
}
let fingerprint = SHA384::hash(id.as_ref().unwrap().to_public_bytes().as_bytes());
id.as_mut().unwrap().fingerprint = fingerprint;
impl Marshalable for Identity {
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
}
}
@ -832,7 +774,7 @@ impl FromStr for Identity {
impl PartialEq for Identity {
#[inline(always)]
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 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -977,7 +811,7 @@ impl Serialize for Identity {
if serializer.is_human_readable() {
serializer.serialize_str(self.to_public_string().as_str())
} 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
E: serde::de::Error,
{
IdentityBytes::try_from(v).map_or_else(
|e| Err(E::custom(e.to_string())),
|b| Identity::from_bytes(&b).map_or_else(|| Err(E::custom("invalid identity")), |id| Ok(id)),
)
todo!()
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
@ -1071,11 +902,11 @@ mod tests {
let gen = Identity::generate();
assert!(gen.agree(&gen).is_some());
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();
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());
if !gen_unmarshaled.eq(&gen) {
println!(
@ -1098,12 +929,12 @@ mod tests {
assert!(id.validate_identity());
assert!(id.p384.is_none());
let idb = id.to_secret_bytes().unwrap();
let id_unmarshal = Identity::from_bytes(&idb).unwrap();
let idb = id.to_secret_bytes(false).unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
assert!(id == id_unmarshal);
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();
assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id);
@ -1128,11 +959,11 @@ mod tests {
assert!(id.p384.is_some());
assert!(id.secret.as_ref().unwrap().p384.is_some());
let idb = id.to_secret_bytes().unwrap();
let id_unmarshal = Identity::from_bytes(&idb).unwrap();
let idb = id.to_secret_bytes(false).unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
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();
assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id);

View file

@ -9,11 +9,9 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::util::marshalable::*;
use crate::error::InvalidFormatError;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
#[cfg(windows)]
use winapi::um::winsock2;
@ -91,7 +89,7 @@ impl ToSocketAddrs for InetAddress {
}
impl TryInto<IpAddr> for InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<IpAddr, Self::Error> {
@ -100,20 +98,20 @@ impl TryInto<IpAddr> for InetAddress {
}
impl TryInto<IpAddr> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<IpAddr, Self::Error> {
match unsafe { self.sa.sa_family } {
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 }))),
_ => Err(crate::error::InvalidParameterError("not an IP address")),
_ => Err(InvalidParameterError("not an IP address")),
}
}
}
impl TryInto<Ipv4Addr> for InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<Ipv4Addr, Self::Error> {
@ -122,19 +120,19 @@ impl TryInto<Ipv4Addr> for InetAddress {
}
impl TryInto<Ipv4Addr> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<Ipv4Addr, Self::Error> {
match unsafe { self.sa.sa_family } {
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 {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<Ipv6Addr, Self::Error> {
@ -143,19 +141,19 @@ impl TryInto<Ipv6Addr> for InetAddress {
}
impl TryInto<Ipv6Addr> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<Ipv6Addr, Self::Error> {
match unsafe { self.sa.sa_family } {
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 {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<SocketAddr, Self::Error> {
@ -164,7 +162,7 @@ impl TryInto<SocketAddr> for InetAddress {
}
impl TryInto<SocketAddr> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<SocketAddr, Self::Error> {
@ -180,14 +178,14 @@ impl TryInto<SocketAddr> for &InetAddress {
0,
0,
))),
_ => Err(crate::error::InvalidParameterError("not an IP address")),
_ => Err(InvalidParameterError("not an IP address")),
}
}
}
}
impl TryInto<SocketAddrV4> for InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<SocketAddrV4, Self::Error> {
@ -196,7 +194,7 @@ impl TryInto<SocketAddrV4> for InetAddress {
}
impl TryInto<SocketAddrV4> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
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()),
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 {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
@ -222,7 +220,7 @@ impl TryInto<SocketAddrV6> for InetAddress {
}
impl TryInto<SocketAddrV6> for &InetAddress {
type Error = crate::error::InvalidParameterError;
type Error = InvalidParameterError;
#[inline(always)]
fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
@ -234,7 +232,7 @@ impl TryInto<SocketAddrV6> for &InetAddress {
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 {
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 {
match self.sa.sa_family as AddressFamilyType {
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)?;
if t == 4 {
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 crate::error::InvalidFormatError;
use crate::util::marshalable::*;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
/// An Ethernet MAC address.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -87,14 +86,14 @@ impl Marshalable for MAC {
const MAX_MARSHAL_SIZE: usize = 6;
#[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..])
.map_err(|_| MarshalUnmarshalError::OutOfBounds)
.map_err(|_| UnmarshalError::OutOfBounds)
}
#[inline(always)]
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, MarshalUnmarshalError> {
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(MarshalUnmarshalError::InvalidData)
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(UnmarshalError::InvalidData)
}
}

View file

@ -9,10 +9,7 @@ use std::time::Duration;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use crate::error::InvalidParameterError;
use crate::protocol::*;
use crate::util::gate::IntervalGate;
use crate::util::marshalable::Marshalable;
use crate::vl1::address::Address;
use crate::vl1::debug_event;
use crate::vl1::endpoint::Endpoint;
@ -24,7 +21,10 @@ use crate::vl1::rootset::RootSet;
use zerotier_crypto::random;
use zerotier_crypto::verified::Verified;
use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::gate::IntervalGate;
use zerotier_utils::hex;
use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::ringbuffer::RingBuffer;
/// 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.set_size(v1::HEADER_SIZE);
let _ = packet.append_u8(verbs::VL1_WHOIS);
} else {
}
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);
}
}
}
}
/// Called by Peer when an identity is received from another node, e.g. via OK(WHOIS).
pub(crate) fn handle_incoming_identity<InnerProtocolImpl: InnerProtocol>(

View file

@ -12,6 +12,7 @@ use crate::vl1::fragmentedpacket::FragmentedPacket;
use crate::vl1::node::*;
use zerotier_crypto::random;
use zerotier_utils::NEVER_HAPPENED_TICKS;
pub(crate) const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
@ -46,8 +47,8 @@ impl<HostSystemImpl: HostSystem> Path<HostSystemImpl> {
endpoint,
local_socket,
local_interface,
last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_send_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
create_time_ticks: time_ticks,
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::salsa::Salsa;
use zerotier_crypto::secret::Secret;
use zerotier_utils::buffer::BufferReader;
use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::memory::array_range;
use zerotier_utils::NEVER_HAPPENED_TICKS;
use crate::protocol::*;
use crate::util::marshalable::Marshalable;
use crate::vl1::address::Address;
use crate::vl1::debug_event;
use crate::vl1::node::*;
@ -69,10 +69,10 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
identity: id,
static_symmetric_key: SymmetricSecret::new(static_secret),
paths: Mutex::new(Vec::with_capacity(4)),
last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_forward_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_hello_reply_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS),
last_send_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_receive_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_forward_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
last_hello_reply_time_ticks: AtomicI64::new(NEVER_HAPPENED_TICKS),
create_time_ticks: time_ticks,
random_ticks_offset: random::xorshift64_random() as u32,
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() {
UDP_DEFAULT_MTU
} else {
usize::MAX
};
let max_fragment_size = path.endpoint.max_fragment_size();
if self.remote_node_info.read().remote_protocol_version >= 12 {
let flags_cipher_hops = if packet.len() > max_fragment_size {
v1::HEADER_FLAG_FRAGMENTED | v1::CIPHER_AES_GMAC_SIV
} else {
@ -297,19 +294,49 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
node.identity.address,
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_finish();
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();
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.src = node.identity.address.to_bytes();
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 {
return false;
};
packet.as_bytes_mut()[v1::MAC_FIELD_INDEX..(v1::MAC_FIELD_INDEX + 8)].copy_from_slice(&tag[0..8]);
}
self.internal_send(
@ -374,11 +401,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
}
};
let max_fragment_size = if destination.requires_fragmentation() {
UDP_DEFAULT_MTU
} else {
usize::MAX
};
let max_fragment_size = destination.max_fragment_size();
let time_ticks = host_system.time_ticks();
let mut packet = PacketBuffer::new();
@ -401,7 +424,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
}
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(
&self.static_symmetric_key,
@ -575,7 +598,7 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
let mut cursor = 0;
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) {
{
let mut remote_node_info = self.remote_node_info.write();
@ -698,15 +721,22 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
verbs::VL1_WHOIS => {
if node.is_peer_root(self) {
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!(
host_system,
"[vl1] {} OK(WHOIS): {}",
"[vl1] {} OK(WHOIS): new identity: {}",
self.identity.address.to_string(),
received_identity.to_string()
);
node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true);
} else {
debug_event!(
host_system,
"[vl1] {} OK(WHOIS): bad identity: {}",
self.identity.address.to_string(),
r.err().unwrap().to_string()
);
break;
}
}
@ -750,14 +780,14 @@ impl<HostSystemImpl: HostSystem> Peer<HostSystemImpl> {
if addresses.len() >= ADDRESS_SIZE {
if let Some(zt_address) = Address::from_bytes(&addresses[..ADDRESS_SIZE]) {
if let Some(peer) = node.peer(zt_address) {
let id_bytes_tmp = peer.identity.to_public_bytes();
let id_bytes = id_bytes_tmp.as_bytes();
if let Ok(id_bytes) = peer.identity.to_public_bytes(self.identity.p384.is_none()) {
if (packet.capacity() - packet.len()) < id_bytes.len() {
self.send(host_system, None, node, time_ticks, &mut packet);
packet.clear();
init_packet(&mut packet);
}
let _ = packet.append_bytes(id_bytes);
let _ = packet.append_bytes(id_bytes.as_bytes());
}
}
}
addresses = &addresses[ADDRESS_SIZE..];

View file

@ -3,12 +3,12 @@
use std::collections::BTreeSet;
use std::io::Write;
use crate::util::marshalable::*;
use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE};
use crate::vl1::Endpoint;
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;
@ -100,43 +100,6 @@ impl RootSet {
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.
fn marshal_for_signing(&self) -> Buffer<{ Self::MAX_MARSHAL_SIZE }> {
let mut tmp = Buffer::<{ Self::MAX_MARSHAL_SIZE }>::new();
@ -254,20 +217,57 @@ impl RootSet {
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 {
const MAX_MARSHAL_SIZE: usize = crate::protocol::v1::SIZE_MAX;
#[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)
}
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);
if buf.read_u8(cursor)? != 0 {
return Err(MarshalUnmarshalError::UnsupportedVersion);
return Err(UnmarshalError::UnsupportedVersion);
}
let name_len = buf.read_varint(cursor)?;
@ -283,7 +283,7 @@ impl Marshalable for RootSet {
let member_count = buf.read_varint(cursor)?;
for _ in 0..member_count {
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,
signature: ArrayVec::new(),
priority: 0,
@ -313,7 +313,7 @@ impl Marshalable for RootSet {
*cursor += buf.read_varint(cursor)? as usize;
if *cursor > buf.len() {
return Err(MarshalUnmarshalError::OutOfBounds);
return Err(UnmarshalError::OutOfBounds);
}
rc.members.sort();

View file

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

View file

@ -3,8 +3,7 @@
use std::collections::BTreeMap;
use std::io::Write;
use zerotier_utils::hex;
use zerotier_utils::hex::HEX_CHARS;
use crate::hex;
const BOOL_TRUTH: &str = "1tTyY";
@ -55,8 +54,8 @@ fn append_printable(s: &mut String, b: &[u8]) {
} else {
s.push('\\');
s.push('x');
s.push(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) >> 4) 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
}
}
#[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 {}
pub struct InvalidParameterError(pub(crate) &'static str);
pub struct InvalidParameterError(pub &'static str);
impl Display for InvalidParameterError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -53,20 +53,3 @@ impl Debug 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> {
#[inline(always)]
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 blob;
pub mod buffer;
pub mod dictionary;
pub mod error;
#[allow(unused)]
pub mod exitcode;
pub mod gate;
pub mod gatherarray;
pub mod hex;
pub mod json;
pub mod marshalable;
pub mod memory;
pub mod pool;
pub mod ringbuffer;
@ -20,6 +24,9 @@ pub mod reaper;
#[cfg(feature = "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.
pub fn ms_since_epoch() -> i64 {
std::time::SystemTime::now()

View file

@ -3,72 +3,31 @@
use std::error::Error;
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().
/// This hack can go away once Rust allows us to reference trait consts as generics.
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.
pub trait Marshalable: Sized {
const MAX_MARSHAL_SIZE: usize;
/// 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.
///
/// 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
/// 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.
///
/// 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.
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();
self.marshal(&mut tmp)?;
Ok(tmp)
@ -77,7 +36,7 @@ pub trait Marshalable: Sized {
/// Unmarshal this object from a buffer.
///
/// 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;
Self::unmarshal(buf, &mut tmp)
}
@ -90,14 +49,55 @@ pub trait Marshalable: Sized {
}
/// 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 {
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
assert!(tmp.append_bytes(b).is_ok());
let mut cursor = 0;
Self::unmarshal(&tmp, &mut cursor)
} 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)
}
}