Identities, buffers, crypto, and more, all getting rustier.

This commit is contained in:
Adam Ierymenko 2021-07-22 19:23:32 -04:00
parent df25f09e6b
commit a88d89a854
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
8 changed files with 346 additions and 91 deletions

View file

@ -2,6 +2,7 @@ use std::convert::TryInto;
use std::io::Write;
use ed25519_dalek::Digest;
use std::error::Error;
pub const C25519_PUBLIC_KEY_SIZE: usize = 32;
pub const C25519_SECRET_KEY_SIZE: usize = 32;
@ -64,21 +65,19 @@ impl Ed25519KeyPair {
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
}
#[inline(always)]
pub fn from_keys(public_key: &[u8], secret_key: &[u8]) -> Ed25519KeyPair {
let mut tmp = [0_u8; 64];
tmp[0..32].copy_from_slice(secret_key);
tmp[32..64].copy_from_slice(public_key);
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()))
let pk = ed25519_dalek::PublicKey::from_bytes(public_bytes);
let sk = ed25519_dalek::SecretKey::from_bytes(secret_bytes);
if pk.is_ok() && sk.is_ok() {
Some(Ed25519KeyPair(ed25519_dalek::Keypair {
public: pk.unwrap(),
secret: sk.unwrap(),
}))
} else {
None
}
} else {
None
}

View file

@ -210,6 +210,15 @@ impl P521PublicKey {
}
}
impl PartialEq for P521PublicKey {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.public_key_bytes.eq(&other.public_key_bytes)
}
}
impl Eq for P521PublicKey {}
impl Clone for P521PublicKey {
fn clone(&self) -> Self {
P521PublicKey::from_bytes(&self.public_key_bytes).unwrap()

View file

@ -1,17 +1,17 @@
use std::error::Error;
use std::fmt::{Display, Debug};
pub struct InvalidFormatError(pub(crate) &'static str);
pub struct InvalidFormatError;
impl Display for InvalidFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "UnmarshalError: {}", self.0)
f.write_str("InvalidFormatError")
}
}
impl Debug for InvalidFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "UnmarshalError: {}", self.0)
f.write_str("InvalidFormatError")
}
}

View file

@ -15,7 +15,7 @@ impl Address {
if b.len() >= 5 {
Ok(Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64))
} else {
Err(InvalidFormatError("invalid ZeroTier address"))
Err(InvalidFormatError)
}
}

View file

@ -1,7 +1,9 @@
use std::mem::size_of;
use std::mem::{size_of, MaybeUninit};
use std::marker::PhantomData;
use std::io::Write;
const OVERFLOW_ERR_MSG: &'static str = "overflow";
/// Annotates a type as containing only primitive types like integers and arrays.
/// This means it's safe to abuse with raw copy, raw zero, or "type punning."
/// This is ONLY used for packed protocol header or segment objects.
@ -35,6 +37,21 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
Self::default()
}
/// Create a buffer that contains a copy of a slice.
/// If the slice is larger than the maximum size L of the buffer, only the first L bytes
/// are copied and the rest is ignored.
#[inline(always)]
pub fn from_bytes_truncate(b: &[u8]) -> Self {
let l = b.len().min(L);
unsafe {
let mut tmp = MaybeUninit::<Self>::uninit().assume_init();
tmp.0 = l;
tmp.1[0..l].copy_from_slice(b);
tmp.1[l..L].fill(0);
tmp
}
}
/// Get a slice containing the entire buffer in raw form including the header.
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
@ -80,7 +97,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
Ok(initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()))
}
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -97,7 +114,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
Ok(initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; N]>()))
}
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -111,7 +128,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
self.0 = end;
Ok(initializer(&mut self.1[ptr..end]))
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -126,7 +143,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
self.1[ptr..end].copy_from_slice(buf);
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -141,7 +158,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
self.1[ptr..end].copy_from_slice(buf);
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -154,7 +171,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
self.1[ptr] = i;
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -168,7 +185,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
crate::util::integer_store_be_u16(i, &mut self.1[ptr..end]);
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -182,7 +199,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
crate::util::integer_store_be_u32(i, &mut self.1[ptr..end]);
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -196,7 +213,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
crate::util::integer_store_be_u64(i, &mut self.1[ptr..end]);
Ok(())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -217,7 +234,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>())
}
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -233,7 +250,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; S]>())
}
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -246,7 +263,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
*cursor = end;
Ok(&self.1[ptr..end])
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -258,7 +275,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
*cursor = ptr + 1;
Ok(self.1[ptr])
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -271,7 +288,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
*cursor = end;
Ok(crate::util::integer_load_be_u16(&self.1[ptr..end]))
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -284,7 +301,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
*cursor = end;
Ok(crate::util::integer_load_be_u32(&self.1[ptr..end]))
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
@ -297,7 +314,7 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
*cursor = end;
Ok(crate::util::integer_load_be_u64(&self.1[ptr..end]))
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}
}
@ -312,7 +329,7 @@ impl<H: RawObject, const L: usize> Write for Buffer<H, L> {
self.1[ptr..end].copy_from_slice(buf);
Ok(buf.len())
} else {
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
}
}

