mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +02:00
Root sets, tweak ballon hash and V1 identities to be shorter, cleanup.
This commit is contained in:
parent
788c310322
commit
0395943d31
13 changed files with 476 additions and 90 deletions
12
network-hypervisor/Cargo.lock
generated
12
network-hypervisor/Cargo.lock
generated
|
@ -61,6 +61,17 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "concat-arrays"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df715824eb382e34b7afb7463b0247bf41538aeba731fba05241ecdb5dc3747"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.1.5"
|
||||
|
@ -605,6 +616,7 @@ version = "2.0.0"
|
|||
dependencies = [
|
||||
"aes-gmac-siv",
|
||||
"base64",
|
||||
"concat-arrays",
|
||||
"dashmap",
|
||||
"ed25519-dalek",
|
||||
"gcrypt",
|
||||
|
|
|
@ -20,6 +20,7 @@ urlencoding = "^2"
|
|||
lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] }
|
||||
dashmap = "^4"
|
||||
parking_lot = "^0"
|
||||
concat-arrays = "^0"
|
||||
|
||||
[target."cfg(not(windows))".dependencies]
|
||||
libc = "^0"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::convert::TryInto;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::crypto::hash::{SHA384, SHA512};
|
||||
|
||||
#[inline(always)]
|
||||
fn hash_int_le(sha: &mut crate::crypto::hash::SHA512, i: u64) {
|
||||
#[cfg(target_endian = "big")] {
|
||||
|
@ -11,10 +13,10 @@ fn hash_int_le(sha: &mut crate::crypto::hash::SHA512, i: u64) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Compute balloon memory-hard hash using SHA-512.
|
||||
/// Compute balloon memory-hard hash using SHA-512 and SHA-384 for the final.
|
||||
/// SPACE_COST must be a multiple of 64. This is checked with an assertion.
|
||||
/// DELTA is usually 3.
|
||||
pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8], salt: &[u8]) -> [u8; crate::crypto::hash::SHA512_HASH_SIZE] {
|
||||
pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8], salt: &[u8]) -> [u8; crate::crypto::hash::SHA384_HASH_SIZE] {
|
||||
debug_assert_ne!(SPACE_COST, 0);
|
||||
debug_assert_ne!(TIME_COST, 0);
|
||||
debug_assert_ne!(DELTA, 0);
|
||||
|
@ -24,7 +26,7 @@ pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>
|
|||
let zero64 = [0_u8; 8];
|
||||
|
||||
/* Initial hash */
|
||||
let mut sha = crate::crypto::hash::SHA512::new();
|
||||
let mut sha = SHA512::new();
|
||||
sha.update(&zero64); // 0 cnt
|
||||
sha.update(password);
|
||||
sha.update(salt);
|
||||
|
@ -106,8 +108,9 @@ pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>
|
|||
}
|
||||
}
|
||||
|
||||
/* Extract */
|
||||
buf[(SPACE_COST - 64)..SPACE_COST].try_into().unwrap()
|
||||
// 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)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -25,6 +25,11 @@ impl<const L: usize> Secret<L> {
|
|||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self(b.try_into().unwrap())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; L] {
|
||||
return &self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Drop for Secret<L> {
|
||||
|
|
|
@ -5,7 +5,7 @@ pub mod gate;
|
|||
pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn equal_bytes(a: *const u8, b: *const u8, l: usize) -> bool {
|
||||
pub(crate) unsafe fn equal_ptr(a: *const u8, b: *const u8, l: usize) -> bool {
|
||||
for i in 0..l {
|
||||
if *a.offset(i as isize) != *b.offset(i as isize) {
|
||||
return false;
|
||||
|
@ -14,22 +14,43 @@ pub(crate) unsafe fn equal_bytes(a: *const u8, b: *const u8, l: usize) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u16(i: u16, d: &mut [u8]) {
|
||||
pub(crate) fn store_u16_be(i: u16, d: &mut [u8]) {
|
||||
unsafe { *d.as_mut_ptr().cast::<u16>() = i.to_be() };
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn store_u16_be(i: u16, d: &mut [u8]) {
|
||||
d[0] = (i >> 8) as u8;
|
||||
d[1] = i as u8;
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u32(i: u32, d: &mut [u8]) {
|
||||
pub(crate) fn store_u32_be(i: u32, d: &mut [u8]) {
|
||||
unsafe { *d.as_mut_ptr().cast::<u32>() = i.to_be() };
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn store_u32_be(i: u32, d: &mut [u8]) {
|
||||
d[0] = (i >> 24) as u8;
|
||||
d[1] = (i >> 16) as u8;
|
||||
d[2] = (i >> 8) as u8;
|
||||
d[3] = i as u8;
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u64(i: u64, d: &mut [u8]) {
|
||||
pub(crate) fn store_u64_be(i: u64, d: &mut [u8]) {
|
||||
unsafe { *d.as_mut_ptr().cast::<u64>() = i.to_be() };
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn store_u64_be(i: u64, d: &mut [u8]) {
|
||||
d[0] = (i >> 56) as u8;
|
||||
d[1] = (i >> 48) as u8;
|
||||
d[2] = (i >> 40) as u8;
|
||||
|
@ -40,18 +61,39 @@ pub(crate) fn integer_store_be_u64(i: u64, d: &mut [u8]) {
|
|||
d[7] = i as u8;
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u16(d: &[u8]) -> u16 {
|
||||
pub(crate) fn load_u16_be(d: &[u8]) -> u16 {
|
||||
unsafe { *d.as_ptr().cast::<u16>() }
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn load_u16_be(d: &[u8]) -> u16 {
|
||||
(d[0] as u16) << 8 | (d[1] as u16)
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 {
|
||||
pub(crate) fn load_u32_be(d: &[u8]) -> u32 {
|
||||
unsafe { *d.as_ptr().cast::<u32>() }
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn load_u32_be(d: &[u8]) -> u32 {
|
||||
(d[0] as u32) << 24 | (d[1] as u32) << 16 | (d[2] as u32) << 8 | (d[3] as u32)
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 {
|
||||
pub(crate) fn load_u64_be(d: &[u8]) -> u64 {
|
||||
unsafe { *d.as_ptr().cast::<u64>() }
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn load_u64_be(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)
|
||||
}
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ impl<const L: usize> Buffer<L> {
|
|||
let end = ptr + 2;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u16(i, &mut self.1[ptr..end]);
|
||||
crate::util::store_u16_be(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
|
@ -207,7 +207,7 @@ impl<const L: usize> Buffer<L> {
|
|||
let end = ptr + 4;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u32(i, &mut self.1[ptr..end]);
|
||||
crate::util::store_u32_be(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
|
@ -221,7 +221,7 @@ impl<const L: usize> Buffer<L> {
|
|||
let end = ptr + 8;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u64(i, &mut self.1[ptr..end]);
|
||||
crate::util::store_u64_be(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
|
@ -330,7 +330,7 @@ impl<const L: usize> Buffer<L> {
|
|||
debug_assert!(end <= L);
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u16(&self.1[ptr..end]))
|
||||
Ok(crate::util::load_u16_be(&self.1[ptr..end]))
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
}
|
||||
|
@ -344,7 +344,7 @@ impl<const L: usize> Buffer<L> {
|
|||
debug_assert!(end <= L);
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u32(&self.1[ptr..end]))
|
||||
Ok(crate::util::load_u32_be(&self.1[ptr..end]))
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ impl<const L: usize> Buffer<L> {
|
|||
debug_assert!(end <= L);
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u64(&self.1[ptr..end]))
|
||||
Ok(crate::util::load_u64_be(&self.1[ptr..end]))
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||
}
|
||||
|
|
|
@ -11,13 +11,6 @@ use crate::util::hex::HEX_CHARS;
|
|||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Dictionary(BTreeMap<String, Vec<u8>>);
|
||||
|
||||
impl Default for Dictionary {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_escaped<W: Write>(b: &[u8], w: &mut W) -> std::io::Result<()> {
|
||||
let mut i = 0_usize;
|
||||
let l = b.len();
|
||||
|
|
|
@ -3,6 +3,10 @@ use crate::vl1::constants::FRAGMENT_COUNT_MAX;
|
|||
|
||||
/// Packet fragment re-assembler and container.
|
||||
/// This is only used in the receive path.
|
||||
///
|
||||
/// Performance note: PacketBuffer is Pooled<Buffer> which is NotNull<*mut Buffer>.
|
||||
/// That means Option<PacketBuffer> is just a pointer, since NotNull permits the
|
||||
/// compiler to optimize out any additional state in Option.
|
||||
pub(crate) struct FragmentedPacket {
|
||||
pub ts_ticks: i64,
|
||||
pub frags: [Option<PacketBuffer>; FRAGMENT_COUNT_MAX],
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
use std::alloc::{Layout, dealloc, alloc};
|
||||
use std::ptr::{slice_from_raw_parts_mut, slice_from_raw_parts};
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryInto;
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryInto;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair, ED25519_SIGNATURE_SIZE};
|
||||
use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE};
|
||||
use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE};
|
||||
use crate::crypto::balloon;
|
||||
use crate::crypto::c25519::*;
|
||||
use crate::crypto::hash::*;
|
||||
use crate::crypto::p521::*;
|
||||
use crate::crypto::salsa::Salsa;
|
||||
use crate::crypto::secret::Secret;
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::constants::PACKET_SIZE_MAX;
|
||||
|
||||
use concat_arrays::concat_arrays;
|
||||
|
||||
// Memory parameter for V0 address derivation work function.
|
||||
const V0_IDENTITY_GEN_MEMORY: usize = 2097152;
|
||||
|
@ -22,10 +26,11 @@ 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;
|
||||
|
||||
const V1_BALLOON_SALT: &'static [u8] = b"zt_id_v1";
|
||||
|
||||
pub const IDENTITY_TYPE_0_SIGNATURE_SIZE: usize = ED25519_SIGNATURE_SIZE + 32;
|
||||
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;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -47,7 +52,7 @@ pub struct Identity {
|
|||
address: Address,
|
||||
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])>,
|
||||
v1: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE], [u8; SHA384_HASH_SIZE])>,
|
||||
secrets: Option<IdentitySecrets>,
|
||||
}
|
||||
|
||||
|
@ -130,22 +135,15 @@ impl Identity {
|
|||
let ed25519 = Ed25519KeyPair::generate(false);
|
||||
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());
|
||||
|
||||
let sign_buf: [u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE] = concat_arrays!(c25519_pub_bytes, ed25519_pub_bytes, *p521_ecdh.public_key_bytes(), *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 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] < 7 {
|
||||
let addr = Address::from_bytes(&bh[59..64]).unwrap();
|
||||
let addr = Address::from_bytes(&bh[43..48]).unwrap();
|
||||
if addr.is_valid() {
|
||||
let p521_ecdh_pub = p521_ecdh.public_key().clone();
|
||||
let p521_ecdsa_pub = p521_ecdsa.public_key().clone();
|
||||
|
@ -166,6 +164,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// Generate a new identity.
|
||||
///
|
||||
/// This is time consuming due to the one-time anti-collision proof of work required
|
||||
/// to generate an address corresponding with a set of identity keys. V0 identities
|
||||
/// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites
|
||||
|
@ -205,6 +204,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
|
@ -232,7 +232,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] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[59..64]).unwrap().eq(&self.address)
|
||||
(bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -243,6 +243,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -281,6 +282,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// Sign this message with this identity.
|
||||
///
|
||||
/// Signature is performed using ed25519 EDDSA or NIST P-521 ECDSA depending on the identity
|
||||
/// type. None is returned if this identity lacks secret keys or another error occurs.
|
||||
pub fn sign(&self, msg: &[u8]) -> Option<Vec<u8>> {
|
||||
|
@ -404,7 +406,7 @@ impl Identity {
|
|||
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 {
|
||||
|
@ -412,7 +414,7 @@ impl Identity {
|
|||
c25519: c25519_public_bytes.clone(),
|
||||
ed25519: ed25519_public_bytes.clone(),
|
||||
v1: None,
|
||||
secrets: None
|
||||
secrets: None,
|
||||
})
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized scret key length (type 0)"))
|
||||
|
@ -423,7 +425,7 @@ impl Identity {
|
|||
let p521_ecdh_public_bytes = buf.read_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?;
|
||||
let p521_ecdsa_public_bytes = buf.read_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?;
|
||||
let p521_signature = buf.read_bytes_fixed::<{ P521_ECDSA_SIGNATURE_SIZE }>(cursor)?;
|
||||
let bh_digest = buf.read_bytes_fixed::<{ SHA512_HASH_SIZE }>(cursor)?;
|
||||
let bh_digest = buf.read_bytes_fixed::<{ SHA384_HASH_SIZE }>(cursor)?;
|
||||
let secrets_len = buf.read_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.read_bytes_fixed::<{ C25519_SECRET_KEY_SIZE }>(cursor)?;
|
||||
|
@ -434,20 +436,20 @@ impl 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())),
|
||||
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
|
||||
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)"))
|
||||
|
@ -459,14 +461,13 @@ impl Identity {
|
|||
|
||||
/// Get this identity in byte array format.
|
||||
pub fn marshal_to_bytes(&self, include_private: bool) -> Vec<u8> {
|
||||
let mut buf: Buffer<2048> = Buffer::new();
|
||||
let mut buf: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
|
||||
self.marshal(&mut buf, include_private).expect("overflow");
|
||||
buf.as_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Unmarshal an identity from a byte slice.
|
||||
/// On success the identity and the number of bytes actually read from the slice are
|
||||
/// returned.
|
||||
/// On success the identity and the number of bytes actually read from the slice are returned.
|
||||
pub fn unmarshal_from_bytes(bytes: &[u8]) -> std::io::Result<(Identity, usize)> {
|
||||
let mut cursor: usize = 0;
|
||||
let id = Self::unmarshal(&Buffer::<2048>::from_bytes(bytes)?, &mut cursor)?;
|
||||
|
@ -477,15 +478,10 @@ impl Identity {
|
|||
pub fn to_secret_string(&self) -> String {
|
||||
self.secrets.as_ref().map_or_else(|| self.to_string(), |secrets| {
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(secrets.c25519.secret_bytes().as_ref()), crate::util::hex::to_string(secrets.ed25519.secret_bytes().as_ref()))
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(secrets.c25519.public_bytes().as_ref()), crate::util::hex::to_string(secrets.ed25519.secret_bytes().as_ref()))
|
||||
}, |p521_secret| {
|
||||
let mut secret_key_blob: Vec<u8> = Vec::new();
|
||||
secret_key_blob.reserve(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE);
|
||||
let _ = secret_key_blob.write_all(&secrets.c25519.secret_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes().as_ref());
|
||||
format!("{}:{}", self.to_string(), base64::encode_config(secret_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
|
||||
let secrets_concat: [u8; C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE] = concat_arrays!(secrets.c25519.secret_bytes().0, secrets.ed25519.secret_bytes().0, p521_secret.0.secret_key_bytes().0, p521_secret.1.secret_key_bytes().0);
|
||||
format!("{}:{}", self.to_string(), base64::encode_config(secrets_concat, base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -496,15 +492,8 @@ impl ToString for Identity {
|
|||
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<u8> = 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 + 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))
|
||||
let public_concat: [u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA384_HASH_SIZE] = concat_arrays!(self.c25519, self.ed25519, *((*p521_public).0.public_key_bytes()), *((*p521_public).1.public_key_bytes()), (*p521_public).2, (*p521_public).3);
|
||||
format!("{:0>10x}:1:{}", self.address.to_u64(), base64::encode_config(public_concat, base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -551,7 +540,7 @@ impl FromStr for Identity {
|
|||
let public_keys_and_sig = base64::decode_config(fields[2], base64::URL_SAFE_NO_PAD);
|
||||
if public_keys_and_sig.is_ok() {
|
||||
let public_keys_and_sig = public_keys_and_sig.unwrap();
|
||||
if public_keys_and_sig.len() == (C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA512_HASH_SIZE) {
|
||||
if public_keys_and_sig.len() == V1_PUBLIC_KEYS_SIGNATURE_AND_POW_SIZE {
|
||||
let p521_ecdh_public = P521PublicKey::from_bytes(&public_keys_and_sig.as_slice()[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)]);
|
||||
let p521_ecdsa_public = P521PublicKey::from_bytes(&public_keys_and_sig.as_slice()[(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)]);
|
||||
if p521_ecdh_public.is_some() && p521_ecdsa_public.is_some() {
|
||||
|
@ -575,6 +564,7 @@ impl FromStr for Identity {
|
|||
return Err(InvalidFormatError);
|
||||
}
|
||||
} else {
|
||||
println!("foo");
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
} else {
|
||||
|
@ -589,7 +579,7 @@ impl FromStr for Identity {
|
|||
p521_ecdh_public,
|
||||
p521_ecdsa_public,
|
||||
public_keys_and_sig.as_slice()[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE)].try_into().unwrap(),
|
||||
public_keys_and_sig.as_slice()[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA512_HASH_SIZE)].try_into().unwrap()
|
||||
public_keys_and_sig.as_slice()[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE)..].try_into().unwrap()
|
||||
)),
|
||||
secrets,
|
||||
});
|
||||
|
@ -632,9 +622,17 @@ impl Ord for Identity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for Identity {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.address.to_u64());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::vl1::identity::{Identity, Type};
|
||||
|
||||
#[test]
|
||||
|
@ -697,10 +695,10 @@ mod tests {
|
|||
panic!("invalid signature verification succeeded");
|
||||
}
|
||||
for good_id in [
|
||||
"9c6095d8e3:1:ZhjuMgE1EP6rfmCW5bV3WbnSNq1EthIEIpvSU_-wCxdBdzzBvc2Hz5ZHXTNie5mxAtxqzvwO6oDhd1zyLfd7xgDSbSpiBO9gGoHjFeJN_MAoH8NJuHOLvfgLO4lb4ld8s9muhx8qVn7ZFgMki0sYJfYNYoTqfwN5lVmRSiSN8zzlHQDCQmSMLwQDaKvYwtIsqvPLgDKeWX7brJrW5hF8PpVapnuP8uJr4efXer5s5_fIlCaI2K-KwjbQ0IfsQrcgC-8JRgGog7KDgEaNjiqS74bFSdpemhYEEK9xWhi7SGFDAEVHJ58LoTsJc6yJ9ISThOQQJ_6DkWDr5vY77GBqmNZI0bPuLQA8H5NgdpAed4yVVE3Q0vYjkklC8zmjfdA05W7kV5dvbOi45qh8I7li2JI47CB1FZ-3B672onKCdoaPSiQzi4GAfwGy71MStTYyiADUKGog9nKAMpmg4EOgDfU6gXCvK9VYlTRY7FIvYVv1TQfNe4EwBnLYG0XRcpl6MQwgeFUvyTQ57ACKfwVdVlVdBwXaglfYk2sFu7E63VrMi1Lh-tyPhZgs4D1GIIONt6Z8UY0IMGwfhr1uZu_2M_evxC7w-4Av6V9T5AbNCg2qjNvab6iZFPQxE8gsT0cnacq6SoT1jdP9kl-6TXqg_f095c0Lbds7xaKd9A0k4HJPAhX87pMynGCV2OM",
|
||||
"23061c9924:1:Wic-UHeYc9-ri2PMLMxoaXpq3Vl7erG79ZD69HvEVjmtQ8Dtbj-kW6LY2fwANEy4EGl1Oq1z6SLI5SbowRaPJQCboc6YqOA6ZQZXPn4sulnLNoEWhclA1Fk5HMEKG1tptJ3D321CNJzb-McaIErA2l33iQLOgLWpcJSuCIapAl_tCgCH03OtJkexLZyfjhlzarWd8NOmzt6z963UMnNo3H3w9StyiNMDvCCUs2j_rP0QOLPH2mlUfw7Z1FbpezBxKmmxxQFq6zgVyDKyXPXLaLfYHJ4wj2uTBaQ_5qZGT3TQMy9ApTE-ywl9K6aM4uMXelO_dqhaoSOb4jWYRLnzrSXBcxtSlgD9xTI2rTTdiYaW8udvFtc_1sB0DwGfXx2E2W8cX2PYdhYxco5tUssuKVyRNuYr3FfnAWvhNKfooNNnuaJaFh6gZwDH1opFWlIz9AfgxeDAJ7F4Bt80O-H0Fxa4OEqyWX944FG5KiIFRP9lHlrkTa2E2cGH8lsa130fhCFreI4WU0qJFwEPHo_H39ukEzrkQhQtHWxsVg7ypokrSfD57vstxU6TCNyAA-6dPTpFZJ0gr848cj5pHPdWs0RaiohaDMYS8V4ZCALwEGoDptswgZsJauyU-GRKteKCEJ6GYBHFLDldlQtA99R70Em2RhlaW2Ic4_MFK8_-Z4ZEP4CsIgCWIwYcmSQ",
|
||||
"dcc7eb3f14:1:rw1eHn1xivAX8zs2yrJLjYWM35rvPEkWGN1ln63BMwL9mVMmjN1kxv_bY3LDwzhVgc6pyL7OsXkhHQDQ2m4q5AGFv1yZLy5LghaA37WX5ayCG2MeD01C243kjwfwE4VOm7576dPtghGXX8EGhtfspHyW8enAJHL9CnBAM8ACLYwakgChp8EzynwsnnsQlW6MTQdxFSZjq0OLmBLDWvCuhcABWdT1iPP9s9ngFah1xTaEiHALsS3M0HC1-vvbmwKzT6-sWwCPqF-XEtaIMNhTzZ0d1BluXj1KJvH6K5vD8o2fvyzHelezA8br7HFdT06DE9_Gh2xaJoaFlUfOdtajEhOiwirI5wBREUjd0q96EgxLKgGDKFrYYoXAs6MbXlc11jYs9RuMu90kAH4KNIsmETPGp5Qk8U3-2I_7lw8xFx3fb70PblNoJAD1yisM70AnpwZSaTr5bWsfV7JiglCOy1F1qFm7U5X8dseZ0d0oLGX4byLDl7lnNgM3KMQd4IY3dljZevUXc58M_wH0b7VecirJr_DRhAacHkJcsnKfqOfDM8yn8ot9N2K5rlLxHKWwSK1p7EO2kwdlBLuR_DzGTJSm2jj8vmk-J6jxcQCYvHvSJTx1j0Uk9w4HIseiw2tSnww9B3dEnlesvoxb2xWLMVvNXSjDuHqqh80gAbg0Ts14bbqZXk__3MfrPxQ",
|
||||
"cf0399c634:1:aSVlK4GVn9KTqf62Qx1LZpkxQL86xc7LapOyC6sbR1eTN1SKO3IJ742sfFufDa133rAUNXDVC2zNA0gRlen2SgCTCC18xqyhtsry-LLwOVSER5TSrlSL7EfDJ4Q9VFIPsj1Vo3UXrPGXur1k1K3XRStEQFdBuy0lW0Kw2fgyWVnTzwASCpOKg3Fgbv8z7XiUlPsPK1ZVbIyXVX4KFkyxnOrnSWALUHlifXRWrGAjZQhqSaU_vMrEVYSJYkFy7pLJKCQeWwFKZIZFure-5YP630qwxeY9pyrez7TalGfZFTCFPXv6BgWhQ1CfkPtJPY_1-amzRJn2IvYm3ci5OiwAcdMhSgl1VgC6dqcnz7HtuovppFTJ5FOxUYhD4YemZ376UZT7VrvKBho-mwBY2V0ZVtO_hgYg3yJbeX6wjv9HXnXsrvl5uuY04ACtZAtczPvkY0Roh2T18OdhVqytZD3QNGiirVb7GE0_SexQ2lcF0aWEsr6bS3iGhZFch1G8SlV4tag2ia-mMcD4HgFOyzlOwK5Paet4i1LfXuVG9vz-mjFoaND0here6s74NlOvizJvamwiid0k-kp4rYbsot0wGRYpNE0NtooBmp64VAFSUgGKr0OX3TRWTdiNFqBf28m8Cba-WjLSirLYZDMo83PWOHMzViHS6IIXo5mqFWJSXds9cx9gYeU3zwOZxjQ"
|
||||
"55ecc4bb1d:1:EEefUe82UfSkeHGroZhgZKp_V3asFzcTct8faJOIiFk16MB6nXNEAk1xjbI9Otjtvudq49JOWR9IRSZuom0VugHW0TDg9z14_8F1L39M9y_6rxhO4oKdpcmN_0dUxOtL8t7dw4PfSS3sKh-rrwWut01hoewy6-J42nJ_hbe7q_nWFABt4BHfWp3qqwYvMosYLquwUD1BJRnF_rIOEX82YhN84eFnntQTqnMMS1M8ILxe5-A7naowp1IxsccD7WW1a3f_BQAmgZmRfWAJqaTERQ2qJtCR6cixGid4raka_YgySFcx6BDi43Okm3rwO3prLjbr_J4d97BXbINKOAEms6AAxO75pwABxMmJVO1VRnXP10Y22XWMWZiN39sDVGzXCD3uzGptr5B5dBJPTEwyK1abxbwiv30hZE9bzLNgsuX12KsHb-yvMQEYQ_NBwGgMtV0fWcc3vPadEqdO7PRofOiAft9CPTrtLsO9AI88PMNId72plYHzYkCvtnnttgHLNqJwOIoOxd0xxQHLz8BMfcTm7t9fPHl6zPOtmakAmHaSQdlMpTqrpR7NL0awixRBFauFkrpD7v0zWkjP5JpUUDK1smCxAxan7oTlkwQou_kY6Ac65-cROf24xyUit1k1IzS1OiwSmWuGplEJxUCGORBAytS9WXFV7MS7HQ",
|
||||
"8b04dcccc5:1:G3esWdhJPDfE4yq9N_oilC_Der7S_iz0ytCA1uvO1RGjp_EDnqHfTO48rYhR2jZ7x3ibNyv_ySHyXvyqqmBvtABS5KdLn-fBCY2YrhH4o-3sAWffqTTMHthlFC8iIwtIh3uWDSbPAZLxRnsKQQSx5ndid31MDIdCTo4hEa-bjtXodQCoMDqOhEQHVb-abI9ljT7rOs1aWyYHI7l6lrvuR9IEV7xt2S0Z1Kdky9jnJXjBDq8H8HipLyFPc_FsURMlT5l1YgDwAFmmEAE43teNv8jZBSBYlQ4fokG-2OLXBtuKQBZ6Sd8Els3YEgXhn2TJXQIK0lPH5lKnEjH5IaDJ8uAxvKrs4ABYmd4OYRCHohHDYOzlzoRFTT-57SsWSfVdtGioRFVwTcB8sAUIKumWCpVsD18zaFXDNwn4nfkvhvBoKlbCGYiERgErKF7_t0YF5nXy2V5LdPPLPVq1KsVR2kMmQyILxCl5PWyKv9dgdos_69MmTSuCA28CZ6lcJJ8ZmCC-v1lUZSqmrwHrYzf9BX4YBBrH2ZjtoYtHzgETagH-_7Tll04Ug9KFUlQgerDMWhhPiMsILg4JDpGM0XHvPMqL8TU4KIiNGet9dga3ONkbrZaUpn-dEJ_3yL1D6BDbgoLex8fW3ejm5SOkNhtqH0QPSFJDKyyLBNzMxQ",
|
||||
"f1544578e8:1:fOBzH-NWHagNRi9qXKCPifYuQoACy4jzB89Nhntxcgtz3ovvrmMSa7hPrSrZlX7iIfDefO2IVZSKKswDsOoRigBp-ZqIPLg_2MbBaFfYoK6K01s7eDvE1VuTLen0phB-hq6OAefSvdg4qlZL8Ti9h0oFTZP_-6L4vtKulo32aShVYwDHsO9qa0Ven_Ha6BO4Ef6UyZW2fWIXRx43QclV7I9Frzdeoi5i_x78DN_O7CIbWt3s2LLIfcxP-UXgpdSZEXKBggECzikEtf9mx5Wmaz5D_C6erXD9AT5eijkD7xBFW1cFYQwiyHwJYJgGF76g4WnSz5Fs_3klR8iOeHDAPaRNAxxfjgAanMzOPVNEa20wtB40kp5o1xYebr312KoT19p7A-7KvmR42_Wseyai2q0tbm0h7ugUk9oOOAmS91g1jiGL_PFdQgHNmGH93GyhSozgkaTAkmCPXVMBWFBkFLDgf5dF7r5pRzxBou4OaWmmxBHGON1BBKwNiojOrAPrtOmvD1d7FLEV3ADrMA6B4nBAco0_LeC43GBIXqMMEJKm7xH-JPDg3HsKOgQUYSIyTquGNT3egZTvb9bTh421XvQJnQ6kazSrsTF9tgS2Fxryllj-QcfbWpyN_Cohrlf3GPMXTQsalFlSmnmAhcWt2YFrHjKdhvbxVEV46A",
|
||||
"ebcc1dfe97:1:6l1EtROT-jp-bSse6vajfbi3EDaWJWjBt97Fp5KETjQNhsla79wLFbaSR5ccjqt9tT5lqlpdqLyoeaI8HIP2XgEQbUMmk5GgLNCwLSyTVkAui9HO01lghRxQll7AhptPOD5CjYQC8qnhSfNaUkLkzqjeEjgclDx7oMclrVDkWTvyegBK6Q_rUkfseWRRfe6zA7wwrOHQ2jeXOY8QbeyAqCRWtXKr16zisNhyuYbeP00kzUAkZvrWey4mvzp7ruJgARsBVAF5FRcq8JRqkF5E99MNe-T4mSPbQZKF40saK4N2p_zzp3vr-Wq8IkevPFn_5gbk8cwINC30Bz_qz45xRAhLX6emigA8EUWzeAW4091wU2wQoXrzmhil9b3weBSEfeLvlycnmwfGMDWo8jaW29aqz5Ix6U7V3hu_kiA4FgzJEIxUYu3L4wBsPI2zYUMZqXBpcPvh8GGzIzgOGLRoi_3FhNtGEDTn8ajCMWUmfpIMiwOw7-iWjUwEZH42Ch-0c6j4RiezD-FphACiwtDZBs5llcBPqmRo8cHJ1DwjFHQCXn5Y0mAE4WgfkSUPDE5h9uXO02ic843qUnd0m-HLVmwhxW9RDP4-FSBPCAA-3K37hyZw3mtGBfKll4NJmQb-gMBRWG_C89grkTJr-QYbekVbtHm8lG7rzB3-lw"
|
||||
] {
|
||||
let id = Identity::from_str(good_id).unwrap();
|
||||
if !id.locally_validate() {
|
||||
|
@ -713,10 +711,10 @@ mod tests {
|
|||
}
|
||||
}
|
||||
for bad_id in [
|
||||
"9c6005d8e3:1:ZhjuMgE1EP6rfmCW5bV3WbnSNq1EthIEIpvSU_-wCxdBdzzBvc2Hz5ZHXTNie5mxAtxqzvwO6oDhd1zyLfd7xgDSbSpiBO9gGoHjFeJN_MAoH8NJuHOLvfgLO4lb4ld8s9muhx8qVn7ZFgMki0sYJfYNYoTqfwN5lVmRSiSN8zzlHQDCQmSMLwQDaKvYwtIsqvPLgDKeWX7brJrW5hF8PpVapnuP8uJr4efXer5s5_fIlCaI2K-KwjbQ0IfsQrcgC-8JRgGog7KDgEaNjiqS74bFSdpemhYEEK9xWhi7SGFDAEVHJ58LoTsJc6yJ9ISThOQQJ_6DkWDr5vY77GBqmNZI0bPuLQA8H5NgdpAed4yVVE3Q0vYjkklC8zmjfdA05W7kV5dvbOi45qh8I7li2JI47CB1FZ-3B672onKCdoaPSiQzi4GAfwGy71MStTYyiADUKGog9nKAMpmg4EOgDfU6gXCvK9VYlTRY7FIvYVv1TQfNe4EwBnLYG0XRcpl6MQwgeFUvyTQ57ACKfwVdVlVdBwXaglfYk2sFu7E63VrMi1Lh-tyPhZgs4D1GIIONt6Z8UY0IMGwfhr1uZu_2M_evxC7w-4Av6V9T5AbNCg2qjNvab6iZFPQxE8gsT0cnacq6SoT1jdP9kl-6TXqg_f095c0Lbds7xaKd9A0k4HJPAhX87pMynGCV2OM",
|
||||
"23061c9934:1:Wic-UHeYc9-ri2PMLMxoaXpq3Vl7erG79ZD69HvEVjmtQ8Dtbj-kW6LY2fwANEy4EGl1Oq1z6SLI5SbowRaPJQCboc6YqOA6ZQZXPn4sulnLNoEWhclA1Fk5HMEKG1tptJ3D321CNJzb-McaIErA2l33iQLOgLWpcJSuCIapAl_tCgCH03OtJkexLZyfjhlzarWd8NOmzt6z963UMnNo3H3w9StyiNMDvCCUs2j_rP0QOLPH2mlUfw7Z1FbpezBxKmmxxQFq6zgVyDKyXPXLaLfYHJ4wj2uTBaQ_5qZGT3TQMy9ApTE-ywl9K6aM4uMXelO_dqhaoSOb4jWYRLnzrSXBcxtSlgD9xTI2rTTdiYaW8udvFtc_1sB0DwGfXx2E2W8cX2PYdhYxco5tUssuKVyRNuYr3FfnAWvhNKfooNNnuaJaFh6gZwDH1opFWlIz9AfgxeDAJ7F4Bt80O-H0Fxa4OEqyWX944FG5KiIFRP9lHlrkTa2E2cGH8lsa130fhCFreI4WU0qJFwEPHo_H39ukEzrkQhQtHWxsVg7ypokrSfD57vstxU6TCNyAA-6dPTpFZJ0gr848cj5pHPdWs0RaiohaDMYS8V4ZCALwEGoDptswgZsJauyU-GRKteKCEJ6GYBHFLDldlQtA99R70Em2RhlaW2Ic4_MFK8_-Z4ZEP4CsIgCWIwYcmSQ",
|
||||
"dcc7eb3f14:1:rw1eHn1xivAX8zs1yrJLjYWM35rvPEkWGN1ln63BMwL9mVMmjN1kxv_bY3LDwzhVgc6pyL7OsXkhHQDQ2m4q5AGFv1yZLy5LghaA37WX5ayCG2MeD01C243kjwfwE4VOm7576dPtghGXX8EGhtfspHyW8enAJHL9CnBAM8ACLYwakgChp8EzynwsnnsQlW6MTQdxFSZjq0OLmBLDWvCuhcABWdT1iPP9s9ngFah1xTaEiHALsS3M0HC1-vvbmwKzT6-sWwCPqF-XEtaIMNhTzZ0d1BluXj1KJvH6K5vD8o2fvyzHelezA8br7HFdT06DE9_Gh2xaJoaFlUfOdtajEhOiwirI5wBREUjd0q96EgxLKgGDKFrYYoXAs6MbXlc11jYs9RuMu90kAH4KNIsmETPGp5Qk8U3-2I_7lw8xFx3fb70PblNoJAD1yisM70AnpwZSaTr5bWsfV7JiglCOy1F1qFm7U5X8dseZ0d0oLGX4byLDl7lnNgM3KMQd4IY3dljZevUXc58M_wH0b7VecirJr_DRhAacHkJcsnKfqOfDM8yn8ot9N2K5rlLxHKWwSK1p7EO2kwdlBLuR_DzGTJSm2jj8vmk-J6jxcQCYvHvSJTx1j0Uk9w4HIseiw2tSnww9B3dEnlesvoxb2xWLMVvNXSjDuHqqh80gAbg0Ts14bbqZXk__3MfrPxQ",
|
||||
"cf0399c634:1:aSVlK4GVn9KTqf62Qx1LZpkxQL86xc7LapOyC5sbR1eTN1SKO3IJ742sfFufDa133rAUNXDVC2zNA0gRlen2SgCTCC18xqyhtsry-LLwOVSER5TSrlSL7EfDJ4Q9VFIPsj1Vo3UXrPGXur1k1K3XRStEQFdBuy0lW0Kw2fgyWVnTzwASCpOKg3Fgbv8z7XiUlPsPK1ZVbIyXVX4KFkyxnOrnSWALUHlifXRWrGAjZQhqSaU_vMrEVYSJYkFy7pLJKCQeWwFKZIZFure-5YP630qwxeY9pyrez7TalGfZFTCFPXv6BgWhQ1CfkPtJPY_1-amzRJn2IvYm3ci5OiwAcdMhSgl1VgC6dqcnz7HtuovppFTJ5FOxUYhD4YemZ376UZT7VrvKBho-mwBY2V0ZVtO_hgYg3yJbeX6wjv9HXnXsrvl5uuY04ACtZAtczPvkY0Roh2T18OdhVqytZD3QNGiirVb7GE0_SexQ2lcF0aWEsr6bS3iGhZFch1G8SlV4tag2ia-mMcD4HgFOyzlOwK5Paet4i1LfXuVG9vz-mjFoaND0here6s74NlOvizJvamwiid0k-kp4rYbsot0wGRYpNE0NtooBmp64VAFSUgGKr0OX3TRWTdiNFqBf28m8Cba-WjLSirLYZDMo83PWOHMzViHS6IIXo5mqFWJSXds9cx9gYeU3zwOZxjQ"
|
||||
"65ecc4bb1d:1:EEefUe82UfSkeHGroZhgZKp_V3asFzcTct8faJOIiFk16MB6nXNEAk1xjbI9Otjtvudq49JOWR9IRSZuom0VugHW0TDg9z14_8F1L39M9y_6rxhO4oKdpcmN_0dUxOtL8t7dw4PfSS3sKh-rrwWut01hoewy6-J42nJ_hbe7q_nWFABt4BHfWp3qqwYvMosYLquwUD1BJRnF_rIOEX82YhN84eFnntQTqnMMS1M8ILxe5-A7naowp1IxsccD7WW1a3f_BQAmgZmRfWAJqaTERQ2qJtCR6cixGid4raka_YgySFcx6BDi43Okm3rwO3prLjbr_J4d97BXbINKOAEms6AAxO75pwABxMmJVO1VRnXP10Y22XWMWZiN39sDVGzXCD3uzGptr5B5dBJPTEwyK1abxbwiv30hZE9bzLNgsuX12KsHb-yvMQEYQ_NBwGgMtV0fWcc3vPadEqdO7PRofOiAft9CPTrtLsO9AI88PMNId72plYHzYkCvtnnttgHLNqJwOIoOxd0xxQHLz8BMfcTm7t9fPHl6zPOtmakAmHaSQdlMpTqrpR7NL0awixRBFauFkrpD7v0zWkjP5JpUUDK1smCxAxan7oTlkwQou_kY6Ac65-cROf24xyUit1k1IzS1OiwSmWuGplEJxUCGORBAytS9WXFV7MS7HQ",
|
||||
"8b04dcccc5:1:G3esWdhJPDff4yq9N_oilC_Der7S_iz0ytCA1uvO1RGjp_EDnqHfTO48rYhR2jZ7x3ibNyv_ySHyXvyqqmBvtABS5KdLn-fBCY2YrhH4o-3sAWffqTTMHthlFC8iIwtIh3uWDSbPAZLxRnsKQQSx5ndid31MDIdCTo4hEa-bjtXodQCoMDqOhEQHVb-abI9ljT7rOs1aWyYHI7l6lrvuR9IEV7xt2S0Z1Kdky9jnJXjBDq8H8HipLyFPc_FsURMlT5l1YgDwAFmmEAE43teNv8jZBSBYlQ4fokG-2OLXBtuKQBZ6Sd8Els3YEgXhn2TJXQIK0lPH5lKnEjH5IaDJ8uAxvKrs4ABYmd4OYRCHohHDYOzlzoRFTT-57SsWSfVdtGioRFVwTcB8sAUIKumWCpVsD18zaFXDNwn4nfkvhvBoKlbCGYiERgErKF7_t0YF5nXy2V5LdPPLPVq1KsVR2kMmQyILxCl5PWyKv9dgdos_69MmTSuCA28CZ6lcJJ8ZmCC-v1lUZSqmrwHrYzf9BX4YBBrH2ZjtoYtHzgETagH-_7Tll04Ug9KFUlQgerDMWhhPiMsILg4JDpGM0XHvPMqL8TU4KIiNGet9dga3ONkbrZaUpn-dEJ_3yL1D6BDbgoLex8fW3ejm5SOkNhtqH0QPSFJDKyyLBNzMxQ",
|
||||
"f1544578e8:1:fOBzH-NWHagNRi9qXKCPifYuQoACy4jzB89Nhntxcgtz3ovvrmMSa7hPrSrZlX7iIfDefO2IVZSKKswDsOoRigBp-ZqIPLg_2MbBaFfYoK6K01s7eDvE1VuTLen0phB-hq6OAefSvdg4qlZL8Ti9h0oFTZP_-6L4vtKulo32aShVYwDHsO9qa0Ven_Ha6BO4Ef6UyZW2fWIXRx43QclV7I9Frzdeoi5i_x78DN_O7CIbWt3s2LLIfcxP-UXgpdSZEXKBggECzikEtf9mx5Wmaz5D_C6erXD9AT5eijkD7xBFW1cFYQwiyHwJYJgGF76g4WnSz5Fs_3klR8iOeHDAPaRNAxxfjgAanMzOPVNEa20wtB40kp5o1xYebr312KoT19p7A-7KvmR42_Wseyai2q0tbm0h7ugUk9oOOAmS91g1jiGL_PFdQgHNmGH93GyhSozgkaTAkmCPXVMBWFBkFLDgf5dF7r5pRzxBou4OaWmmxBHGON1BBKwNiojOrAPrtOmvD1d7FLEV3ADrMA6B4nBAco0_LeC43GBIXqMMEJKm7xH-JPDg3HsKOgQUYSIyTquGNT3egZTvb9bTh421XvQJnQ6kazSrsTF9tgS2Fxryllj-QcfbWpyN_Cohrlf3GPMXTQsalFlSmnmAhcWt2YFrHjKdhvbx3EV46A",
|
||||
"ebcc1dfe97:1:6l1EtROT-jp-bSse6vajfbi3EDaWJWjBt97Fp5KETjQNhsla79wLFbaSR5ccjqt9tT5lqlpdqLyoeaI8HIP2XgEQbUMmk5GgLNCwLSyTVkAui9HO01lghRxQll7AhptPOD5CjYQC8qnhSfNaUkLkzqjeEjgclDx7oMclrVDkWTvyegBK6Q_rUkfseWRRfe6zA7wwrOHQ2jeXOY8QbeyAqCRWtXKr16zisNhyuYbeP00kzUAkZvrWey4mvzp7ruJgARsBVAF5FRcq8JRqkF5E99MNe-T4mSPbQZKF40saK4N2p_zzp3vr-Wq8IkevPFn_5gbk8cwINC30Bz_qz45xRAhLX6emigA8EUWzeAW4091wU2wQoXrzmhil9b3weBSEfeLvlycnmwfGMDWo8jaW29aqz5Ix6U7V3hu_kiA4FgzJEIxUYu3L4wBsPI2zYUMZqXBpcPvh8GGzIzgOGLRoi_3FhNtGEDTn8ajCMWUmfpIMiwOw7-iWjUwEZH42Ch-0c6j4RiezD-FphACiwtDZBs5llcBPqmRo8cHJ1DwjFHQCXn5Y0mAE4WWfkSUPDE5h9uXO02ic843qUnd0m-HLVmwhxW9RDP4-FSBPCAA-3K37hyZw3mtGBfKll4NJmQb-gMBRWG_C89grkTJr-QYbekVbtHm8lG7rzB3-lw"
|
||||
] {
|
||||
let id = Identity::from_str(bad_id).unwrap();
|
||||
if id.locally_validate() {
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::hash::{Hash, Hasher};
|
|||
use std::net::{IpAddr, Ipv6Addr};
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::equal_bytes;
|
||||
use crate::util::equal_ptr;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -405,11 +405,11 @@ impl InetAddress {
|
|||
match buf.read_u8(cursor)? {
|
||||
4 => {
|
||||
let b: &[u8; 6] = buf.read_bytes_fixed(cursor)?;
|
||||
Ok(InetAddress::from_ip_port(&b[0..4], crate::util::integer_load_be_u16(&b[4..6])))
|
||||
Ok(InetAddress::from_ip_port(&b[0..4], crate::util::load_u16_be(&b[4..6])))
|
||||
}
|
||||
6 => {
|
||||
let b: &[u8; 18] = buf.read_bytes_fixed(cursor)?;
|
||||
Ok(InetAddress::from_ip_port(&b[0..16], crate::util::integer_load_be_u16(&b[16..18])))
|
||||
Ok(InetAddress::from_ip_port(&b[0..16], crate::util::load_u16_be(&b[16..18])))
|
||||
}
|
||||
_ => Ok(InetAddress::new())
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ impl PartialEq for InetAddress {
|
|||
AF_INET => { self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr }
|
||||
AF_INET6 => {
|
||||
if self.sin6.sin6_port == other.sin6.sin6_port {
|
||||
equal_bytes((&(self.sin6.sin6_addr) as *const in6_addr).cast(), (&(other.sin6.sin6_addr) as *const in6_addr).cast(), 16)
|
||||
equal_ptr((&(self.sin6.sin6_addr) as *const in6_addr).cast(), (&(other.sin6.sin6_addr) as *const in6_addr).cast(), 16)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ pub mod identity;
|
|||
pub mod inetaddress;
|
||||
pub mod endpoint;
|
||||
pub mod locator;
|
||||
pub mod rootset;
|
||||
|
||||
pub use address::Address;
|
||||
pub use mac::MAC;
|
||||
|
|
|
@ -59,6 +59,7 @@ pub trait VL1CallerInterface {
|
|||
fn load_peer(&self, address: Address) -> Option<&[u8]>;
|
||||
|
||||
/// Save a peer's state.
|
||||
///
|
||||
/// The state contains the identity, so there's no need to save that separately.
|
||||
/// It's just supplied for the address and if the external code wants it.
|
||||
fn save_peer(&self, id: &Identity, peer: &[u8]);
|
||||
|
@ -87,6 +88,7 @@ pub trait VL1CallerInterface {
|
|||
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>) -> bool;
|
||||
|
||||
/// Called to look up a path to a known node.
|
||||
///
|
||||
/// If a path is found, this returns a tuple of an endpoint and optional local socket and local
|
||||
/// interface IDs. If these are None they will be None when this is sent with wire_send.
|
||||
fn get_path_hints(&self, id: &Identity) -> Option<&[(&Endpoint, Option<i64>, Option<i64>)]>;
|
||||
|
@ -101,6 +103,7 @@ pub trait VL1CallerInterface {
|
|||
/// Trait implemented by VL2 to handle messages after they are unwrapped by VL1.
|
||||
pub trait VL1PacketHandler {
|
||||
/// Handle a packet, returning true if the verb was recognized.
|
||||
///
|
||||
/// True should be returned even if the packet is not valid, since the return value is used
|
||||
/// to determine if this is a VL2 or VL1 packet. ERROR and OK should not be handled here but
|
||||
/// in handle_error() and handle_ok() instead.
|
||||
|
@ -135,6 +138,7 @@ pub struct Node {
|
|||
|
||||
impl Node {
|
||||
/// Create a new Node.
|
||||
///
|
||||
/// If the auto-generate identity type is not None, a new identity will be generated if
|
||||
/// no identity is currently stored in the data store.
|
||||
pub fn new<CI: VL1CallerInterface>(ci: &CI, auto_generate_identity_type: Option<crate::vl1::identity::Type>) -> Result<Self, InvalidParameterError> {
|
||||
|
@ -207,6 +211,7 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Run background tasks and return desired delay until next call in milliseconds.
|
||||
///
|
||||
/// This should only be called once at a time. It technically won't hurt anything to
|
||||
/// call concurrently but it will waste CPU cycles.
|
||||
pub fn do_background_tasks<CI: VL1CallerInterface>(&self, ci: &CI) -> Duration {
|
||||
|
@ -310,6 +315,7 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Get the canonical Path object for a given endpoint and local socket information.
|
||||
///
|
||||
/// This is a canonicalizing function that returns a unique path object for every tuple
|
||||
/// of endpoint, local socket, and local interface.
|
||||
pub(crate) fn path(&self, ep: &Endpoint, local_socket: i64, local_interface: i64) -> Arc<Path> {
|
||||
|
|
321
network-hypervisor/src/vl1/rootset.rs
Normal file
321
network-hypervisor/src/vl1/rootset.rs
Normal file
|
@ -0,0 +1,321 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
|
||||
use concat_arrays::concat_arrays;
|
||||
|
||||
use crate::crypto::c25519::{ED25519_PUBLIC_KEY_SIZE, ED25519_SECRET_KEY_SIZE, ED25519_SIGNATURE_SIZE, ed25519_verify, Ed25519KeyPair};
|
||||
use crate::crypto::hash::SHA384;
|
||||
use crate::crypto::p521::{P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE, P521KeyPair};
|
||||
use crate::crypto::secret::Secret;
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::vl1::{Endpoint, Identity};
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::constants::PACKET_SIZE_MAX;
|
||||
|
||||
const ROOT_SET_TYPE_LEGACY_PLANET: u8 = 1;
|
||||
const ROOT_SET_TYPE_LEGACY_MOON: u8 = 127;
|
||||
const ROOT_SET_TYPE_ED25519_P521: u8 = 128;
|
||||
|
||||
/// Root set type.
|
||||
///
|
||||
/// Two of these are legacy from ZeroTier V1. The third is a root set signed by both
|
||||
/// an Ed25519 key and a NIST P-521 key with these keys being bundled together.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
LegacyPlanet(u64),
|
||||
LegacyMoon(u64),
|
||||
Ed25519P521RootSet([u8; 48]),
|
||||
}
|
||||
|
||||
impl Hash for Type {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::LegacyPlanet(id) => state.write_u64(*id),
|
||||
Self::LegacyMoon(id) => state.write_u64(*id),
|
||||
Self::Ed25519P521RootSet(id) => state.write(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Secret keys that can be used to update root sets after creation.
|
||||
pub struct RootSetSecretKeys {
|
||||
ed25519: Ed25519KeyPair,
|
||||
p521: P521KeyPair,
|
||||
}
|
||||
|
||||
impl RootSetSecretKeys {
|
||||
const SECRET_BYTES_LEN: usize = 1 + ED25519_PUBLIC_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_SECRET_KEY_SIZE;
|
||||
const PUBLIC_BYTES_LEN: usize = 1 + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE;
|
||||
|
||||
/// Generate a new set of root set secret keys.
|
||||
pub fn generate() -> Self {
|
||||
Self {
|
||||
ed25519: Ed25519KeyPair::generate(false),
|
||||
p521: P521KeyPair::generate(false).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconstruct from bytes as returned by to_secret_bytes() or return None if invalid.
|
||||
pub fn from_bytes(b: &[u8]) -> Option<Self> {
|
||||
if b.len() == Self::SECRET_BYTES_LEN && b[0] == ROOT_SET_TYPE_ED25519_P521 {
|
||||
let ed25519 = Ed25519KeyPair::from_bytes(&b[1..ED25519_PUBLIC_KEY_SIZE + 1], &b[1 + ED25519_PUBLIC_KEY_SIZE..1 + ED25519_PUBLIC_KEY_SIZE + ED25519_SECRET_KEY_SIZE]);
|
||||
let p521 = P521KeyPair::from_bytes(&b[1 + ED25519_PUBLIC_KEY_SIZE + ED25519_SECRET_KEY_SIZE..1 + ED25519_PUBLIC_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_PUBLIC_KEY_SIZE], &b[1 + ED25519_PUBLIC_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_PUBLIC_KEY_SIZE..]);
|
||||
if ed25519.is_none() || p521.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(Self {
|
||||
ed25519: ed25519.unwrap(),
|
||||
p521: p521.unwrap(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get both public and secret keys in byte format.
|
||||
pub fn to_secret_bytes(&self) -> Secret<{ Self::SECRET_BYTES_LEN }> {
|
||||
Secret(concat_arrays!([ROOT_SET_TYPE_ED25519_P521], self.ed25519.public_bytes(), self.ed25519.secret_bytes().0, *self.p521.public_key_bytes(), self.p521.secret_key_bytes().0))
|
||||
}
|
||||
|
||||
/// Get only public keys in byte format.
|
||||
pub fn to_public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE] {
|
||||
concat_arrays!(self.ed25519.public_bytes(), *self.p521.public_key_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// A single root node with static endpoints where it can be reached.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Root {
|
||||
/// Root node ZeroTier identity.
|
||||
pub identity: Identity,
|
||||
|
||||
/// Static endpoints at which this root node may be reached.
|
||||
pub endpoints: BTreeSet<Endpoint>,
|
||||
}
|
||||
|
||||
/// A signed bundle of root nodes.
|
||||
///
|
||||
/// This is how roots are normally specified to nodes. The embedded signing key allows the
|
||||
/// root set to be updated automatically. Updates can add, remove, or change the endpoints
|
||||
/// of roots, allowing infrastructure updates with automatic client configuration as long
|
||||
/// 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>,
|
||||
signer: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
root_set_type: Type,
|
||||
}
|
||||
|
||||
impl RootSet {
|
||||
pub const MAX_ROOTS: usize = u8::MAX as usize;
|
||||
pub const MAX_ENDPOINTS_PER_ROOT: usize = u8::MAX as usize;
|
||||
|
||||
/// Sign this root set and return true on success.
|
||||
/// The fields timestamp, name, contact, and roots must have been set. The signer, signature, and type will be set.
|
||||
/// This can only sign new format root sets. Legacy "planet" and "moon" root sets can be used by V2 but
|
||||
/// cannot be created by this code.
|
||||
pub fn sign(&mut self, keys: &RootSetSecretKeys) -> bool {
|
||||
self.signer = keys.to_public_bytes().to_vec();
|
||||
self.root_set_type = Type::Ed25519P521RootSet(SHA384::hash(self.signer.as_slice()));
|
||||
|
||||
let mut buf: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
|
||||
if self.marshal_internal(&mut buf, true).is_err() {
|
||||
return false;
|
||||
}
|
||||
let ed25519 = keys.ed25519.sign(buf.as_bytes());
|
||||
let p521 = keys.p521.sign(buf.as_bytes());
|
||||
if p521.is_none() {
|
||||
return false;
|
||||
}
|
||||
let p521 = p521.unwrap();
|
||||
|
||||
self.signature.clear();
|
||||
let _ = self.signature.write_all(&ed25519);
|
||||
let _ = self.signature.write_all(&p521);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, for_signing: bool) -> std::io::Result<()> {
|
||||
if self.roots.len() > u8::MAX as usize {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "maximum roots per root set: 255"));
|
||||
}
|
||||
|
||||
let name = self.name.as_bytes();
|
||||
let contact = self.contact.as_bytes();
|
||||
if name.len() > u8::MAX as usize {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "maximum roots per root set: 255"));
|
||||
}
|
||||
if contact.len() > u8::MAX as usize {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "maximum roots per root set: 255"));
|
||||
}
|
||||
|
||||
if for_signing {
|
||||
buf.append_u64(0x7f7f7f7f7f7f7f7f)?;
|
||||
}
|
||||
|
||||
match &self.root_set_type {
|
||||
Type::LegacyPlanet(id) | Type::LegacyMoon(id) => {
|
||||
buf.append_u8(if matches!(self.root_set_type, Type::LegacyPlanet(_)) {
|
||||
ROOT_SET_TYPE_LEGACY_PLANET
|
||||
} else {
|
||||
ROOT_SET_TYPE_LEGACY_MOON
|
||||
})?;
|
||||
buf.append_u64(*id)?;
|
||||
buf.append_u64(self.timestamp as u64)?;
|
||||
if self.signer.len() != 64 {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "legacy signer can only be 64 bytes"));
|
||||
}
|
||||
buf.append_bytes(self.signer.as_slice())?;
|
||||
if !for_signing {
|
||||
if self.signature.len() != 96 {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "legacy signature can only be 96 bytes"));
|
||||
}
|
||||
buf.append_bytes(self.signature.as_slice())?;
|
||||
}
|
||||
}
|
||||
|
||||
Type::Ed25519P521RootSet(_) => {
|
||||
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_u8(contact.len() as u8)?;
|
||||
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"));
|
||||
}
|
||||
buf.append_u8(self.signer.len() as u8)?;
|
||||
buf.append_bytes(self.signer.as_slice())?;
|
||||
if !for_signing {
|
||||
if self.signature.len() != (ED25519_SIGNATURE_SIZE + P521_ECDSA_SIGNATURE_SIZE) {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "signature can only be 192 bytes"));
|
||||
}
|
||||
buf.append_u8(self.signature.len() as u8)?;
|
||||
buf.append_bytes(self.signature.as_slice())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append_u8(self.roots.len() as u8)?;
|
||||
for root in self.roots.iter() {
|
||||
root.identity.marshal(buf, false)?;
|
||||
if root.endpoints.len() > u8::MAX as usize {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "maximum endpoints per root: 255"));
|
||||
}
|
||||
buf.append_u8(root.endpoints.len() as u8)?;
|
||||
for ep in root.endpoints.iter() {
|
||||
ep.marshal(buf)?;
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(self.root_set_type, Type::LegacyMoon(_)) {
|
||||
buf.append_u8(0)?;
|
||||
}
|
||||
|
||||
if for_signing {
|
||||
buf.append_u64(0x7f7f7f7f7f7f7f7f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||
self.marshal_internal(buf, false)
|
||||
}
|
||||
|
||||
pub fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
||||
let read_roots = |buf: &Buffer<BL>, cursor: &mut usize| -> std::io::Result<BTreeSet<Root>> {
|
||||
let mut roots = BTreeSet::<Root>::new();
|
||||
let root_count = buf.read_u8(cursor)? as usize;
|
||||
for _ in 0..root_count {
|
||||
let identity = Identity::unmarshal(buf, cursor)?;
|
||||
let mut endpoints = BTreeSet::<Endpoint>::new();
|
||||
let endpoint_count = buf.read_u8(cursor)? as usize;
|
||||
for _ in 0..endpoint_count {
|
||||
endpoints.insert(Endpoint::unmarshal(buf, cursor)?);
|
||||
}
|
||||
roots.insert(Root {
|
||||
identity,
|
||||
endpoints
|
||||
});
|
||||
}
|
||||
Ok(roots)
|
||||
};
|
||||
|
||||
let type_id = buf.read_u8(cursor)?;
|
||||
match type_id {
|
||||
ROOT_SET_TYPE_LEGACY_PLANET | ROOT_SET_TYPE_LEGACY_MOON => {
|
||||
let root_set_type = if type_id == ROOT_SET_TYPE_LEGACY_PLANET {
|
||||
Type::LegacyPlanet(buf.read_u64(cursor)?)
|
||||
} else {
|
||||
Type::LegacyMoon(buf.read_u64(cursor)?)
|
||||
};
|
||||
let timestamp = buf.read_u64(cursor)?;
|
||||
let signer = buf.read_bytes(64, cursor)?.to_vec();
|
||||
let signature = buf.read_bytes(96, cursor)?.to_vec();
|
||||
let roots = read_roots(buf, cursor)?;
|
||||
if type_id == ROOT_SET_TYPE_LEGACY_MOON {
|
||||
*cursor += buf.read_u8(cursor)? as usize;
|
||||
}
|
||||
Ok(Self {
|
||||
timestamp: timestamp as i64,
|
||||
name: String::new(),
|
||||
contact: String::new(),
|
||||
roots,
|
||||
signer,
|
||||
signature,
|
||||
root_set_type,
|
||||
})
|
||||
}
|
||||
|
||||
ROOT_SET_TYPE_ED25519_P521 => {
|
||||
let timestamp = buf.read_u64(cursor)?;
|
||||
let name = String::from_utf8_lossy(buf.read_bytes(buf.read_u8(cursor)? as usize, cursor)?).to_string();
|
||||
let contact = String::from_utf8_lossy(buf.read_bytes(buf.read_u8(cursor)? as usize, cursor)?).to_string();
|
||||
let signer = buf.read_bytes(buf.read_u8(cursor)? as usize, cursor)?.to_vec();
|
||||
let signature = buf.read_bytes(buf.read_u8(cursor)? as usize, cursor)?.to_vec();
|
||||
let root_set_type = Type::Ed25519P521RootSet(SHA384::hash(signer.as_slice()));
|
||||
Ok(Self {
|
||||
timestamp: timestamp as i64,
|
||||
name,
|
||||
contact,
|
||||
roots: read_roots(buf, cursor)?,
|
||||
signer,
|
||||
signature,
|
||||
root_set_type,
|
||||
})
|
||||
}
|
||||
|
||||
_ => {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized type"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this root set's globally unique ID.
|
||||
///
|
||||
/// For new root set format this is a hash of its public keys. For old style planet/moon
|
||||
/// this is a user assigned 64-bit ID. The latter is deprecated but still supported.
|
||||
pub fn id(&self) -> Vec<u8> {
|
||||
match self.root_set_type {
|
||||
Type::LegacyPlanet(id) => id.to_be_bytes().to_vec(),
|
||||
Type::LegacyMoon(id) => id.to_be_bytes().to_vec(),
|
||||
Type::Ed25519P521RootSet(id) => id.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RootSet {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.timestamp as u64);
|
||||
self.root_set_type.hash(state);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue