mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-07-26 04:02:50 +02:00
Revise some V1 crypto stuff, rearrange some things, simplify Buffer.
This commit is contained in:
parent
7a2361b62c
commit
ed142273d0
15 changed files with 229 additions and 306 deletions
12
network-hypervisor/src/crypto/kbkdf.rs
Normal file
12
network-hypervisor/src/crypto/kbkdf.rs
Normal file
|
@ -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])
|
||||
}
|
|
@ -4,3 +4,6 @@ pub mod p521;
|
|||
pub mod salsa;
|
||||
pub mod poly1305;
|
||||
pub mod balloon;
|
||||
pub mod kbkdf;
|
||||
|
||||
pub use aes_gmac_siv;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<H: RawObject, const L: usize>(usize, [u8; L], PhantomData<H>);
|
||||
pub struct Buffer<const L: usize>(usize, [u8; L]);
|
||||
|
||||
unsafe impl<H: RawObject, const L: usize> RawObject for Buffer<H, L> {}
|
||||
unsafe impl<const L: usize> RawObject for Buffer<L> {}
|
||||
|
||||
impl<H: RawObject, const L: usize> Default for Buffer<H, L> {
|
||||
impl<const L: usize> Default for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
Buffer(size_of::<H>(), [0_u8; L], PhantomData::default())
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> Buffer<H, L> {
|
||||
impl<const L: usize> Buffer<L> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
debug_assert!(size_of::<H>() <= 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<NH: RawObject>(&self) -> &Buffer<NH, L> {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
debug_assert!(size_of::<NH>() <= L);
|
||||
unsafe {
|
||||
&*(self as *const Self).cast::<Buffer<NH, L>>()
|
||||
}
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
|
||||
/// Create a buffer that contains a copy of a slice, truncating if the slice is too long.
|
||||
|
@ -78,8 +54,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
/// 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::<H>();
|
||||
unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) }
|
||||
}
|
||||
|
||||
/// Get the length of this buffer (including header, if any).
|
||||
|
@ -88,20 +63,6 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
self.0
|
||||
}
|
||||
|
||||
/// Get a reference to the header (in place).
|
||||
#[inline(always)]
|
||||
pub fn header(&self) -> &H {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
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 {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
unsafe { &mut *self.1.as_mut_ptr().cast::<H>() }
|
||||
}
|
||||
|
||||
/// 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<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the index of the start of the payload after the header.
|
||||
#[inline(always)]
|
||||
pub fn cursor_after_header(&self) -> usize {
|
||||
size_of::<H>()
|
||||
}
|
||||
|
||||
/// Get a structure at a given position in the buffer and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_struct<T: RawObject>(&self, cursor: &mut usize) -> std::io::Result<&T> {
|
||||
|
@ -336,7 +291,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> Write for Buffer<H, L> {
|
||||
impl<const L: usize> Write for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let ptr = self.0;
|
||||
|
@ -356,41 +311,16 @@ impl<H: RawObject, const L: usize> Write for Buffer<H, L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> AsRef<[u8]> for Buffer<H, L> {
|
||||
impl<const L: usize> AsRef<[u8]> for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> AsMut<[u8]> for Buffer<H, L> {
|
||||
impl<const L: usize> AsMut<[u8]> for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.as_bytes_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> AsRef<H> for Buffer<H, L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &H {
|
||||
self.header()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> AsMut<H> for Buffer<H, L> {
|
||||
#[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::<NoHeader>(), 0);
|
||||
}
|
||||
}
|
||||
|
|
58
network-hypervisor/src/vl1/constants.rs
Normal file
58
network-hypervisor/src/vl1/constants.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;
|
|
@ -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<BH: RawObject, const BL: usize>(&self, buf: &mut Buffer<BH, BL>) -> std::io::Result<()> {
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> 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<BH: RawObject, const BL: usize>(buf: &Buffer<BH, BL>, cursor: &mut usize) -> std::io::Result<Endpoint> {
|
||||
pub fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Endpoint> {
|
||||
let type_byte = buf.get_u8(cursor)?;
|
||||
if type_byte < 16 {
|
||||
let ip = InetAddress::unmarshal(buf, cursor)?;
|
||||
|
|
|
@ -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<Vec<u8>> {
|
||||
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<BH: RawObject, const BL: usize>(&self, buf: &mut Buffer<BH, BL>, include_private: bool) -> std::io::Result<()> {
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>, 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<BH: RawObject, const BL: usize>(buf: &Buffer<BH, BL>, cursor: &mut usize) -> std::io::Result<Identity> {
|
||||
pub fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Identity> {
|
||||
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<u8> {
|
||||
let mut buf: Buffer<NoHeader, 2048> = 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::<NoHeader, 2048>::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))
|
||||
|
|
|
@ -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<BH: RawObject, const BL: usize>(&self, buf: &mut Buffer<BH, BL>) -> std::io::Result<()> {
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||
unsafe {
|
||||
match self.sa.sa_family as u8 {
|
||||
AF_INET => {
|
||||
|
@ -399,7 +399,7 @@ impl InetAddress {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unmarshal<BH: RawObject, const BL: usize>(buf: &Buffer<BH, BL>, cursor: &mut usize) -> std::io::Result<InetAddress> {
|
||||
pub fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<InetAddress> {
|
||||
match buf.get_u8(cursor)? {
|
||||
4 => {
|
||||
let b: &[u8; 6] = buf.get_bytes_fixed(cursor)?;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Vec<u8>>;
|
||||
|
||||
/// 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<InetAddress>;
|
||||
}
|
||||
|
||||
pub struct Node<H: VL1NodeEventHandler> {
|
||||
handler: H,
|
||||
}
|
||||
|
||||
impl<H: VL1NodeEventHandler> Node<H> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -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::<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
|
||||
}
|
||||
}
|
||||
|
||||
pub type Packet = crate::vl1::buffer::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 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<FragmentHeader, { crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
0
network-hypervisor/src/vl1/path.rs
Normal file
0
network-hypervisor/src/vl1/path.rs
Normal file
0
network-hypervisor/src/vl1/peer.rs
Normal file
0
network-hypervisor/src/vl1/peer.rs
Normal file
|
@ -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::<PacketHeader>(), PACKET_HEADER_SIZE);
|
||||
assert_eq!(size_of::<FragmentHeader>(), FRAGMENT_SIZE_MIN);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue