More Rust stuff, cleanup, rework identity, add array concat convenience functions.

This commit is contained in:
Adam Ierymenko 2021-12-07 11:20:39 -05:00
parent 810a1fb229
commit 9b71b354b0
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
12 changed files with 154 additions and 42 deletions

View file

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2021 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::mem::MaybeUninit;
#[inline(always)]
pub fn concat_2_slices<const S0: usize, const S1: usize, const S: usize>(s0: &[u8], s1: &[u8]) -> [u8; S] {
debug_assert_eq!(S0 + S1, S);
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
tmp[..S0].copy_from_slice(s0);
tmp[S0..].copy_from_slice(s1);
tmp
}
#[inline(always)]
pub fn concat_2_arrays<const S0: usize, const S1: usize, const S: usize>(s0: &[u8; S0], s1: &[u8; S1]) -> [u8; S] {
concat_2_slices::<S0, S1, S>(s0, s1)
}
#[inline(always)]
pub fn concat_3_slices<const S0: usize, const S1: usize, const S2: usize, const S: usize>(s0: &[u8], s1: &[u8], s2: &[u8]) -> [u8; S] {
debug_assert_eq!(S0 + S1 + S2, S);
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
tmp[..S0].copy_from_slice(s0);
tmp[S0..S1].copy_from_slice(s1);
tmp[(S0 + S1)..].copy_from_slice(s2);
tmp
}
#[inline(always)]
pub fn concat_3_arrays<const S0: usize, const S1: usize, const S2: usize, const S: usize>(s0: &[u8; S0], s1: &[u8; S1], s2: &[u8; S2]) -> [u8; S] {
concat_3_slices::<S0, S1, S2, S>(s0, s1, s2)
}
#[inline(always)]
pub fn concat_4_slices<const S0: usize, const S1: usize, const S2: usize, const S3: usize, const S: usize>(s0: &[u8], s1: &[u8], s2: &[u8], s3: &[u8]) -> [u8; S] {
debug_assert_eq!(S0 + S1 + S2 + S3, S);
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
tmp[..S0].copy_from_slice(s0);
tmp[S0..S1].copy_from_slice(s1);
tmp[(S0 + S1)..(S0 + S1 + S2)].copy_from_slice(s2);
tmp[(S0 + S1 + S2)..].copy_from_slice(s3);
tmp
}
#[inline(always)]
pub fn concat_4_arrays<const S0: usize, const S1: usize, const S2: usize, const S3: usize, const S: usize>(s0: &[u8; S0], s1: &[u8; S1], s2: &[u8; S2], s3: &[u8; S3]) -> [u8; S] {
concat_4_slices::<S0, S1, S2, S3, S>(s0, s1, s2, s3)
}

View file

