mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +02:00
Rusty rusty rusty rust...
This commit is contained in:
parent
52585e9262
commit
5515be2e25
14 changed files with 714 additions and 4 deletions
|
@ -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<usize> {
|
||||
self.0.update(b);
|
||||
Ok(b.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
pub mod c25519;
|
||||
pub mod hash;
|
||||
pub mod p521;
|
||||
pub mod salsa;
|
||||
pub mod poly1305;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
use std::convert::TryInto;
|
||||
use std::io::Write;
|
||||
|
||||
use gcrypt::sexp::SExpression;
|
||||
|
||||
|
|
30
network-hypervisor/src/crypto/poly1305.rs
Normal file
30
network-hypervisor/src/crypto/poly1305.rs
Normal file
|
@ -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<Poly1305> {
|
||||
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
|
||||
}
|
||||
}
|
30
network-hypervisor/src/crypto/salsa.rs
Normal file
30
network-hypervisor/src/crypto/salsa.rs
Normal file
|
@ -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<Salsa> {
|
||||
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);
|
||||
}
|
||||
}
|
18
network-hypervisor/src/error.rs
Normal file
18
network-hypervisor/src/error.rs
Normal file
|
@ -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 {}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod crypto;
|
||||
pub mod vl1;
|
||||
pub mod util;
|
||||
pub mod error;
|
||||
|
|
|
@ -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<u8> {
|
||||
let mut b: Vec<u8> = 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]) {
|
||||
|
|
104
network-hypervisor/src/vl1/address.rs
Normal file
104
network-hypervisor/src/vl1/address.rs
Normal file
|
@ -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<Address, InvalidFormatError> {
|
||||
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<Self, Self::Err> {
|
||||
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<H: Hasher>(&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<u64> for Address {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> Address {
|
||||
Address(i)
|
||||
}
|
||||
}
|
178
network-hypervisor/src/vl1/buffer.rs
Normal file
178
network-hypervisor/src/vl1/buffer.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
use std::mem::size_of;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
const FAULT_BIT: usize = 1_usize << ((size_of::<usize>() * 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<H: RawObject, const L: usize>(usize, [u8; L], PhantomData<H>);
|
||||
|
||||
unsafe impl<H: RawObject, const L: usize> RawObject for Buffer<H, L> {}
|
||||
|
||||
impl<H: RawObject, const L: usize> Default for Buffer<H, L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
assert!(size_of::<H>() <= L);
|
||||
Buffer(size_of::<H>(), [0_u8; L], PhantomData::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> Buffer<H, L> {
|
||||
#[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::<H>();
|
||||
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::<H>() }
|
||||
}
|
||||
|
||||
/// 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::<H>() }
|
||||
}
|
||||
|
||||
/// 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<T: RawObject, F: FnOnce(&mut T)>(&mut self, initializer: F) {
|
||||
let bl = self.0;
|
||||
let s = bl + size_of::<T>();
|
||||
if s <= L {
|
||||
unsafe {
|
||||
self.0 = s;
|
||||
initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(bl as isize).cast::<T>());
|
||||
}
|
||||
} 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<F: FnOnce(&mut [u8; N]), const N: usize>(&mut self, initializer: F) {
|
||||
let bl = self.0;
|
||||
let s = bl + N;
|
||||
if s <= L {
|
||||
unsafe {
|
||||
let ptr = self.1.as_mut_ptr().cast::<u8>().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<F: FnOnce(&mut [u8])>(&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<H, L> {
|
||||
Reader {
|
||||
buffer: self,
|
||||
ptr: size_of::<H>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reader<'a, H: RawObject, const L: usize> {
|
||||
ptr: usize,
|
||||
buffer: &'a Buffer<H, L>,
|
||||
}
|
||||
|
||||
impl<'a, H: RawObject, const L: usize> Reader<'a, H, L> {
|
||||
pub fn read_struct<T: RawObject, R, F: FnOnce(&T, &mut Self) -> bool>(&mut self, visitor: F) -> bool {
|
||||
let rl = self.ptr;
|
||||
let s = rl + size_of::<T>();
|
||||
if s <= L {
|
||||
unsafe {
|
||||
self.ptr = s;
|
||||
visitor(&*self.buffer.1.as_ptr().cast::<u8>().offset(rl as isize).cast::<T>(), 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::<NoHeader>(), 0);
|
||||
}
|
||||
}
|
81
network-hypervisor/src/vl1/mac.rs
Normal file
81
network-hypervisor/src/vl1/mac.rs
Normal file
|
@ -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<MAC, InvalidFormatError> {
|
||||
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<Self, Self::Err> {
|
||||
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<H: Hasher>(&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<u64> for MAC {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> MAC {
|
||||
MAC(i)
|
||||
}
|
||||
}
|
|
@ -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;
|
97
network-hypervisor/src/vl1/packet.rs
Normal file
97
network-hypervisor/src/vl1/packet.rs
Normal file
|
@ -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::<u64>() }
|
||||
#[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<Header, { crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
||||
|
||||
#[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::<Header>(), PACKET_HEADER_SIZE);
|
||||
assert_eq!(size_of::<FragmentHeader>(), FRAGMENT_SIZE_MIN);
|
||||
}
|
||||
}
|
58
network-hypervisor/src/vl1/protocol.rs
Normal file
58
network-hypervisor/src/vl1/protocol.rs
Normal file
|
@ -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;
|
Loading…
Add table
Reference in a new issue