diff --git a/network-hypervisor/src/crypto/kbkdf.rs b/network-hypervisor/src/crypto/kbkdf.rs new file mode 100644 index 000000000..c234a7435 --- /dev/null +++ b/network-hypervisor/src/crypto/kbkdf.rs @@ -0,0 +1,12 @@ +use crate::crypto::hash::SHA384; + +/// Derive a key using KBKDF prefaced by the bytes 'ZT' for use in ZeroTier. +/// This is a fixed cost key derivation function used to derive sub-keys from a single original +/// shared secret for different uses, such as the K0/K1 in AES-GMAC-SIV. +/// Key must be 384 bits in length. +pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> [u8; 48] { + debug_assert!(key.len() == 48); + // HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4] + // See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf + SHA384::hmac(key, &[(iter >> 24) as u8, (iter >> 16) as u8, (iter >> 8) as u8, iter as u8, b'Z', b'T', label, 0, context, 0, 0, 0x01, 0x80]) +} diff --git a/network-hypervisor/src/crypto/mod.rs b/network-hypervisor/src/crypto/mod.rs index ba3cde81e..4cb86c978 100644 --- a/network-hypervisor/src/crypto/mod.rs +++ b/network-hypervisor/src/crypto/mod.rs @@ -4,3 +4,6 @@ pub mod p521; pub mod salsa; pub mod poly1305; pub mod balloon; +pub mod kbkdf; + +pub use aes_gmac_siv; diff --git a/network-hypervisor/src/lib.rs b/network-hypervisor/src/lib.rs index 86b0e1063..eff8e280a 100644 --- a/network-hypervisor/src/lib.rs +++ b/network-hypervisor/src/lib.rs @@ -1,5 +1,10 @@ pub mod crypto; -pub mod vl1; pub mod util; pub mod error; +pub mod vl1; pub mod vl2; + +pub const VERSION_MAJOR: u8 = 1; +pub const VERSION_MINOR: u8 = 9; +pub const VERSION_REVISION: u8 = 1; +pub const VERSION_STR: &'static str = "1.9.1"; diff --git a/network-hypervisor/src/vl1/address.rs b/network-hypervisor/src/vl1/address.rs index 43cb15dd6..250ae3b2e 100644 --- a/network-hypervisor/src/vl1/address.rs +++ b/network-hypervisor/src/vl1/address.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use std::hash::{Hash, Hasher}; -use crate::vl1::protocol::ADDRESS_RESERVED_PREFIX; +use crate::vl1::constants::ADDRESS_RESERVED_PREFIX; use crate::error::InvalidFormatError; use crate::util::hex::HEX_CHARS; diff --git a/network-hypervisor/src/vl1/buffer.rs b/network-hypervisor/src/vl1/buffer.rs index e569c33fa..2e331e633 100644 --- a/network-hypervisor/src/vl1/buffer.rs +++ b/network-hypervisor/src/vl1/buffer.rs @@ -1,5 +1,5 @@ -use std::mem::{size_of, MaybeUninit, transmute}; -use std::marker::PhantomData; +use std::mem::{size_of, MaybeUninit, zeroed}; +use std::ptr::write_bytes; use std::io::Write; const OVERFLOW_ERR_MSG: &'static str = "overflow"; @@ -9,47 +9,23 @@ const OVERFLOW_ERR_MSG: &'static str = "overflow"; /// This is ONLY used for packed protocol header or segment objects. pub unsafe trait RawObject: Sized {} -/// A zero length RawObject for using a Buffer when you don't want a header. -pub struct NoHeader; - -unsafe impl RawObject for NoHeader {} - /// A byte array that supports safe appending of data or raw objects. -/// -/// This also supports a generic header that must be a RawObject and will always be -/// placed at the beginning of the buffer. When you construct or clear() a buffer -/// space will be maintained for the header by setting the buffer's size to the -/// header size. The header must have a size less than or equal to L. Use NoHeader -/// if you don't want a header. #[derive(Clone, PartialEq, Eq)] -pub struct Buffer(usize, [u8; L], PhantomData); +pub struct Buffer(usize, [u8; L]); -unsafe impl RawObject for Buffer {} +unsafe impl RawObject for Buffer {} -impl Default for Buffer { +impl Default for Buffer { #[inline(always)] fn default() -> Self { - debug_assert!(size_of::() <= L); - Buffer(size_of::(), [0_u8; L], PhantomData::default()) + unsafe { zeroed() } } } -impl Buffer { +impl Buffer { #[inline(always)] pub fn new() -> Self { - debug_assert!(size_of::() <= L); - Self::default() - } - - /// Change the header "personality" of this buffer. - /// This is a free operation, but the returned reference obviously only lives as long as the source. - #[inline(always)] - pub fn transmute_header(&self) -> &Buffer { - debug_assert!(size_of::() <= L); - debug_assert!(size_of::() <= L); - unsafe { - &*(self as *const Self).cast::>() - } + unsafe { zeroed() } } /// Create a buffer that contains a copy of a slice, truncating if the slice is too long. @@ -78,8 +54,7 @@ impl Buffer { /// Erase contents and reset size to the size of the header. #[inline(always)] pub fn clear(&mut self) { - self.1[0..self.0].fill(0); - self.0 = size_of::(); + unsafe { write_bytes((self as *mut Self).cast::(), 0, size_of::()) } } /// Get the length of this buffer (including header, if any). @@ -88,20 +63,6 @@ impl Buffer { self.0 } - /// Get a reference to the header (in place). - #[inline(always)] - pub fn header(&self) -> &H { - debug_assert!(size_of::() <= L); - unsafe { &*self.1.as_ptr().cast::() } - } - - /// Get a mutable reference to the header (in place). - #[inline(always)] - pub fn header_mut(&mut self) -> &mut H { - debug_assert!(size_of::() <= L); - unsafe { &mut *self.1.as_mut_ptr().cast::() } - } - /// Append a packed structure and call a function to initialize it in place. /// Anything not initialized will be zero. #[inline(always)] @@ -234,12 +195,6 @@ impl Buffer { } } - /// Get the index of the start of the payload after the header. - #[inline(always)] - pub fn cursor_after_header(&self) -> usize { - size_of::() - } - /// Get a structure at a given position in the buffer and advance the cursor. #[inline(always)] pub fn get_struct(&self, cursor: &mut usize) -> std::io::Result<&T> { @@ -336,7 +291,7 @@ impl Buffer { } } -impl Write for Buffer { +impl Write for Buffer { #[inline(always)] fn write(&mut self, buf: &[u8]) -> std::io::Result { let ptr = self.0; @@ -356,41 +311,16 @@ impl Write for Buffer { } } -impl AsRef<[u8]> for Buffer { +impl AsRef<[u8]> for Buffer { #[inline(always)] fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl AsMut<[u8]> for Buffer { +impl AsMut<[u8]> for Buffer { #[inline(always)] fn as_mut(&mut self) -> &mut [u8] { self.as_bytes_mut() } } - -impl AsRef for Buffer { - #[inline(always)] - fn as_ref(&self) -> &H { - self.header() - } -} - -impl AsMut for Buffer { - #[inline(always)] - fn as_mut(&mut self) -> &mut H { - self.header_mut() - } -} - -#[cfg(test)] -mod tests { - use std::mem::size_of; - use crate::vl1::buffer::NoHeader; - - #[test] - fn object_sizing() { - assert_eq!(size_of::(), 0); - } -} diff --git a/network-hypervisor/src/vl1/constants.rs b/network-hypervisor/src/vl1/constants.rs new file mode 100644 index 000000000..4482d44f2 --- /dev/null +++ b/network-hypervisor/src/vl1/constants.rs @@ -0,0 +1,58 @@ +/// Length of an address in bytes. +pub const ADDRESS_SIZE: usize = 5; + +/// Size of packet header that lies outside the encryption envelope. +pub const PACKET_HEADER_SIZE: usize = 27; + +/// Maximum packet payload size including the verb/flags field. +/// This is large enough to carry "jumbo MTU" packets. The size is +/// odd because 10005+27 == 10032 which is divisible by 16. This +/// improves memory layout and alignment when buffers are allocated. +/// This value could technically be increased but it would require a +/// protocol version bump and only new nodes would be able to accept +/// the new size. +pub const PACKET_PAYLOAD_SIZE_MAX: usize = 10005; + +/// Minimum packet, which is the header plus a verb. +pub const PACKET_SIZE_MIN: usize = PACKET_HEADER_SIZE + 1; + +/// Maximum size of an entire packet. +pub const PACKET_SIZE_MAX: usize = PACKET_HEADER_SIZE + PACKET_PAYLOAD_SIZE_MAX; + +/// Mask to select cipher from header flags field. +pub const HEADER_FLAGS_FIELD_MASK_CIPHER: u8 = 0x30; + +/// Mask to select packet hops from header flags field. +pub const HEADER_FLAGS_FIELD_MASK_HOPS: u8 = 0x07; + +/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext. +/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305. +pub const CIPHER_NOCRYPT_POLY1305: u8 = 0; + +/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305. +/// Construction is the same as that which is used in the NaCl secret box functions. +pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10; + +/// Packet is encrypted and authenticated with AES-GMAC-SIV. +pub const CIPHER_AES_GMAC_SIV: u8 = 0x30; + +/// Header (outer) flag indicating that this packet has additional fragments. +pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40; + +/// Minimum size of a fragment. +pub const FRAGMENT_SIZE_MIN: usize = 16; + +/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed. +pub const VERB_FLAG_COMPRESSED: u8 = 0x80; + +/// Maximum number of packet hops allowed by the protocol. +pub const PROTOCOL_MAX_HOPS: usize = 7; + +/// Index of packet fragment indicator byte to detect fragments. +pub const FRAGMENT_INDICATOR_INDEX: usize = 13; + +/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment. +pub const FRAGMENT_INDICATOR: u8 = 0xff; + +/// Prefix indicating reserved addresses (that can't actually be addresses). +pub const ADDRESS_RESERVED_PREFIX: u8 = 0xff; diff --git a/network-hypervisor/src/vl1/endpoint.rs b/network-hypervisor/src/vl1/endpoint.rs index f6b66ddbb..d62a73c6f 100644 --- a/network-hypervisor/src/vl1/endpoint.rs +++ b/network-hypervisor/src/vl1/endpoint.rs @@ -1,6 +1,6 @@ use crate::vl1::{Address, MAC}; use crate::vl1::inetaddress::InetAddress; -use crate::vl1::buffer::{RawObject, Buffer}; +use crate::vl1::buffer::Buffer; use std::hash::{Hash, Hasher}; const TYPE_NIL: u8 = 0; @@ -75,7 +75,7 @@ impl Endpoint { } } - pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { match self { Endpoint::Nil => { buf.append_u8(Type::Nil as u8) @@ -101,6 +101,9 @@ impl Endpoint { ip.marshal(buf) } Endpoint::IpUdp(ip) => { + // IP/UDP endpoints are marshaled as naked InetAddress objects for backward + // compatibility. This is why 16 is added to all the other type IDs. Naked + // InetAddress objects always start with either 4 or 6. ip.marshal(buf) } Endpoint::IpTcp(ip) => { @@ -122,7 +125,7 @@ impl Endpoint { } } - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { let type_byte = buf.get_u8(cursor)?; if type_byte < 16 { let ip = InetAddress::unmarshal(buf, cursor)?; diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index d99777159..0b9790a70 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -2,17 +2,17 @@ use std::alloc::{Layout, dealloc, alloc}; use std::ptr::{slice_from_raw_parts_mut, slice_from_raw_parts}; use std::io::Write; use std::str::FromStr; +use std::convert::TryInto; +use std::cmp::Ordering; use crate::vl1::Address; -use crate::vl1::buffer::{Buffer, RawObject, NoHeader}; -use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair}; +use crate::vl1::buffer::Buffer; +use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair, ED25519_SIGNATURE_SIZE}; use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE}; use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE}; use crate::crypto::balloon; use crate::crypto::salsa::Salsa; use crate::error::InvalidFormatError; -use std::convert::TryInto; -use std::cmp::Ordering; // Memory parameter for V0 address derivation work function. const V0_IDENTITY_GEN_MEMORY: usize = 2097152; @@ -22,6 +22,9 @@ const V1_BALLOON_SPACE_COST: usize = 16384; const V1_BALLOON_TIME_COST: usize = 3; const V1_BALLOON_DELTA: usize = 3; +pub const IDENTITY_TYPE_0_SIGNATURE_SIZE: usize = ED25519_SIGNATURE_SIZE + 32; +pub const IDENTITY_TYPE_1_SIGNATURE_SIZE: usize = P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE; + #[derive(Copy, Clone)] #[repr(u8)] pub enum Type { @@ -241,13 +244,33 @@ impl Identity { /// its secrets or a key being invalid. pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> { self.secrets.as_ref().map_or(None, |secrets| { + let c25519_secret = SHA384::hash(&secrets.c25519.agree(&other_identity.c25519)); secrets.v1.as_ref().map_or_else(|| { - Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519))) + Some(c25519_secret) }, |p521_secret| { other_identity.v1.as_ref().map_or_else(|| { - Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519))) + Some(c25519_secret) }, |other_p521_public| { - p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret))) + p521_secret.0.agree(&other_p521_public.0).map_or(None, |p521_secret| { + // + // For NIST P-521 key agreement, we use a single step key derivation function to derive + // the final shared secret using the C25519 shared secret as a "salt." This should be + // FIPS140-compliant as per section 8.2 of: + // + // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr2.pdf + // + // This is also stated in the following FAQ, which though it pertains to post-quantum + // algorithms states that non-FIPS derived shared secrets can be used as salts in key + // derivation from a FIPS-compliant algorithm derived shared secret: + // + // https://csrc.nist.gov/Projects/post-quantum-cryptography/faqs + // + // Hashing the C25519 secret with the P521 secret results in a secret that is as strong + // as the stronger of the two algorithms. This should make the FIPS people happy and the + // people who are paranoid about NIST curves happy. + // + Some(SHA384::hmac(&c25519_secret, &p521_secret)) + }) }) }) }) @@ -258,10 +281,25 @@ impl Identity { /// type. None is returned if this identity lacks secret keys or another error occurs. pub fn sign(&self, msg: &[u8]) -> Option> { self.secrets.as_ref().map_or(None, |secrets| { + let c25519_sig = secrets.ed25519.sign_zt(msg); secrets.v1.as_ref().map_or_else(|| { - Some(secrets.ed25519.sign(msg).to_vec()) + Some(c25519_sig.to_vec()) }, |p521_secret| { - p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec())) + p521_secret.1.sign(msg).map_or(None, |p521_sig| { + // + // For type 1 identity signatures we sign with both algorithms and append the Ed25519 + // signature to the NIST P-521 signature. The Ed25519 signature is only checked if the + // P-521 signature validates. Note that we only append the first 64 bytes of sign_zt() + // output. For legacy reasons type 0 signatures include the first 32 bytes of the message + // hash after the signature, but this is not required and isn't included here. + // + // This should once again make both the FIPS people and the people paranoid about NIST + // curves happy. + // + let mut p521_sig = p521_sig.to_vec(); + let _ = p521_sig.write_all(&c25519_sig[0..64]); + Some(p521_sig) + }) }) }) } @@ -271,7 +309,11 @@ impl Identity { self.v1.as_ref().map_or_else(|| { crate::crypto::c25519::ed25519_verify(&self.ed25519, signature, msg) }, |p521| { - (*p521).1.verify(msg, signature) + if signature.len() == IDENTITY_TYPE_1_SIGNATURE_SIZE { + (*p521).1.verify(msg, &signature[0..P521_ECDSA_SIGNATURE_SIZE]) && crate::crypto::c25519::ed25519_verify(&self.ed25519, &signature[P521_ECDSA_SIGNATURE_SIZE..], msg) + } else { + false + } }) } @@ -297,7 +339,7 @@ impl Identity { } /// Append this in binary format to a buffer. - pub fn marshal(&self, buf: &mut Buffer, include_private: bool) -> std::io::Result<()> { + pub fn marshal(&self, buf: &mut Buffer, include_private: bool) -> std::io::Result<()> { buf.append_bytes_fixed(&self.address.to_bytes())?; if self.v1.is_some() { let p521 = self.v1.as_ref().unwrap(); @@ -339,7 +381,7 @@ impl Identity { /// Deserialize an Identity from a buffer. /// The supplied cursor is advanced. - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { let addr = Address::from_bytes(buf.get_bytes_fixed::<5>(cursor)?).unwrap(); let id_type = buf.get_u8(cursor)?; if id_type == Type::C25519 as u8 { @@ -413,7 +455,7 @@ impl Identity { /// Get this identity in byte array format. pub fn marshal_to_bytes(&self, include_private: bool) -> Vec { - let mut buf: Buffer = Buffer::new(); + let mut buf: Buffer<2048> = Buffer::new(); self.marshal(&mut buf, include_private).expect("overflow"); buf.as_bytes().to_vec() } @@ -422,7 +464,7 @@ impl Identity { /// On success the identity and the number of bytes actually read from the slice are /// returned. pub fn unmarshal_from_bytes(bytes: &[u8]) -> std::io::Result<(Identity, usize)> { - let buf = Buffer::::from_bytes_lossy(bytes); + let buf = Buffer::<2048>::from_bytes_lossy(bytes); let mut cursor: usize = 0; let id = Self::unmarshal(&buf, &mut cursor)?; Ok((id, cursor)) diff --git a/network-hypervisor/src/vl1/inetaddress.rs b/network-hypervisor/src/vl1/inetaddress.rs index db7d702f4..e9c839b91 100644 --- a/network-hypervisor/src/vl1/inetaddress.rs +++ b/network-hypervisor/src/vl1/inetaddress.rs @@ -7,10 +7,10 @@ use std::net::{IpAddr, Ipv6Addr}; use crate::error::InvalidFormatError; use crate::util::equal_bytes; +use crate::vl1::buffer::Buffer; #[cfg(windows)] use winapi::um::winsock2 as winsock2; -use crate::vl1::buffer::{RawObject, Buffer}; #[allow(non_camel_case_types)] #[cfg(not(windows))] @@ -203,7 +203,7 @@ impl InetAddress { /// Get raw IP bytes, with length dependent on address family. #[inline(always)] - pub fn ip(&self) -> &[u8] { + pub fn ip_bytes(&self) -> &[u8] { unsafe { match self.sa.sa_family as u8 { AF_INET => &*(&self.sin.sin_addr.s_addr as *const u32).cast::<[u8; 4]>(), @@ -373,7 +373,7 @@ impl InetAddress { } } - pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { unsafe { match self.sa.sa_family as u8 { AF_INET => { @@ -399,7 +399,7 @@ impl InetAddress { } } - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { match buf.get_u8(cursor)? { 4 => { let b: &[u8; 6] = buf.get_bytes_fixed(cursor)?; diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index e918af472..ae0262728 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -1,7 +1,9 @@ +pub(crate) mod constants; pub(crate) mod protocol; -pub(crate) mod packet; pub(crate) mod buffer; pub(crate) mod node; +pub(crate) mod path; +pub(crate) mod peer; pub mod dictionary; pub mod identity; diff --git a/network-hypervisor/src/vl1/node.rs b/network-hypervisor/src/vl1/node.rs index b404175b9..e69de29bb 100644 --- a/network-hypervisor/src/vl1/node.rs +++ b/network-hypervisor/src/vl1/node.rs @@ -1,59 +0,0 @@ -//use crate::vl1::Address; - -/* -/// Handler for events generated by the node that pertain to VL1. -pub trait VL1NodeEventHandler: Sync + Send { - /// Called when a core ZeroTier event occurs. - fn event(&self, event: Event, event_data: &[u8]); - - /// Called to store an object into the object store. - fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()>; - - /// Called to retrieve an object from the object store. - fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result>; - - /// Called to send a packet over the physical network (virtual -> physical). - fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32; - - /// Called to check and see if a physical address should be used for ZeroTier traffic. - fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool; - - /// Called to look up a path to a known node, allowing out of band lookup methods for physical paths to nodes. - fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option; -} - -pub struct Node { - handler: H, -} - -impl Node { - pub fn new(handler: H) -> Self { - Self { - handler, - } - } - - pub fn handler(&self) -> &H { - &self.handler - } - - pub fn handler_mut(&mut self) -> &mut H { - &mut self.handler - } - - /// Perform periodic background tasks. - /// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds - /// since the node was created, and after this runs it returns the amount of time the caller - /// should wait before calling it again. - #[inline(always)] - pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 { - 0 - } - - /// Get the address of this node. - #[inline(always)] - pub fn address(&self) -> Address { - Address::default() - } -} -*/ \ No newline at end of file diff --git a/network-hypervisor/src/vl1/packet.rs b/network-hypervisor/src/vl1/packet.rs deleted file mode 100644 index c5e037543..000000000 --- a/network-hypervisor/src/vl1/packet.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::vl1::protocol::{HEADER_FLAGS_FIELD_MASK_CIPHER, HEADER_FLAGS_FIELD_MASK_HOPS, HEADER_FLAG_FRAGMENTED}; -use std::ops::Not; - -type PacketID = u64; - -#[derive(Clone)] -#[repr(packed)] -pub struct Header { - pub id: [u8; 8], - pub dest: [u8; 5], - pub src: [u8; 5], - pub flags_cipher_hops: u8, - pub message_auth: [u8; 8], -} - -unsafe impl crate::vl1::buffer::RawObject for Header {} - -impl Header { - /// Get this packet's ID as a u64. - /// While this returns u64, the returned integer contains raw packet ID bytes - /// in "native" byte order and should be treated conceptually like [u8; 8]. - /// In particular, greater/less than comparisons may differ across architectures. - #[inline(always)] - pub fn to_id(&self) -> PacketID { - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] - unsafe { *self.id.as_ptr().cast::() } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] - u64::from_ne_bytes(self.id.clone()) - } - - #[inline(always)] - pub fn cipher(&self) -> u8 { - self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER - } - - #[inline(always)] - pub fn hops(&self) -> u8 { - self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS - } - - #[inline(always)] - pub fn increment_hops(&mut self) { - let f = self.flags_cipher_hops; - self.flags_cipher_hops = (f & HEADER_FLAGS_FIELD_MASK_HOPS.not()) | ((f + 1) & HEADER_FLAGS_FIELD_MASK_HOPS); - } - - #[inline(always)] - pub fn is_fragmented(&self) -> bool { - (self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0 - } -} - -pub type Packet = crate::vl1::buffer::Buffer; - -#[derive(Clone)] -#[repr(packed)] -pub struct FragmentHeader { - pub id: [u8; 8], - pub dest: [u8; 5], - pub fragment_indicator: u8, - pub total_and_fragment_no: u8, - pub hops: u8, -} - -unsafe impl crate::vl1::buffer::RawObject for FragmentHeader {} - -impl FragmentHeader { - #[inline(always)] - pub fn total_fragments(&self) -> u8 { - self.total_and_fragment_no >> 4 - } - - #[inline(always)] - pub fn fragment_no(&self) -> u8 { - self.total_and_fragment_no & 0x0f - } - - #[inline(always)] - pub fn hops(&self) -> u8 { - self.hops & HEADER_FLAGS_FIELD_MASK_HOPS - } -} - -pub type Fragment = crate::vl1::buffer::Buffer; - -#[cfg(test)] -mod tests { - use std::mem::size_of; - use crate::vl1::packet::{Header, FragmentHeader}; - use crate::vl1::protocol::{PACKET_HEADER_SIZE, FRAGMENT_SIZE_MIN}; - - #[test] - fn object_sizing() { - assert_eq!(size_of::
(), PACKET_HEADER_SIZE); - assert_eq!(size_of::(), FRAGMENT_SIZE_MIN); - } -} diff --git a/network-hypervisor/src/vl1/path.rs b/network-hypervisor/src/vl1/path.rs new file mode 100644 index 000000000..e69de29bb diff --git a/network-hypervisor/src/vl1/peer.rs b/network-hypervisor/src/vl1/peer.rs new file mode 100644 index 000000000..e69de29bb diff --git a/network-hypervisor/src/vl1/protocol.rs b/network-hypervisor/src/vl1/protocol.rs index 4482d44f2..fe554ed01 100644 --- a/network-hypervisor/src/vl1/protocol.rs +++ b/network-hypervisor/src/vl1/protocol.rs @@ -1,58 +1,82 @@ -/// Length of an address in bytes. -pub const ADDRESS_SIZE: usize = 5; +use crate::vl1::constants::{HEADER_FLAGS_FIELD_MASK_CIPHER, HEADER_FLAGS_FIELD_MASK_HOPS, HEADER_FLAG_FRAGMENTED}; +use std::ops::Not; +use crate::vl1::buffer::RawObject; -/// Size of packet header that lies outside the encryption envelope. -pub const PACKET_HEADER_SIZE: usize = 27; +type PacketID = u64; -/// Maximum packet payload size including the verb/flags field. -/// This is large enough to carry "jumbo MTU" packets. The size is -/// odd because 10005+27 == 10032 which is divisible by 16. This -/// improves memory layout and alignment when buffers are allocated. -/// This value could technically be increased but it would require a -/// protocol version bump and only new nodes would be able to accept -/// the new size. -pub const PACKET_PAYLOAD_SIZE_MAX: usize = 10005; +#[derive(Clone)] +#[repr(packed)] +pub struct PacketHeader { + pub id: PacketID, + pub dest: [u8; 5], + pub src: [u8; 5], + pub flags_cipher_hops: u8, + pub message_auth: [u8; 8], +} -/// Minimum packet, which is the header plus a verb. -pub const PACKET_SIZE_MIN: usize = PACKET_HEADER_SIZE + 1; +unsafe impl RawObject for PacketHeader {} -/// Maximum size of an entire packet. -pub const PACKET_SIZE_MAX: usize = PACKET_HEADER_SIZE + PACKET_PAYLOAD_SIZE_MAX; +impl PacketHeader { + #[inline(always)] + pub fn cipher(&self) -> u8 { + self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER + } -/// Mask to select cipher from header flags field. -pub const HEADER_FLAGS_FIELD_MASK_CIPHER: u8 = 0x30; + #[inline(always)] + pub fn hops(&self) -> u8 { + self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS + } -/// Mask to select packet hops from header flags field. -pub const HEADER_FLAGS_FIELD_MASK_HOPS: u8 = 0x07; + #[inline(always)] + pub fn increment_hops(&mut self) { + let f = self.flags_cipher_hops; + self.flags_cipher_hops = (f & HEADER_FLAGS_FIELD_MASK_HOPS.not()) | ((f + 1) & HEADER_FLAGS_FIELD_MASK_HOPS); + } -/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext. -/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305. -pub const CIPHER_NOCRYPT_POLY1305: u8 = 0; + #[inline(always)] + pub fn is_fragmented(&self) -> bool { + (self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0 + } +} -/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305. -/// Construction is the same as that which is used in the NaCl secret box functions. -pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10; +#[derive(Clone)] +#[repr(packed)] +pub struct FragmentHeader { + pub id: PacketID, + pub dest: [u8; 5], + pub fragment_indicator: u8, + pub total_and_fragment_no: u8, + pub hops: u8, +} -/// Packet is encrypted and authenticated with AES-GMAC-SIV. -pub const CIPHER_AES_GMAC_SIV: u8 = 0x30; +unsafe impl crate::vl1::buffer::RawObject for FragmentHeader {} -/// Header (outer) flag indicating that this packet has additional fragments. -pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40; +impl FragmentHeader { + #[inline(always)] + pub fn total_fragments(&self) -> u8 { + self.total_and_fragment_no >> 4 + } -/// Minimum size of a fragment. -pub const FRAGMENT_SIZE_MIN: usize = 16; + #[inline(always)] + pub fn fragment_no(&self) -> u8 { + self.total_and_fragment_no & 0x0f + } -/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed. -pub const VERB_FLAG_COMPRESSED: u8 = 0x80; + #[inline(always)] + pub fn hops(&self) -> u8 { + self.hops & HEADER_FLAGS_FIELD_MASK_HOPS + } +} -/// Maximum number of packet hops allowed by the protocol. -pub const PROTOCOL_MAX_HOPS: usize = 7; +#[cfg(test)] +mod tests { + use std::mem::size_of; + use crate::vl1::protocol::{PacketHeader, FragmentHeader}; + use crate::vl1::constants::{PACKET_HEADER_SIZE, FRAGMENT_SIZE_MIN}; -/// Index of packet fragment indicator byte to detect fragments. -pub const FRAGMENT_INDICATOR_INDEX: usize = 13; - -/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment. -pub const FRAGMENT_INDICATOR: u8 = 0xff; - -/// Prefix indicating reserved addresses (that can't actually be addresses). -pub const ADDRESS_RESERVED_PREFIX: u8 = 0xff; + #[test] + fn object_sizing() { + assert_eq!(size_of::(), PACKET_HEADER_SIZE); + assert_eq!(size_of::(), FRAGMENT_SIZE_MIN); + } +}