Root sets, tweak ballon hash and V1 identities to be shorter, cleanup.

This commit is contained in:
Adam Ierymenko 2021-08-11 22:09:31 -04:00
parent 788c310322
commit 0395943d31
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
13 changed files with 476 additions and 90 deletions

View file

@ -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",

View file

@ -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"

View file

@ -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)
}
/*

View file

@ -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> {

View file

@ -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)
}

View file

@ -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))
}

View file

@ -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();

View file

@ -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],

View file

@ -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() {

View file

@ -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
}

View file

@ -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;

View file

@ -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> {

View 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);
}
}