@ -29,7 +29,7 @@ fn hash_int_le(sha: &mut SHA512, i: u64) {
/// used for the expand step and the final hash hashes the entire buffer. It
/// also takes no salt since it's only used for one purpose here and that's
/// not password hashing.
pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8]) -> [u8; crate::hash::SHA384_HASH_SIZE] {
pub fn zt_variant_hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8]) -> [u8; crate::hash::SHA384_HASH_SIZE] {
debug_assert_ne!(SPACE_COST, 0);
debug_assert_ne!(TIME_COST, 0);
debug_assert_ne!(DELTA, 0);
@ -117,9 +117,12 @@ pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>
}
}
// Standard balloon hashing just returns the last hash, but we want a 384-bit
// result. This just hashes the whole buffer including the last hash.
SHA384::hash(&buf)
// Standard balloon hashing just returns the last hash in the shuffled array.
// We use AES to init the array to make things more memory bound and less CPU
// bound and we want a 384-bit result, so use SHA384 over the whole array
// instead. We also use the FIPS/NIST HMAC(salt, key) construction in case
// someone complains about FIPS stuff even though this is not a KDF.
SHA384::hmac(&buf, password)
}
/*

View file

@ -18,6 +18,7 @@ pub mod secret;
pub mod hex;
pub mod varint;
pub mod sidhp751;
pub mod array_concat;
pub use aes_gmac_siv;
pub use rand_core;

View file

@ -1,17 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2021 ZeroTier, Inc.
* https://www.zerotier.com/
*/
/// Default root set that uses ZeroTier's own global roots.
pub const ROOT_SET: [u8; 570] = [ 0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x78,0xcc,0x8e,0xf8,0xcb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x31,0xdc,0x40,0xa9,0xc7,0xb5,0xd2,0xf9,0x8e,0xd9,0x7b,0xf6,0x41,0x27,0x29,0x02,0xb6,0xb3,0x34,0x6f,0x56,0x16,0x11,0x45,0x82,0x44,0x55,0x85,0x78,0x79,0xb9,0x30,0xcb,0x01,0x51,0x15,0x49,0xf3,0x38,0x24,0xd8,0xd4,0x78,0x7d,0x77,0x23,0xda,0xc3,0x51,0x50,0x0b,0xe7,0xdf,0x5b,0x8f,0x72,0xdd,0x25,0x81,0xa5,0x0b,0x4a,0x36,0x01,0x46,0x85,0x95,0xbe,0x4d,0x5e,0xe6,0x3b,0x46,0xc2,0x9b,0x15,0x3c,0x43,0x8a,0x30,0xe0,0xa2,0xbf,0xba,0x1a,0x57,0xfc,0x98,0x7b,0x42,0x71,0xde,0x9c,0x53,0x6c,0x00,0x04,0x61,0xd2,0x94,0xb9,0xcb,0x00,0xe6,0x53,0xef,0x7a,0xd9,0x25,0x59,0x52,0xb7,0xc9,0xfc,0xa1,0x68,0x6d,0x3b,0x17,0xc6,0x10,0xb0,0x4e,0x6b,0x6c,0x82,0xd2,0xd3,0x7c,0xd3,0xa6,0xef,0xb2,0x56,0x3d,0x57,0x7f,0x81,0x22,0x24,0x37,0x62,0x02,0x09,0xe9,0x23,0x48,0xad,0x33,0x7b,0xd1,0x91,0xac,0x00,0xb7,0x49,0x2c,0xfd,0x55,0xce,0x0f,0xa0,0x36,0xd8,0xc5,0x62,0x83,0x00,0x02,0x04,0x32,0x07,0x49,0x22,0x27,0x09,0x06,0x20,0x01,0x49,0xf0,0xd0,0x02,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x77,0x8c,0xde,0x71,0x90,0x00,0x3f,0x66,0x81,0xa9,0x9e,0x5a,0xd1,0x89,0x5e,0x9f,0xba,0x33,0xe6,0x21,0x2d,0x44,0x54,0xe1,0x68,0xbc,0xec,0x71,0x12,0x10,0x1b,0xf0,0x00,0x95,0x6e,0xd8,0xe9,0x2e,0x42,0x89,0x2c,0xb6,0xf2,0xec,0x41,0x08,0x81,0xa8,0x4a,0xb1,0x9d,0xa5,0x0e,0x12,0x87,0xba,0x3d,0x92,0x6c,0x3a,0x1f,0x75,0x5c,0xcc,0xf2,0x99,0xa1,0x20,0x70,0x55,0x00,0x02,0x04,0x67,0xc3,0x67,0x42,0x27,0x09,0x06,0x26,0x05,0x98,0x80,0x04,0x00,0x00,0xc3,0x02,0x54,0xf2,0xbc,0xa1,0xf7,0x00,0x19,0x27,0x09,0x62,0xf8,0x65,0xae,0x71,0x00,0xe2,0x07,0x6c,0x57,0xde,0x87,0x0e,0x62,0x88,0xd7,0xd5,0xe7,0x40,0x44,0x08,0xb1,0x54,0x5e,0xfc,0xa3,0x7d,0x67,0xf7,0x7b,0x87,0xe9,0xe5,0x41,0x68,0xc2,0x5d,0x3e,0xf1,0xa9,0xab,0xf2,0x90,0x5e,0xa5,0xe7,0x85,0xc0,0x1d,0xff,0x23,0x88,0x7a,0xd4,0x23,0x2d,0x95,0xc7,0xa8,0xfd,0x2c,0x27,0x11,0x1a,0x72,0xbd,0x15,0x93,0x22,0xdc,0x00,0x02,0x04,0x32,0x07,0xfc,0x8a,0x27,0x09,0x06,0x20,0x01,0x49,0xf0,0xd0,0xdb,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x99,0x2f,0xcf,0x1d,0xb7,0x00,0x20,0x6e,0xd5,0x93,0x50,0xb3,0x19,0x16,0xf7,0x49,0xa1,0xf8,0x5d,0xff,0xb3,0xa8,0x78,0x7d,0xcb,0xf8,0x3b,0x8c,0x6e,0x94,0x48,0xd4,0xe3,0xea,0x0e,0x33,0x69,0x30,0x1b,0xe7,0x16,0xc3,0x60,0x93,0x44,0xa9,0xd1,0x53,0x38,0x50,0xfb,0x44,0x60,0xc5,0x0a,0xf4,0x33,0x22,0xbc,0xfc,0x8e,0x13,0xd3,0x30,0x1a,0x1f,0x10,0x03,0xce,0xb6,0x00,0x02,0x04,0xc3,0xb5,0xad,0x9f,0x27,0x09,0x06,0x2a,0x02,0x6e,0xa0,0xc0,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x09 ];
/// Default maximum payload size for UDP transport.
///
/// This is small enough to traverse numerous weird networks including PPPoE and Google Cloud's
/// weird exit MTU restriction, but is large enough that a standard 2800 byte frame creates only
/// two fragments.
pub const UDP_DEFAULT_MTU: usize = 1432;

