diff --git a/controller/src/controller.rs b/controller/src/controller.rs index 37bae3e5b..821aad9e2 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -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; diff --git a/network-hypervisor/src/lib.rs b/network-hypervisor/src/lib.rs index 5deb2bb6c..465780ce6 100644 --- a/network-hypervisor/src/lib.rs +++ b/network-hypervisor/src/lib.rs @@ -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; diff --git a/network-hypervisor/src/util/mod.rs b/network-hypervisor/src/util/mod.rs deleted file mode 100644 index bbc2e1121..000000000 --- a/network-hypervisor/src/util/mod.rs +++ /dev/null @@ -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; diff --git a/network-hypervisor/src/vl1/address.rs b/network-hypervisor/src/vl1/address.rs index df6f3f7a8..0f897915c 100644 --- a/network-hypervisor/src/vl1/address.rs +++ b/network-hypervisor/src/vl1/address.rs @@ -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. diff --git a/network-hypervisor/src/vl1/endpoint.rs b/network-hypervisor/src/vl1/endpoint.rs index cdd664157..bf02294d8 100644 --- a/network-hypervisor/src/vl1/endpoint.rs +++ b/network-hypervisor/src/vl1/endpoint.rs @@ -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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError> { + fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { match self { Endpoint::Nil => { buf.append_u8(16 + TYPE_NIL)?; @@ -189,7 +192,7 @@ impl Marshalable for Endpoint { Ok(()) } - fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { 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::(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), } } } diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index f85ae4f19..ac5310f2b 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -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(&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(&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 { - 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> { + 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 { - 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: &mut R) -> std::io::Result { - 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> { + 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(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { + self.write_public(buf, false).map_err(|e| e.into()) + } + + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + 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::(cursor)?; + let ed25519 = buf.read_bytes_fixed::(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::(cursor)?); + ed25519_s = Some(buf.read_bytes_fixed::(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::(cursor)?); + ed25519_s = Some(buf.read_bytes_fixed::(cursor)?); + } + + if (type_flags & Self::ALGORITHM_EC_NIST_P384) != 0 { + ecdh = Some(buf.read_bytes_fixed::(cursor)?); + ecdsa = Some(buf.read_bytes_fixed::(cursor)?); + if (type_flags & Self::FLAG_INCLUDES_SECRETS) != 0 { + ecdh_s = Some(buf.read_bytes_fixed::(cursor)?); + ecdsa_s = Some(buf.read_bytes_fixed::(cursor)?); + } + ecdsa_self_signature = Some(buf.read_bytes_fixed::(cursor)?); + ed25519_self_signature = Some(buf.read_bytes_fixed::(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 { - 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::() { - std::ptr::write_volatile((self as *mut Self).cast::().add(i), 0); - } - } - } -} - impl Serialize for Identity { fn serialize(&self, serializer: S) -> Result 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(self, v: &str) -> Result @@ -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); diff --git a/network-hypervisor/src/vl1/inetaddress.rs b/network-hypervisor/src/vl1/inetaddress.rs index 0254471ca..2d6157da4 100644 --- a/network-hypervisor/src/vl1/inetaddress.rs +++ b/network-hypervisor/src/vl1/inetaddress.rs @@ -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 for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -100,20 +98,20 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { 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 for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -122,19 +120,19 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { 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 for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -143,19 +141,19 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { 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 for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -164,7 +162,7 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -180,14 +178,14 @@ impl TryInto for &InetAddress { 0, 0, ))), - _ => Err(crate::error::InvalidParameterError("not an IP address")), + _ => Err(InvalidParameterError("not an IP address")), } } } } impl TryInto for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -196,7 +194,7 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -206,14 +204,14 @@ impl TryInto 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 for InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -222,7 +220,7 @@ impl TryInto for InetAddress { } impl TryInto for &InetAddress { - type Error = crate::error::InvalidParameterError; + type Error = InvalidParameterError; #[inline(always)] fn try_into(self) -> Result { @@ -234,7 +232,7 @@ impl TryInto 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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError> { + fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { unsafe { match self.sa.sa_family as AddressFamilyType { AF_INET => { @@ -810,7 +808,7 @@ impl Marshalable for InetAddress { } } - fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { let t = buf.read_u8(cursor)?; if t == 4 { let b: &[u8; 6] = buf.read_bytes_fixed(cursor)?; diff --git a/network-hypervisor/src/vl1/mac.rs b/network-hypervisor/src/vl1/mac.rs index 84cf14e14..72dc7934b 100644 --- a/network-hypervisor/src/vl1/mac.rs +++ b/network-hypervisor/src/vl1/mac.rs @@ -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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError> { + fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { buf.append_bytes(&self.0.get().to_be_bytes()[2..]) - .map_err(|_| MarshalUnmarshalError::OutOfBounds) + .map_err(|_| UnmarshalError::OutOfBounds) } #[inline(always)] - fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { - Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(MarshalUnmarshalError::InvalidData) + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData) } } diff --git a/network-hypervisor/src/vl1/node.rs b/network-hypervisor/src/vl1/node.rs index 5115f97db..3dc8a0f8d 100644 --- a/network-hypervisor/src/vl1/node.rs +++ b/network-hypervisor/src/vl1/node.rs @@ -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,11 +820,12 @@ impl Node { packet.clear(); packet.set_size(v1::HEADER_SIZE); 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); } } } diff --git a/network-hypervisor/src/vl1/path.rs b/network-hypervisor/src/vl1/path.rs index 3335db789..dedd75911 100644 --- a/network-hypervisor/src/vl1/path.rs +++ b/network-hypervisor/src/vl1/path.rs @@ -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 Path { 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()))), } diff --git a/network-hypervisor/src/vl1/peer.rs b/network-hypervisor/src/vl1/peer.rs index 770c3f582..ea51187cc 100644 --- a/network-hypervisor/src/vl1/peer.rs +++ b/network-hypervisor/src/vl1/peer.rs @@ -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 Peer { 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,37 +279,64 @@ impl Peer { } }; - let max_fragment_size = if path.endpoint.requires_fragmentation() { - UDP_DEFAULT_MTU - } else { - usize::MAX - }; - let flags_cipher_hops = if packet.len() > max_fragment_size { - v1::HEADER_FLAG_FRAGMENTED | v1::CIPHER_AES_GMAC_SIV - } else { - v1::CIPHER_AES_GMAC_SIV - }; + 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 { + v1::CIPHER_AES_GMAC_SIV + }; + + let mut aes_gmac_siv = self.static_symmetric_key.aes_gmac_siv.get(); + aes_gmac_siv.encrypt_init(&self.next_message_id().to_ne_bytes()); + aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes( + self.identity.address, + node.identity.address, + flags_cipher_hops, + )); + 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); + aes_gmac_siv.encrypt_second_pass_finish() + } else { + return false; + }; - let mut aes_gmac_siv = self.static_symmetric_key.aes_gmac_siv.get(); - aes_gmac_siv.encrypt_init(&self.next_message_id().to_ne_bytes()); - aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes( - self.identity.address, - node.identity.address, - flags_cipher_hops, - )); - 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(); let header = packet.struct_mut_at::(0).unwrap(); - header.id = *array_range::(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::(tag); + header.mac.copy_from_slice(&tag[8..16]); } else { - return false; + 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::(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 Peer { } }; - 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 Peer { } 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 Peer { let mut cursor = 0; if let Ok(hello_fixed_headers) = payload.read_struct::(&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 Peer { 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 Peer { 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 (packet.capacity() - packet.len()) < id_bytes.len() { - self.send(host_system, None, node, time_ticks, &mut packet); - packet.clear(); - init_packet(&mut packet); + 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.as_bytes()); } - let _ = packet.append_bytes(id_bytes); } } addresses = &addresses[ADDRESS_SIZE..]; diff --git a/network-hypervisor/src/vl1/rootset.rs b/network-hypervisor/src/vl1/rootset.rs index da7f75146..4b672f301 100644 --- a/network-hypervisor/src/vl1/rootset.rs +++ b/network-hypervisor/src/vl1/rootset.rs @@ -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(&self, buf: &mut Buffer, 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(&self, buf: &mut Buffer, 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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError> { + fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { self.marshal_internal(buf, true) } - fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { 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(); diff --git a/network-hypervisor/src/vl2/networkid.rs b/network-hypervisor/src/vl2/networkid.rs index 867258a64..1afcbe719 100644 --- a/network-hypervisor/src/vl2/networkid.rs +++ b/network-hypervisor/src/vl2/networkid.rs @@ -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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError> { - buf.append_u64(self.0.get()).map_err(|_| MarshalUnmarshalError::OutOfBounds) + fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError> { + buf.append_u64(self.0.get()).map_err(|_| UnmarshalError::OutOfBounds) } #[inline(always)] - fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { - Self::from_u64(buf.read_u64(cursor)?).ok_or(MarshalUnmarshalError::InvalidData) + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result { + Self::from_u64(buf.read_u64(cursor)?).ok_or(UnmarshalError::InvalidData) } } diff --git a/service/src/cli/rootset.rs b/service/src/cli/rootset.rs index aa7e7aa4e..d51385b46 100644 --- a/service/src/cli/rootset.rs +++ b/service/src/cli/rootset.rs @@ -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() { diff --git a/utils/src/arrayvec.rs b/utils/src/arrayvec.rs index 81048f601..7e91357dc 100644 --- a/utils/src/arrayvec.rs +++ b/utils/src/arrayvec.rs @@ -191,6 +191,22 @@ impl ArrayVec { } } +impl ArrayVec { + /// 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 Drop for ArrayVec { #[inline(always)] fn drop(&mut self) { diff --git a/network-hypervisor/src/util/dictionary.rs b/utils/src/dictionary.rs similarity index 54% rename from network-hypervisor/src/util/dictionary.rs rename to utils/src/dictionary.rs index b369f0e39..192398da6 100644 --- a/network-hypervisor/src/util/dictionary.rs +++ b/utils/src/dictionary.rs @@ -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; - - use super::{Dictionary, BOOL_TRUTH}; - use std::collections::HashMap; - - fn randstring(len: u8) -> String { - (0..len) - .map(|_| (rand::random::() % 26) + 'a' as u8) - .map(|c| { - if rand::random::() { - (c as char).to_ascii_uppercase() - } else { - c as char - } - }) - .map(|c| c.to_string()) - .collect::>() - .join("") - } - - fn make_dictionary() -> (Dictionary, TypeMap) { - let mut d = Dictionary::new(); - let mut tm = TypeMap::new(); - - for _ in 0..(rand::random::() % 20) + 1 { - // NOTE: just doing this twice because I want to keep the code a little cleaner. - let selection = rand::random::() % 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::() % 10) + 1)) - .into_iter() - .map(|_| rand::random()) - .collect::>(), - ), - 3 => d.set_bool(&key, rand::random::()), - _ => 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()); - } - } - } - } - } -} diff --git a/network-hypervisor/src/error.rs b/utils/src/error.rs similarity index 72% rename from network-hypervisor/src/error.rs rename to utils/src/error.rs index fd57bcc2f..221ac2433 100644 --- a/network-hypervisor/src/error.rs +++ b/utils/src/error.rs @@ -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 { - ::fmt(self, f) - } -} - -impl Error for MalformedRecordError {} diff --git a/network-hypervisor/src/util/gate.rs b/utils/src/gate.rs similarity index 97% rename from network-hypervisor/src/util/gate.rs rename to utils/src/gate.rs index 4c5a626b7..32c157e5e 100644 --- a/network-hypervisor/src/util/gate.rs +++ b/utils/src/gate.rs @@ -9,7 +9,7 @@ pub struct IntervalGate(i64); impl Default for IntervalGate { #[inline(always)] fn default() -> Self { - Self(crate::util::NEVER_HAPPENED_TICKS) + Self(crate::NEVER_HAPPENED_TICKS) } } diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 1aee6ec9e..2e7cb0ff8 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -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() diff --git a/network-hypervisor/src/util/marshalable.rs b/utils/src/marshalable.rs similarity index 81% rename from network-hypervisor/src/util/marshalable.rs rename to utils/src/marshalable.rs index c30267415..8d23f5fec 100644 --- a/network-hypervisor/src/util/marshalable.rs +++ b/utils/src/marshalable.rs @@ -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 for MarshalUnmarshalError { - #[inline(always)] - fn from(_: OutOfBoundsError) -> Self { - Self::OutOfBounds - } -} - -impl From 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(&self, buf: &mut Buffer) -> Result<(), MarshalUnmarshalError>; + fn marshal(&self, buf: &mut Buffer) -> 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(buf: &Buffer, cursor: &mut usize) -> Result; + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result; /// 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(&self) -> Result, MarshalUnmarshalError> { + fn to_buffer(&self) -> Result, 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(buf: &Buffer) -> Result { + fn from_buffer(buf: &Buffer) -> Result { 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 { + fn from_bytes(b: &[u8]) -> Result { if b.len() <= TEMP_BUF_SIZE { let mut tmp = Buffer::::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 for UnmarshalError { + #[inline(always)] + fn from(_: crate::buffer::OutOfBoundsError) -> Self { + Self::OutOfBounds + } +} + +impl From for UnmarshalError { + #[inline(always)] + fn from(e: std::io::Error) -> Self { + Self::IoError(e) + } +}