mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 05:23:44 +02:00
A bunch more of ZeroTier rusting...
This commit is contained in:
parent
d70d958b5c
commit
df25f09e6b
12 changed files with 599 additions and 71 deletions
0
vli/Cargo.lock → attic/vli/Cargo.lock
generated
0
vli/Cargo.lock → attic/vli/Cargo.lock
generated
|
@ -3,7 +3,7 @@ name = "vli"
|
|||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[profile.test]
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
|
@ -13,6 +13,7 @@ const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|||
/// This also means the size of a VLI must be a multiple of 64 bits. Note that the actual
|
||||
/// integer in it need not be exactly that length, just the capacity of the container.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct VLI<const LIMBS: usize> {
|
||||
n: [u64; LIMBS]
|
||||
}
|
||||
|
@ -31,6 +32,14 @@ impl<const LIMBS: usize> VLI<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.
|
||||
#[inline(always)]
|
||||
pub fn zero(&mut self) {
|
||||
|
@ -99,18 +108,33 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
carry
|
||||
}
|
||||
|
||||
/// Multiply two inputs half the size of this integer to yield a full size result in this integer..
|
||||
/// Subtract from this integer and return any remaining borrow bits.
|
||||
pub fn sub_assign_borrow(&mut self, rhs: &Self) -> u64 {
|
||||
let mut borrow = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow;
|
||||
borrow = (diff > left) as u64;
|
||||
*left_ptr = diff;
|
||||
}
|
||||
borrow
|
||||
}
|
||||
|
||||
/// Multiply two inputs half the size of this integer to yield a full size result in this integer.
|
||||
/// The multiplicand sizes MULT_LIMBS must be one half the LIMBS size of this integer.
|
||||
/// This is checked with an assertion. This isn't computed with the type system due
|
||||
/// to current limitations in const generics.
|
||||
pub fn mul_extend_assign<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);
|
||||
let mut r01 = 0_u128;
|
||||
let mut r2 = 0_u64;
|
||||
let mut k = 0_usize;
|
||||
while k < MULT_LIMBS {
|
||||
for i in 0..k {
|
||||
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128);
|
||||
debug_assert!(i < MULT_LIMBS);
|
||||
debug_assert!((k - i) < MULT_LIMBS);
|
||||
let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128);
|
||||
r01 += l_product;
|
||||
r2 += (r01 < l_product) as u64;
|
||||
}
|
||||
|
@ -120,12 +144,12 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
k += 1;
|
||||
}
|
||||
while k < (LIMBS - 1) {
|
||||
let mut i = (k + 1) - MULT_LIMBS;
|
||||
while i < k && i < MULT_LIMBS {
|
||||
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128);
|
||||
for i in ((k + 1) - MULT_LIMBS)..k {
|
||||
debug_assert!(i < MULT_LIMBS);
|
||||
debug_assert!((k - i) < MULT_LIMBS);
|
||||
let l_product = (*unsafe { lhs.n.get_unchecked(i) } as u128) * (*unsafe { rhs.n.get_unchecked(k - i) } as u128);
|
||||
r01 += l_product;
|
||||
r2 += (r01 < l_product) as u64;
|
||||
i += 1;
|
||||
}
|
||||
*unsafe { self.n.get_unchecked_mut(k) } = r01 as u64;
|
||||
r01 += (r01 >> 64) | ((r2 as u128) << 64);
|
||||
|
@ -139,14 +163,14 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
/// If skip_leading_zeroes is true the returned byte vector will be the minimum size
|
||||
/// needed to hold the integer, or empty if it is zero. Otherwise it will always be
|
||||
/// LIMBS * 8 bytes in length.
|
||||
pub fn to_vec(&self, skip_leading_zeroes: bool) -> Vec<u8> {
|
||||
pub fn to_be_bytes(&self, skip_leading_zeroes: bool) -> Vec<u8> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
bytes.reserve(LIMBS * 8);
|
||||
|
||||
let mut i = LIMBS as isize - 1;
|
||||
if skip_leading_zeroes {
|
||||
while i >= 0 {
|
||||
let x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let x: u64 = self.n[i as usize];
|
||||
if x != 0 {
|
||||
let x = x.to_be_bytes();
|
||||
for j in 0..8 {
|
||||
|
@ -161,7 +185,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
}
|
||||
}
|
||||
while i >= 0 {
|
||||
let _ = bytes.write_all(&(unsafe { self.n.get_unchecked(i as usize) }.to_be_bytes()));
|
||||
let _ = bytes.write_all(&self.n[i as usize].to_be_bytes());
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
|
@ -178,7 +202,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
let mut i = LIMBS as isize - 1;
|
||||
if skip_leading_zeroes {
|
||||
while i >= 0 {
|
||||
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let mut x = self.n[i as usize];
|
||||
if x != 0 {
|
||||
let mut j = 0;
|
||||
while j < 16 {
|
||||
|
@ -199,7 +223,7 @@ impl<const LIMBS: usize> VLI<LIMBS> {
|
|||
}
|
||||
}
|
||||
while i >= 0 {
|
||||
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let mut x: u64 = self.n[i as usize];
|
||||
for _ in 0..16 {
|
||||
s.push(HEX_CHARS[(x >> 60) as usize]);
|
||||
x <<= 4;
|
||||
|
@ -220,21 +244,15 @@ impl<const LIMBS: usize> Add<&Self> for VLI<LIMBS> {
|
|||
|
||||
#[inline(always)]
|
||||
fn add(mut self, rhs: &Self) -> Self::Output {
|
||||
self.add_assign(rhs);
|
||||
let _ = self.add_assign_carry(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> AddAssign<&Self> for VLI<LIMBS> {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
let mut carry = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let sum = left + *unsafe { rhs.n.get_unchecked(i) } + carry;
|
||||
carry = (sum < left) as u64;
|
||||
*left_ptr = sum;
|
||||
}
|
||||
let _ = self.add_assign_carry(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,21 +261,15 @@ impl<const LIMBS: usize> Sub<&Self> for VLI<LIMBS> {
|
|||
|
||||
#[inline(always)]
|
||||
fn sub(mut self, rhs: &Self) -> Self::Output {
|
||||
self.sub_assign(rhs);
|
||||
let _ = self.sub_assign_borrow(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> SubAssign<&Self> for VLI<LIMBS> {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: &Self) {
|
||||
let mut borrow = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow;
|
||||
borrow = (diff > left) as u64;
|
||||
*left_ptr = diff;
|
||||
}
|
||||
let _ = self.sub_assign_borrow(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,7 +286,13 @@ impl<const LIMBS: usize> Shl<usize> for VLI<LIMBS> {
|
|||
impl<const LIMBS: usize> ShlAssign<usize> for VLI<LIMBS> {
|
||||
fn shl_assign(&mut self, rhs: usize) {
|
||||
if rhs != 0 {
|
||||
if rhs < 64 {
|
||||
if rhs < (LIMBS * 64) {
|
||||
let whole_limb_shifts = rhs >> 6;
|
||||
if whole_limb_shifts != 0 {
|
||||
self.n.copy_within(0..(LIMBS - whole_limb_shifts), whole_limb_shifts);
|
||||
self.n[0..whole_limb_shifts].fill(0);
|
||||
}
|
||||
let rhs = rhs & 63;
|
||||
let mut carry = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let x_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
|
@ -302,7 +320,13 @@ impl<const LIMBS: usize> Shr<usize> for VLI<LIMBS> {
|
|||
impl<const LIMBS: usize> ShrAssign<usize> for VLI<LIMBS> {
|
||||
fn shr_assign(&mut self, rhs: usize) {
|
||||
if rhs != 0 {
|
||||
if rhs < 64 {
|
||||
if rhs < (LIMBS * 64) {
|
||||
let whole_limb_shifts = rhs >> 6;
|
||||
if whole_limb_shifts != 0 {
|
||||
self.n.copy_within(whole_limb_shifts..LIMBS, 0);
|
||||
self.n[(LIMBS - whole_limb_shifts)..LIMBS].fill(0);
|
||||
}
|
||||
let rhs = rhs & 63;
|
||||
let mut carry = 0_u64;
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
|
@ -3,6 +3,17 @@ name = "zerotier-network-hypervisor"
|
|||
version = "2.0.0"
|
||||
edition = "2018"
|
||||
|
||||
# [profile.test]
|
||||
# opt-level = 3
|
||||
# lto = true
|
||||
# codegen-units = 1
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
codegen-units = 1
|
||||
panic = 'abort'
|
||||
|
||||
[dependencies]
|
||||
rand_core = "^0"
|
||||
aes-gmac-siv = { path = "../aes-gmac-siv" }
|
||||
|
|
126
network-hypervisor/src/crypto/balloon.rs
Normal file
126
network-hypervisor/src/crypto/balloon.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -15,14 +15,14 @@ pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
|
|||
|
||||
impl C25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn new() -> C25519KeyPair {
|
||||
pub fn generate() -> C25519KeyPair {
|
||||
let sk = x25519_dalek::StaticSecret::new(rand_core::OsRng);
|
||||
let pk = x25519_dalek::PublicKey::from(&sk);
|
||||
C25519KeyPair(sk, pk)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_keys(public_key: &[u8], secret_key: &[u8]) -> Option<C25519KeyPair> {
|
||||
pub fn from_bytes(public_key: &[u8], secret_key: &[u8]) -> Option<C25519KeyPair> {
|
||||
if public_key.len() == 32 && secret_key.len() == 32 {
|
||||
let pk: [u8; 32] = public_key.try_into().unwrap();
|
||||
let sk: [u8; 32] = secret_key.try_into().unwrap();
|
||||
|
@ -59,7 +59,7 @@ pub struct Ed25519KeyPair(ed25519_dalek::Keypair);
|
|||
|
||||
impl Ed25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Ed25519KeyPair {
|
||||
pub fn generate() -> Ed25519KeyPair {
|
||||
let mut rng = rand_core::OsRng::default();
|
||||
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
|
||||
}
|
||||
|
@ -72,6 +72,18 @@ impl Ed25519KeyPair {
|
|||
Ed25519KeyPair(ed25519_dalek::Keypair::from_bytes(&tmp).unwrap())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<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)]
|
||||
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] {
|
||||
self.0.public.to_bytes()
|
||||
|
|
|
@ -3,3 +3,4 @@ pub mod hash;
|
|||
pub mod p521;
|
||||
pub mod salsa;
|
||||
pub mod poly1305;
|
||||
pub mod balloon;
|
||||
|
|
|
@ -66,7 +66,7 @@ pub struct P521PublicKey {
|
|||
pub struct P521KeyPair {
|
||||
public_key: P521PublicKey,
|
||||
secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression
|
||||
secret_key_for_ecdh: SExpression, // secret key as a "data" S-expression for the weird gcrypt ECDH interface
|
||||
secret_key_for_ecdh: SExpression, // the same secret key as a "data" S-expression for the weird gcrypt ECDH interface
|
||||
secret_key_bytes: [u8; P521_SECRET_KEY_SIZE],
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ impl P521KeyPair {
|
|||
public_key: pk_exp,
|
||||
public_key_bytes: [0_u8; P521_PUBLIC_KEY_SIZE],
|
||||
},
|
||||
secret_key_for_ecdsa: sk_exp,
|
||||
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #{}#)(d #{}#)))", crate::util::hex::to_string(pk), crate::util::hex::to_string(sk)).as_str()).unwrap(),
|
||||
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(sk)).as_str()).unwrap(),
|
||||
secret_key_bytes: [0_u8; P521_SECRET_KEY_SIZE],
|
||||
};
|
||||
|
@ -111,6 +111,23 @@ impl P521KeyPair {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct this key pair from both a public and a private key.
|
||||
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<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)]
|
||||
pub fn public_key(&self) -> &P521PublicKey {
|
||||
&self.public_key
|
||||
|
@ -161,6 +178,9 @@ impl P521KeyPair {
|
|||
}
|
||||
|
||||
impl P521PublicKey {
|
||||
/// Construct a public key from a byte serialized representation.
|
||||
/// None is returned if the input is not valid. No advanced checking such as
|
||||
/// determining if this is a point on the curve is performed.
|
||||
pub fn from_bytes(b: &[u8]) -> Option<P521PublicKey> {
|
||||
if b.len() == P521_PUBLIC_KEY_SIZE {
|
||||
Some(P521PublicKey {
|
||||
|
@ -188,10 +208,11 @@ impl P521PublicKey {
|
|||
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] {
|
||||
&self.public_key_bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> [u8; P521_PUBLIC_KEY_SIZE] {
|
||||
self.public_key_bytes.clone()
|
||||
impl Clone for P521PublicKey {
|
||||
fn clone(&self) -> Self {
|
||||
P521PublicKey::from_bytes(&self.public_key_bytes).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,5 +238,11 @@ mod tests {
|
|||
if !sec0.eq(&sec1) {
|
||||
panic!("ECDH secrets do not match");
|
||||
}
|
||||
|
||||
let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes()).unwrap();
|
||||
let sig = kp3.sign(&[3_u8]).unwrap();
|
||||
if !kp.public_key().verify(&[3_u8], &sig) {
|
||||
panic!("ECDSA verify failed (from key reconstructed from bytes)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,3 +40,5 @@ pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 {
|
|||
pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 {
|
||||
(d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64)
|
||||
}
|
||||
|
||||
pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];
|
||||
|
|
|
@ -118,7 +118,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
/// Append a dynamic byte slice (copy into buffer).
|
||||
/// Use append_and_init_ functions if possible as these avoid extra copies.
|
||||
#[inline(always)]
|
||||
fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
pub fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + buf.len();
|
||||
if end <= L {
|
||||
|
@ -133,7 +133,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
/// Append a fixed length byte array (copy into buffer).
|
||||
/// Use append_and_init_ functions if possible as these avoid extra copies.
|
||||
#[inline(always)]
|
||||
fn append_bytes_fixed<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 end = ptr + S;
|
||||
if end <= L {
|
||||
|
@ -147,7 +147,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
|
||||
/// Append a byte
|
||||
#[inline(always)]
|
||||
fn append_u8(&mut self, i: u8) -> std::io::Result<()> {
|
||||
pub fn append_u8(&mut self, i: u8) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
if ptr < L {
|
||||
self.0 = ptr + 1;
|
||||
|
@ -160,7 +160,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
|
||||
/// Append a 16-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u16(&mut self, i: u16) -> std::io::Result<()> {
|
||||
pub fn append_u16(&mut self, i: u16) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 2;
|
||||
if end <= L {
|
||||
|
@ -174,7 +174,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
|
||||
/// Append a 32-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u32(&mut self, i: u32) -> std::io::Result<()> {
|
||||
pub fn append_u32(&mut self, i: u32) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 4;
|
||||
if end <= L {
|
||||
|
@ -188,7 +188,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
|
||||
/// Append a 64-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u64(&mut self, i: u64) -> std::io::Result<()> {
|
||||
pub fn append_u64(&mut self, i: u64) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 8;
|
||||
if end <= L {
|
||||
|
@ -200,6 +200,12 @@ impl<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.
|
||||
#[inline(always)]
|
||||
pub fn get_struct<T: RawObject>(&self, cursor: &mut usize) -> std::io::Result<&T> {
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
use std::alloc::{Layout, dealloc, alloc};
|
||||
use std::ptr::{slice_from_raw_parts_mut, slice_from_raw_parts};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::buffer::{Buffer, RawObject, NoHeader};
|
||||
use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair};
|
||||
use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE};
|
||||
use crate::vl1::Address;
|
||||
use crate::crypto::hash::SHA384;
|
||||
use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE};
|
||||
use crate::crypto::balloon;
|
||||
use crate::crypto::salsa::Salsa;
|
||||
|
||||
const V0_IDENTITY_GEN_MEMORY: usize = 2097152;
|
||||
|
||||
const V1_BALLOON_SPACE_COST: usize = 16384;
|
||||
const V1_BALLOON_TIME_COST: usize = 3;
|
||||
const V1_BALLOON_DELTA: usize = 3;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
|
@ -11,28 +22,134 @@ pub enum Type {
|
|||
/// Curve25519 / Ed25519 identity (type 0)
|
||||
C25519 = 0,
|
||||
/// NIST P-521 ECDH / ECDSA identity (also has c25519/ed25519 keys for backward compability) (type 1)
|
||||
P521 = 1
|
||||
P521 = 1,
|
||||
}
|
||||
|
||||
struct IdentitySecrets {
|
||||
c25519: C25519KeyPair,
|
||||
ed25519: Ed25519KeyPair,
|
||||
p521: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key
|
||||
v1: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key
|
||||
}
|
||||
|
||||
pub struct Identity {
|
||||
address: Address,
|
||||
c25519_public: [u8; C25519_PUBLIC_KEY_SIZE],
|
||||
ed25519_public: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||
p521_public: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE])>, // ecdh key, ecdsa key, ecdsa signature of all keys
|
||||
c25519: [u8; C25519_PUBLIC_KEY_SIZE],
|
||||
ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||
v1: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE], [u8; SHA512_HASH_SIZE])>,
|
||||
secrets: Option<IdentitySecrets>,
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
fn generate_c25519() {
|
||||
/// Compute result from the bespoke "frankenhash" from the old V0 work function.
|
||||
/// The supplied genmem_ptr must be of size V0_IDENTITY_GEN_MEMORY and aligned to an 8-byte boundary.
|
||||
fn v0_frankenhash(digest: &mut [u8; 64], genmem_ptr: *mut u8) {
|
||||
let genmem = unsafe { &mut *slice_from_raw_parts_mut(genmem_ptr, V0_IDENTITY_GEN_MEMORY) };
|
||||
let genmem_alias_hack = unsafe { &*slice_from_raw_parts(genmem_ptr, V0_IDENTITY_GEN_MEMORY) };
|
||||
let genmem_u64_ptr = genmem_ptr.cast::<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.
|
||||
|
@ -41,24 +158,66 @@ impl Identity {
|
|||
/// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites
|
||||
/// take about 500ms. Generation can take a lot longer on low power devices, but only
|
||||
/// has to be done once.
|
||||
pub fn generate(id_type: Type) {
|
||||
pub fn generate(id_type: Type) -> Identity {
|
||||
match id_type {
|
||||
Type::C25519 => Self::generate_c25519(),
|
||||
Type::P521 => Self::generate_p521()
|
||||
}
|
||||
}
|
||||
|
||||
/// Locally validate this identity.
|
||||
/// This can take a few milliseconds, especially on slower systems. V0 identities are slower
|
||||
/// to fully validate than V1 identities.
|
||||
pub fn locally_validate(&self) -> bool {
|
||||
if self.v1.is_some() {
|
||||
if self.address.is_valid() {
|
||||
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
|
||||
let genmem_ptr = unsafe { alloc(genmem_layout) };
|
||||
if genmem_ptr.is_null() {
|
||||
false
|
||||
} else {
|
||||
let mut sha = SHA512::new();
|
||||
sha.update(&self.c25519);
|
||||
sha.update(&self.ed25519);
|
||||
let mut digest = sha.finish();
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
(digest[0] < 17) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
if self.address.is_valid() {
|
||||
let p521 = self.v1.as_ref().unwrap();
|
||||
let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE];
|
||||
signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&self.c25519);
|
||||
signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519);
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).0.public_key_bytes());
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).1.public_key_bytes());
|
||||
if (*p521).1.verify(&signing_buf, &(*p521).2) {
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2, b"zt_id_v1");
|
||||
(bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[59..64]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute ECDH key agreement and return SHA384(shared secret).
|
||||
/// If both keys are type 1, key agreement is done with NIST P-521. Otherwise it's done
|
||||
/// with Curve25519. None is returned if there is an error such as this identity missing
|
||||
/// its secrets or a key being invalid.
|
||||
pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public)))
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519)))
|
||||
}, |p521_secret| {
|
||||
other_identity.p521_public.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public)))
|
||||
other_identity.v1.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519)))
|
||||
}, |other_p521_public| {
|
||||
p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret)))
|
||||
})
|
||||
|
@ -71,7 +230,7 @@ impl Identity {
|
|||
/// type. None is returned if this identity lacks secret keys or another error occurs.
|
||||
pub fn sign(&self, msg: &[u8]) -> Option<Vec<u8>> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
Some(secrets.ed25519.sign(msg).to_vec())
|
||||
}, |p521_secret| {
|
||||
p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec()))
|
||||
|
@ -79,10 +238,19 @@ impl Identity {
|
|||
})
|
||||
}
|
||||
|
||||
/// Verify a signature.
|
||||
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
|
||||
self.v1.as_ref().map_or_else(|| {
|
||||
crate::crypto::c25519::ed25519_verify(&self.ed25519, signature, msg)
|
||||
}, |p521| {
|
||||
(*p521).1.verify(msg, signature)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get this identity's type.
|
||||
#[inline(always)]
|
||||
pub fn id_type(&self) -> Type {
|
||||
if self.p521_public.is_some() {
|
||||
if self.v1.is_some() {
|
||||
Type::P521
|
||||
} else {
|
||||
Type::C25519
|
||||
|
@ -100,7 +268,7 @@ impl Identity {
|
|||
self.secrets.as_ref().map_or_else(|| {
|
||||
self.to_string()
|
||||
}, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
|
||||
}, |p521_secret| {
|
||||
let mut secret_key_blob: Vec<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 {
|
||||
fn to_string(&self) -> String {
|
||||
self.p521_public.as_ref().map_or_else(|| {
|
||||
format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519_public), crate::util::hex::to_string(&self.ed25519_public))
|
||||
self.v1.as_ref().map_or_else(|| {
|
||||
format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519), crate::util::hex::to_string(&self.ed25519))
|
||||
}, |p521_public| {
|
||||
let mut public_key_blob: Vec<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);
|
||||
let _ = public_key_blob.write_all(&self.c25519_public);
|
||||
let _ = public_key_blob.write_all(&self.ed25519_public);
|
||||
public_key_blob.reserve(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + SHA512_HASH_SIZE);
|
||||
let _ = public_key_blob.write_all(&self.c25519);
|
||||
let _ = public_key_blob.write_all(&self.ed25519);
|
||||
let _ = public_key_blob.write_all(p521_public.0.public_key_bytes());
|
||||
let _ = public_key_blob.write_all(p521_public.1.public_key_bytes());
|
||||
let _ = public_key_blob.write_all(&p521_public.2);
|
||||
let _ = public_key_blob.write_all(&p521_public.3);
|
||||
format!("{:0>10x}:1:{}", self.address.to_u64(), base64::encode_config(public_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[allow(unused_imports)]
|
||||
use crate::vl1::Identity;
|
||||
#[allow(unused_imports)]
|
||||
use crate::vl1::identity::Type;
|
||||
|
||||
#[test]
|
||||
fn p521() {
|
||||
/*
|
||||
let mut ms = 0.0;
|
||||
let mut id: Option<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());
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ pub mod protocol;
|
|||
pub mod packet;
|
||||
pub mod buffer;
|
||||
pub mod node;
|
||||
pub mod identity;
|
||||
|
||||
mod address;
|
||||
mod identity;
|
||||
|
||||
pub use address::Address;
|
||||
pub use identity::Identity;
|
||||
|
|
Loading…
Add table
Reference in a new issue