From a88d89a85403f08af3ef703767606b7bbc11ac69 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Jul 2021 19:23:32 -0400 Subject: [PATCH] Identities, buffers, crypto, and more, all getting rustier. --- network-hypervisor/src/crypto/c25519.rs | 23 +- network-hypervisor/src/crypto/p521.rs | 9 + network-hypervisor/src/error.rs | 6 +- network-hypervisor/src/vl1/address.rs | 2 +- network-hypervisor/src/vl1/buffer.rs | 53 ++-- network-hypervisor/src/vl1/identity.rs | 340 ++++++++++++++++++++---- network-hypervisor/src/vl1/mod.rs | 2 - network-hypervisor/src/vl2/mac.rs | 2 +- 8 files changed, 346 insertions(+), 91 deletions(-) diff --git a/network-hypervisor/src/crypto/c25519.rs b/network-hypervisor/src/crypto/c25519.rs index f7b1c8d37..1b2f8b233 100644 --- a/network-hypervisor/src/crypto/c25519.rs +++ b/network-hypervisor/src/crypto/c25519.rs @@ -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 { 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 } diff --git a/network-hypervisor/src/crypto/p521.rs b/network-hypervisor/src/crypto/p521.rs index 466282f19..bfb7a1d41 100644 --- a/network-hypervisor/src/crypto/p521.rs +++ b/network-hypervisor/src/crypto/p521.rs @@ -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() diff --git a/network-hypervisor/src/error.rs b/network-hypervisor/src/error.rs index f61266a10..41ed41d0c 100644 --- a/network-hypervisor/src/error.rs +++ b/network-hypervisor/src/error.rs @@ -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") } } diff --git a/network-hypervisor/src/vl1/address.rs b/network-hypervisor/src/vl1/address.rs index 0c985e988..822bf8042 100644 --- a/network-hypervisor/src/vl1/address.rs +++ b/network-hypervisor/src/vl1/address.rs @@ -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) } } diff --git a/network-hypervisor/src/vl1/buffer.rs b/network-hypervisor/src/vl1/buffer.rs index 6a13d6e87..87d9e7b3f 100644 --- a/network-hypervisor/src/vl1/buffer.rs +++ b/network-hypervisor/src/vl1/buffer.rs @@ -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 Buffer { 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::::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 Buffer { Ok(initializer(&mut *self.1.as_mut_ptr().cast::().offset(ptr as isize).cast::())) } } 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 Buffer { Ok(initializer(&mut *self.1.as_mut_ptr().cast::().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 Buffer { 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 Buffer { 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 Buffer { 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 Buffer { 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 Buffer { 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 Buffer { 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 Buffer { 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 Buffer { Ok(&*self.1.as_ptr().cast::().offset(ptr as isize).cast::()) } } 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 Buffer { Ok(&*self.1.as_ptr().cast::().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 Buffer { *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 Buffer { *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 Buffer { *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 Buffer { *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 Buffer { *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 Write for Buffer { 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)) } } diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index 41cf5d1a2..488a36bbd 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -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 = 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 { + pub fn marshal_to_bytes(&self, include_private: bool) -> Vec { let mut buf: Buffer = 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::::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 = 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 = 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 { + 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 = 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 = 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 { + 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()); - */ } } diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index 466187dcf..34e88cece 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -5,6 +5,4 @@ pub mod node; pub mod identity; mod address; - pub use address::Address; -pub use identity::Identity; diff --git a/network-hypervisor/src/vl2/mac.rs b/network-hypervisor/src/vl2/mac.rs index e70ee79fb..b5ee9d488 100644 --- a/network-hypervisor/src/vl2/mac.rs +++ b/network-hypervisor/src/vl2/mac.rs @@ -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) } }