View file

@ -1,6 +1,7 @@
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 crate::vl1::Address;
use crate::vl1::buffer::{Buffer, RawObject, NoHeader};
@ -9,9 +10,14 @@ use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE,
use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE};
use crate::crypto::balloon;
use crate::crypto::salsa::Salsa;
use crate::error::InvalidFormatError;
use std::convert::TryInto;
use std::cmp::Ordering;
// Memory parameter for V0 address derivation work function.
const V0_IDENTITY_GEN_MEMORY: usize = 2097152;
// Balloon hash parameters for V1 address derivation work function.
const V1_BALLOON_SPACE_COST: usize = 16384;
const V1_BALLOON_TIME_COST: usize = 3;
const V1_BALLOON_DELTA: usize = 3;
@ -85,8 +91,8 @@ impl Identity {
let c25519 = C25519KeyPair::generate();
let c25519_pub_bytes = c25519.public_bytes();
sha.update(&ed25519_pub_bytes);
sha.update(&c25519_pub_bytes);
sha.update(&ed25519_pub_bytes);
let mut digest = sha.finish();
v0_frankenhash(&mut digest, genmem_ptr);
@ -165,17 +171,41 @@ impl Identity {
}
}
/// Get this identity's 40-bit address.
#[inline(always)]
pub fn address(&self) -> Address {
self.address
}
/// Compute a SHA384 hash of this identity's keys, including private keys if present.
pub fn hash_all_keys(&self) -> [u8; 48] {
let mut sha = SHA384::new();
sha.update(&self.c25519);
sha.update(&self.ed25519);
self.v1.as_ref().map(|p521| {
sha.update((*p521).0.public_key_bytes());
sha.update((*p521).1.public_key_bytes());
});
self.secrets.as_ref().map(|secrets| {
sha.update(&secrets.c25519.secret_bytes());
sha.update(&secrets.ed25519.secret_bytes());
secrets.v1.as_ref().map(|p521_secrets| {
sha.update((*p521_secrets).0.secret_key_bytes());
sha.update((*p521_secrets).1.secret_key_bytes());
});
});
sha.finish()
}
/// 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() {
if self.address.is_valid() {
if self.v1.is_none() {
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 {
if !genmem_ptr.is_null() {
let mut sha = SHA512::new();
sha.update(&self.c25519);
sha.update(&self.ed25519);
@ -183,12 +213,10 @@ impl Identity {
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 {
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);
@ -201,9 +229,9 @@ impl Identity {
} else {
false
}
} else {
false
}
} else {
false
}
}
@ -263,23 +291,9 @@ impl Identity {
self.secrets.is_some()
}
/// Get this identity in string format, including its secret keys.
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()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
}, |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());
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes());
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes());
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes());
format!("{}:{}", self.to_string(), base64::encode_config(secret_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
})
})
/// Erase secrets from this identity object, if present.
pub fn forget_secrets(&mut self) {
let _ = self.secrets.take();
}
/// Append this in binary format to a buffer.
@ -308,11 +322,9 @@ impl Identity {
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);
})?;
buf.append_u8(0)?; // type 0
buf.append_bytes_fixed(&self.c25519)?;
buf.append_bytes_fixed(&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)?;
@ -400,11 +412,38 @@ impl Identity {
}
/// Get this identity in byte array format.
pub fn marshal_to_vec(&self, include_private: bool) -> Vec<u8> {
pub fn marshal_to_bytes(&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()
}
/// Unmarshal an identity from a byte slice.
/// 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 buf = Buffer::<NoHeader, 2048>::from_bytes_truncate(bytes);
let mut cursor: usize = 0;
let id = Self::unmarshal(&buf, &mut cursor)?;
Ok((id, cursor))
}
/// Get this identity in string format, including its secret keys.
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()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
}, |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());
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes());
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes());
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes());
format!("{}:{}", self.to_string(), base64::encode_config(secret_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
})
})
}
}
impl ToString for Identity {
@ -425,26 +464,219 @@ impl ToString for Identity {
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use crate::vl1::Identity;
#[allow(unused_imports)]
use crate::vl1::identity::Type;
impl FromStr for Identity {
type Err = InvalidFormatError;
#[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;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let fields_v: Vec<&str> = s.split(':').collect();
let fields = fields_v.as_slice();
if fields.len() == 3 || fields.len() == 4 {
let addr = Address::from_str(fields[0])?;
if fields[1] == "0" {
let public_keys = crate::util::hex::from_string(fields[2]);
if public_keys.len() == (C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE) {
let mut secrets: Option<IdentitySecrets> = None;
if fields.len() == 4 {
let secret_keys = crate::util::hex::from_string(fields[3]);
if secret_keys.len() == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) {
let c25519_secret = C25519KeyPair::from_bytes(&public_keys.as_slice()[0..32], &secret_keys.as_slice()[0..32]);
let ed25519_secret = Ed25519KeyPair::from_bytes(&public_keys.as_slice()[32..64], &secret_keys.as_slice()[32..64]);
if c25519_secret.is_some() && ed25519_secret.is_some() {
secrets = Some(IdentitySecrets {
c25519: c25519_secret.unwrap(),
ed25519: ed25519_secret.unwrap(),
v1: None,
});
} else {
return Err(InvalidFormatError);
}
} else {
return Err(InvalidFormatError);
}
}
return Ok(Identity {
address: addr,
c25519: public_keys.as_slice()[0..32].try_into().unwrap(),
ed25519: public_keys.as_slice()[32..64].try_into().unwrap(),
v1: None,
secrets,
});
}
} else if fields[1] == "1" {
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) {
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() {
let p521_ecdh_public = p521_ecdh_public.unwrap();
let p521_ecdsa_public = p521_ecdsa_public.unwrap();
let mut secrets: Option<IdentitySecrets> = None;
if fields.len() == 4 {
let secret_keys = base64::decode_config(fields[3], base64::URL_SAFE_NO_PAD);
if secret_keys.is_ok() {
let secret_keys = secret_keys.unwrap();
if secret_keys.len() == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) {
let p521_ecdh_secret = P521KeyPair::from_bytes(p521_ecdh_public.public_key_bytes(), &secret_keys.as_slice()[(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE)..(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE)]);
let p521_ecdsa_secret = P521KeyPair::from_bytes(p521_ecdsa_public.public_key_bytes(), &secret_keys.as_slice()[(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE)..(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE)]);
if p521_ecdh_secret.is_some() && p521_ecdsa_secret.is_some() {
secrets = Some(IdentitySecrets {
c25519: C25519KeyPair::from_bytes(&public_keys_and_sig.as_slice()[0..32], &secret_keys.as_slice()[0..32]).unwrap(),
ed25519: Ed25519KeyPair::from_bytes(&public_keys_and_sig.as_slice()[32..64], &secret_keys.as_slice()[32..64]).unwrap(),
v1: Some((p521_ecdh_secret.unwrap(), p521_ecdsa_secret.unwrap())),
});
} else {
return Err(InvalidFormatError);
}
} else {
return Err(InvalidFormatError);
}
} else {
return Err(InvalidFormatError);
}
}
return Ok(Identity {
address: addr,
c25519: public_keys_and_sig.as_slice()[0..32].try_into().unwrap(),
ed25519: public_keys_and_sig.as_slice()[32..64].try_into().unwrap(),
v1: Some((
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()
)),
secrets,
});
}
}
}
}
}
Err(InvalidFormatError)
}
}
impl PartialEq for Identity {
fn eq(&self, other: &Self) -> bool {
self.address.eq(&other.address) &&
self.c25519.eq(&other.c25519) &&
self.ed25519.eq(&other.ed25519) &&
self.v1.as_ref().map_or_else(|| other.v1.is_none(), |v1| other.v1.as_ref().map_or(false, |other_v1| (*v1).0.eq(&(*other_v1).0) && (*v1).1.eq(&(*other_v1).1)))
}
}
impl Eq for Identity {}
impl PartialOrd for Identity {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Identity {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
let c = self.address.cmp(&other.address);
if c.is_eq() {
self.c25519.cmp(&other.c25519)
} else {
c
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::vl1::identity::{Identity, Type};
#[test]
fn type0() {
let id = Identity::generate(Type::C25519);
//println!("V0: {}", id.to_string());
if !id.locally_validate() {
panic!("new V0 identity validation failed");
}
let sig = id.sign(&[1_u8]).unwrap();
//println!("sig: {}", crate::util::hex::to_string(sig.as_slice()));
if !id.verify(&[1_u8], sig.as_slice()) {
panic!("valid signature verification failed");
}
if id.verify(&[0_u8], sig.as_slice()) {
panic!("invalid signature verification succeeded");
}
for good_id in [
"7f3a8e50db:0:936b698c68f51508e9184f7510323a01da0e5778158244c83520614822e2352855ff4d82443823b866cdb553d02d8fa5da833fbee62472e666a60605b76194b9:0d46684e30d561c859bf7d530d2de0452605d8cf392db4beb2768ceda55e63673f11d84a9f31ce7504f0e3ce5dc9ab7ecf9662e555846d130422916482be5fbb",
"f529e17b64:0:56676b36b94212cc479825cbf685527a097287950cf9642ae336d57bf17fdd6e4c96ac65e2cf9f757151bbb65e63abbd90b655df0934394906176cc07e81ff64:99e5f483dedf4b26b72524cfe1385e5b44d1eb9c8435316a551c6b4674ab484f6d72c2fdbb3d5b1f01ff1c092fc05d97734d6410c21acf8640cd1fa8e03a110a",
"cbdb6f47e9:0:ec7f3ffd9c139b31eb6f5903f4a2d069ec77c51fea228ab80d679dd0ce79fe12f531046634f1f94c51ce806910de3ad73df1940fe466bb65d247b3e492d75183:26e7c8473514205186704d5cf9ee3f82a6f45dc719b91f54e7f31f982071003100a86689de8abd82817f607e192d0e84cc344defe3bb3795f2bdcfcbff41c8cb",
"8bd225d6a9:0:08e7fc755ee0aa2e10bf37c0b8dd6f33b3164de04cf3f716584ee44df1fe9506ce1f3f2874c6d1450fc8fab339a95092ec7e628cddd26af93c4392e6564d9ee7:431bb44d22734d925538cbcdc7c2a80c0f71968041949f76ccb6f690f01b6cf45976071c86fcf2ddda2d463c8cfe6444b36c8ee0d057d665350acdcb86dff06f"
] {
let id = Identity::from_str(good_id).unwrap();
if !id.locally_validate() {
panic!("known-good V0 identity failed local validation");
}
let id_bytes = id.marshal_to_bytes(true);
let id2 = Identity::unmarshal_from_bytes(id_bytes.as_slice()).unwrap().0;
if !id.eq(&id2) {
panic!("identity V0 marshal/unmarshal failed");
}
}
for bad_id in [
"7f3b8e50db:0:936b698c68f51508e9184f7510323a01da0e5778158244c83520614822e2352855ff4d82443823b866cdb553d02d8fa5da833fbee62472e666a60605b76194b9:0d46684e30d561c859bf7d530d2de0452605d8cf392db4beb2768ceda55e63673f11d84a9f31ce7504f0e3ce5dc9ab7ecf9662e555846d130422916482be5fbb",
"f529e17b64:0:56676b36b94212cc479825cbf685527a097287951cf9642ae336d57bf17fdd6e4c96ac65e2cf9f757151bbb65e63abbd90b655df0934394906176cc07e81ff64:99e5f483dedf4b26b72524cfe1385e5b44d1eb9c8435316a551c6b4674ab484f6d72c2fdbb3d5b1f01ff1c092fc05d97734d6410c21acf8640cd1fa8e03a110a",
"cbdb6f47e9:0:ec7f3ffd9c139b31eb6f5903f4a2d069ec77c51fea228ab80d679dd0ce79fe12f531046634f1f94c51ce806910de3ad73df1940fe466cb65d247b3e492d75183:26e7c8473514205186704d5cf9ee3f82a6f45dc719b91f54e7f31f982071003100a86689de8abd82817f607e192d0e84cc344defe3bb3795f2bdcfcbff41c8cb",
"8bd225d6a9:0:98e7fc755ee0aa2e10bf37c0b8dd6f33b3164de04cf3f716584ee44df1fe9506ce1f3f2874c6d1450fc8fab339a95092ec7e628cddd26af93c4392e6564d9ee7:431bb44d22734d925538cbcdc7c2a80c0f71968041949f76ccb6f690f01b6cf45976071c86fcf2ddda2d463c8cfe6444b36c8ee0d057d665350acdcb86dff06f"
] {
let id = Identity::from_str(bad_id).unwrap();
if id.locally_validate() {
panic!("known-bad V0 identity validated");
}
}
}
#[test]
fn type1() {
let id = Identity::generate(Type::P521);
//println!("V1: {}", id.to_string());
if !id.locally_validate() {
panic!("new V1 identity validation failed");
}
let sig = id.sign(&[1_u8]).unwrap();
//println!("sig: {}", crate::util::hex::to_string(sig.as_slice()));
if !id.verify(&[1_u8], sig.as_slice()) {
panic!("valid signature verification failed");
}
if id.verify(&[0_u8], sig.as_slice()) {
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"
] {
let id = Identity::from_str(good_id).unwrap();
if !id.locally_validate() {
panic!("known-good V1 identity failed local validation");
}
let id_bytes = id.marshal_to_bytes(true);
let id2 = Identity::unmarshal_from_bytes(id_bytes.as_slice()).unwrap().0;
if !id.eq(&id2) {
panic!("identity V1 marshal/unmarshal failed");
}
}
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"
] {
let id = Identity::from_str(bad_id).unwrap();
if id.locally_validate() {
panic!("known-bad V1 identity validated");
}
}
ms /= 64.0;
println!("{}ms {}", ms, id.unwrap().to_secret_string());
*/
}
}

View file

@ -5,6 +5,4 @@ pub mod node;
pub mod identity;
mod address;
pub use address::Address;
pub use identity::Identity;

View file

@ -13,7 +13,7 @@ impl MAC {
if b.len() >= 6 {
Ok(MAC((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64))
} else {
Err(InvalidFormatError("invalid MAC address"))
Err(InvalidFormatError)
}
}