View file

@ -10,7 +10,6 @@ pub mod util;
pub mod error;
pub mod vl1;
pub mod vl2;
pub mod defaults;
mod networkhypervisor;
pub use networkhypervisor::{Interface, NetworkHypervisor};

View file

@ -6,6 +6,67 @@
* https://www.zerotier.com/
*/
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
use zerotier_core_crypto::c25519::{C25519_PUBLIC_KEY_SIZE, C25519KeyPair, ED25519_PUBLIC_KEY_SIZE, Ed25519KeyPair};
use zerotier_core_crypto::p521::{P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521KeyPair};
use zerotier_core_crypto::salsa::Salsa;
// Work function used to derive address from keys.
fn zt_frankenhash(digest: &mut [u8; 64], genmem_ptr: *mut u8) {
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_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_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_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>();
*genmem_u64_at_idx2_ptr = *digest_u64_ptr;
*digest_u64_ptr = tmp;
}
s20.crypt_in_place(digest);
i += 2;
}
}
struct P521Secret {
ecdh: P521KeyPair,
ecdsa: P521KeyPair,
}
struct P521Public {
ecdh: [u8; P521_PUBLIC_KEY_SIZE],
ecdsa: [u8; P521_PUBLIC_KEY_SIZE],
self_signature: [u8; P521_ECDSA_SIGNATURE_SIZE]
}
pub struct IdentitySecrets {
c25519: C25519KeyPair,
ed25519: Ed25519KeyPair,
p521: Option<P521Secret>,
}
pub struct Identity {
c25519: [u8; C25519_PUBLIC_KEY_SIZE],
ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
p521: Option<P521Public>,
}
/*
use std::alloc::{alloc, dealloc, Layout};
use std::cmp::Ordering;
use std::convert::TryInto;
@ -191,7 +252,7 @@ impl Identity {
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);
let bh = balloon::zt_variant_hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&sig);
if bh[0] < V1_POW_THRESHOLD {
let addr = Address::from_bytes(&bh[43..48]);
if addr.is_some() {
@ -254,7 +315,7 @@ impl Identity {
let p521 = self.v1.as_ref().unwrap();
let signing_buf = concat_v1_public_keys(&self.c25519, &self.ed25519, (*p521).0.public_key_bytes(), (*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);
let bh = balloon::zt_variant_hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2);
(bh[0] < V1_POW_THRESHOLD) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
} else {
false
@ -704,3 +765,5 @@ mod tests {
}
}
}
*/

View file

