diff --git a/network-hypervisor/src/crypto/hash.rs b/network-hypervisor/src/crypto/hash.rs index f4453511e..2d7de9f96 100644 --- a/network-hypervisor/src/crypto/hash.rs +++ b/network-hypervisor/src/crypto/hash.rs @@ -3,13 +3,14 @@ use std::convert::TryInto; use std::io::Write; pub const SHA512_HASH_SIZE: usize = 64; +pub const SHA384_HASH_SIZE: usize = 64; pub struct SHA512(gcrypt::digest::MessageDigest); impl SHA512 { #[inline(always)] pub fn hash(b: &[u8]) -> [u8; SHA512_HASH_SIZE] { - let mut h = unsafe { MaybeUninit::<[u8; 64]>::uninit().assume_init() }; + let mut h = unsafe { MaybeUninit::<[u8; SHA512_HASH_SIZE]>::uninit().assume_init() }; gcrypt::digest::hash(gcrypt::digest::Algorithm::Sha512, b, &mut h); h } @@ -21,7 +22,7 @@ impl SHA512 { let _ = m.set_key(key); let _ = m.update(msg); let mut h = [0_u8; SHA512_HASH_SIZE]; - m.get_mac(&mut h); + let _ = m.get_mac(&mut h); h } @@ -67,3 +68,67 @@ impl Write for SHA512 { self.0.flush() } } + +pub struct SHA384(gcrypt::digest::MessageDigest); + +impl SHA384 { + #[inline(always)] + pub fn hash(b: &[u8]) -> [u8; SHA384_HASH_SIZE] { + let mut h = unsafe { MaybeUninit::<[u8; SHA384_HASH_SIZE]>::uninit().assume_init() }; + gcrypt::digest::hash(gcrypt::digest::Algorithm::Sha384, b, &mut h); + h + } + + /// Compute HMAC-SHA384(key, msg) + #[inline(always)] + pub fn hmac(key: &[u8], msg: &[u8]) -> [u8; SHA384_HASH_SIZE] { + let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha384).unwrap(); + let _ = m.set_key(key); + let _ = m.update(msg); + let mut h = [0_u8; SHA384_HASH_SIZE]; + let _ = m.get_mac(&mut h); + h + } + + #[inline(always)] + pub fn new() -> Self { + Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha384).unwrap()) + } + + #[inline(always)] + pub fn reset(&mut self) { + self.0.reset(); + } + + #[inline(always)] + pub fn update(&mut self, b: &[u8]) { + self.0.update(b); + } + + #[inline(always)] + pub fn finish(&mut self) -> [u8; SHA384_HASH_SIZE] { + self.0.finish(); + self.0.get_only_digest().unwrap().try_into().unwrap() + } + + /// Return a reference to an internally stored result. + /// This saves a copy, but the returned result is only valid so long as no other methods are called. + #[inline(always)] + pub fn finish_get_ref(&mut self) -> &[u8] { + self.0.finish(); + self.0.get_only_digest().unwrap() + } +} + +impl Write for SHA384 { + #[inline(always)] + fn write(&mut self, b: &[u8]) -> std::io::Result { + self.0.update(b); + Ok(b.len()) + } + + #[inline(always)] + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush() + } +} diff --git a/network-hypervisor/src/crypto/mod.rs b/network-hypervisor/src/crypto/mod.rs index 1a45f96cd..a44d8d9c9 100644 --- a/network-hypervisor/src/crypto/mod.rs +++ b/network-hypervisor/src/crypto/mod.rs @@ -1,3 +1,5 @@ pub mod c25519; pub mod hash; pub mod p521; +pub mod salsa; +pub mod poly1305; diff --git a/network-hypervisor/src/crypto/p521.rs b/network-hypervisor/src/crypto/p521.rs index a452b759d..c96438431 100644 --- a/network-hypervisor/src/crypto/p521.rs +++ b/network-hypervisor/src/crypto/p521.rs @@ -1,6 +1,5 @@ use std::str::FromStr; use std::convert::TryInto; -use std::io::Write; use gcrypt::sexp::SExpression; diff --git a/network-hypervisor/src/crypto/poly1305.rs b/network-hypervisor/src/crypto/poly1305.rs new file mode 100644 index 000000000..a5f49cb9f --- /dev/null +++ b/network-hypervisor/src/crypto/poly1305.rs @@ -0,0 +1,30 @@ +pub struct Poly1305(gcrypt::mac::Mac); + +pub const POLY1305_ONE_TIME_KEY_SIZE: usize = 32; +pub const POLY1305_MAC_SIZE: usize = 16; + +impl Poly1305 { + #[inline(always)] + pub fn new(key: &[u8]) -> Option { + if key.len() == 32 { + gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::Poly1305).map_or(None, |mut poly| { + let _ = poly.set_key(key); + Some(Poly1305(poly)) + }) + } else { + None + } + } + + #[inline(always)] + pub fn update(&mut self, data: &[u8]) { + let _ = self.0.update(data); + } + + #[inline(always)] + pub fn finish(&mut self) -> [u8; POLY1305_MAC_SIZE] { + let mut mac = [0_u8; POLY1305_MAC_SIZE]; + let _ = self.0.get_mac(&mut mac); + mac + } +} diff --git a/network-hypervisor/src/crypto/salsa.rs b/network-hypervisor/src/crypto/salsa.rs new file mode 100644 index 000000000..ef4195898 --- /dev/null +++ b/network-hypervisor/src/crypto/salsa.rs @@ -0,0 +1,30 @@ +pub struct Salsa(gcrypt::cipher::Cipher); + +impl Salsa { + /// Initialize Salsa cipher. + /// Key must be 32 bytes and iv must be 8 bytes. If r12 is true the 12-round + /// variant of Salsa will be used, otherwise 20 rounds are used. + #[inline(always)] + pub fn new(key: &[u8], iv: &[u8], r12: bool) -> Option { + if key.len() == 32 && iv.len() == 8 { + gcrypt::cipher::Cipher::new(if r12 { gcrypt::cipher::Algorithm::Salsa20r12 } else { gcrypt::cipher::Algorithm::Salsa20 }, gcrypt::cipher::Mode::Stream).map_or(None, |mut salsa| { + let _ = salsa.set_key(key); + let _ = salsa.set_iv(iv); + Some(Salsa(salsa)) + }) + } else { + None + } + } + + /// Encrypt or decrypt, which for Salsa is the same operation. + #[inline(always)] + pub fn crypt(&mut self, plaintext: &[u8], ciphertext: &mut [u8]) { + let _ = self.0.encrypt(plaintext, ciphertext); + } + + #[inline(always)] + pub fn crypt_in_place(&mut self, plaintext_to_ciphertext: &mut [u8]) { + let _ = self.0.encrypt_inplace(plaintext_to_ciphertext); + } +} diff --git a/network-hypervisor/src/error.rs b/network-hypervisor/src/error.rs new file mode 100644 index 000000000..f61266a10 --- /dev/null +++ b/network-hypervisor/src/error.rs @@ -0,0 +1,18 @@ +use std::error::Error; +use std::fmt::{Display, Debug}; + +pub struct InvalidFormatError(pub(crate) &'static str); + +impl Display for InvalidFormatError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "UnmarshalError: {}", self.0) + } +} + +impl Debug for InvalidFormatError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "UnmarshalError: {}", self.0) + } +} + +impl Error for InvalidFormatError {} diff --git a/network-hypervisor/src/lib.rs b/network-hypervisor/src/lib.rs index 4d72f9e05..5e5d45548 100644 --- a/network-hypervisor/src/lib.rs +++ b/network-hypervisor/src/lib.rs @@ -1,3 +1,4 @@ pub mod crypto; pub mod vl1; pub mod util; +pub mod error; diff --git a/network-hypervisor/src/util/hex.rs b/network-hypervisor/src/util/hex.rs index 23bf7ff65..195257926 100644 --- a/network-hypervisor/src/util/hex.rs +++ b/network-hypervisor/src/util/hex.rs @@ -1,6 +1,6 @@ pub(crate) const HEX_CHARS: [u8; 16] = [ b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f']; -/// Encode a binary string to a series of hex bytes. +/// Encode a byte slice to a hexadecimal string. pub fn to_string(b: &[u8]) -> String { let mut s = String::new(); s.reserve(b.len() * 2); @@ -12,6 +12,45 @@ pub fn to_string(b: &[u8]) -> String { s } +/// Decode a hex string, ignoring non-hexadecimal characters. +pub fn from_string(s: &str) -> Vec { + let mut b: Vec = Vec::new(); + b.reserve((s.len() / 2) + 1); + + let mut byte = 0; + let mut have_8: bool = false; + for cc in s.as_bytes() { + let c = *cc; + if c >= 48 && c <= 57 { + byte <<= 4; + byte |= c - 48; + if have_8 { + b.push(byte); + byte = 0; + } + have_8 = !have_8; + } else if c >= 65 && c <= 70 { + byte <<= 4; + byte |= c - 55; + if have_8 { + b.push(byte); + byte = 0; + } + have_8 = !have_8; + } else if c >= 97 && c <= 102 { + byte <<= 4; + byte |= c - 87; + if have_8 { + b.push(byte); + byte = 0; + } + have_8 = !have_8; + } + } + + b +} + /// Encode bytes from 'b' into hex characters in 'dest'. /// This will panic if the destination slice is smaller than twice the length of the source. pub fn to_hex_bytes(b: &[u8], dest: &mut [u8]) { diff --git a/network-hypervisor/src/vl1/address.rs b/network-hypervisor/src/vl1/address.rs new file mode 100644 index 000000000..2370466ac --- /dev/null +++ b/network-hypervisor/src/vl1/address.rs @@ -0,0 +1,104 @@ +use std::str::FromStr; +use std::hash::{Hash, Hasher}; + +use crate::vl1::protocol::ADDRESS_RESERVED_PREFIX; +use crate::error::InvalidFormatError; +use crate::util::hex::HEX_CHARS; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct Address(u64); + +impl Address { + #[inline(always)] + pub fn from_bytes(b: &[u8]) -> Result { + if b.len() >= 5 { + Ok(Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 as u64 | b[4] as u64)) + } else { + Err(InvalidFormatError("invalid ZeroTier address")) + } + } + + #[inline(always)] + pub fn is_reserved(&self) -> bool { + (self.0 >> 32) as usize == ADDRESS_RESERVED_PREFIX as usize + } + + #[inline(always)] + pub fn is_valid(&self) -> bool { + self.0 != 0 && !self.is_reserved() + } + + #[inline(always)] + pub fn is_nil(&self) -> bool { + self.0 == 0 + } + + #[inline(always)] + pub fn to_bytes(&self) -> [u8; 5] { + [(self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8] + } + + #[inline(always)] + pub fn to_u64(&self) -> u64 { + self.0 + } +} + +impl ToString for Address { + #[inline(always)] + fn to_string(&self) -> String { + let mut v = self.0 << 24; + let mut s = String::new(); + s.reserve(10); + for _ in 0..10 { + s.push(HEX_CHARS[(v >> 60) as usize] as char); + v <<= 4; + } + s + } +} + +impl FromStr for Address { + type Err = InvalidFormatError; + + #[inline(always)] + fn from_str(s: &str) -> Result { + Address::from_bytes(crate::util::hex::from_string(s).as_slice()) + } +} + +impl Default for Address { + #[inline(always)] + fn default() -> Address { + Address(0) + } +} + +impl Hash for Address { + #[inline(always)] + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl From<&[u8; 5]> for Address { + #[inline(always)] + fn from(b: &[u8; 5]) -> Address { + Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 as u64 | b[4] as u64) + } +} + +impl From<[u8; 5]> for Address { + #[inline(always)] + fn from(b: [u8; 5]) -> Address { + Self::from(&b) + } +} + +impl From for Address { + #[inline(always)] + fn from(i: u64) -> Address { + Address(i) + } +} diff --git a/network-hypervisor/src/vl1/buffer.rs b/network-hypervisor/src/vl1/buffer.rs new file mode 100644 index 000000000..f0a246cc2 --- /dev/null +++ b/network-hypervisor/src/vl1/buffer.rs @@ -0,0 +1,178 @@ +use std::mem::size_of; +use std::marker::PhantomData; + +const FAULT_BIT: usize = 1_usize << ((size_of::() * 8) - 1); +const FAULT_CLEAR_MASK: usize = !FAULT_BIT; + +/// Annotates a type as containing only primitive types like integers and arrays. +/// This means it's safe to abuse with raw copy, raw zero, or "type punning." +/// 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. Use NoHeader if you don't want a header. +/// +/// If a write overflow occurs during append operations, the operations fail silently +/// without increasing the buffer's size and an internal fault bit is set. The +/// check_overflow() method must be used before the buffer is actually complete to +/// ensure that no write overflows occurred. If this check isn't performed a buffer +/// could be used with incomplete or corrupt data, but no crash or memory errors will +/// occur. +#[derive(Clone)] +pub struct Buffer(usize, [u8; L], PhantomData); + +unsafe impl RawObject for Buffer {} + +impl Default for Buffer { + #[inline(always)] + fn default() -> Self { + assert!(size_of::() <= L); + Buffer(size_of::(), [0_u8; L], PhantomData::default()) + } +} + +impl Buffer { + #[inline(always)] + pub fn new() -> Self { + Self::default() + } + + /// Returns true if there has been a write overflow. + #[inline(always)] + pub fn check_overflow(&self) -> bool { + (self.0 & FAULT_BIT) != 0 + } + + /// Get a slice containing the entire buffer in raw form including the header. + #[inline(always)] + pub fn as_bytes(&self) -> &[u8] { + &self.1[0..(self.0 & FAULT_CLEAR_MASK)] + } + + /// Erase contents and reset size to the size of the header. + #[inline(always)] + pub fn clear(&mut self) { + self.0 = size_of::(); + self.1.fill(0); + } + + /// Get the length of this buffer (including header, if any). + #[inline(always)] + pub fn len(&self) -> usize { + self.0 & FAULT_CLEAR_MASK + } + + /// Get a reference to the header (in place). + #[inline(always)] + pub fn header(&self) -> &H { + 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 { + unsafe { &mut *self.1.as_mut_ptr().cast::() } + } + + /// Append a packed structure and initializing it in place via the supplied function. + /// + /// If an overflow occurs the overflow fault bit is set internally (see check_overflow()) + /// and the supplied function will never be called. + #[inline(always)] + pub fn append_and_init_struct(&mut self, initializer: F) { + let bl = self.0; + let s = bl + size_of::(); + if s <= L { + unsafe { + self.0 = s; + initializer(&mut *self.1.as_mut_ptr().cast::().offset(bl as isize).cast::()); + } + } else { + self.0 = bl | FAULT_BIT; + } + } + + /// Append and initialize a byte array with a fixed size set at compile time. + /// + /// This is more efficient than setting a size at runtime as it may allow the compiler to + /// skip some bounds checking. + /// + /// If an overflow occurs the overflow fault bit is set internally (see check_overflow()) + /// and the supplied function will never be called. + #[inline(always)] + pub fn append_and_init_bytes_fixed(&mut self, initializer: F) { + let bl = self.0; + let s = bl + N; + if s <= L { + unsafe { + let ptr = self.1.as_mut_ptr().cast::().offset(bl as isize); + self.0 = s; + initializer(&mut *ptr.cast::<[u8; N]>()); + } + } else { + self.0 = bl | FAULT_BIT; + } + } + + /// Append and initialize a slice with a size that is set at runtime. + /// + /// If an overflow occurs the overflow fault bit is set internally (see check_overflow()) + /// and the supplied function will never be called. + #[inline(always)] + pub fn append_and_init_bytes(&mut self, l: usize, initializer: F) { + let bl = self.0; + let s = bl + l; + if s <= L { + self.0 = s; + initializer(&mut self.1[bl..s]); + } else { + self.0 = bl | FAULT_BIT; + } + } + + pub fn read_payload(&self) -> Reader { + Reader { + buffer: self, + ptr: size_of::(), + } + } +} + +pub struct Reader<'a, H: RawObject, const L: usize> { + ptr: usize, + buffer: &'a Buffer, +} + +impl<'a, H: RawObject, const L: usize> Reader<'a, H, L> { + pub fn read_struct bool>(&mut self, visitor: F) -> bool { + let rl = self.ptr; + let s = rl + size_of::(); + if s <= L { + unsafe { + self.ptr = s; + visitor(&*self.buffer.1.as_ptr().cast::().offset(rl as isize).cast::(), self) + } + } else { + false + } + } +} + +#[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/mac.rs b/network-hypervisor/src/vl1/mac.rs new file mode 100644 index 000000000..e70ee79fb --- /dev/null +++ b/network-hypervisor/src/vl1/mac.rs @@ -0,0 +1,81 @@ +use std::str::FromStr; +use std::hash::{Hash, Hasher}; + +use crate::error::InvalidFormatError; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct MAC(u64); + +impl MAC { + #[inline(always)] + pub fn from_bytes(b: &[u8]) -> Result { + if b.len() >= 6 { + Ok(MAC((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64)) + } else { + Err(InvalidFormatError("invalid MAC address")) + } + } + + #[inline(always)] + pub fn to_bytes(&self) -> [u8; 6] { + [(self.0 >> 40) as u8, (self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8] + } + + #[inline(always)] + pub fn to_u64(&self) -> u64 { + self.0 + } +} + +impl ToString for MAC { + #[inline(always)] + fn to_string(&self) -> String { + let b: [u8; 6] = self.to_bytes(); + format!("{}:{}:{}:{}:{}:{}", b[0], b[1], b[2], b[3], b[4], b[5]) + } +} + +impl FromStr for MAC { + type Err = InvalidFormatError; + + #[inline(always)] + fn from_str(s: &str) -> Result { + MAC::from_bytes(crate::util::hex::from_string(s).as_slice()) + } +} + +impl Default for MAC { + #[inline(always)] + fn default() -> MAC { + MAC(0) + } +} + +impl Hash for MAC { + #[inline(always)] + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl From<&[u8; 6]> for MAC { + #[inline(always)] + fn from(b: &[u8; 6]) -> MAC { + MAC((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64) + } +} + +impl From<[u8; 6]> for MAC { + #[inline(always)] + fn from(b: [u8; 6]) -> MAC { + Self::from(&b) + } +} + +impl From for MAC { + #[inline(always)] + fn from(i: u64) -> MAC { + MAC(i) + } +} diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index e69de29bb..7ae531cc2 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -0,0 +1,8 @@ +pub mod protocol; +pub mod packet; +pub mod buffer; +mod address; +mod mac; + +pub use address::Address; +pub use mac::MAC; diff --git a/network-hypervisor/src/vl1/packet.rs b/network-hypervisor/src/vl1/packet.rs new file mode 100644 index 000000000..a5923ec13 --- /dev/null +++ b/network-hypervisor/src/vl1/packet.rs @@ -0,0 +1,97 @@ +use crate::vl1::buffer::{Buffer, RawObject}; +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 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 + } +} + +/// Packet is a Buffer with the packet Header and the packet max payload size. +pub type Packet = 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 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 + } +} + +#[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/protocol.rs b/network-hypervisor/src/vl1/protocol.rs new file mode 100644 index 000000000..4482d44f2 --- /dev/null +++ b/network-hypervisor/src/vl1/protocol.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;