diff --git a/vli/Cargo.lock b/attic/vli/Cargo.lock similarity index 100% rename from vli/Cargo.lock rename to attic/vli/Cargo.lock diff --git a/vli/Cargo.toml b/attic/vli/Cargo.toml similarity index 86% rename from vli/Cargo.toml rename to attic/vli/Cargo.toml index 021a7a1c9..63f5e8814 100644 --- a/vli/Cargo.toml +++ b/attic/vli/Cargo.toml @@ -3,7 +3,7 @@ name = "vli" version = "0.1.0" edition = "2018" -[profile.test] +[profile.release] opt-level = 3 lto = true codegen-units = 1 diff --git a/vli/src/lib.rs b/attic/vli/src/lib.rs similarity index 82% rename from vli/src/lib.rs rename to attic/vli/src/lib.rs index d4ef3302d..ae79d8c6e 100644 --- a/vli/src/lib.rs +++ b/attic/vli/src/lib.rs @@ -13,6 +13,7 @@ const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', /// This also means the size of a VLI must be a multiple of 64 bits. Note that the actual /// integer in it need not be exactly that length, just the capacity of the container. #[derive(Clone, PartialEq, Eq)] +#[repr(transparent)] pub struct VLI { n: [u64; LIMBS] } @@ -31,6 +32,14 @@ impl VLI { Self { n: [0_u64; LIMBS ]} } + /// Get an integer with an undefined value. + /// This can be used in high performance code to avoid zeroing on new() when the + /// result will be overwritten right away by an operation. + #[inline(always)] + pub unsafe fn uninit() -> Self { + MaybeUninit::>::uninit().assume_init() + } + /// Set to zero. #[inline(always)] pub fn zero(&mut self) { @@ -99,18 +108,33 @@ impl VLI { carry } - /// Multiply two inputs half the size of this integer to yield a full size result in this integer.. + /// Subtract from this integer and return any remaining borrow bits. + pub fn sub_assign_borrow(&mut self, rhs: &Self) -> u64 { + let mut borrow = 0_u64; + for i in 0..LIMBS { + let left_ptr = unsafe { self.n.get_unchecked_mut(i) }; + let left = *left_ptr; + let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow; + borrow = (diff > left) as u64; + *left_ptr = diff; + } + borrow + } + + /// Multiply two inputs half the size of this integer to yield a full size result in this integer. /// The multiplicand sizes MULT_LIMBS must be one half the LIMBS size of this integer. /// This is checked with an assertion. This isn't computed with the type system due /// to current limitations in const generics. - pub fn mul_extend_assign(&mut self, lhs: &VLI<{ MULT_LIMBS }>, rhs: &VLI<{ MULT_LIMBS }>) { + pub fn mul_assign_widening(&mut self, lhs: &VLI<{ MULT_LIMBS }>, rhs: &VLI<{ MULT_LIMBS }>) { assert_eq!(MULT_LIMBS, LIMBS / 2); let mut r01 = 0_u128; let mut r2 = 0_u64; let mut k = 0_usize; while k < MULT_LIMBS { for i in 0..k { - let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128); + debug_assert!(i < MULT_LIMBS); + debug_assert!((k - i) < MULT_LIMBS); + let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128); r01 += l_product; r2 += (r01 < l_product) as u64; } @@ -120,12 +144,12 @@ impl VLI { k += 1; } while k < (LIMBS - 1) { - let mut i = (k + 1) - MULT_LIMBS; - while i < k && i < MULT_LIMBS { - let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128); + for i in ((k + 1) - MULT_LIMBS)..k { + debug_assert!(i < MULT_LIMBS); + debug_assert!((k - i) < MULT_LIMBS); + let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128); r01 += l_product; r2 += (r01 < l_product) as u64; - i += 1; } *unsafe { self.n.get_unchecked_mut(k) } = r01 as u64; r01 += (r01 >> 64) | ((r2 as u128) << 64); @@ -139,14 +163,14 @@ impl VLI { /// If skip_leading_zeroes is true the returned byte vector will be the minimum size /// needed to hold the integer, or empty if it is zero. Otherwise it will always be /// LIMBS * 8 bytes in length. - pub fn to_vec(&self, skip_leading_zeroes: bool) -> Vec { + pub fn to_be_bytes(&self, skip_leading_zeroes: bool) -> Vec { let mut bytes: Vec = Vec::new(); bytes.reserve(LIMBS * 8); let mut i = LIMBS as isize - 1; if skip_leading_zeroes { while i >= 0 { - let x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; + let x: u64 = self.n[i as usize]; if x != 0 { let x = x.to_be_bytes(); for j in 0..8 { @@ -161,7 +185,7 @@ impl VLI { } } while i >= 0 { - let _ = bytes.write_all(&(unsafe { self.n.get_unchecked(i as usize) }.to_be_bytes())); + let _ = bytes.write_all(&self.n[i as usize].to_be_bytes()); i -= 1; } @@ -178,7 +202,7 @@ impl VLI { let mut i = LIMBS as isize - 1; if skip_leading_zeroes { while i >= 0 { - let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; + let mut x = self.n[i as usize]; if x != 0 { let mut j = 0; while j < 16 { @@ -199,7 +223,7 @@ impl VLI { } } while i >= 0 { - let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; + let mut x: u64 = self.n[i as usize]; for _ in 0..16 { s.push(HEX_CHARS[(x >> 60) as usize]); x <<= 4; @@ -220,21 +244,15 @@ impl Add<&Self> for VLI { #[inline(always)] fn add(mut self, rhs: &Self) -> Self::Output { - self.add_assign(rhs); + let _ = self.add_assign_carry(rhs); self } } impl AddAssign<&Self> for VLI { + #[inline(always)] fn add_assign(&mut self, rhs: &Self) { - let mut carry = 0_u64; - for i in 0..LIMBS { - let left_ptr = unsafe { self.n.get_unchecked_mut(i) }; - let left = *left_ptr; - let sum = left + *unsafe { rhs.n.get_unchecked(i) } + carry; - carry = (sum < left) as u64; - *left_ptr = sum; - } + let _ = self.add_assign_carry(rhs); } } @@ -243,21 +261,15 @@ impl Sub<&Self> for VLI { #[inline(always)] fn sub(mut self, rhs: &Self) -> Self::Output { - self.sub_assign(rhs); + let _ = self.sub_assign_borrow(rhs); self } } impl SubAssign<&Self> for VLI { + #[inline(always)] fn sub_assign(&mut self, rhs: &Self) { - let mut borrow = 0_u64; - for i in 0..LIMBS { - let left_ptr = unsafe { self.n.get_unchecked_mut(i) }; - let left = *left_ptr; - let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow; - borrow = (diff > left) as u64; - *left_ptr = diff; - } + let _ = self.sub_assign_borrow(rhs); } } @@ -274,7 +286,13 @@ impl Shl for VLI { impl ShlAssign for VLI { fn shl_assign(&mut self, rhs: usize) { if rhs != 0 { - if rhs < 64 { + if rhs < (LIMBS * 64) { + let whole_limb_shifts = rhs >> 6; + if whole_limb_shifts != 0 { + self.n.copy_within(0..(LIMBS - whole_limb_shifts), whole_limb_shifts); + self.n[0..whole_limb_shifts].fill(0); + } + let rhs = rhs & 63; let mut carry = 0_u64; for i in 0..LIMBS { let x_ptr = unsafe { self.n.get_unchecked_mut(i) }; @@ -302,7 +320,13 @@ impl Shr for VLI { impl ShrAssign for VLI { fn shr_assign(&mut self, rhs: usize) { if rhs != 0 { - if rhs < 64 { + if rhs < (LIMBS * 64) { + let whole_limb_shifts = rhs >> 6; + if whole_limb_shifts != 0 { + self.n.copy_within(whole_limb_shifts..LIMBS, 0); + self.n[(LIMBS - whole_limb_shifts)..LIMBS].fill(0); + } + let rhs = rhs & 63; let mut carry = 0_u64; let mut i = LIMBS as isize - 1; while i >= 0 { diff --git a/network-hypervisor/Cargo.toml b/network-hypervisor/Cargo.toml index 154ebf4a3..a7d893557 100644 --- a/network-hypervisor/Cargo.toml +++ b/network-hypervisor/Cargo.toml @@ -3,6 +3,17 @@ name = "zerotier-network-hypervisor" version = "2.0.0" edition = "2018" +# [profile.test] +# opt-level = 3 +# lto = true +# codegen-units = 1 + +[profile.release] +lto = true +opt-level = 'z' +codegen-units = 1 +panic = 'abort' + [dependencies] rand_core = "^0" aes-gmac-siv = { path = "../aes-gmac-siv" } diff --git a/network-hypervisor/src/crypto/balloon.rs b/network-hypervisor/src/crypto/balloon.rs new file mode 100644 index 000000000..3c3ee5c32 --- /dev/null +++ b/network-hypervisor/src/crypto/balloon.rs @@ -0,0 +1,126 @@ +use std::convert::TryInto; +use std::mem::MaybeUninit; + +#[inline(always)] +fn hash_int_le(sha: &mut crate::crypto::hash::SHA512, i: u64) { + #[cfg(target_endian = "big")] { + sha.update(i.to_le_bytes()); + } + #[cfg(target_endian = "little")] { + sha.update(unsafe { &*(&i as *const u64).cast::<[u8; 8]>() }); + } +} + +/// Compute balloon memory-hard hash using SHA-512. +/// SPACE_COST must be a multiple of 64. This is checked with an assertion. +/// DELTA is usually 3. +pub fn hash(password: &[u8], salt: &[u8]) -> [u8; crate::crypto::hash::SHA512_HASH_SIZE] { + assert_ne!(SPACE_COST, 0); + assert_eq!((SPACE_COST % 64), 0); + + let mut buf: [u8; SPACE_COST] = unsafe { MaybeUninit::uninit().assume_init() }; + let zero64 = [0_u8; 8]; + + /* Initial hash */ + let mut sha = crate::crypto::hash::SHA512::new(); + sha.update(&zero64); // 0 cnt + sha.update(password); + sha.update(salt); + buf[0..64].copy_from_slice(sha.finish_get_ref()); + + /* Expand */ + let mut cnt = 1_u64; + let mut s: usize = 64; + while s < SPACE_COST { + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(&buf[(s - 64)..s]); + let ss = s + 64; + buf[s..ss].copy_from_slice(sha.finish_get_ref()); + s = ss; + cnt += 1; + } + + /* Mix */ + for t in 0..TIME_COST { + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(&buf[(SPACE_COST - 64)..SPACE_COST]); // "previous" initially wraps back around to end + sha.update(&buf[0..64]); + buf[0..64].copy_from_slice(sha.finish_get_ref()); + cnt += 1; + + for i in 0..DELTA { + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(salt); + hash_int_le(&mut sha, t as u64); + sha.update(&zero64); // s == 0 + hash_int_le(&mut sha, i as u64); + cnt += 1; + + let other = sha.finish_get_ref(); + let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize; + + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(&buf[0..64]); + sha.update(&buf[other..(other + 64)]); + buf[0..64].copy_from_slice(sha.finish_get_ref()); + cnt += 1; + } + + let mut s = 64; + while s < SPACE_COST { + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(&buf[(s - 64)..s]); + let ss = s + 64; + sha.update(&buf[s..ss]); + buf[s..ss].copy_from_slice(sha.finish_get_ref()); + cnt += 1; + + for i in 0..DELTA { + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(salt); + hash_int_le(&mut sha, t as u64); + hash_int_le(&mut sha, s as u64); + hash_int_le(&mut sha, i as u64); + cnt += 1; + + let other = sha.finish_get_ref(); + let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize; + + sha.reset(); + hash_int_le(&mut sha, cnt); + sha.update(&buf[s..ss]); + sha.update(&buf[other..(other + 64)]); + buf[s..ss].copy_from_slice(sha.finish_get_ref()); + cnt += 1; + } + + s = ss; + } + } + + /* Extract */ + buf[(SPACE_COST - 64)..SPACE_COST].try_into().unwrap() +} + +/* +#[cfg(test)] +mod tests { + #[test] + fn balloon_test() { + let start = std::time::SystemTime::now(); + let mut tmp = 0_u8; + for _ in 0..100 { + let foo = crate::crypto::balloon::hash::<16384, 3, 3>(&[1_u8], &[2_u8]); + tmp = tmp.wrapping_add(foo[0]); + } + let duration = std::time::SystemTime::now().duration_since(start).unwrap(); + println!("Benchmark: {}ms per hash (junk to prevent optimizing out: {})", (duration.as_nanos() as f64 / 100.0) / 1000000.0, tmp); + } +} +*/ diff --git a/network-hypervisor/src/crypto/c25519.rs b/network-hypervisor/src/crypto/c25519.rs index 8ae85c288..f7b1c8d37 100644 --- a/network-hypervisor/src/crypto/c25519.rs +++ b/network-hypervisor/src/crypto/c25519.rs @@ -15,14 +15,14 @@ pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey); impl C25519KeyPair { #[inline(always)] - pub fn new() -> C25519KeyPair { + pub fn generate() -> C25519KeyPair { let sk = x25519_dalek::StaticSecret::new(rand_core::OsRng); let pk = x25519_dalek::PublicKey::from(&sk); C25519KeyPair(sk, pk) } #[inline(always)] - pub fn from_keys(public_key: &[u8], secret_key: &[u8]) -> Option { + pub fn from_bytes(public_key: &[u8], secret_key: &[u8]) -> Option { if public_key.len() == 32 && secret_key.len() == 32 { let pk: [u8; 32] = public_key.try_into().unwrap(); let sk: [u8; 32] = secret_key.try_into().unwrap(); @@ -59,7 +59,7 @@ pub struct Ed25519KeyPair(ed25519_dalek::Keypair); impl Ed25519KeyPair { #[inline(always)] - pub fn new() -> Ed25519KeyPair { + pub fn generate() -> Ed25519KeyPair { let mut rng = rand_core::OsRng::default(); Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng)) } @@ -72,6 +72,18 @@ impl Ed25519KeyPair { Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap()) } + #[inline(always)] + pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option { + if public_bytes.len() == ED25519_PUBLIC_KEY_SIZE && secret_bytes.len() == ED25519_SECRET_KEY_SIZE { + let mut tmp = [0_u8; 64]; + tmp[0..32].copy_from_slice(public_bytes); + tmp[32..64].copy_from_slice(secret_bytes); + Some(Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap())) + } else { + None + } + } + #[inline(always)] pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] { self.0.public.to_bytes() diff --git a/network-hypervisor/src/crypto/mod.rs b/network-hypervisor/src/crypto/mod.rs index a44d8d9c9..ba3cde81e 100644 --- a/network-hypervisor/src/crypto/mod.rs +++ b/network-hypervisor/src/crypto/mod.rs @@ -3,3 +3,4 @@ pub mod hash; pub mod p521; pub mod salsa; pub mod poly1305; +pub mod balloon; diff --git a/network-hypervisor/src/crypto/p521.rs b/network-hypervisor/src/crypto/p521.rs index 72ead04a4..466282f19 100644 --- a/network-hypervisor/src/crypto/p521.rs +++ b/network-hypervisor/src/crypto/p521.rs @@ -66,7 +66,7 @@ pub struct P521PublicKey { pub struct P521KeyPair { public_key: P521PublicKey, secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression - secret_key_for_ecdh: SExpression, // secret key as a "data" S-expression for the weird gcrypt ECDH interface + secret_key_for_ecdh: SExpression, // the same secret key as a "data" S-expression for the weird gcrypt ECDH interface secret_key_bytes: [u8; P521_SECRET_KEY_SIZE], } @@ -97,7 +97,7 @@ impl P521KeyPair { public_key: pk_exp, public_key_bytes: [0_u8; P521_PUBLIC_KEY_SIZE], }, - secret_key_for_ecdsa: sk_exp, + secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #{}#)(d #{}#)))", crate::util::hex::to_string(pk), crate::util::hex::to_string(sk)).as_str()).unwrap(), secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(sk)).as_str()).unwrap(), secret_key_bytes: [0_u8; P521_SECRET_KEY_SIZE], }; @@ -111,6 +111,23 @@ impl P521KeyPair { }) } + /// Construct this key pair from both a public and a private key. + pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option { + if secret_bytes.len() != P521_SECRET_KEY_SIZE { + return None; + } + let public_key = P521PublicKey::from_bytes(public_bytes); + if public_key.is_none() { + return None; + } + Some(P521KeyPair { + public_key: public_key.unwrap(), + secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #04{}#)(d #{}#)))", crate::util::hex::to_string(public_bytes), crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(), + secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(), + secret_key_bytes: secret_bytes.try_into().unwrap(), + }) + } + #[inline(always)] pub fn public_key(&self) -> &P521PublicKey { &self.public_key @@ -161,6 +178,9 @@ impl P521KeyPair { } impl P521PublicKey { + /// Construct a public key from a byte serialized representation. + /// None is returned if the input is not valid. No advanced checking such as + /// determining if this is a point on the curve is performed. pub fn from_bytes(b: &[u8]) -> Option { if b.len() == P521_PUBLIC_KEY_SIZE { Some(P521PublicKey { @@ -188,10 +208,11 @@ impl P521PublicKey { pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] { &self.public_key_bytes } +} - #[inline(always)] - pub fn as_bytes(&self) -> [u8; P521_PUBLIC_KEY_SIZE] { - self.public_key_bytes.clone() +impl Clone for P521PublicKey { + fn clone(&self) -> Self { + P521PublicKey::from_bytes(&self.public_key_bytes).unwrap() } } @@ -217,5 +238,11 @@ mod tests { if !sec0.eq(&sec1) { panic!("ECDH secrets do not match"); } + + let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes()).unwrap(); + let sig = kp3.sign(&[3_u8]).unwrap(); + if !kp.public_key().verify(&[3_u8], &sig) { + panic!("ECDSA verify failed (from key reconstructed from bytes)"); + } } } diff --git a/network-hypervisor/src/util/mod.rs b/network-hypervisor/src/util/mod.rs index 05eadb521..c9cb9ac43 100644 --- a/network-hypervisor/src/util/mod.rs +++ b/network-hypervisor/src/util/mod.rs @@ -40,3 +40,5 @@ pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 { pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 { (d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64) } + +pub(crate) const ZEROES: [u8; 64] = [0_u8; 64]; diff --git a/network-hypervisor/src/vl1/buffer.rs b/network-hypervisor/src/vl1/buffer.rs index 3dd4619d3..6a13d6e87 100644 --- a/network-hypervisor/src/vl1/buffer.rs +++ b/network-hypervisor/src/vl1/buffer.rs @@ -118,7 +118,7 @@ impl Buffer { /// Append a dynamic byte slice (copy into buffer). /// Use append_and_init_ functions if possible as these avoid extra copies. #[inline(always)] - fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> { + pub fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> { let ptr = self.0; let end = ptr + buf.len(); if end <= L { @@ -133,7 +133,7 @@ impl Buffer { /// Append a fixed length byte array (copy into buffer). /// Use append_and_init_ functions if possible as these avoid extra copies. #[inline(always)] - fn append_bytes_fixed(&mut self, buf: &[u8; S]) -> std::io::Result<()> { + pub fn append_bytes_fixed(&mut self, buf: &[u8; S]) -> std::io::Result<()> { let ptr = self.0; let end = ptr + S; if end <= L { @@ -147,7 +147,7 @@ impl Buffer { /// Append a byte #[inline(always)] - fn append_u8(&mut self, i: u8) -> std::io::Result<()> { + pub fn append_u8(&mut self, i: u8) -> std::io::Result<()> { let ptr = self.0; if ptr < L { self.0 = ptr + 1; @@ -160,7 +160,7 @@ impl Buffer { /// Append a 16-bit integer (in big-endian form) #[inline(always)] - fn append_u16(&mut self, i: u16) -> std::io::Result<()> { + pub fn append_u16(&mut self, i: u16) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 2; if end <= L { @@ -174,7 +174,7 @@ impl Buffer { /// Append a 32-bit integer (in big-endian form) #[inline(always)] - fn append_u32(&mut self, i: u32) -> std::io::Result<()> { + pub fn append_u32(&mut self, i: u32) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 4; if end <= L { @@ -188,7 +188,7 @@ impl Buffer { /// Append a 64-bit integer (in big-endian form) #[inline(always)] - fn append_u64(&mut self, i: u64) -> std::io::Result<()> { + pub fn append_u64(&mut self, i: u64) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 8; if end <= L { @@ -200,6 +200,12 @@ impl Buffer { } } + /// Get the index of the start of the payload after the header. + #[inline(always)] + pub fn cursor_after_header(&self) -> usize { + size_of::() + } + /// Get a structure at a given position in the buffer and advance the cursor. #[inline(always)] pub fn get_struct(&self, cursor: &mut usize) -> std::io::Result<&T> { diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index c23e189d0..41cf5d1a2 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -1,9 +1,20 @@ +use std::alloc::{Layout, dealloc, alloc}; +use std::ptr::{slice_from_raw_parts_mut, slice_from_raw_parts}; use std::io::Write; +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::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE}; -use crate::vl1::Address; -use crate::crypto::hash::SHA384; +use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE}; +use crate::crypto::balloon; +use crate::crypto::salsa::Salsa; + +const V0_IDENTITY_GEN_MEMORY: usize = 2097152; + +const V1_BALLOON_SPACE_COST: usize = 16384; +const V1_BALLOON_TIME_COST: usize = 3; +const V1_BALLOON_DELTA: usize = 3; #[derive(Copy, Clone)] #[repr(u8)] @@ -11,28 +22,134 @@ pub enum Type { /// Curve25519 / Ed25519 identity (type 0) C25519 = 0, /// NIST P-521 ECDH / ECDSA identity (also has c25519/ed25519 keys for backward compability) (type 1) - P521 = 1 + P521 = 1, } struct IdentitySecrets { c25519: C25519KeyPair, ed25519: Ed25519KeyPair, - p521: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key + v1: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key } pub struct Identity { address: Address, - c25519_public: [u8; C25519_PUBLIC_KEY_SIZE], - ed25519_public: [u8; ED25519_PUBLIC_KEY_SIZE], - p521_public: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE])>, // ecdh key, ecdsa key, ecdsa signature of all keys + c25519: [u8; C25519_PUBLIC_KEY_SIZE], + ed25519: [u8; ED25519_PUBLIC_KEY_SIZE], + v1: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE], [u8; SHA512_HASH_SIZE])>, secrets: Option, } -impl Identity { - fn generate_c25519() { +/// 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 = unsafe { &mut *slice_from_raw_parts_mut(genmem_ptr, V0_IDENTITY_GEN_MEMORY) }; + let genmem_alias_hack = unsafe { &*slice_from_raw_parts(genmem_ptr, V0_IDENTITY_GEN_MEMORY) }; + let genmem_u64_ptr = genmem_ptr.cast::(); + + 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 { + let ii = i + 64; + s20.crypt(&genmem_alias_hack[(i - 64)..i], &mut genmem[i..ii]); + i = ii; } - fn generate_p521() { + i = 0; + while i < (V0_IDENTITY_GEN_MEMORY / 8) { + let idx1 = ((unsafe { *genmem_u64_ptr.offset(i as isize) }.to_be() % 8) * 8) as usize; + let idx2 = (unsafe { *genmem_u64_ptr.offset((i + 1) as isize) }.to_be() % (V0_IDENTITY_GEN_MEMORY as u64 / 8)) as usize; + let genmem_u64_at_idx2_ptr = unsafe { genmem_u64_ptr.offset(idx2 as isize) }; + let tmp = unsafe { *genmem_u64_at_idx2_ptr }; + let digest_u64_ptr = unsafe { digest.as_mut_ptr().offset(idx1 as isize).cast::() }; + unsafe { *genmem_u64_at_idx2_ptr = *digest_u64_ptr }; + unsafe { *digest_u64_ptr = tmp }; + s20.crypt_in_place(digest); + i += 2; + } +} + +impl Identity { + fn generate_c25519() -> Identity { + let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap(); + let genmem_ptr = unsafe { alloc(genmem_layout) }; + if genmem_ptr.is_null() { + panic!("unable to allocate memory for V0 identity generation"); + } + + let ed25519 = Ed25519KeyPair::generate(); + let ed25519_pub_bytes = ed25519.public_bytes(); + let mut sha = SHA512::new(); + loop { + let c25519 = C25519KeyPair::generate(); + let c25519_pub_bytes = c25519.public_bytes(); + + sha.update(&ed25519_pub_bytes); + sha.update(&c25519_pub_bytes); + let mut digest = sha.finish(); + + v0_frankenhash(&mut digest, genmem_ptr); + if digest[0] < 17 { + let addr = Address::from_bytes(&digest[59..64]).unwrap(); + if addr.is_valid() { + unsafe { dealloc(genmem_ptr, genmem_layout) }; + return Identity { + address: addr, + c25519: c25519_pub_bytes, + ed25519: ed25519_pub_bytes, + v1: None, + secrets: Some(IdentitySecrets { + c25519, + ed25519, + v1: None, + }), + }; + } + } + + sha.reset(); + } + } + + fn generate_p521() -> Identity { + let c25519 = C25519KeyPair::generate(); + let ed25519 = Ed25519KeyPair::generate(); + let p521_ecdh = P521KeyPair::generate(false).unwrap(); + let p521_ecdsa = P521KeyPair::generate(false).unwrap(); + + let c25519_pub_bytes = c25519.public_bytes(); + let ed25519_pub_bytes = ed25519.public_bytes(); + + let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE]; + signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&c25519_pub_bytes); + signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&ed25519_pub_bytes); + signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice(p521_ecdh.public_key_bytes()); + 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_ecdsa.public_key_bytes()); + + loop { + // ECDSA is a randomized signature algorithm, so each signature will be different. + let sig = p521_ecdsa.sign(&signing_buf).unwrap(); + let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&sig, b"zt_id_v1"); + if bh[0] < 7 { + let addr = Address::from_bytes(&bh[59..64]).unwrap(); + if addr.is_valid() { + let p521_ecdh_pub = p521_ecdh.public_key().clone(); + let p521_ecdsa_pub = p521_ecdsa.public_key().clone(); + return Identity { + address: addr, + c25519: c25519_pub_bytes, + ed25519: ed25519_pub_bytes, + v1: Some((p521_ecdh_pub, p521_ecdsa_pub, sig, bh)), + secrets: Some(IdentitySecrets { + c25519, + ed25519, + v1: Some((p521_ecdh, p521_ecdsa)), + }), + }; + } + } + } } /// Generate a new identity. @@ -41,24 +158,66 @@ impl Identity { /// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites /// take about 500ms. Generation can take a lot longer on low power devices, but only /// has to be done once. - pub fn generate(id_type: Type) { + pub fn generate(id_type: Type) -> Identity { match id_type { Type::C25519 => Self::generate_c25519(), Type::P521 => Self::generate_p521() } } + /// Locally validate this identity. + /// This can take a few milliseconds, especially on slower systems. V0 identities are slower + /// to fully validate than V1 identities. + pub fn locally_validate(&self) -> bool { + if self.v1.is_some() { + if self.address.is_valid() { + let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap(); + let genmem_ptr = unsafe { alloc(genmem_layout) }; + if genmem_ptr.is_null() { + false + } else { + let mut sha = SHA512::new(); + sha.update(&self.c25519); + sha.update(&self.ed25519); + let mut digest = sha.finish(); + v0_frankenhash(&mut digest, genmem_ptr); + unsafe { dealloc(genmem_ptr, genmem_layout) }; + (digest[0] < 17) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address) + } + } else { + false + } + } else { + if self.address.is_valid() { + let p521 = self.v1.as_ref().unwrap(); + let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE]; + signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&self.c25519); + signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519); + signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).0.public_key_bytes()); + 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, b"zt_id_v1"); + (bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[59..64]).unwrap().eq(&self.address) + } else { + false + } + } else { + false + } + } + } + /// Execute ECDH key agreement and return SHA384(shared secret). /// If both keys are type 1, key agreement is done with NIST P-521. Otherwise it's done /// with Curve25519. None is returned if there is an error such as this identity missing /// 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| { - secrets.p521.as_ref().map_or_else(|| { - Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public))) + secrets.v1.as_ref().map_or_else(|| { + Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519))) }, |p521_secret| { - other_identity.p521_public.as_ref().map_or_else(|| { - Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public))) + other_identity.v1.as_ref().map_or_else(|| { + Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519))) }, |other_p521_public| { p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret))) }) @@ -71,7 +230,7 @@ impl Identity { /// type. None is returned if this identity lacks secret keys or another error occurs. pub fn sign(&self, msg: &[u8]) -> Option> { self.secrets.as_ref().map_or(None, |secrets| { - secrets.p521.as_ref().map_or_else(|| { + secrets.v1.as_ref().map_or_else(|| { Some(secrets.ed25519.sign(msg).to_vec()) }, |p521_secret| { p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec())) @@ -79,10 +238,19 @@ impl Identity { }) } + /// Verify a signature. + pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool { + self.v1.as_ref().map_or_else(|| { + crate::crypto::c25519::ed25519_verify(&self.ed25519, signature, msg) + }, |p521| { + (*p521).1.verify(msg, signature) + }) + } + /// Get this identity's type. #[inline(always)] pub fn id_type(&self) -> Type { - if self.p521_public.is_some() { + if self.v1.is_some() { Type::P521 } else { Type::C25519 @@ -100,7 +268,7 @@ impl Identity { self.secrets.as_ref().map_or_else(|| { self.to_string() }, |secrets| { - secrets.p521.as_ref().map_or_else(|| { + secrets.v1.as_ref().map_or_else(|| { format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes())) }, |p521_secret| { let mut secret_key_blob: Vec = Vec::new(); @@ -113,21 +281,170 @@ impl Identity { }) }) } + + /// Append this in binary format to a buffer. + pub fn marshal(&self, buf: &mut Buffer, include_private: bool) -> std::io::Result<()> { + buf.append_bytes_fixed(&self.address.to_bytes())?; + if self.v1.is_some() { + let p521 = self.v1.as_ref().unwrap(); + buf.append_u8(1)?; // type 1 + buf.append_bytes_fixed(&self.c25519)?; + buf.append_bytes_fixed(&self.ed25519)?; + buf.append_bytes_fixed((*p521).0.public_key_bytes())?; + buf.append_bytes_fixed((*p521).1.public_key_bytes())?; + buf.append_bytes_fixed(&(*p521).2)?; + buf.append_bytes_fixed(&(*p521).3)?; + if include_private && self.secrets.is_some() { + let secrets = self.secrets.as_ref().unwrap(); + if secrets.v1.is_some() { + let p521_secrets = secrets.v1.as_ref().unwrap(); + buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u8)?; + buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?; + buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?; + buf.append_bytes_fixed((*p521_secrets).0.secret_key_bytes())?; + buf.append_bytes_fixed((*p521_secrets).1.secret_key_bytes())?; + } + } else { + buf.append_u8(0)?; // 0 secret bytes if not adding any + } + } else { + buf.append_and_init_bytes_fixed(|b: &mut [u8; 1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE]| { + b[0] = 0; // type 0 + b[1..(1 + C25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.c25519); + b[(1 + C25519_PUBLIC_KEY_SIZE)..(1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519); + })?; + if include_private && self.secrets.is_some() { + let secrets = self.secrets.as_ref().unwrap(); + buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8)?; + buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?; + buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?; + } else { + buf.append_u8(0)?; // 0 secret bytes if not adding any + } + } + Ok(()) + } + + /// Deserialize an Identity from a buffer. + /// The supplied cursor is advanced. + pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + let addr = Address::from_bytes(buf.get_bytes_fixed::<5>(cursor)?).unwrap(); + let id_type = buf.get_u8(cursor)?; + if id_type == Type::C25519 as u8 { + let c25519_public_bytes = buf.get_bytes_fixed::<{ C25519_PUBLIC_KEY_SIZE }>(cursor)?; + let ed25519_public_bytes = buf.get_bytes_fixed::<{ ED25519_PUBLIC_KEY_SIZE }>(cursor)?; + let secrets_len = buf.get_u8(cursor)?; + if secrets_len == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 { + let c25519_secret_bytes = buf.get_bytes_fixed::<{ C25519_SECRET_KEY_SIZE }>(cursor)?; + let ed25519_secret_bytes = buf.get_bytes_fixed::<{ ED25519_SECRET_KEY_SIZE }>(cursor)?; + Ok(Identity { + address: addr, + c25519: c25519_public_bytes.clone(), + ed25519: ed25519_public_bytes.clone(), + v1: None, + secrets: Some(IdentitySecrets { + c25519: C25519KeyPair::from_bytes(c25519_public_bytes, c25519_secret_bytes).unwrap(), + ed25519: Ed25519KeyPair::from_bytes(ed25519_public_bytes, ed25519_secret_bytes).unwrap(), + v1: None, + }) + }) + } else if secrets_len == 0 { + Ok(Identity { + address: addr, + c25519: c25519_public_bytes.clone(), + ed25519: ed25519_public_bytes.clone(), + v1: None, + secrets: None + }) + } else { + std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized scret key length (type 0)")) + } + } else if id_type == Type::P521 as u8 { + let c25519_public_bytes = buf.get_bytes_fixed::<{ C25519_PUBLIC_KEY_SIZE }>(cursor)?; + let ed25519_public_bytes = buf.get_bytes_fixed::<{ ED25519_PUBLIC_KEY_SIZE }>(cursor)?; + let p521_ecdh_public_bytes = buf.get_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?; + let p521_ecdsa_public_bytes = buf.get_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?; + let p521_signature = buf.get_bytes_fixed::<{ P521_ECDSA_SIGNATURE_SIZE }>(cursor)?; + let bh_digest = buf.get_bytes_fixed::<{ SHA512_HASH_SIZE }>(cursor)?; + let secrets_len = buf.get_u8(cursor)?; + if secrets_len == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u8 { + let c25519_secret_bytes = buf.get_bytes_fixed::<{ C25519_SECRET_KEY_SIZE }>(cursor)?; + let ed25519_secret_bytes = buf.get_bytes_fixed::<{ ED25519_SECRET_KEY_SIZE }>(cursor)?; + let p521_ecdh_secret_bytes = buf.get_bytes_fixed::<{ P521_SECRET_KEY_SIZE }>(cursor)?; + let p521_ecdsa_secret_bytes = buf.get_bytes_fixed::<{ P521_SECRET_KEY_SIZE }>(cursor)?; + Ok(Identity { + address: addr, + c25519: c25519_public_bytes.clone(), + ed25519: ed25519_public_bytes.clone(), + v1: Some((P521PublicKey::from_bytes(p521_ecdh_public_bytes).unwrap(), P521PublicKey::from_bytes(p521_ecdsa_public_bytes).unwrap(), p521_signature.clone(), bh_digest. clone())), + secrets: Some(IdentitySecrets { + c25519: C25519KeyPair::from_bytes(c25519_public_bytes, c25519_secret_bytes).unwrap(), + ed25519: Ed25519KeyPair::from_bytes(ed25519_public_bytes, ed25519_secret_bytes).unwrap(), + v1: Some((P521KeyPair::from_bytes(p521_ecdh_public_bytes, p521_ecdh_secret_bytes).unwrap(), P521KeyPair::from_bytes(p521_ecdsa_public_bytes, p521_ecdsa_secret_bytes).unwrap())), + }) + }) + } else if secrets_len == 0 { + Ok(Identity { + address: addr, + c25519: c25519_public_bytes.clone(), + ed25519: ed25519_public_bytes.clone(), + v1: Some((P521PublicKey::from_bytes(p521_ecdh_public_bytes).unwrap(), P521PublicKey::from_bytes(p521_ecdsa_public_bytes).unwrap(), p521_signature.clone(), bh_digest. clone())), + secrets: None + }) + } else { + std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid secret key length (type 1)")) + } + } else { + std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized identity type")) + } + } + + /// Get this identity in byte array format. + pub fn marshal_to_vec(&self, include_private: bool) -> Vec { + let mut buf: Buffer = Buffer::new(); + self.marshal(&mut buf, include_private).expect("overflow"); + buf.as_bytes().to_vec() + } } impl ToString for Identity { fn to_string(&self) -> String { - self.p521_public.as_ref().map_or_else(|| { - format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519_public), crate::util::hex::to_string(&self.ed25519_public)) + self.v1.as_ref().map_or_else(|| { + format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519), crate::util::hex::to_string(&self.ed25519)) }, |p521_public| { let mut public_key_blob: Vec = Vec::new(); - public_key_blob.reserve(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE); - let _ = public_key_blob.write_all(&self.c25519_public); - let _ = public_key_blob.write_all(&self.ed25519_public); + public_key_blob.reserve(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA512_HASH_SIZE); + let _ = public_key_blob.write_all(&self.c25519); + let _ = public_key_blob.write_all(&self.ed25519); let _ = public_key_blob.write_all(p521_public.0.public_key_bytes()); let _ = public_key_blob.write_all(p521_public.1.public_key_bytes()); let _ = public_key_blob.write_all(&p521_public.2); + let _ = public_key_blob.write_all(&p521_public.3); format!("{:0>10x}:1:{}", self.address.to_u64(), base64::encode_config(public_key_blob.as_slice(), base64::URL_SAFE_NO_PAD)) }) } } + +#[cfg(test)] +mod tests { + #[allow(unused_imports)] + use crate::vl1::Identity; + #[allow(unused_imports)] + use crate::vl1::identity::Type; + + #[test] + fn p521() { + /* + let mut ms = 0.0; + let mut id: Option = None; + for _ in 0..64 { + let start = std::time::SystemTime::now(); + id.replace(Identity::generate(Type::P521)); + let duration = std::time::SystemTime::now().duration_since(start).unwrap(); + ms += duration.as_nanos() as f64 / 1000000.0; + } + ms /= 64.0; + println!("{}ms {}", ms, id.unwrap().to_secret_string()); + */ + } +} diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index 4ca557fe1..466187dcf 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -2,7 +2,9 @@ pub mod protocol; pub mod packet; pub mod buffer; pub mod node; +pub mod identity; + mod address; -mod identity; pub use address::Address; +pub use identity::Identity;