A bunch more of ZeroTier rusting...

This commit is contained in:
Adam Ierymenko 2021-07-22 00:03:22 -04:00
parent d70d958b5c
commit df25f09e6b
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
12 changed files with 599 additions and 71 deletions

View file

@ -3,7 +3,7 @@ name = "vli"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
[profile.test] [profile.release]
opt-level = 3 opt-level = 3
lto = true lto = true
codegen-units = 1 codegen-units = 1

View file

@ -13,6 +13,7 @@ const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
/// This also means the size of a VLI must be a multiple of 64 bits. Note that the actual /// This also means the size of a VLI must be a multiple of 64 bits. Note that the actual
/// integer in it need not be exactly that length, just the capacity of the container. /// integer in it need not be exactly that length, just the capacity of the container.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct VLI<const LIMBS: usize> { pub struct VLI<const LIMBS: usize> {
n: [u64; LIMBS] n: [u64; LIMBS]
} }
@ -31,6 +32,14 @@ impl<const LIMBS: usize> VLI<LIMBS> {
Self { n: [0_u64; LIMBS ]} Self { n: [0_u64; LIMBS ]}
} }
/// Get an integer with an undefined value.
/// This can be used in high performance code to avoid zeroing on new() when the
/// result will be overwritten right away by an operation.
#[inline(always)]
pub unsafe fn uninit() -> Self {
MaybeUninit::<VLI<LIMBS>>::uninit().assume_init()
}
/// Set to zero. /// Set to zero.
#[inline(always)] #[inline(always)]
pub fn zero(&mut self) { pub fn zero(&mut self) {
@ -99,18 +108,33 @@ impl<const LIMBS: usize> VLI<LIMBS> {
carry carry
} }
/// Multiply two inputs half the size of this integer to yield a full size result in this integer.. /// Subtract from this integer and return any remaining borrow bits.
pub fn sub_assign_borrow(&mut self, rhs: &Self) -> u64 {
let mut borrow = 0_u64;
for i in 0..LIMBS {
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
let left = *left_ptr;
let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow;
borrow = (diff > left) as u64;
*left_ptr = diff;
}
borrow
}
/// Multiply two inputs half the size of this integer to yield a full size result in this integer.
/// The multiplicand sizes MULT_LIMBS must be one half the LIMBS size of this integer. /// The multiplicand sizes MULT_LIMBS must be one half the LIMBS size of this integer.
/// This is checked with an assertion. This isn't computed with the type system due /// This is checked with an assertion. This isn't computed with the type system due
/// to current limitations in const generics. /// to current limitations in const generics.
pub fn mul_extend_assign<const MULT_LIMBS: usize>(&mut self, lhs: &VLI<{ MULT_LIMBS }>, rhs: &VLI<{ MULT_LIMBS }>) { pub fn mul_assign_widening<const MULT_LIMBS: usize>(&mut self, lhs: &VLI<{ MULT_LIMBS }>, rhs: &VLI<{ MULT_LIMBS }>) {
assert_eq!(MULT_LIMBS, LIMBS / 2); assert_eq!(MULT_LIMBS, LIMBS / 2);
let mut r01 = 0_u128; let mut r01 = 0_u128;
let mut r2 = 0_u64; let mut r2 = 0_u64;
let mut k = 0_usize; let mut k = 0_usize;
while k < MULT_LIMBS { while k < MULT_LIMBS {
for i in 0..k { for i in 0..k {
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128); debug_assert!(i < MULT_LIMBS);
debug_assert!((k - i) < MULT_LIMBS);
let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128);
r01 += l_product; r01 += l_product;
r2 += (r01 < l_product) as u64; r2 += (r01 < l_product) as u64;
} }
@ -120,12 +144,12 @@ impl<const LIMBS: usize> VLI<LIMBS> {
k += 1; k += 1;
} }
while k < (LIMBS - 1) { while k < (LIMBS - 1) {
let mut i = (k + 1) - MULT_LIMBS; for i in ((k + 1) - MULT_LIMBS)..k {
while i < k && i < MULT_LIMBS { debug_assert!(i < MULT_LIMBS);
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128); debug_assert!((k - i) < MULT_LIMBS);
let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128);
r01 += l_product; r01 += l_product;
r2 += (r01 < l_product) as u64; r2 += (r01 < l_product) as u64;
i += 1;
} }
*unsafe { self.n.get_unchecked_mut(k) } = r01 as u64; *unsafe { self.n.get_unchecked_mut(k) } = r01 as u64;
r01 += (r01 >> 64) | ((r2 as u128) << 64); r01 += (r01 >> 64) | ((r2 as u128) << 64);
@ -139,14 +163,14 @@ impl<const LIMBS: usize> VLI<LIMBS> {
/// If skip_leading_zeroes is true the returned byte vector will be the minimum size /// If skip_leading_zeroes is true the returned byte vector will be the minimum size
/// needed to hold the integer, or empty if it is zero. Otherwise it will always be /// needed to hold the integer, or empty if it is zero. Otherwise it will always be
/// LIMBS * 8 bytes in length. /// LIMBS * 8 bytes in length.
pub fn to_vec(&self, skip_leading_zeroes: bool) -> Vec<u8> { pub fn to_be_bytes(&self, skip_leading_zeroes: bool) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.reserve(LIMBS * 8); bytes.reserve(LIMBS * 8);
let mut i = LIMBS as isize - 1; let mut i = LIMBS as isize - 1;
if skip_leading_zeroes { if skip_leading_zeroes {
while i >= 0 { while i >= 0 {
let x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; let x: u64 = self.n[i as usize];
if x != 0 { if x != 0 {
let x = x.to_be_bytes(); let x = x.to_be_bytes();
for j in 0..8 { for j in 0..8 {
@ -161,7 +185,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
} }
} }
while i >= 0 { while i >= 0 {
let _ = bytes.write_all(&(unsafe { self.n.get_unchecked(i as usize) }.to_be_bytes())); let _ = bytes.write_all(&self.n[i as usize].to_be_bytes());
i -= 1; i -= 1;
} }
@ -178,7 +202,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
let mut i = LIMBS as isize - 1; let mut i = LIMBS as isize - 1;
if skip_leading_zeroes { if skip_leading_zeroes {
while i >= 0 { while i >= 0 {
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; let mut x = self.n[i as usize];
if x != 0 { if x != 0 {
let mut j = 0; let mut j = 0;
while j < 16 { while j < 16 {
@ -199,7 +223,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
} }
} }
while i >= 0 { while i >= 0 {
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) }; let mut x: u64 = self.n[i as usize];
for _ in 0..16 { for _ in 0..16 {
s.push(HEX_CHARS[(x >> 60) as usize]); s.push(HEX_CHARS[(x >> 60) as usize]);
x <<= 4; x <<= 4;
@ -220,21 +244,15 @@ impl<const LIMBS: usize> Add<&Self> for VLI<LIMBS> {
#[inline(always)] #[inline(always)]
fn add(mut self, rhs: &Self) -> Self::Output { fn add(mut self, rhs: &Self) -> Self::Output {
self.add_assign(rhs); let _ = self.add_assign_carry(rhs);
self self
} }
} }
impl<const LIMBS: usize> AddAssign<&Self> for VLI<LIMBS> { impl<const LIMBS: usize> AddAssign<&Self> for VLI<LIMBS> {
#[inline(always)]
fn add_assign(&mut self, rhs: &Self) { fn add_assign(&mut self, rhs: &Self) {
let mut carry = 0_u64; let _ = self.add_assign_carry(rhs);
for i in 0..LIMBS {
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
let left = *left_ptr;
let sum = left + *unsafe { rhs.n.get_unchecked(i) } + carry;
carry = (sum < left) as u64;
*left_ptr = sum;
}
} }
} }
@ -243,21 +261,15 @@ impl<const LIMBS: usize> Sub<&Self> for VLI<LIMBS> {
#[inline(always)] #[inline(always)]
fn sub(mut self, rhs: &Self) -> Self::Output { fn sub(mut self, rhs: &Self) -> Self::Output {
self.sub_assign(rhs); let _ = self.sub_assign_borrow(rhs);
self self
} }
} }
impl<const LIMBS: usize> SubAssign<&Self> for VLI<LIMBS> { impl<const LIMBS: usize> SubAssign<&Self> for VLI<LIMBS> {
#[inline(always)]
fn sub_assign(&mut self, rhs: &Self) { fn sub_assign(&mut self, rhs: &Self) {
let mut borrow = 0_u64; let _ = self.sub_assign_borrow(rhs);
for i in 0..LIMBS {
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
let left = *left_ptr;
let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow;
borrow = (diff > left) as u64;
*left_ptr = diff;
}
} }
} }
@ -274,7 +286,13 @@ impl<const LIMBS: usize> Shl<usize> for VLI<LIMBS> {
impl<const LIMBS: usize> ShlAssign<usize> for VLI<LIMBS> { impl<const LIMBS: usize> ShlAssign<usize> for VLI<LIMBS> {
fn shl_assign(&mut self, rhs: usize) { fn shl_assign(&mut self, rhs: usize) {
if rhs != 0 { if rhs != 0 {
if rhs < 64 { if rhs < (LIMBS * 64) {
let whole_limb_shifts = rhs >> 6;
if whole_limb_shifts != 0 {
self.n.copy_within(0..(LIMBS - whole_limb_shifts), whole_limb_shifts);
self.n[0..whole_limb_shifts].fill(0);
}
let rhs = rhs & 63;
let mut carry = 0_u64; let mut carry = 0_u64;
for i in 0..LIMBS { for i in 0..LIMBS {
let x_ptr = unsafe { self.n.get_unchecked_mut(i) }; let x_ptr = unsafe { self.n.get_unchecked_mut(i) };
@ -302,7 +320,13 @@ impl<const LIMBS: usize> Shr<usize> for VLI<LIMBS> {
impl<const LIMBS: usize> ShrAssign<usize> for VLI<LIMBS> { impl<const LIMBS: usize> ShrAssign<usize> for VLI<LIMBS> {
fn shr_assign(&mut self, rhs: usize) { fn shr_assign(&mut self, rhs: usize) {
if rhs != 0 { if rhs != 0 {
if rhs < 64 { if rhs < (LIMBS * 64) {
let whole_limb_shifts = rhs >> 6;
if whole_limb_shifts != 0 {
self.n.copy_within(whole_limb_shifts..LIMBS, 0);
self.n[(LIMBS - whole_limb_shifts)..LIMBS].fill(0);
}
let rhs = rhs & 63;
let mut carry = 0_u64; let mut carry = 0_u64;
let mut i = LIMBS as isize - 1; let mut i = LIMBS as isize - 1;
while i >= 0 { while i >= 0 {

View file

@ -3,6 +3,17 @@ name = "zerotier-network-hypervisor"
version = "2.0.0" version = "2.0.0"
edition = "2018" edition = "2018"
# [profile.test]
# opt-level = 3
# lto = true
# codegen-units = 1
[profile.release]
lto = true
opt-level = 'z'
codegen-units = 1
panic = 'abort'
[dependencies] [dependencies]
rand_core = "^0" rand_core = "^0"
aes-gmac-siv = { path = "../aes-gmac-siv" } aes-gmac-siv = { path = "../aes-gmac-siv" }

View file

@ -0,0 +1,126 @@
use std::convert::TryInto;
use std::mem::MaybeUninit;
#[inline(always)]
fn hash_int_le(sha: &mut crate::crypto::hash::SHA512, i: u64) {
#[cfg(target_endian = "big")] {
sha.update(i.to_le_bytes());
}
#[cfg(target_endian = "little")] {
sha.update(unsafe { &*(&i as *const u64).cast::<[u8; 8]>() });
}
}
/// Compute balloon memory-hard hash using SHA-512.
/// SPACE_COST must be a multiple of 64. This is checked with an assertion.
/// DELTA is usually 3.
pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8], salt: &[u8]) -> [u8; crate::crypto::hash::SHA512_HASH_SIZE] {
assert_ne!(SPACE_COST, 0);
assert_eq!((SPACE_COST % 64), 0);
let mut buf: [u8; SPACE_COST] = unsafe { MaybeUninit::uninit().assume_init() };
let zero64 = [0_u8; 8];
/* Initial hash */
let mut sha = crate::crypto::hash::SHA512::new();
sha.update(&zero64); // 0 cnt
sha.update(password);
sha.update(salt);
buf[0..64].copy_from_slice(sha.finish_get_ref());
/* Expand */
let mut cnt = 1_u64;
let mut s: usize = 64;
while s < SPACE_COST {
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(&buf[(s - 64)..s]);
let ss = s + 64;
buf[s..ss].copy_from_slice(sha.finish_get_ref());
s = ss;
cnt += 1;
}
/* Mix */
for t in 0..TIME_COST {
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(&buf[(SPACE_COST - 64)..SPACE_COST]); // "previous" initially wraps back around to end
sha.update(&buf[0..64]);
buf[0..64].copy_from_slice(sha.finish_get_ref());
cnt += 1;
for i in 0..DELTA {
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(salt);
hash_int_le(&mut sha, t as u64);
sha.update(&zero64); // s == 0
hash_int_le(&mut sha, i as u64);
cnt += 1;
let other = sha.finish_get_ref();
let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize;
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(&buf[0..64]);
sha.update(&buf[other..(other + 64)]);
buf[0..64].copy_from_slice(sha.finish_get_ref());
cnt += 1;
}
let mut s = 64;
while s < SPACE_COST {
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(&buf[(s - 64)..s]);
let ss = s + 64;
sha.update(&buf[s..ss]);
buf[s..ss].copy_from_slice(sha.finish_get_ref());
cnt += 1;
for i in 0..DELTA {
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(salt);
hash_int_le(&mut sha, t as u64);
hash_int_le(&mut sha, s as u64);
hash_int_le(&mut sha, i as u64);
cnt += 1;
let other = sha.finish_get_ref();
let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize;
sha.reset();
hash_int_le(&mut sha, cnt);
sha.update(&buf[s..ss]);
sha.update(&buf[other..(other + 64)]);
buf[s..ss].copy_from_slice(sha.finish_get_ref());
cnt += 1;
}
s = ss;
}
}
/* Extract */
buf[(SPACE_COST - 64)..SPACE_COST].try_into().unwrap()
}
/*
#[cfg(test)]
mod tests {
#[test]
fn balloon_test() {
let start = std::time::SystemTime::now();
let mut tmp = 0_u8;
for _ in 0..100 {
let foo = crate::crypto::balloon::hash::<16384, 3, 3>(&[1_u8], &[2_u8]);
tmp = tmp.wrapping_add(foo[0]);
}
let duration = std::time::SystemTime::now().duration_since(start).unwrap();
println!("Benchmark: {}ms per hash (junk to prevent optimizing out: {})", (duration.as_nanos() as f64 / 100.0) / 1000000.0, tmp);
}
}
*/

View file

@ -15,14 +15,14 @@ pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
impl C25519KeyPair { impl C25519KeyPair {
#[inline(always)] #[inline(always)]
pub fn new() -> C25519KeyPair { pub fn generate() -> C25519KeyPair {
let sk = x25519_dalek::StaticSecret::new(rand_core::OsRng); let sk = x25519_dalek::StaticSecret::new(rand_core::OsRng);
let pk = x25519_dalek::PublicKey::from(&sk); let pk = x25519_dalek::PublicKey::from(&sk);
C25519KeyPair(sk, pk) C25519KeyPair(sk, pk)
} }
#[inline(always)] #[inline(always)]
pub fn from_keys(public_key: &[u8], secret_key: &[u8]) -> Option<C25519KeyPair> { pub fn from_bytes(public_key: &[u8], secret_key: &[u8]) -> Option<C25519KeyPair> {
if public_key.len() == 32 && secret_key.len() == 32 { if public_key.len() == 32 && secret_key.len() == 32 {
let pk: [u8; 32] = public_key.try_into().unwrap(); let pk: [u8; 32] = public_key.try_into().unwrap();
let sk: [u8; 32] = secret_key.try_into().unwrap(); let sk: [u8; 32] = secret_key.try_into().unwrap();
@ -59,7 +59,7 @@ pub struct Ed25519KeyPair(ed25519_dalek::Keypair);
impl Ed25519KeyPair { impl Ed25519KeyPair {
#[inline(always)] #[inline(always)]
pub fn new() -> Ed25519KeyPair { pub fn generate() -> Ed25519KeyPair {
let mut rng = rand_core::OsRng::default(); let mut rng = rand_core::OsRng::default();
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng)) Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
} }
@ -72,6 +72,18 @@ impl Ed25519KeyPair {
Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap()) Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap())
} }
#[inline(always)]
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<Ed25519KeyPair> {
if public_bytes.len() == ED25519_PUBLIC_KEY_SIZE && secret_bytes.len() == ED25519_SECRET_KEY_SIZE {
let mut tmp = [0_u8; 64];
tmp[0..32].copy_from_slice(public_bytes);
tmp[32..64].copy_from_slice(secret_bytes);
Some(Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap()))
} else {
None
}
}
#[inline(always)] #[inline(always)]
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] { pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] {
self.0.public.to_bytes() self.0.public.to_bytes()

View file

@ -3,3 +3,4 @@ pub mod hash;
pub mod p521; pub mod p521;
pub mod salsa; pub mod salsa;
pub mod poly1305; pub mod poly1305;
pub mod balloon;

View file

@ -66,7 +66,7 @@ pub struct P521PublicKey {
pub struct P521KeyPair { pub struct P521KeyPair {
public_key: P521PublicKey, public_key: P521PublicKey,
secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression
secret_key_for_ecdh: SExpression, // secret key as a "data" S-expression for the weird gcrypt ECDH interface secret_key_for_ecdh: SExpression, // the same secret key as a "data" S-expression for the weird gcrypt ECDH interface
secret_key_bytes: [u8; P521_SECRET_KEY_SIZE], secret_key_bytes: [u8; P521_SECRET_KEY_SIZE],
} }
@ -97,7 +97,7 @@ impl P521KeyPair {
public_key: pk_exp, public_key: pk_exp,
public_key_bytes: [0_u8; P521_PUBLIC_KEY_SIZE], public_key_bytes: [0_u8; P521_PUBLIC_KEY_SIZE],
}, },
secret_key_for_ecdsa: sk_exp, secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #{}#)(d #{}#)))", crate::util::hex::to_string(pk), crate::util::hex::to_string(sk)).as_str()).unwrap(),
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(sk)).as_str()).unwrap(), secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(sk)).as_str()).unwrap(),
secret_key_bytes: [0_u8; P521_SECRET_KEY_SIZE], secret_key_bytes: [0_u8; P521_SECRET_KEY_SIZE],
}; };
@ -111,6 +111,23 @@ impl P521KeyPair {
}) })
} }
/// Construct this key pair from both a public and a private key.
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<P521KeyPair> {
if secret_bytes.len() != P521_SECRET_KEY_SIZE {
return None;
}
let public_key = P521PublicKey::from_bytes(public_bytes);
if public_key.is_none() {
return None;
}
Some(P521KeyPair {
public_key: public_key.unwrap(),
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #04{}#)(d #{}#)))", crate::util::hex::to_string(public_bytes), crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(),
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(),
secret_key_bytes: secret_bytes.try_into().unwrap(),
})
}
#[inline(always)] #[inline(always)]
pub fn public_key(&self) -> &P521PublicKey { pub fn public_key(&self) -> &P521PublicKey {
&self.public_key &self.public_key
@ -161,6 +178,9 @@ impl P521KeyPair {
} }
impl P521PublicKey { impl P521PublicKey {
/// Construct a public key from a byte serialized representation.
/// None is returned if the input is not valid. No advanced checking such as
/// determining if this is a point on the curve is performed.
pub fn from_bytes(b: &[u8]) -> Option<P521PublicKey> { pub fn from_bytes(b: &[u8]) -> Option<P521PublicKey> {
if b.len() == P521_PUBLIC_KEY_SIZE { if b.len() == P521_PUBLIC_KEY_SIZE {
Some(P521PublicKey { Some(P521PublicKey {
@ -188,10 +208,11 @@ impl P521PublicKey {
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] { pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] {
&self.public_key_bytes &self.public_key_bytes
} }
}
#[inline(always)] impl Clone for P521PublicKey {
pub fn as_bytes(&self) -> [u8; P521_PUBLIC_KEY_SIZE] { fn clone(&self) -> Self {
self.public_key_bytes.clone() P521PublicKey::from_bytes(&self.public_key_bytes).unwrap()
} }
} }
@ -217,5 +238,11 @@ mod tests {
if !sec0.eq(&sec1) { if !sec0.eq(&sec1) {
panic!("ECDH secrets do not match"); panic!("ECDH secrets do not match");
} }
let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes()).unwrap();
let sig = kp3.sign(&[3_u8]).unwrap();
if !kp.public_key().verify(&[3_u8], &sig) {
panic!("ECDSA verify failed (from key reconstructed from bytes)");
}
} }
} }

View file

@ -40,3 +40,5 @@ pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 {
pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 { pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 {
(d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64) (d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64)
} }
pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];

View file

@ -118,7 +118,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a dynamic byte slice (copy into buffer). /// Append a dynamic byte slice (copy into buffer).
/// Use append_and_init_ functions if possible as these avoid extra copies. /// Use append_and_init_ functions if possible as these avoid extra copies.
#[inline(always)] #[inline(always)]
fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> { pub fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
let end = ptr + buf.len(); let end = ptr + buf.len();
if end <= L { if end <= L {
@ -133,7 +133,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a fixed length byte array (copy into buffer). /// Append a fixed length byte array (copy into buffer).
/// Use append_and_init_ functions if possible as these avoid extra copies. /// Use append_and_init_ functions if possible as these avoid extra copies.
#[inline(always)] #[inline(always)]
fn append_bytes_fixed<const S: usize>(&mut self, buf: &[u8; S]) -> std::io::Result<()> { pub fn append_bytes_fixed<const S: usize>(&mut self, buf: &[u8; S]) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
let end = ptr + S; let end = ptr + S;
if end <= L { if end <= L {
@ -147,7 +147,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a byte /// Append a byte
#[inline(always)] #[inline(always)]
fn append_u8(&mut self, i: u8) -> std::io::Result<()> { pub fn append_u8(&mut self, i: u8) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
if ptr < L { if ptr < L {
self.0 = ptr + 1; self.0 = ptr + 1;
@ -160,7 +160,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a 16-bit integer (in big-endian form) /// Append a 16-bit integer (in big-endian form)
#[inline(always)] #[inline(always)]
fn append_u16(&mut self, i: u16) -> std::io::Result<()> { pub fn append_u16(&mut self, i: u16) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
let end = ptr + 2; let end = ptr + 2;
if end <= L { if end <= L {
@ -174,7 +174,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a 32-bit integer (in big-endian form) /// Append a 32-bit integer (in big-endian form)
#[inline(always)] #[inline(always)]
fn append_u32(&mut self, i: u32) -> std::io::Result<()> { pub fn append_u32(&mut self, i: u32) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
let end = ptr + 4; let end = ptr + 4;
if end <= L { if end <= L {
@ -188,7 +188,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
/// Append a 64-bit integer (in big-endian form) /// Append a 64-bit integer (in big-endian form)
#[inline(always)] #[inline(always)]
fn append_u64(&mut self, i: u64) -> std::io::Result<()> { pub fn append_u64(&mut self, i: u64) -> std::io::Result<()> {
let ptr = self.0; let ptr = self.0;
let end = ptr + 8; let end = ptr + 8;
if end <= L { if end <= L {
@ -200,6 +200,12 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
} }
} }
/// Get the index of the start of the payload after the header.
#[inline(always)]
pub fn cursor_after_header(&self) -> usize {
size_of::<usize>()
}
/// Get a structure at a given position in the buffer and advance the cursor. /// Get a structure at a given position in the buffer and advance the cursor.
#[inline(always)] #[inline(always)]
pub fn get_struct<T: RawObject>(&self, cursor: &mut usize) -> std::io::Result<&T> { pub fn get_struct<T: RawObject>(&self, cursor: &mut usize) -> std::io::Result<&T> {

View file

@ -1,9 +1,20 @@
use std::alloc::{Layout, dealloc, alloc};
use std::ptr::{slice_from_raw_parts_mut, slice_from_raw_parts};
use std::io::Write; use std::io::Write;
use crate::vl1::Address;
use crate::vl1::buffer::{Buffer, RawObject, NoHeader};
use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair}; use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair};
use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE}; use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE};
use crate::vl1::Address; use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE};
use crate::crypto::hash::SHA384; use crate::crypto::balloon;
use crate::crypto::salsa::Salsa;
const V0_IDENTITY_GEN_MEMORY: usize = 2097152;
const V1_BALLOON_SPACE_COST: usize = 16384;
const V1_BALLOON_TIME_COST: usize = 3;
const V1_BALLOON_DELTA: usize = 3;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[repr(u8)] #[repr(u8)]
@ -11,28 +22,134 @@ pub enum Type {
/// Curve25519 / Ed25519 identity (type 0) /// Curve25519 / Ed25519 identity (type 0)
C25519 = 0, C25519 = 0,
/// NIST P-521 ECDH / ECDSA identity (also has c25519/ed25519 keys for backward compability) (type 1) /// NIST P-521 ECDH / ECDSA identity (also has c25519/ed25519 keys for backward compability) (type 1)
P521 = 1 P521 = 1,
} }
struct IdentitySecrets { struct IdentitySecrets {
c25519: C25519KeyPair, c25519: C25519KeyPair,
ed25519: Ed25519KeyPair, ed25519: Ed25519KeyPair,
p521: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key v1: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key
} }
pub struct Identity { pub struct Identity {
address: Address, address: Address,
c25519_public: [u8; C25519_PUBLIC_KEY_SIZE], c25519: [u8; C25519_PUBLIC_KEY_SIZE],
ed25519_public: [u8; ED25519_PUBLIC_KEY_SIZE], ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
p521_public: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE])>, // ecdh key, ecdsa key, ecdsa signature of all keys v1: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE], [u8; SHA512_HASH_SIZE])>,
secrets: Option<IdentitySecrets>, secrets: Option<IdentitySecrets>,
} }
impl Identity { /// Compute result from the bespoke "frankenhash" from the old V0 work function.
fn generate_c25519() { /// The supplied genmem_ptr must be of size V0_IDENTITY_GEN_MEMORY and aligned to an 8-byte boundary.
fn v0_frankenhash(digest: &mut [u8; 64], genmem_ptr: *mut u8) {
let genmem = unsafe { &mut *slice_from_raw_parts_mut(genmem_ptr, V0_IDENTITY_GEN_MEMORY) };
let genmem_alias_hack = unsafe { &*slice_from_raw_parts(genmem_ptr, V0_IDENTITY_GEN_MEMORY) };
let genmem_u64_ptr = genmem_ptr.cast::<u64>();
let mut s20 = Salsa::new(&digest[0..32], &digest[32..40], false).unwrap();
s20.crypt(&crate::util::ZEROES[0..64], &mut genmem[0..64]);
let mut i: usize = 64;
while i < V0_IDENTITY_GEN_MEMORY {
let ii = i + 64;
s20.crypt(&genmem_alias_hack[(i - 64)..i], &mut genmem[i..ii]);
i = ii;
} }
fn generate_p521() { i = 0;
while i < (V0_IDENTITY_GEN_MEMORY / 8) {
let idx1 = ((unsafe { *genmem_u64_ptr.offset(i as isize) }.to_be() % 8) * 8) as usize;
let idx2 = (unsafe { *genmem_u64_ptr.offset((i + 1) as isize) }.to_be() % (V0_IDENTITY_GEN_MEMORY as u64 / 8)) as usize;
let genmem_u64_at_idx2_ptr = unsafe { genmem_u64_ptr.offset(idx2 as isize) };
let tmp = unsafe { *genmem_u64_at_idx2_ptr };
let digest_u64_ptr = unsafe { digest.as_mut_ptr().offset(idx1 as isize).cast::<u64>() };
unsafe { *genmem_u64_at_idx2_ptr = *digest_u64_ptr };
unsafe { *digest_u64_ptr = tmp };
s20.crypt_in_place(digest);
i += 2;
}
}
impl Identity {
fn generate_c25519() -> Identity {
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
let genmem_ptr = unsafe { alloc(genmem_layout) };
if genmem_ptr.is_null() {
panic!("unable to allocate memory for V0 identity generation");
}
let ed25519 = Ed25519KeyPair::generate();
let ed25519_pub_bytes = ed25519.public_bytes();
let mut sha = SHA512::new();
loop {
let c25519 = C25519KeyPair::generate();
let c25519_pub_bytes = c25519.public_bytes();
sha.update(&ed25519_pub_bytes);
sha.update(&c25519_pub_bytes);
let mut digest = sha.finish();
v0_frankenhash(&mut digest, genmem_ptr);
if digest[0] < 17 {
let addr = Address::from_bytes(&digest[59..64]).unwrap();
if addr.is_valid() {
unsafe { dealloc(genmem_ptr, genmem_layout) };
return Identity {
address: addr,
c25519: c25519_pub_bytes,
ed25519: ed25519_pub_bytes,
v1: None,
secrets: Some(IdentitySecrets {
c25519,
ed25519,
v1: None,
}),
};
}
}
sha.reset();
}
}
fn generate_p521() -> Identity {
let c25519 = C25519KeyPair::generate();
let ed25519 = Ed25519KeyPair::generate();
let p521_ecdh = P521KeyPair::generate(false).unwrap();
let p521_ecdsa = P521KeyPair::generate(false).unwrap();
let c25519_pub_bytes = c25519.public_bytes();
let ed25519_pub_bytes = ed25519.public_bytes();
let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE];
signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&c25519_pub_bytes);
signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&ed25519_pub_bytes);
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice(p521_ecdh.public_key_bytes());
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice(p521_ecdsa.public_key_bytes());
loop {
// ECDSA is a randomized signature algorithm, so each signature will be different.
let sig = p521_ecdsa.sign(&signing_buf).unwrap();
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&sig, b"zt_id_v1");
if bh[0] < 7 {
let addr = Address::from_bytes(&bh[59..64]).unwrap();
if addr.is_valid() {
let p521_ecdh_pub = p521_ecdh.public_key().clone();
let p521_ecdsa_pub = p521_ecdsa.public_key().clone();
return Identity {
address: addr,
c25519: c25519_pub_bytes,
ed25519: ed25519_pub_bytes,
v1: Some((p521_ecdh_pub, p521_ecdsa_pub, sig, bh)),
secrets: Some(IdentitySecrets {
c25519,
ed25519,
v1: Some((p521_ecdh, p521_ecdsa)),
}),
};
}
}
}
} }
/// Generate a new identity. /// Generate a new identity.
@ -41,24 +158,66 @@ impl Identity {
/// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites /// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites
/// take about 500ms. Generation can take a lot longer on low power devices, but only /// take about 500ms. Generation can take a lot longer on low power devices, but only
/// has to be done once. /// has to be done once.
pub fn generate(id_type: Type) { pub fn generate(id_type: Type) -> Identity {
match id_type { match id_type {
Type::C25519 => Self::generate_c25519(), Type::C25519 => Self::generate_c25519(),
Type::P521 => Self::generate_p521() Type::P521 => Self::generate_p521()
} }
} }
/// Locally validate this identity.
/// This can take a few milliseconds, especially on slower systems. V0 identities are slower
/// to fully validate than V1 identities.
pub fn locally_validate(&self) -> bool {
if self.v1.is_some() {
if self.address.is_valid() {
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
let genmem_ptr = unsafe { alloc(genmem_layout) };
if genmem_ptr.is_null() {
false
} else {
let mut sha = SHA512::new();
sha.update(&self.c25519);
sha.update(&self.ed25519);
let mut digest = sha.finish();
v0_frankenhash(&mut digest, genmem_ptr);
unsafe { dealloc(genmem_ptr, genmem_layout) };
(digest[0] < 17) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
}
} else {
false
}
} else {
if self.address.is_valid() {
let p521 = self.v1.as_ref().unwrap();
let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE];
signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&self.c25519);
signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519);
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).0.public_key_bytes());
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).1.public_key_bytes());
if (*p521).1.verify(&signing_buf, &(*p521).2) {
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2, b"zt_id_v1");
(bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[59..64]).unwrap().eq(&self.address)
} else {
false
}
} else {
false
}
}
}
/// Execute ECDH key agreement and return SHA384(shared secret). /// 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 /// 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 /// with Curve25519. None is returned if there is an error such as this identity missing
/// its secrets or a key being invalid. /// its secrets or a key being invalid.
pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> { pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> {
self.secrets.as_ref().map_or(None, |secrets| { self.secrets.as_ref().map_or(None, |secrets| {
secrets.p521.as_ref().map_or_else(|| { secrets.v1.as_ref().map_or_else(|| {
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public))) Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519)))
}, |p521_secret| { }, |p521_secret| {
other_identity.p521_public.as_ref().map_or_else(|| { other_identity.v1.as_ref().map_or_else(|| {
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public))) Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519)))
}, |other_p521_public| { }, |other_p521_public| {
p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret))) p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret)))
}) })
@ -71,7 +230,7 @@ impl Identity {
/// type. None is returned if this identity lacks secret keys or another error occurs. /// type. None is returned if this identity lacks secret keys or another error occurs.
pub fn sign(&self, msg: &[u8]) -> Option<Vec<u8>> { pub fn sign(&self, msg: &[u8]) -> Option<Vec<u8>> {
self.secrets.as_ref().map_or(None, |secrets| { self.secrets.as_ref().map_or(None, |secrets| {
secrets.p521.as_ref().map_or_else(|| { secrets.v1.as_ref().map_or_else(|| {
Some(secrets.ed25519.sign(msg).to_vec()) Some(secrets.ed25519.sign(msg).to_vec())
}, |p521_secret| { }, |p521_secret| {
p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec())) p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec()))
@ -79,10 +238,19 @@ impl Identity {
}) })
} }
/// Verify a signature.
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
self.v1.as_ref().map_or_else(|| {
crate::crypto::c25519::ed25519_verify(&self.ed25519, signature, msg)
}, |p521| {
(*p521).1.verify(msg, signature)
})
}
/// Get this identity's type. /// Get this identity's type.
#[inline(always)] #[inline(always)]
pub fn id_type(&self) -> Type { pub fn id_type(&self) -> Type {
if self.p521_public.is_some() { if self.v1.is_some() {
Type::P521 Type::P521
} else { } else {
Type::C25519 Type::C25519
@ -100,7 +268,7 @@ impl Identity {
self.secrets.as_ref().map_or_else(|| { self.secrets.as_ref().map_or_else(|| {
self.to_string() self.to_string()
}, |secrets| { }, |secrets| {
secrets.p521.as_ref().map_or_else(|| { secrets.v1.as_ref().map_or_else(|| {
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes())) format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
}, |p521_secret| { }, |p521_secret| {
let mut secret_key_blob: Vec<u8> = Vec::new(); let mut secret_key_blob: Vec<u8> = Vec::new();
@ -113,21 +281,170 @@ impl Identity {
}) })
}) })
} }
/// Append this in binary format to a buffer.
pub fn marshal<BH: RawObject, const BL: usize>(&self, buf: &mut Buffer<BH, BL>, include_private: bool) -> std::io::Result<()> {
buf.append_bytes_fixed(&self.address.to_bytes())?;
if self.v1.is_some() {
let p521 = self.v1.as_ref().unwrap();
buf.append_u8(1)?; // type 1
buf.append_bytes_fixed(&self.c25519)?;
buf.append_bytes_fixed(&self.ed25519)?;
buf.append_bytes_fixed((*p521).0.public_key_bytes())?;
buf.append_bytes_fixed((*p521).1.public_key_bytes())?;
buf.append_bytes_fixed(&(*p521).2)?;
buf.append_bytes_fixed(&(*p521).3)?;
if include_private && self.secrets.is_some() {
let secrets = self.secrets.as_ref().unwrap();
if secrets.v1.is_some() {
let p521_secrets = secrets.v1.as_ref().unwrap();
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u8)?;
buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?;
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?;
buf.append_bytes_fixed((*p521_secrets).0.secret_key_bytes())?;
buf.append_bytes_fixed((*p521_secrets).1.secret_key_bytes())?;
}
} else {
buf.append_u8(0)?; // 0 secret bytes if not adding any
}
} else {
buf.append_and_init_bytes_fixed(|b: &mut [u8; 1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE]| {
b[0] = 0; // type 0
b[1..(1 + C25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.c25519);
b[(1 + C25519_PUBLIC_KEY_SIZE)..(1 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519);
})?;
if include_private && self.secrets.is_some() {
let secrets = self.secrets.as_ref().unwrap();
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8)?;
buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?;
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?;
} else {
buf.append_u8(0)?; // 0 secret bytes if not adding any
}
}
Ok(())
}
/// Deserialize an Identity from a buffer.
/// The supplied cursor is advanced.
pub fn unmarshal<BH: RawObject, const BL: usize>(buf: &Buffer<BH, BL>, cursor: &mut usize) -> std::io::Result<Identity> {
let addr = Address::from_bytes(buf.get_bytes_fixed::<5>(cursor)?).unwrap();
let id_type = buf.get_u8(cursor)?;
if id_type == Type::C25519 as u8 {
let c25519_public_bytes = buf.get_bytes_fixed::<{ C25519_PUBLIC_KEY_SIZE }>(cursor)?;
let ed25519_public_bytes = buf.get_bytes_fixed::<{ ED25519_PUBLIC_KEY_SIZE }>(cursor)?;
let secrets_len = buf.get_u8(cursor)?;
if secrets_len == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 {
let c25519_secret_bytes = buf.get_bytes_fixed::<{ C25519_SECRET_KEY_SIZE }>(cursor)?;
let ed25519_secret_bytes = buf.get_bytes_fixed::<{ ED25519_SECRET_KEY_SIZE }>(cursor)?;
Ok(Identity {
address: addr,
c25519: c25519_public_bytes.clone(),
ed25519: ed25519_public_bytes.clone(),
v1: None,
secrets: Some(IdentitySecrets {
c25519: C25519KeyPair::from_bytes(c25519_public_bytes, c25519_secret_bytes).unwrap(),
ed25519: Ed25519KeyPair::from_bytes(ed25519_public_bytes, ed25519_secret_bytes).unwrap(),
v1: None,
})
})
} else if secrets_len == 0 {
Ok(Identity {
address: addr,
c25519: c25519_public_bytes.clone(),
ed25519: ed25519_public_bytes.clone(),
v1: None,
secrets: None
})
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized scret key length (type 0)"))
}
} else if id_type == Type::P521 as u8 {
let c25519_public_bytes = buf.get_bytes_fixed::<{ C25519_PUBLIC_KEY_SIZE }>(cursor)?;
let ed25519_public_bytes = buf.get_bytes_fixed::<{ ED25519_PUBLIC_KEY_SIZE }>(cursor)?;
let p521_ecdh_public_bytes = buf.get_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?;
let p521_ecdsa_public_bytes = buf.get_bytes_fixed::<{ P521_PUBLIC_KEY_SIZE }>(cursor)?;
let p521_signature = buf.get_bytes_fixed::<{ P521_ECDSA_SIGNATURE_SIZE }>(cursor)?;
let bh_digest = buf.get_bytes_fixed::<{ SHA512_HASH_SIZE }>(cursor)?;
let secrets_len = buf.get_u8(cursor)?;
if secrets_len == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u8 {
let c25519_secret_bytes = buf.get_bytes_fixed::<{ C25519_SECRET_KEY_SIZE }>(cursor)?;
let ed25519_secret_bytes = buf.get_bytes_fixed::<{ ED25519_SECRET_KEY_SIZE }>(cursor)?;
let p521_ecdh_secret_bytes = buf.get_bytes_fixed::<{ P521_SECRET_KEY_SIZE }>(cursor)?;
let p521_ecdsa_secret_bytes = buf.get_bytes_fixed::<{ P521_SECRET_KEY_SIZE }>(cursor)?;
Ok(Identity {
address: addr,
c25519: c25519_public_bytes.clone(),
ed25519: ed25519_public_bytes.clone(),
v1: Some((P521PublicKey::from_bytes(p521_ecdh_public_bytes).unwrap(), P521PublicKey::from_bytes(p521_ecdsa_public_bytes).unwrap(), p521_signature.clone(), bh_digest. clone())),
secrets: Some(IdentitySecrets {
c25519: C25519KeyPair::from_bytes(c25519_public_bytes, c25519_secret_bytes).unwrap(),
ed25519: Ed25519KeyPair::from_bytes(ed25519_public_bytes, ed25519_secret_bytes).unwrap(),
v1: Some((P521KeyPair::from_bytes(p521_ecdh_public_bytes, p521_ecdh_secret_bytes).unwrap(), P521KeyPair::from_bytes(p521_ecdsa_public_bytes, p521_ecdsa_secret_bytes).unwrap())),
})
})
} else if secrets_len == 0 {
Ok(Identity {
address: addr,
c25519: c25519_public_bytes.clone(),
ed25519: ed25519_public_bytes.clone(),
v1: Some((P521PublicKey::from_bytes(p521_ecdh_public_bytes).unwrap(), P521PublicKey::from_bytes(p521_ecdsa_public_bytes).unwrap(), p521_signature.clone(), bh_digest. clone())),
secrets: None
})
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid secret key length (type 1)"))
}
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized identity type"))
}
}
/// Get this identity in byte array format.
pub fn marshal_to_vec(&self, include_private: bool) -> Vec<u8> {
let mut buf: Buffer<NoHeader, 2048> = Buffer::new();
self.marshal(&mut buf, include_private).expect("overflow");
buf.as_bytes().to_vec()
}
} }
impl ToString for Identity { impl ToString for Identity {
fn to_string(&self) -> String { fn to_string(&self) -> String {
self.p521_public.as_ref().map_or_else(|| { self.v1.as_ref().map_or_else(|| {
format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519_public), crate::util::hex::to_string(&self.ed25519_public)) format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519), crate::util::hex::to_string(&self.ed25519))
}, |p521_public| { }, |p521_public| {
let mut public_key_blob: Vec<u8> = Vec::new(); 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); 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_public); let _ = public_key_blob.write_all(&self.c25519);
let _ = public_key_blob.write_all(&self.ed25519_public); 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.0.public_key_bytes());
let _ = public_key_blob.write_all(p521_public.1.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.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)) format!("{:0>10x}:1:{}", self.address.to_u64(), base64::encode_config(public_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
}) })
} }
} }
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use crate::vl1::Identity;
#[allow(unused_imports)]
use crate::vl1::identity::Type;
#[test]
fn p521() {
/*
let mut ms = 0.0;
let mut id: Option<Identity> = None;
for _ in 0..64 {
let start = std::time::SystemTime::now();
id.replace(Identity::generate(Type::P521));
let duration = std::time::SystemTime::now().duration_since(start).unwrap();
ms += duration.as_nanos() as f64 / 1000000.0;
}
ms /= 64.0;
println!("{}ms {}", ms, id.unwrap().to_secret_string());
*/
}
}

View file

@ -2,7 +2,9 @@ pub mod protocol;
pub mod packet; pub mod packet;
pub mod buffer; pub mod buffer;
pub mod node; pub mod node;
pub mod identity;
mod address; mod address;
mod identity;
pub use address::Address; pub use address::Address;
pub use identity::Identity;