mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
Formatting.
This commit is contained in:
parent
bcfd35a1f7
commit
d2e19c889f
19 changed files with 210 additions and 239 deletions
|
@ -66,6 +66,8 @@ NOP, as the name suggests, does nothing. Any payload is ignored.
|
|||
| [2] u16 | Length of encrypted Dictionary in bytes |
|
||||
| Dictionary | Key/value dictionary containing additional fields |
|
||||
| -- | -- END of AES-256-CTR encrypted section -- |
|
||||
| [2] u16 | Length of unencrypted Dictionary in bytes |
|
||||
| Dictionary | Unencrypted dictionary (not currently used) |
|
||||
|
||||
HELLO establishes a full session with another peer and carries information such as protocol and software versions, the full identity of the peer, and ephemeral keys for forward secrecy. Without a HELLO exchange only limited communication with the most conservative assumptions is possible, and communication without a session may be completely removed in the future. (It's only allowed now for backward compatibility with ZeroTier 1.x, and must be disabled in FIPS mode.)
|
||||
|
||||
|
@ -91,7 +93,7 @@ OK(HELLO) response payload, which must be sent if the HELLO receipient wishes to
|
|||
| [2] u16 | Length of encrypted Dictionary in bytes |
|
||||
| Dictionary | Key/value dictionary containing additional fields |
|
||||
|
||||
Recommended dictionary fields in both HELLO and OK(HELLO):
|
||||
The unencrypted dictionary is not currently used. The encrypted dictionary can contain the following fields in both HELLO and OK(HELLO):
|
||||
|
||||
| Name | Key | Type | Description |
|
||||
| -------------------- | --- | ------------ | ------------------------------------------------ |
|
||||
|
@ -120,7 +122,7 @@ Optional dictionary fields that can be included in either HELLO or OK(HELLO):
|
|||
| VENDOR | `V` | string | Node software vendor if not ZeroTier, Inc. |
|
||||
| FLAGS | `+` | string | Flags (see below) |
|
||||
|
||||
FLAGS is a string that can contain the following boolean flags: `F` to indicate that the node is running in FIPS compliant mode, and `w` to indicate that the node is a "wimp." "Wimpy" nodes are things like mobile phones, and this flag can be used to exempt these devices from selection for any intensive role (such as use in VL2 to propagate multicasts).
|
||||
FLAGS is a string that can contain the following boolean flags: `F` to indicate that the node is running in FIPS compliant mode.
|
||||
|
||||
System information such as OS_NAME is currently only sent to roots and not to any other node. This allows roots to collect a bit of very generic statistical and diagnostic telemtry about the nodes using them.
|
||||
|
||||
|
|
|
@ -38,14 +38,10 @@ impl C25519KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn public_bytes(&self) -> [u8; C25519_PUBLIC_KEY_SIZE] {
|
||||
self.1.to_bytes()
|
||||
}
|
||||
pub fn public_bytes(&self) -> [u8; C25519_PUBLIC_KEY_SIZE] { self.1.to_bytes() }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.to_bytes())
|
||||
}
|
||||
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> { Secret(self.0.to_bytes()) }
|
||||
|
||||
/// Execute ECDH agreement and return a raw (un-hashed) shared secret key.
|
||||
#[inline(always)]
|
||||
|
@ -86,14 +82,10 @@ impl Ed25519KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] {
|
||||
self.0.public.to_bytes()
|
||||
}
|
||||
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] { self.0.public.to_bytes() }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.secret.to_bytes())
|
||||
}
|
||||
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> { Secret(self.0.secret.to_bytes()) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sign(&self, msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
|
||||
|
|
|
@ -26,19 +26,13 @@ impl SHA512 {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha512).unwrap())
|
||||
}
|
||||
pub fn new() -> Self { Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha512).unwrap()) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reset(&mut self) {
|
||||
self.0.reset();
|
||||
}
|
||||
pub fn reset(&mut self) { self.0.reset(); }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn update(&mut self, b: &[u8]) {
|
||||
self.0.update(b);
|
||||
}
|
||||
pub fn update(&mut self, b: &[u8]) { self.0.update(b); }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish(&mut self) -> [u8; SHA512_HASH_SIZE] {
|
||||
|
@ -87,19 +81,13 @@ impl SHA384 {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha384).unwrap())
|
||||
}
|
||||
pub fn new() -> Self { Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha384).unwrap()) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reset(&mut self) {
|
||||
self.0.reset();
|
||||
}
|
||||
pub fn reset(&mut self) { self.0.reset(); }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn update(&mut self, b: &[u8]) {
|
||||
self.0.update(b);
|
||||
}
|
||||
pub fn update(&mut self, b: &[u8]) { self.0.update(b); }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish(&mut self) -> [u8; SHA384_HASH_SIZE] {
|
||||
|
|
|
@ -130,23 +130,17 @@ impl P521KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn public_key(&self) -> &P521PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
pub fn public_key(&self) -> &P521PublicKey { &self.public_key }
|
||||
|
||||
/// Get the raw ECC public "q" point for this key pair.
|
||||
/// The returned point is not compressed. To use this with other interfaces that expect a format
|
||||
/// prefix, prepend 0x04 to the beginning of this public key. This prefix is always the same in
|
||||
/// our system and so is omitted.
|
||||
#[inline(always)]
|
||||
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] {
|
||||
&self.public_key.public_key_bytes
|
||||
}
|
||||
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] { &self.public_key.public_key_bytes }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_key_bytes(&self) -> &Secret<{ P521_SECRET_KEY_SIZE }> {
|
||||
&self.secret_key_bytes
|
||||
}
|
||||
pub fn secret_key_bytes(&self) -> &Secret<{ P521_SECRET_KEY_SIZE }> { &self.secret_key_bytes }
|
||||
|
||||
/// Create an ECDSA signature of the input message.
|
||||
/// Message data does not need to be pre-hashed.
|
||||
|
@ -213,17 +207,13 @@ impl P521PublicKey {
|
|||
|
||||
impl PartialEq for P521PublicKey {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.public_key_bytes.eq(&other.public_key_bytes)
|
||||
}
|
||||
fn eq(&self, other: &Self) -> bool { self.public_key_bytes.eq(&other.public_key_bytes) }
|
||||
}
|
||||
|
||||
impl Eq for P521PublicKey {}
|
||||
|
||||
impl Clone for P521PublicKey {
|
||||
fn clone(&self) -> Self {
|
||||
P521PublicKey::from_bytes(&self.public_key_bytes).unwrap()
|
||||
}
|
||||
fn clone(&self) -> Self { P521PublicKey::from_bytes(&self.public_key_bytes).unwrap() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -16,34 +16,21 @@ pub struct Secret<const L: usize>(pub(crate) [u8; L]);
|
|||
|
||||
impl<const L: usize> Secret<L> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self([0_u8; L])
|
||||
}
|
||||
pub fn new() -> Self { Self([0_u8; L]) }
|
||||
|
||||
/// Copy bytes into secret, will panic if size does not match.
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self(b.try_into().unwrap())
|
||||
}
|
||||
pub fn from_bytes(b: &[u8]) -> Self { Self(b.try_into().unwrap()) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; L] {
|
||||
return &self.0
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8; L] { return &self.0 }
|
||||
}
|
||||
|
||||
impl<const L: usize> Drop for Secret<L> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let p = self.0.as_mut_ptr();
|
||||
if (L % size_of::<usize>()) == 0 {
|
||||
for i in 0..(L / size_of::<usize>()) {
|
||||
write_volatile(p.cast::<usize>().offset(i as isize), 0_usize);
|
||||
}
|
||||
} else {
|
||||
for i in 0..L {
|
||||
write_volatile(p.offset(i as isize), 0_u8);
|
||||
}
|
||||
for i in 0..L {
|
||||
write_volatile(self.0.as_mut_ptr().offset(i as isize), 0_u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,35 +38,25 @@ impl<const L: usize> Drop for Secret<L> {
|
|||
|
||||
impl<const L: usize> Default for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self([0_u8; L])
|
||||
}
|
||||
fn default() -> Self { Self([0_u8; L]) }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsRef<[u8]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
fn as_ref(&self) -> &[u8] { &self.0 }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsRef<[u8; L]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8; L] {
|
||||
&self.0
|
||||
}
|
||||
fn as_ref(&self) -> &[u8; L] { &self.0 }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsMut<[u8]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0
|
||||
}
|
||||
fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsMut<[u8; L]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8; L] {
|
||||
&mut self.0
|
||||
}
|
||||
fn as_mut(&mut self) -> &mut [u8; L] { &mut self.0 }
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@ pub struct Address(NonZeroU64);
|
|||
impl Address {
|
||||
/// Get an address from a 64-bit integer or return None if it is zero or reserved.
|
||||
#[inline(always)]
|
||||
pub fn from_u64(i: u64) -> Option<Address> {
|
||||
pub fn from_u64(mut i: u64) -> Option<Address> {
|
||||
i &= 0xffffffffff;
|
||||
if i != 0 && (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 {
|
||||
Some(Address(unsafe { NonZeroU64::new_unchecked(i & 0xffffffffff) }))
|
||||
Some(Address(unsafe { NonZeroU64::new_unchecked(i) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@ impl<const L: usize> Buffer<L> {
|
|||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool { self.0 == 0 }
|
||||
|
||||
/// Set the size of this buffer's data.
|
||||
///
|
||||
/// This is marked unsafe because no bounds checking is done here and because it
|
||||
/// technically violates the assurance that all data in the buffer is valid. Use
|
||||
/// with care.
|
||||
#[inline(always)]
|
||||
pub unsafe fn set_size(&mut self, s: usize) { self.0 = s; }
|
||||
|
||||
/// Append a packed structure and call a function to initialize it in place.
|
||||
/// Anything not initialized will be zero.
|
||||
#[inline(always)]
|
||||
|
@ -305,7 +313,7 @@ impl<const L: usize> Buffer<L> {
|
|||
loop {
|
||||
let b = self.read_u8(cursor)?;
|
||||
if (b & 0x80) == 0 {
|
||||
i |= (b as u64) << p;
|
||||
i |= (b as u64).wrapping_shl(p);
|
||||
p += 7;
|
||||
} else {
|
||||
i |= ((b & 0x7f) as u64) << p;
|
||||
|
|
|
@ -47,9 +47,7 @@ pub enum Endpoint {
|
|||
|
||||
impl Default for Endpoint {
|
||||
#[inline(always)]
|
||||
fn default() -> Endpoint {
|
||||
Endpoint::Nil
|
||||
}
|
||||
fn default() -> Endpoint { Endpoint::Nil }
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
|
|
|
@ -6,6 +6,8 @@ use std::io::Write;
|
|||
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use concat_arrays::concat_arrays;
|
||||
|
||||
use crate::crypto::balloon;
|
||||
use crate::crypto::c25519::*;
|
||||
use crate::crypto::hash::*;
|
||||
|
@ -17,12 +19,12 @@ use crate::vl1::Address;
|
|||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::protocol::PACKET_SIZE_MAX;
|
||||
|
||||
use concat_arrays::concat_arrays;
|
||||
pub const IDENTITY_TYPE_0_SIGNATURE_SIZE: usize = 96;
|
||||
pub const IDENTITY_TYPE_1_SIGNATURE_SIZE: usize = P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE;
|
||||
|
||||
// Memory parameter for V0 address derivation work function.
|
||||
const V0_IDENTITY_GEN_MEMORY: usize = 2097152;
|
||||
|
||||
// Balloon hash parameters for V1 address derivation work function.
|
||||
const V0_POW_MEMORY: usize = 2097152;
|
||||
const V0_POW_THRESHOLD: u8 = 17;
|
||||
const V1_POW_THRESHOLD: u8 = 5;
|
||||
const V1_BALLOON_SPACE_COST: usize = 16384;
|
||||
const V1_BALLOON_TIME_COST: usize = 3;
|
||||
const V1_BALLOON_DELTA: usize = 3;
|
||||
|
@ -30,12 +32,6 @@ const V1_BALLOON_SALT: &'static [u8] = b"zt_id_v1";
|
|||
|
||||
const V1_PUBLIC_KEYS_SIGNATURE_AND_POW_SIZE: usize = C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA384_HASH_SIZE;
|
||||
|
||||
pub const IDENTITY_TYPE_0_SIGNATURE_SIZE: usize = 96;
|
||||
pub const IDENTITY_TYPE_1_SIGNATURE_SIZE: usize = P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE;
|
||||
|
||||
const IDENTITY_V0_POW_THRESHOLD: u8 = 17;
|
||||
const IDENTITY_V1_POW_THRESHOLD: u8 = 5;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum Type {
|
||||
|
@ -62,24 +58,24 @@ pub struct Identity {
|
|||
/// Compute result from the bespoke "frankenhash" from the old V0 work function.
|
||||
/// The supplied genmem_ptr must be of size V0_IDENTITY_GEN_MEMORY and aligned to an 8-byte boundary.
|
||||
fn v0_frankenhash(digest: &mut [u8; 64], genmem_ptr: *mut u8) {
|
||||
let (genmem, genmem_alias_hack) = unsafe { (&mut *slice_from_raw_parts_mut(genmem_ptr, V0_IDENTITY_GEN_MEMORY), &*slice_from_raw_parts(genmem_ptr, V0_IDENTITY_GEN_MEMORY)) };
|
||||
let (genmem, genmem_alias_hack) = unsafe { (&mut *slice_from_raw_parts_mut(genmem_ptr, V0_POW_MEMORY), &*slice_from_raw_parts(genmem_ptr, V0_POW_MEMORY)) };
|
||||
let genmem_u64_ptr = genmem_ptr.cast::<u64>();
|
||||
|
||||
let mut s20 = Salsa::new(&digest[0..32], &digest[32..40], false).unwrap();
|
||||
|
||||
s20.crypt(&crate::util::ZEROES[0..64], &mut genmem[0..64]);
|
||||
let mut i: usize = 64;
|
||||
while i < V0_IDENTITY_GEN_MEMORY {
|
||||
while i < V0_POW_MEMORY {
|
||||
let ii = i + 64;
|
||||
s20.crypt(&genmem_alias_hack[(i - 64)..i], &mut genmem[i..ii]);
|
||||
i = ii;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while i < (V0_IDENTITY_GEN_MEMORY / 8) {
|
||||
while i < (V0_POW_MEMORY / 8) {
|
||||
unsafe {
|
||||
let idx1 = (((*genmem_u64_ptr.offset(i as isize)).to_be() % 8) * 8) as usize;
|
||||
let idx2 = ((*genmem_u64_ptr.offset((i + 1) as isize)).to_be() % (V0_IDENTITY_GEN_MEMORY as u64 / 8)) as usize;
|
||||
let idx2 = ((*genmem_u64_ptr.offset((i + 1) as isize)).to_be() % (V0_POW_MEMORY as u64 / 8)) as usize;
|
||||
let genmem_u64_at_idx2_ptr = genmem_u64_ptr.offset(idx2 as isize);
|
||||
let tmp = *genmem_u64_at_idx2_ptr;
|
||||
let digest_u64_ptr = digest.as_mut_ptr().offset(idx1 as isize).cast::<u64>();
|
||||
|
@ -93,7 +89,7 @@ fn v0_frankenhash(digest: &mut [u8; 64], genmem_ptr: *mut u8) {
|
|||
|
||||
impl Identity {
|
||||
fn generate_c25519() -> Identity {
|
||||
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
|
||||
let genmem_layout = Layout::from_size_align(V0_POW_MEMORY, 8).unwrap();
|
||||
let genmem_ptr = unsafe { alloc(genmem_layout) };
|
||||
if genmem_ptr.is_null() {
|
||||
panic!("unable to allocate memory for V0 identity generation");
|
||||
|
@ -111,7 +107,7 @@ impl Identity {
|
|||
let mut digest = sha.finish();
|
||||
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
if digest[0] < IDENTITY_V0_POW_THRESHOLD {
|
||||
if digest[0] < V0_POW_THRESHOLD {
|
||||
let addr = Address::from_bytes(&digest[59..64]);
|
||||
if addr.is_some() {
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
|
@ -145,7 +141,7 @@ impl Identity {
|
|||
// ECDSA is a randomized signature algorithm, so each signature will be different.
|
||||
let sig = p521_ecdsa.sign(&sign_buf).unwrap();
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&sig, V1_BALLOON_SALT);
|
||||
if bh[0] < IDENTITY_V1_POW_THRESHOLD {
|
||||
if bh[0] < V1_POW_THRESHOLD {
|
||||
let addr = Address::from_bytes(&bh[43..48]);
|
||||
if addr.is_some() {
|
||||
let p521_ecdh_pub = p521_ecdh.public_key().clone();
|
||||
|
@ -210,7 +206,7 @@ impl Identity {
|
|||
/// to fully validate than V1 identities.
|
||||
pub fn locally_validate(&self) -> bool {
|
||||
if self.v1.is_none() {
|
||||
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
|
||||
let genmem_layout = Layout::from_size_align(V0_POW_MEMORY, 8).unwrap();
|
||||
let genmem_ptr = unsafe { alloc(genmem_layout) };
|
||||
if !genmem_ptr.is_null() {
|
||||
let mut sha = SHA512::new();
|
||||
|
@ -219,7 +215,7 @@ impl Identity {
|
|||
let mut digest = sha.finish();
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
(digest[0] < IDENTITY_V0_POW_THRESHOLD) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
|
||||
(digest[0] < V0_POW_THRESHOLD) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -232,7 +228,7 @@ impl Identity {
|
|||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).1.public_key_bytes());
|
||||
if (*p521).1.verify(&signing_buf, &(*p521).2) {
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2, V1_BALLOON_SALT);
|
||||
(bh[0] < IDENTITY_V1_POW_THRESHOLD) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
|
||||
(bh[0] < V1_POW_THRESHOLD) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -247,12 +243,8 @@ impl Identity {
|
|||
pub fn agree(&self, other_identity: &Identity) -> Option<Secret<48>> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
let c25519_secret = || Secret::<48>(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519).as_ref()));
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
Some(c25519_secret())
|
||||
}, |p521_secret| {
|
||||
other_identity.v1.as_ref().map_or_else(|| {
|
||||
Some(c25519_secret())
|
||||
}, |other_p521_public| {
|
||||
secrets.v1.as_ref().map_or_else(|| Some(c25519_secret()), |p521_secret| {
|
||||
other_identity.v1.as_ref().map_or_else(|| Some(c25519_secret()), |other_p521_public| {
|
||||
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
|
||||
|
@ -285,25 +277,21 @@ impl Identity {
|
|||
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(c25519_sig.to_vec())
|
||||
}, |p521_secret| {
|
||||
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)
|
||||
})
|
||||
})
|
||||
secrets.v1.as_ref().map_or_else(|| Some(c25519_sig.to_vec()), |p521_secret| 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)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -312,34 +300,20 @@ impl Identity {
|
|||
self.v1.as_ref().map_or_else(|| {
|
||||
crate::crypto::c25519::ed25519_verify(&self.ed25519, signature, msg)
|
||||
}, |p521| {
|
||||
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
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get this identity's type.
|
||||
#[inline(always)]
|
||||
pub fn id_type(&self) -> Type {
|
||||
if self.v1.is_some() {
|
||||
Type::P521
|
||||
} else {
|
||||
Type::C25519
|
||||
}
|
||||
}
|
||||
pub fn id_type(&self) -> Type { if self.v1.is_some() { Type::P521 } else { Type::C25519 } }
|
||||
|
||||
/// Returns true if this identity also holds its secret keys.
|
||||
#[inline(always)]
|
||||
pub fn has_secrets(&self) -> bool {
|
||||
self.secrets.is_some()
|
||||
}
|
||||
pub fn has_secrets(&self) -> bool { self.secrets.is_some() }
|
||||
|
||||
/// Erase secrets from this identity object, if present.
|
||||
pub fn forget_secrets(&mut self) {
|
||||
let _ = self.secrets.take();
|
||||
}
|
||||
pub fn forget_secrets(&mut self) { let _ = self.secrets.take(); }
|
||||
|
||||
/// Append this in binary format to a buffer.
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_private: bool) -> std::io::Result<()> {
|
||||
|
@ -598,9 +572,7 @@ impl Eq for Identity {}
|
|||
|
||||
impl PartialOrd for Identity {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
impl Ord for Identity {
|
||||
|
|
|
@ -348,12 +348,8 @@ impl InetAddress {
|
|||
let ip = &*(&self.sin.sin_addr.s_addr as *const u32).cast::<[u8; 4]>();
|
||||
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
||||
}
|
||||
AF_INET6 => {
|
||||
Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string()
|
||||
}
|
||||
_ => {
|
||||
String::from("(null)")
|
||||
}
|
||||
AF_INET6 => Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string(),
|
||||
_ => String::from("(null)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,9 +373,7 @@ impl InetAddress {
|
|||
b[18] = *(&self.sin6.sin6_port as *const u16).cast::<u8>().offset(1);
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
buf.append_u8(0)
|
||||
}
|
||||
_ => buf.append_u8(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -455,7 +449,6 @@ impl FromStr for InetAddress {
|
|||
}
|
||||
|
||||
impl PartialEq for InetAddress {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe {
|
||||
if self.sa.sa_family == other.sa.sa_family {
|
||||
|
|
|
@ -94,13 +94,13 @@ impl Locator {
|
|||
self.subject.marshal(buf)?;
|
||||
self.signer.marshal(buf)?;
|
||||
buf.append_u64(self.timestamp as u64)?;
|
||||
buf.append_varint(self.endpoints.len() as u64);
|
||||
buf.append_varint(self.endpoints.len() as u64)?;
|
||||
for e in self.endpoints.iter() {
|
||||
e.marshal(buf)?;
|
||||
}
|
||||
buf.append_varint(0); // length of any additional fields
|
||||
buf.append_varint(0)?; // length of any additional fields
|
||||
if !exclude_signature {
|
||||
buf.append_varint(self.signature.len() as u64);
|
||||
buf.append_varint(self.signature.len() as u64)?;
|
||||
buf.append_bytes(self.signature.as_slice())?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -11,23 +11,12 @@ pub struct MAC(NonZeroU64);
|
|||
|
||||
impl MAC {
|
||||
#[inline(always)]
|
||||
pub fn from_u64(i: u64) -> Option<MAC> {
|
||||
if i != 0 {
|
||||
Some(MAC(unsafe { NonZeroU64::new_unchecked(i & 0xffffffffffff) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn from_u64(i: u64) -> Option<MAC> { NonZeroU64::new(i & 0xffffffffffff).map_or(None, |i| Some(MAC(i))) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
||||
if b.len() >= 6 {
|
||||
let i = (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;
|
||||
if i != 0 {
|
||||
Some(MAC(unsafe { NonZeroU64::new_unchecked(i) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
NonZeroU64::new((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).map_or(None, |i| Some(MAC(i)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub mod locator;
|
|||
pub mod rootset;
|
||||
|
||||
// These are either only used inside network-hypervisor or are selectively exported below.
|
||||
#[allow(unused)]
|
||||
pub(crate) mod protocol;
|
||||
pub(crate) mod buffer;
|
||||
pub(crate) mod node;
|
||||
|
|
|
@ -86,6 +86,9 @@ pub trait VL1CallerInterface {
|
|||
}
|
||||
|
||||
/// Trait implemented by VL2 to handle messages after they are unwrapped by VL1.
|
||||
///
|
||||
/// This normally isn't used from outside this crate except for testing or if you want to harness VL1
|
||||
/// for some entirely unrelated purpose.
|
||||
pub trait VL1PacketHandler {
|
||||
/// Handle a packet, returning true if the verb was recognized.
|
||||
///
|
||||
|
@ -95,10 +98,10 @@ pub trait VL1PacketHandler {
|
|||
fn handle_packet(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, verb: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool;
|
||||
|
||||
/// Handle errors, returning true if the error was recognized.
|
||||
fn handle_error(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, in_re_verb: u8, in_re_packet_id: PacketID, error_code: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool;
|
||||
fn handle_error(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, in_re_verb: u8, in_re_packet_id: PacketID, error_code: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool;
|
||||
|
||||
/// Handle an OK, returing true if the OK was recognized.
|
||||
fn handle_ok(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, in_re_verb: u8, in_re_packet_id: PacketID, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool;
|
||||
fn handle_ok(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, in_re_verb: u8, in_re_packet_id: PacketID, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -120,8 +123,7 @@ pub struct Node {
|
|||
whois: WhoisQueue,
|
||||
buffer_pool: Pool<Buffer<{ PACKET_SIZE_MAX }>, PooledBufferFactory<{ PACKET_SIZE_MAX }>>,
|
||||
secure_prng: SecureRandom,
|
||||
pub(crate) fips_mode: bool,
|
||||
pub(crate) wimp: bool,
|
||||
fips_mode: bool,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
|
@ -164,19 +166,15 @@ impl Node {
|
|||
buffer_pool: Pool::new(64, PooledBufferFactory),
|
||||
secure_prng: SecureRandom::get(),
|
||||
fips_mode: false,
|
||||
wimp: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get address, short for .identity().address()
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address { self.identity.address() }
|
||||
|
||||
/// Get identity, which includes secret keys.
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> &Identity { &self.identity }
|
||||
|
||||
/// Get this node's current locator or None if no locator created.
|
||||
#[inline(always)]
|
||||
pub fn locator(&self) -> Option<Arc<Locator>> { self.locator.lock().clone() }
|
||||
|
||||
|
@ -187,6 +185,9 @@ impl Node {
|
|||
/// Get a peer by address.
|
||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> { self.peers.get(&a).map(|peer| peer.value().clone()) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fips_mode(&self) -> bool { self.fips_mode }
|
||||
|
||||
/// Get all peers currently in the peer cache.
|
||||
pub fn peers(&self) -> Vec<Arc<Peer>> {
|
||||
let mut v: Vec<Arc<Peer>> = Vec::new();
|
||||
|
@ -319,9 +320,7 @@ impl Node {
|
|||
self.paths.get(ep).map_or_else(|| {
|
||||
let p = Arc::new(Path::new(ep.clone(), local_socket, local_interface));
|
||||
self.paths.insert(ep.clone(), p.clone()).unwrap_or(p) // if another thread added one, return that instead
|
||||
}, |path| {
|
||||
path.value().clone()
|
||||
})
|
||||
}, |path| path.value().clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ impl Path {
|
|||
local_interface,
|
||||
last_send_time_ticks: AtomicI64::new(0),
|
||||
last_receive_time_ticks: AtomicI64::new(0),
|
||||
fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(8, U64PassThroughHasher::new())),
|
||||
fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(4, U64PassThroughHasher::new())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use aes_gmac_siv::{AesCtr, AesGmacSiv};
|
|||
|
||||
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
||||
use crate::crypto::c25519::C25519KeyPair;
|
||||
use crate::crypto::hash::SHA384;
|
||||
use crate::crypto::hash::{SHA384, SHA384_HASH_SIZE};
|
||||
use crate::crypto::kbkdf::zt_kbkdf_hmac_sha384;
|
||||
use crate::crypto::p521::P521KeyPair;
|
||||
use crate::crypto::poly1305::Poly1305;
|
||||
|
@ -31,14 +31,10 @@ struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
|
|||
|
||||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||
#[inline(always)]
|
||||
fn create(&self) -> AesGmacSiv {
|
||||
AesGmacSiv::new(&self.0.0[0..32], &self.1.0[0..32])
|
||||
}
|
||||
fn create(&self) -> AesGmacSiv { AesGmacSiv::new(&self.0.0[0..32], &self.1.0[0..32]) }
|
||||
|
||||
#[inline(always)]
|
||||
fn reset(&self, obj: &mut AesGmacSiv) {
|
||||
obj.reset();
|
||||
}
|
||||
fn reset(&self, obj: &mut AesGmacSiv) { obj.reset(); }
|
||||
}
|
||||
|
||||
struct PeerSecret {
|
||||
|
@ -92,7 +88,7 @@ pub struct Peer {
|
|||
// Either None or the current ephemeral key pair whose public keys are on offer.
|
||||
ephemeral_pair: Mutex<Option<EphemeralKeyPair>>,
|
||||
|
||||
// Paths sorted in ascending order of quality / preference.
|
||||
// Paths sorted in descending order of quality / preference.
|
||||
paths: Mutex<Vec<Arc<Path>>>,
|
||||
|
||||
// Local external address most recently reported by this peer (IP transport only).
|
||||
|
@ -144,8 +140,7 @@ fn salsa_poly_create(secret: &PeerSecret, header: &PacketHeader, packet_size: us
|
|||
let mut salsa = Salsa::new(&key.0[0..32], header.id_bytes(), true).unwrap();
|
||||
let mut poly1305_key = [0_u8; 32];
|
||||
salsa.crypt_in_place(&mut poly1305_key);
|
||||
let mut poly = Poly1305::new(&poly1305_key).unwrap();
|
||||
(salsa, poly)
|
||||
(salsa, Poly1305::new(&poly1305_key).unwrap())
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
|
@ -192,9 +187,7 @@ impl Peer {
|
|||
|
||||
/// Get the next packet ID / IV.
|
||||
#[inline(always)]
|
||||
pub(crate) fn next_packet_id(&self) -> PacketID {
|
||||
self.packet_id_counter.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
pub(crate) fn next_packet_id(&self) -> PacketID { self.packet_id_counter.fetch_add(1, Ordering::Relaxed) }
|
||||
|
||||
/// Receive, decrypt, authenticate, and process an incoming packet from this peer.
|
||||
/// If the packet comes in multiple fragments, the fragments slice should contain all
|
||||
|
@ -287,15 +280,34 @@ impl Peer {
|
|||
self.total_bytes_received.fetch_add((payload.len() + PACKET_HEADER_SIZE) as u64, Ordering::Relaxed);
|
||||
|
||||
let _ = payload.u8_at(0).map(|verb| {
|
||||
let mut extended_authentication = false;
|
||||
if (verb & VERB_FLAG_EXTENDED_AUTHENTICATION) != 0 {
|
||||
let auth_bytes = payload.as_bytes();
|
||||
if auth_bytes.len() >= (1 + SHA384_HASH_SIZE) {
|
||||
let packet_hmac_start = auth_bytes.len() - SHA384_HASH_SIZE;
|
||||
if !SHA384::hmac(self.static_secret_packet_hmac.as_ref(), &auth_bytes[1..packet_hmac_start]).eq(&auth_bytes[packet_hmac_start..]) {
|
||||
return;
|
||||
}
|
||||
extended_authentication = true;
|
||||
unsafe { payload.set_size(payload.len() - SHA384_HASH_SIZE) };
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (verb & VERB_FLAG_COMPRESSED) != 0 {
|
||||
}
|
||||
|
||||
let verb = verb & VERB_MASK;
|
||||
|
||||
// For performance reasons we let VL2 handle packets first. It returns false
|
||||
// if it didn't handle the packet, in which case it's handled at VL1.
|
||||
let verb = verb & VERB_MASK;
|
||||
if !ph.handle_packet(self, source_path, forward_secrecy, verb, &payload) {
|
||||
match verb {
|
||||
//VERB_VL1_NOP => {}
|
||||
VERB_VL1_HELLO => self.receive_hello(ci, node, time_ticks, source_path, &payload),
|
||||
VERB_VL1_ERROR => self.receive_error(ci, node, time_ticks, source_path, &payload),
|
||||
VERB_VL1_OK => self.receive_ok(ci, node, time_ticks, source_path, &payload),
|
||||
VERB_VL1_ERROR => self.receive_error(ci, ph, node, time_ticks, source_path, forward_secrecy, &payload),
|
||||
VERB_VL1_OK => self.receive_ok(ci, ph, node, time_ticks, source_path, forward_secrecy, &payload),
|
||||
VERB_VL1_WHOIS => self.receive_whois(ci, node, time_ticks, source_path, &payload),
|
||||
VERB_VL1_RENDEZVOUS => self.receive_rendezvous(ci, node, time_ticks, source_path, &payload),
|
||||
VERB_VL1_ECHO => self.receive_echo(ci, node, time_ticks, source_path, &payload),
|
||||
|
@ -406,7 +418,7 @@ impl Peer {
|
|||
header.flags_cipher_hops = CIPHER_NOCRYPT_POLY1305;
|
||||
}).is_ok());
|
||||
debug_assert!(packet.append_and_init_struct(|header: &mut message_component_structs::HelloFixedHeaderFields| {
|
||||
header.verb = VERB_VL1_HELLO | VERB_FLAG_HMAC;
|
||||
header.verb = VERB_VL1_HELLO | VERB_FLAG_EXTENDED_AUTHENTICATION;
|
||||
header.version_proto = VERSION_PROTO;
|
||||
header.version_major = VERSION_MAJOR;
|
||||
header.version_minor = VERSION_MINOR;
|
||||
|
@ -456,12 +468,9 @@ impl Peer {
|
|||
dict.set_str(HELLO_DICT_KEY_OS_NAME, std::env::consts::OS);
|
||||
}
|
||||
let mut flags = String::new();
|
||||
if node.fips_mode {
|
||||
if node.fips_mode() {
|
||||
flags.push('F');
|
||||
}
|
||||
if node.wimp {
|
||||
flags.push('w');
|
||||
}
|
||||
dict.set_str(HELLO_DICT_KEY_FLAGS, flags.as_str());
|
||||
debug_assert!(dict.write_to(&mut packet).is_ok());
|
||||
|
||||
|
@ -470,7 +479,9 @@ impl Peer {
|
|||
dict_aes.crypt_in_place(&mut packet.as_bytes_mut()[dict_start_position..]);
|
||||
drop(dict_aes);
|
||||
|
||||
debug_assert!(packet.append_bytes_fixed(&SHA384::hmac(self.static_secret_packet_hmac.as_ref(), &packet.as_bytes()[PACKET_HEADER_SIZE + 1..])).is_ok());
|
||||
debug_assert!(packet.append_u16(0).is_ok());
|
||||
|
||||
debug_assert!(packet.append_bytes_fixed(&SHA384::hmac(self.static_secret_packet_hmac.as_ref(), packet.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap())).is_ok());
|
||||
|
||||
let (_, mut poly) = salsa_poly_create(&self.static_secret, packet.struct_at::<PacketHeader>(0).unwrap(), packet.len());
|
||||
poly.update(packet.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
|
||||
|
@ -497,10 +508,48 @@ impl Peer {
|
|||
fn receive_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_error<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_error<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
let mut cursor: usize = 0;
|
||||
let _ = payload.read_struct::<message_component_structs::ErrorHeader>(&mut cursor).map(|error_header| {
|
||||
let in_re_packet_id = error_header.in_re_packet_id;
|
||||
let current_packet_id_counter = self.packet_id_counter.load(Ordering::Relaxed);
|
||||
if current_packet_id_counter.checked_sub(in_re_packet_id).map_or_else(|| {
|
||||
(!in_re_packet_id).wrapping_add(current_packet_id_counter) < PACKET_RESPONSE_COUNTER_DELTA_MAX
|
||||
}, |packets_ago| {
|
||||
packets_ago <= PACKET_RESPONSE_COUNTER_DELTA_MAX
|
||||
}) {
|
||||
match error_header.in_re_verb {
|
||||
_ => {
|
||||
ph.handle_error(self, source_path, forward_secrecy, error_header.in_re_verb, in_re_packet_id, error_header.error_code, payload, &mut cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_ok<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_ok<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
let mut cursor: usize = 0;
|
||||
let _ = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor).map(|ok_header| {
|
||||
let in_re_packet_id = ok_header.in_re_packet_id;
|
||||
let current_packet_id_counter = self.packet_id_counter.load(Ordering::Relaxed);
|
||||
if current_packet_id_counter.checked_sub(in_re_packet_id).map_or_else(|| {
|
||||
(!in_re_packet_id).wrapping_add(current_packet_id_counter) < PACKET_RESPONSE_COUNTER_DELTA_MAX
|
||||
}, |packets_ago| {
|
||||
packets_ago <= PACKET_RESPONSE_COUNTER_DELTA_MAX
|
||||
}) {
|
||||
match ok_header.in_re_verb {
|
||||
VERB_VL1_HELLO => {
|
||||
}
|
||||
VERB_VL1_WHOIS => {
|
||||
}
|
||||
_ => {
|
||||
ph.handle_ok(self, source_path, forward_secrecy, ok_header.in_re_verb, in_re_packet_id, payload, &mut cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_whois<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
@ -518,9 +567,7 @@ impl Peer {
|
|||
fn receive_user_message<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
/// Get current best path or None if there are no direct paths to this peer.
|
||||
pub fn direct_path(&self) -> Option<Arc<Path>> {
|
||||
self.paths.lock().last().map(|p| p.clone())
|
||||
}
|
||||
pub fn direct_path(&self) -> Option<Arc<Path>> { self.paths.lock().first().map(|p| p.clone()) }
|
||||
|
||||
/// Get either the current best direct path or an indirect path.
|
||||
pub fn path(&self, node: &Node) -> Option<Arc<Path>> {
|
||||
|
|
|
@ -131,8 +131,8 @@ pub const FRAGMENT_INDICATOR: u8 = 0xff;
|
|||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
||||
|
||||
/// Verb (inner) flag indicating that payload after verb is authenticated with HMAC-SHA384.
|
||||
pub const VERB_FLAG_HMAC: u8 = 0x40;
|
||||
/// Verb (inner) flag indicating that payload is authenticated with HMAC-SHA384.
|
||||
pub const VERB_FLAG_EXTENDED_AUTHENTICATION: u8 = 0x40;
|
||||
|
||||
/// Mask to get only the verb from the verb + verb flags byte.
|
||||
pub const VERB_MASK: u8 = 0x1f;
|
||||
|
@ -146,8 +146,8 @@ pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
|||
/// Maximum number of hops to allow.
|
||||
pub const FORWARD_MAX_HOPS: u8 = 3;
|
||||
|
||||
/// Maximum difference between an OK in-re packet ID and the current packet ID counter.
|
||||
pub const OK_PACKET_SEQUENCE_CUTOFF: u64 = 1000;
|
||||
/// Maximum difference between current packet ID counter and OK/ERROR in-re packet ID.
|
||||
pub const PACKET_RESPONSE_COUNTER_DELTA_MAX: u64 = 1024;
|
||||
|
||||
/// Frequency for WHOIS retries
|
||||
pub const WHOIS_RETRY_INTERVAL: i64 = 1000;
|
||||
|
@ -267,6 +267,24 @@ impl FragmentHeader {
|
|||
|
||||
pub(crate) mod message_component_structs {
|
||||
use crate::vl1::buffer::RawObject;
|
||||
use crate::vl1::protocol::PacketID;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct OkHeader {
|
||||
pub in_re_verb: u8,
|
||||
pub in_re_packet_id: PacketID,
|
||||
}
|
||||
|
||||
unsafe impl RawObject for OkHeader {}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct ErrorHeader {
|
||||
pub in_re_verb: u8,
|
||||
pub in_re_packet_id: PacketID,
|
||||
pub error_code: u8,
|
||||
}
|
||||
|
||||
unsafe impl RawObject for ErrorHeader {}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HelloFixedHeaderFields {
|
||||
|
@ -300,6 +318,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn representation() {
|
||||
assert_eq!(size_of::<message_component_structs::OkHeader>(), 9);
|
||||
assert_eq!(size_of::<message_component_structs::ErrorHeader>(), 10);
|
||||
assert_eq!(size_of::<PacketHeader>(), PACKET_HEADER_SIZE);
|
||||
assert_eq!(size_of::<FragmentHeader>(), FRAGMENT_HEADER_SIZE);
|
||||
|
||||
|
|
|
@ -103,10 +103,10 @@ pub struct Root {
|
|||
/// as at least one of the old roots is up to distribute the new ones.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct RootSet {
|
||||
pub timestamp: i64,
|
||||
pub name: String,
|
||||
pub contact: String,
|
||||
pub roots: BTreeSet<Root>,
|
||||
timestamp: i64,
|
||||
name: String,
|
||||
contact: String,
|
||||
roots: BTreeSet<Root>,
|
||||
signer: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
root_set_type: Type,
|
||||
|
@ -185,9 +185,9 @@ impl RootSet {
|
|||
buf.append_u8(ROOT_SET_TYPE_ED25519_P521)?;
|
||||
buf.append_u64(self.timestamp as u64)?;
|
||||
buf.append_u8(name.len() as u8)?;
|
||||
buf.append_bytes(name);
|
||||
buf.append_bytes(name)?;
|
||||
buf.append_u8(contact.len() as u8)?;
|
||||
buf.append_bytes(contact);
|
||||
buf.append_bytes(contact)?;
|
||||
if self.signer.len() != (ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE) {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "signer can only be 164 bytes"));
|
||||
}
|
||||
|
|
|
@ -19,21 +19,15 @@ struct WhoisQueueItem {
|
|||
retry_count: u16,
|
||||
}
|
||||
|
||||
pub(crate) struct WhoisQueue {
|
||||
queue: Mutex<HashMap<Address, WhoisQueueItem>>
|
||||
}
|
||||
pub(crate) struct WhoisQueue(Mutex<HashMap<Address, WhoisQueueItem>>);
|
||||
|
||||
impl WhoisQueue {
|
||||
pub(crate) const INTERVAL: i64 = WHOIS_RETRY_INTERVAL;
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
queue: Mutex::new(HashMap::new())
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self { Self(Mutex::new(HashMap::new())) }
|
||||
|
||||
pub fn query<CI: VL1CallerInterface>(&self, node: &Node, ci: &CI, target: Address, packet: Option<QueuedPacket>) {
|
||||
let mut q = self.queue.lock();
|
||||
let mut q = self.0.lock();
|
||||
|
||||
let qi = q.entry(target).or_insert_with(|| WhoisQueueItem {
|
||||
packet_queue: LinkedList::new(),
|
||||
|
@ -55,14 +49,14 @@ impl WhoisQueue {
|
|||
|
||||
/// Remove a WHOIS request from the queue and call the supplied function for all queued packets.
|
||||
pub fn response_received_get_packets<F: FnMut(&mut QueuedPacket)>(&self, address: Address, packet_handler: F) {
|
||||
let mut qi = self.queue.lock().remove(&address);
|
||||
let mut qi = self.0.lock().remove(&address);
|
||||
let _ = qi.map(|mut qi| qi.packet_queue.iter_mut().for_each(packet_handler));
|
||||
}
|
||||
|
||||
/// Called every INTERVAL during background tasks.
|
||||
pub fn on_interval<CI: VL1CallerInterface>(&self, node: &Node, ci: &CI, time_ticks: i64) {
|
||||
let mut targets: Vec<Address> = Vec::new();
|
||||
self.queue.lock().retain(|target, qi| {
|
||||
self.0.lock().retain(|target, qi| {
|
||||
if qi.retry_count < WHOIS_RETRY_MAX {
|
||||
if qi.retry_gate.gate(time_ticks) {
|
||||
qi.retry_count += 1;
|
||||
|
|
Loading…
Add table
Reference in a new issue