@ -115,8 +115,8 @@ pub trait VL1PacketHandler {
#[derive(Default)]
struct BackgroundTaskIntervals {
whois: IntervalGate<{ WhoisQueue::INTERVAL }>,
paths: IntervalGate<{ Path::CALL_EVERY_INTERVAL_INTERVAL }>,
peers: IntervalGate<{ Peer::INTERVAL }>,
paths: IntervalGate<{ Path::CALL_EVERY_INTERVAL_MS }>,
peers: IntervalGate<{ Peer::CALL_EVERY_INTERVAL_MS }>,
}
pub struct Node {

View file

@ -103,7 +103,7 @@ impl Path {
}
/// Desired period between calls to call_every_interval().
pub(crate) const CALL_EVERY_INTERVAL_INTERVAL: i64 = PATH_KEEPALIVE_INTERVAL;
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
/// Called every INTERVAL during background tasks.
#[inline(always)]

View file

@ -26,20 +26,16 @@ use zerotier_core_crypto::random::next_u64_secure;
use zerotier_core_crypto::salsa::Salsa;
use zerotier_core_crypto::secret::Secret;
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION, PacketBuffer};
use crate::defaults::UDP_DEFAULT_MTU;
use crate::util::pool::{Pool, PoolFactory};
use crate::util::buffer::Buffer;
use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
use crate::util::{array_range, u64_as_bytes};
use crate::util::buffer::Buffer;
use crate::util::pool::{Pool, PoolFactory};
use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path};
use crate::vl1::ephemeral::EphemeralSymmetricSecret;
use crate::vl1::node::*;
use crate::vl1::protocol::*;
use crate::vl1::symmetricsecret::SymmetricSecret;
/// Interval for servicing and background operations on peers.
pub(crate) const PEER_SERVICE_INTERVAL: i64 = 30000;
struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
@ -196,7 +192,7 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
})
});
}
aes.decrypt_finish().map_or_else(false, |tag| {
aes.decrypt_finish().map_or(false, |tag| {
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
// decrypted tag.
@ -211,8 +207,6 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
}
impl Peer {
pub(crate) const INTERVAL: i64 = PEER_SERVICE_INTERVAL;
/// Create a new peer.
/// This only returns None if this_node_identity does not have its secrets or if some
/// fatal error occurs performing key agreement between the two identities.
@ -252,15 +246,16 @@ impl Peer {
let _ = packet.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| {
let mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_nozero() };
let mut message_id = 0_u64;
let mut forward_secrecy = true;
let ephemeral_secret: Option<Arc<EphemeralSymmetricSecret>> = self.ephemeral_secret.lock().clone();
if !ephemeral_secret.map_or(false, |ephemeral_secret| try_aead_decrypt(&ephemeral_secret.secret, packet_frag0_payload_bytes, header, fragments, &mut payload, &mut message_id)) {
let forward_secrecy = if !ephemeral_secret.map_or(false, |ephemeral_secret| try_aead_decrypt(&ephemeral_secret.secret, packet_frag0_payload_bytes, header, fragments, &mut payload, &mut message_id)) {
unsafe { payload.set_size_unchecked(0); }
if !try_aead_decrypt(&self.static_secret, packet_frag0_payload_bytes, header, fragments, &mut payload, &mut message_id) {
return;
}
forward_secrecy = false;
}
false
} else {
true
};
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
self.total_bytes_received.fetch_add((payload.len() + PACKET_HEADER_SIZE) as u64, Ordering::Relaxed);
@ -483,6 +478,8 @@ impl Peer {
})
}
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
/// Called every INTERVAL during background tasks.
#[inline(always)]
pub(crate) fn call_every_interval<CI: NodeInterface>(&self, ct: &CI, time_ticks: i64) {}

View file

@ -25,6 +25,13 @@ pub const VERB_VL1_USER_MESSAGE: u8 = 0x14;
pub const HELLO_DICT_KEY_INSTANCE_ID: &'static str = "I";
pub const HELLO_DICT_KEY_CLOCK: &'static str = "C";
/// Default maximum payload size for UDP transport.
///
/// This is small enough to traverse numerous weird networks including PPPoE and Google Cloud's
/// weird exit MTU restriction, but is large enough that a standard 2800 byte frame creates only
/// two fragments.
pub const UDP_DEFAULT_MTU: usize = 1432;
/// KBKDF usage label indicating a key used to encrypt the dictionary inside HELLO.
pub const KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT: u8 = b'H';

View file

@ -86,6 +86,11 @@ pub struct RootSet {
}
impl RootSet {
/// Create a new root set populated with compiled-in ZeroTier defaults.
pub fn zerotier_default() -> Self {
Self::from_bytes(include_bytes!("./rootset-default.bin")).unwrap()
}
/// Create and sign a new root set.
/// This cannot create legacy "planet" or "moon" type root sets. For those the old mkworld code must be used.
pub fn create(roots: &[Root], timestamp: i64, oob_update_url: Option<&str>, name: Option<&str>, contact: Option<&str>, signing_key: &RootSetSecretSigningKey) -> Result<Self, InvalidParameterError> {
@ -327,7 +332,7 @@ mod tests {
#[test]
fn default_root_set() {
let rs = RootSet::from_bytes(&crate::defaults::ROOT_SET).unwrap();
let rs = RootSet::zerotier_default();
rs.roots.iter().for_each(|r| {
println!("{}", r.identity.to_string());
r.endpoints.iter().for_each(|ep| {