mirror of
https://github.com/zerotier/identity.git
synced 2026-05-22 16:29:03 -07:00
P384 work and cleaning up old code.
This commit is contained in:
-328
@@ -1,328 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::array::TryFromSliceError;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem::{size_of, transmute};
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use zerotier_utils::error::InvalidParameterError;
|
||||
use zerotier_utils::hex::{self, HEX_CHARS};
|
||||
use zerotier_utils::tofrombytes::ToFromBytes;
|
||||
use zerotier_utils::{base24, memory};
|
||||
|
||||
/// A unique identifier for an identity on the ZeroTier VL1 network
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Address(pub(crate) [u8; Self::SIZE]);
|
||||
|
||||
impl Address {
|
||||
/// The size of a full address, 384 bits.
|
||||
pub const SIZE: usize = 48;
|
||||
|
||||
/// The size of a short address.
|
||||
pub const SHORT_SIZE: usize = 16;
|
||||
|
||||
/// The size of a legacy ZeroTier One short address.
|
||||
pub const LEGACY_SHORT_SIZE: usize = 5;
|
||||
|
||||
/// Legacy ZeroTier One addresses may not begin with 0xff.
|
||||
pub const LEGACY_RESERVED_PREFIX: u8 = 0xff;
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_uninit() -> Self {
|
||||
Self([0u8; Self::SIZE])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_short_bytes(b: &[u8]) -> Option<Self> {
|
||||
if b.len() == Self::SHORT_SIZE {
|
||||
let mut a = Address([0u8; Self::SIZE]);
|
||||
a.0[..Self::SHORT_SIZE].copy_from_slice(b);
|
||||
Some(a)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; Self::SIZE] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_short_bytes(&self) -> &[u8; Self::SHORT_SIZE] {
|
||||
memory::array_range::<u8, { Self::SIZE }, 0, { Self::SHORT_SIZE }>(&self.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_legacy_short_bytes(&self) -> &[u8; Self::LEGACY_SHORT_SIZE] {
|
||||
memory::array_range::<u8, { Self::SIZE }, 0, { Self::LEGACY_SHORT_SIZE }>(&self.0)
|
||||
}
|
||||
|
||||
/// True if this is a full-length 384-bit address.
|
||||
#[inline]
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.0[Self::SHORT_SIZE..].iter().any(|i| *i != 0)
|
||||
}
|
||||
|
||||
/// True if this is a short (128-bit or legacy 40-bit) address.
|
||||
#[inline]
|
||||
pub fn is_short(&self) -> bool {
|
||||
self.0[Self::SHORT_SIZE..].iter().all(|i| *i == 0)
|
||||
}
|
||||
|
||||
/// Append a string representation of this address to a mutable string.
|
||||
pub fn to_string_append(&self, s: &mut String) {
|
||||
let mut i = 0;
|
||||
while i < Self::SHORT_SIZE {
|
||||
let ii = i + 4;
|
||||
if i > 0 {
|
||||
s.push('-');
|
||||
}
|
||||
base24::encode_4to7(&self.0[i..ii], s);
|
||||
i = ii;
|
||||
}
|
||||
if self.is_complete() {
|
||||
s.push('.');
|
||||
while i < Self::SIZE {
|
||||
let ii = i + 4;
|
||||
if i > 16 {
|
||||
s.push('-');
|
||||
}
|
||||
base24::encode_4to7(&self.0[i..ii], s);
|
||||
i = ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the legacy 40-bit ZeroTier One address in the least significant 40 bits of a u64.
|
||||
#[cfg(feature = "legacy_zt1")]
|
||||
#[inline]
|
||||
pub fn to_legacy_short_u64(&self) -> u64 {
|
||||
u64::from_be_bytes(self.0[..8].try_into().unwrap()).wrapping_shr(24)
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy_zt1")]
|
||||
#[inline]
|
||||
pub fn from_legacy_short_u64(i: u64) -> Self {
|
||||
let mut a = Self([0u8; Self::SIZE]);
|
||||
a.0[..Self::LEGACY_SHORT_SIZE].copy_from_slice(&i.to_be_bytes()[3..]);
|
||||
a
|
||||
}
|
||||
|
||||
#[cfg(feature = "legacy_zt1")]
|
||||
#[inline(always)]
|
||||
pub fn from_legacy_short_bytes(b: &[u8]) -> Option<Self> {
|
||||
if b.len() == Self::LEGACY_SHORT_SIZE {
|
||||
let mut a = Address([0u8; Self::SIZE]);
|
||||
a.0[..Self::LEGACY_SHORT_SIZE].copy_from_slice(b);
|
||||
Some(a)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Output a legacy short ZeroTier One address (first 5 bytes) in string form.
|
||||
/// This is only meaningful if this address belongs to an Identity with the appropriate flag set.
|
||||
#[cfg(feature = "legacy_zt1")]
|
||||
pub fn to_legacy_short_string(&self) -> String {
|
||||
let mut s = String::with_capacity(Self::LEGACY_SHORT_SIZE * 2);
|
||||
for b in self.0[..Self::LEGACY_SHORT_SIZE].iter() {
|
||||
let b = *b;
|
||||
s.push(HEX_CHARS[b.wrapping_shr(4) as usize] as char);
|
||||
s.push(HEX_CHARS[(b & 0xf) as usize] as char);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Parse a legacy 10-digit hex ZeroTier One address.
|
||||
///
|
||||
/// This must be used instead of from_str() to parse these short addresses. None is returned if
|
||||
/// the provided address is not valid.
|
||||
#[cfg(feature = "legacy_zt1")]
|
||||
pub fn from_legacy_short_string(s: &str) -> Result<Address, InvalidParameterError> {
|
||||
let i = hex::from_string_u64(s) & 0xffffffffff;
|
||||
if i == 0 || s.len() != (Self::LEGACY_SHORT_SIZE * 2) {
|
||||
return Err(InvalidParameterError("invalid legacy address"));
|
||||
}
|
||||
let i = i.to_be_bytes();
|
||||
if i[3] == Self::LEGACY_RESERVED_PREFIX {
|
||||
return Err(InvalidParameterError("invalid legacy address"));
|
||||
}
|
||||
let mut a = Address([0u8; Self::SIZE]);
|
||||
a.0[..Self::LEGACY_SHORT_SIZE].copy_from_slice(&i[3..]);
|
||||
Ok(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; Address::SIZE]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(value: [u8; Address::SIZE]) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8; Address::SIZE]> for &Address {
|
||||
#[inline(always)]
|
||||
fn from(value: &[u8; Address::SIZE]) -> Self {
|
||||
assert_eq!(size_of::<[u8; Address::SIZE]>(), size_of::<Address>());
|
||||
unsafe { transmute(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Address> for [u8; Address::SIZE] {
|
||||
#[inline(always)]
|
||||
fn from(value: Address) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Address> for [u8; Address::SIZE] {
|
||||
#[inline(always)]
|
||||
fn from(value: &Address) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Address {
|
||||
type Error = TryFromSliceError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
value.try_into().map(|a| Self(a))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Address {
|
||||
fn to_string(&self) -> String {
|
||||
let mut s = String::with_capacity(96);
|
||||
self.to_string_append(&mut s);
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Address {
|
||||
type Err = InvalidParameterError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut a = Self([0u8; Self::SIZE]);
|
||||
let mut i = 0;
|
||||
for ss in s.split(&['-', '.']) {
|
||||
if ss.len() == 7 {
|
||||
for b in base24::decode_7to4(ss.as_bytes())? {
|
||||
if i >= Self::SIZE {
|
||||
return Err(InvalidParameterError("invalid address"));
|
||||
}
|
||||
a.0[i] = b;
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
return Err(InvalidParameterError("invalid address"));
|
||||
}
|
||||
}
|
||||
return Ok(a);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Address {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToFromBytes for Address {
|
||||
#[inline(always)]
|
||||
fn read_bytes<R: std::io::Read>(r: &mut R) -> std::io::Result<Self> {
|
||||
let mut tmp = Address([0u8; Self::SIZE]);
|
||||
r.read_exact(&mut tmp.0)?;
|
||||
Ok(tmp)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
|
||||
Ok(Self(
|
||||
b.try_into()
|
||||
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "invalid address"))?,
|
||||
))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_bytes<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
w.write_all(&self.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Address {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
state.write(&self.0[..8])
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Address {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
self.to_string().serialize(serializer)
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressDeserializeVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for AddressDeserializeVisitor {
|
||||
type Value = Address;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("ZeroTier address")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Address::from_str(v.trim()).map_err(|e| serde::de::Error::custom(e.to_string()))
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
v.try_into()
|
||||
.map(|b| Address(b))
|
||||
.map_err(|_| serde::de::Error::invalid_length(v.len(), &self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Address {
|
||||
#[inline]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(AddressDeserializeVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(AddressDeserializeVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use zerotier_common_utils::error::InvalidParameterError;
|
||||
|
||||
/// All unambiguous letters, thus easy to type on the alphabetic keyboards on phones without extra shift taps.
|
||||
/// The letters 'l' and 'u' are skipped.
|
||||
const BASE24_ALPHABET: [u8; 24] = [
|
||||
b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'v', b'w', b'x', b'y', b'z',
|
||||
];
|
||||
|
||||
/// Reverse table for BASE24 alphabet, indexed relative to 'a' or 'A'.
|
||||
const BASE24_ALPHABET_INV: [u8; 26] = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 11, 12, 13, 14, 15, 16, 17, 18, 255, 19, 20, 21, 22, 23,
|
||||
];
|
||||
|
||||
/// Encode 4 binary bytes into 7 base24 characters.
|
||||
pub fn encode_4to7(b: &[u8], s: &mut String) {
|
||||
let mut n = u32::from_be_bytes(b[..4].try_into().unwrap());
|
||||
for _ in 0..6 {
|
||||
let (d, r) = (n / 24, n % 24);
|
||||
n = d;
|
||||
s.push(BASE24_ALPHABET[r as usize] as char);
|
||||
}
|
||||
s.push(BASE24_ALPHABET[n as usize] as char);
|
||||
}
|
||||
|
||||
pub fn decode_7to4(mut s: &[u8]) -> Result<[u8; 4], InvalidParameterError> {
|
||||
let mut n = 0u32;
|
||||
if s.len() > 7 {
|
||||
s = &s[..7];
|
||||
}
|
||||
for c in s.iter().rev() {
|
||||
let mut c = *c;
|
||||
if c >= 97 && c <= 122 {
|
||||
c -= 97;
|
||||
} else if c >= 65 && c <= 90 {
|
||||
c -= 65;
|
||||
} else {
|
||||
return Err(InvalidParameterError("invalid base24"));
|
||||
}
|
||||
let i = BASE24_ALPHABET_INV[c as usize];
|
||||
if i == 255 {
|
||||
return Err(InvalidParameterError("invalid base24"));
|
||||
}
|
||||
n *= 24;
|
||||
n = n.wrapping_add(i as u32);
|
||||
}
|
||||
return Ok(n.to_be_bytes());
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use zerotier_common_utils::error::InvalidParameterError;
|
||||
|
||||
pub fn encode_8to11(mut n: u64, s: &mut String) {
|
||||
for _ in 0..11 {
|
||||
let (d, r) = (n / 62, n % 62);
|
||||
n = d;
|
||||
let r = r as u8;
|
||||
s.push(if r < 26 {
|
||||
r + 97 // a..z
|
||||
} else if r < 52 {
|
||||
r + 39 // A..Z
|
||||
} else {
|
||||
r - 4 // 0..9
|
||||
} as char);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_11to8(s: &[u8]) -> Result<u64, InvalidParameterError> {
|
||||
let mut n = 0u64;
|
||||
for c in s.iter().rev() {
|
||||
let c = *c;
|
||||
n *= 62;
|
||||
n = n.wrapping_add(if c >= 97 && c <= 122 {
|
||||
n - 97
|
||||
} else if c >= 65 && c <= 90 {
|
||||
n - 39
|
||||
} else if c >= 48 && c <= 57 {
|
||||
n + 4
|
||||
} else {
|
||||
return Err(InvalidParameterError("invalid base62"));
|
||||
} as u64);
|
||||
}
|
||||
return Ok(n);
|
||||
}
|
||||
-647
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+10
-96
@@ -9,6 +9,10 @@
|
||||
use std::hash::Hash;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use zerotier_common_utils::error::InvalidParameterError;
|
||||
use zerotier_common_utils::tofrombytes::ToFromBytes;
|
||||
|
||||
/// A unique global identifier for a ZeroTier identity.
|
||||
@@ -33,7 +37,7 @@ pub trait Identity: ToString + FromStr + ToFromBytes + Sync + Send + Clone + Par
|
||||
}
|
||||
|
||||
/// Secret keys that correspond to a public Identity.
|
||||
pub trait IdentitySecret: Sync + Send + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + 'static {
|
||||
pub trait IdentitySecret: Sync + Send + Clone + PartialEq + Eq + Serialize + DeserializeOwned + 'static {
|
||||
type Public: Identity;
|
||||
|
||||
/// Type returned by sign(), should typically be [u8; Public::SIGNATURE_SIZE].
|
||||
@@ -42,7 +46,7 @@ pub trait IdentitySecret: Sync + Send + Clone + PartialEq + Eq + Hash + PartialO
|
||||
|
||||
/// Generate a new identity.
|
||||
/// This may in some cases be a time consuming operation.
|
||||
fn generate() -> Self;
|
||||
fn generate(timestamp: u64) -> Self;
|
||||
|
||||
/// Get the public portion of this secret identity.
|
||||
fn public(&self) -> &Self::Public;
|
||||
@@ -51,100 +55,10 @@ pub trait IdentitySecret: Sync + Send + Clone + PartialEq + Eq + Hash + PartialO
|
||||
fn sign(&self, data: &[u8]) -> Self::Signature;
|
||||
}
|
||||
|
||||
mod base24;
|
||||
mod base62;
|
||||
pub mod p384;
|
||||
pub mod x25519;
|
||||
|
||||
//mod address;
|
||||
//mod signature;
|
||||
|
||||
//pub mod identity;
|
||||
//pub mod identitysecret;
|
||||
|
||||
//pub use address::Address;
|
||||
//pub use identity::Identity;
|
||||
//pub use identitysecret::IdentitySecret;
|
||||
//pub use signature::Signature;
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use zerotier_utils::tofrombytes::ToFromBytes;
|
||||
|
||||
#[test]
|
||||
fn identity_generate_sign_verify_serialize_deserialize() {
|
||||
let id0 = IdentitySecret::generate_x25519();
|
||||
let id1 = IdentitySecret::generate_x25519p384(1, None).unwrap();
|
||||
let id2 = IdentitySecret::generate_p384(1);
|
||||
|
||||
let sig_data = "hello".as_bytes();
|
||||
let sig_wrong_data = "goodbye".as_bytes();
|
||||
|
||||
let sig0 = id0.sign(sig_data);
|
||||
let sig1 = id1.sign(sig_data);
|
||||
let sig2 = id2.sign(sig_data);
|
||||
|
||||
/*
|
||||
println!(
|
||||
"identity: signature lengths: x25519 {}, x25519p384 {}, p384 {}, p384pqc {}",
|
||||
sig0.len(),
|
||||
sig1.len(),
|
||||
sig2.len(),
|
||||
sig3.len()
|
||||
);
|
||||
*/
|
||||
|
||||
assert!(id0.public.verify(sig0.as_ref(), sig_data));
|
||||
assert!(id1.public.verify(sig1.as_ref(), sig_data));
|
||||
assert!(id2.public.verify(sig2.as_ref(), sig_data));
|
||||
|
||||
assert!(!id0.public.verify(sig0.as_ref(), sig_wrong_data));
|
||||
assert!(!id1.public.verify(sig1.as_ref(), sig_wrong_data));
|
||||
assert!(!id2.public.verify(sig2.as_ref(), sig_wrong_data));
|
||||
|
||||
let id0_bytes = id0.to_bytes();
|
||||
let id1_bytes = id1.to_bytes();
|
||||
let id2_bytes = id2.to_bytes();
|
||||
|
||||
/*
|
||||
println!(
|
||||
"identity: serialized secret lengths: x25519 {}, x25519p384 {}, p384 {}, p384pqc {}",
|
||||
id0_bytes.len(),
|
||||
id1_bytes.len(),
|
||||
id2_bytes.len(),
|
||||
id3_bytes.len()
|
||||
);
|
||||
*/
|
||||
|
||||
let id0_de = IdentitySecret::from_bytes(id0_bytes.as_slice()).unwrap();
|
||||
let id1_de = IdentitySecret::from_bytes(id1_bytes.as_slice()).unwrap();
|
||||
let id2_de = IdentitySecret::from_bytes(id2_bytes.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(id0_de.to_bytes(), id0.to_bytes());
|
||||
assert_eq!(id1_de.to_bytes(), id1.to_bytes());
|
||||
assert_eq!(id2_de.to_bytes(), id2.to_bytes());
|
||||
|
||||
let id0_bytes = id0.public.to_bytes();
|
||||
let id1_bytes = id1.public.to_bytes();
|
||||
let id2_bytes = id2.public.to_bytes();
|
||||
|
||||
/*
|
||||
println!(
|
||||
"identity: serialized public lengths: x25519 {}, x25519p384 {}, p384 {}, p384pqc {}",
|
||||
id0_bytes.len(),
|
||||
id1_bytes.len(),
|
||||
id2_bytes.len(),
|
||||
id3_bytes.len()
|
||||
);
|
||||
*/
|
||||
|
||||
let id0_de = Identity::from_bytes(id0_bytes.as_slice()).unwrap();
|
||||
let id1_de = Identity::from_bytes(id1_bytes.as_slice()).unwrap();
|
||||
let id2_de = Identity::from_bytes(id2_bytes.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(id0_de.to_bytes(), id0.public.to_bytes());
|
||||
assert_eq!(id1_de.to_bytes(), id1.public.to_bytes());
|
||||
assert_eq!(id2_de.to_bytes(), id2.public.to_bytes());
|
||||
}
|
||||
}
|
||||
*/
|
||||
pub(crate) const ADDRESS_ERR: InvalidParameterError = InvalidParameterError("invalid address");
|
||||
pub(crate) const IDENTITY_ERR: InvalidParameterError = InvalidParameterError("invalid identity");
|
||||
|
||||
+647
File diff suppressed because it is too large
Load Diff
@@ -1,60 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use zerotier_crypto::p384::P384_ECDSA_SIGNATURE_SIZE;
|
||||
use zerotier_crypto::x25519::ED25519_SIGNATURE_SIZE;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
|
||||
/// Buffer type large enough to store any possible signature combination.
|
||||
/// This can be enlarged if necessary.
|
||||
pub type Signature = ArrayVec<u8, 192>;
|
||||
|
||||
const SIGNATURE_FLAG_ECDSA_P384: u8 = 0x01;
|
||||
const SIGNATURE_FLAG_EDDSA_ED25519: u8 = 0x02;
|
||||
|
||||
pub(crate) fn make_signature(ecdsa_p384: &[u8], eddsa_ed25519: &[u8]) -> Signature {
|
||||
let mut s = Signature::new();
|
||||
s.push(0);
|
||||
let mut flags = 0;
|
||||
|
||||
if ecdsa_p384.len() == P384_ECDSA_SIGNATURE_SIZE {
|
||||
flags |= SIGNATURE_FLAG_ECDSA_P384;
|
||||
s.push_slice(ecdsa_p384);
|
||||
}
|
||||
if eddsa_ed25519.len() == ED25519_SIGNATURE_SIZE {
|
||||
flags |= SIGNATURE_FLAG_EDDSA_ED25519;
|
||||
s.push_slice(eddsa_ed25519);
|
||||
}
|
||||
|
||||
s.as_mut()[0] = flags;
|
||||
s
|
||||
}
|
||||
|
||||
/// Returns which signatures are present: (p384, ed25519)
|
||||
pub(crate) fn parse_signature(mut s: &[u8]) -> (Option<&[u8]>, Option<&[u8]>) {
|
||||
let mut sigs = (None, None);
|
||||
|
||||
if !s.is_empty() {
|
||||
let flags = s[0];
|
||||
s = &s[1..];
|
||||
if (flags & SIGNATURE_FLAG_ECDSA_P384) != 0 {
|
||||
if s.len() >= P384_ECDSA_SIGNATURE_SIZE {
|
||||
sigs.0 = Some(&s[..P384_ECDSA_SIGNATURE_SIZE]);
|
||||
s = &s[P384_ECDSA_SIGNATURE_SIZE..];
|
||||
}
|
||||
}
|
||||
if (flags & SIGNATURE_FLAG_EDDSA_ED25519) != 0 {
|
||||
if s.len() >= ED25519_SIGNATURE_SIZE {
|
||||
sigs.1 = Some(&s[..ED25519_SIGNATURE_SIZE]);
|
||||
//s = &s[ED25519_SIGNATURE_SIZE..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sigs
|
||||
}
|
||||
+44
-58
@@ -14,8 +14,7 @@ use zerotier_crypto_glue::hash::SHA512;
|
||||
use zerotier_crypto_glue::salsa::Salsa;
|
||||
use zerotier_crypto_glue::x25519::*;
|
||||
|
||||
const ADDRESS_ERR: InvalidParameterError = InvalidParameterError("invalid address");
|
||||
const IDENTITY_ERR: InvalidParameterError = InvalidParameterError("invalid identity");
|
||||
use crate::{ADDRESS_ERR, IDENTITY_ERR};
|
||||
|
||||
/// Legacy 40-bit ZeroTier address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -72,7 +71,7 @@ impl FromStr for Address {
|
||||
if s.len() == 10 {
|
||||
Self::try_from(hex::from_string_u64(s))
|
||||
} else {
|
||||
Err(InvalidParameterError("invalid address"))
|
||||
Err(InvalidParameterError(ADDRESS_ERR.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +84,7 @@ impl ToFromBytes for Address {
|
||||
if tmp.is_valid() {
|
||||
Ok(tmp)
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::Other, "invalid address"))
|
||||
Err(std::io::Error::new(std::io::ErrorKind::Other, ADDRESS_ERR.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ impl Serialize for Address {
|
||||
if serializer.is_human_readable() {
|
||||
self.to_string().serialize(serializer)
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.0)
|
||||
(self.0[0], self.0[1], self.0[2], self.0[3], self.0[4]).serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +122,13 @@ impl<'de> Deserialize<'de> for Address {
|
||||
if deserializer.is_human_readable() {
|
||||
Address::from_str(<&str>::deserialize(deserializer)?).map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
|
||||
} else {
|
||||
Address::from_bytes(<&[u8]>::deserialize(deserializer)?).map_err(|_| serde::de::Error::custom(ADDRESS_ERR.0))
|
||||
let b = <(u8, u8, u8, u8, u8)>::deserialize(deserializer)?;
|
||||
let a = Self([b.0, b.1, b.2, b.3, b.4]);
|
||||
if a.is_valid() {
|
||||
Ok(a)
|
||||
} else {
|
||||
Err(serde::de::Error::custom(ADDRESS_ERR.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,12 +147,16 @@ pub struct Identity {
|
||||
|
||||
impl Identity {
|
||||
fn locally_validate(&self) -> bool {
|
||||
let mut legacy_address_hasher = SHA512::new();
|
||||
legacy_address_hasher.update(&self.ecdh);
|
||||
legacy_address_hasher.update(&self.eddsa);
|
||||
let mut legacy_address_hash = legacy_address_hasher.finish();
|
||||
legacy_address_derivation_work_function(&mut legacy_address_hash);
|
||||
legacy_address_hash[0] < LEGACY_ADDRESS_POW_THRESHOLD && legacy_address_hash[59..64].eq(&self.address.0)
|
||||
if self.address.is_valid() {
|
||||
let mut legacy_address_hasher = SHA512::new();
|
||||
legacy_address_hasher.update(&self.ecdh);
|
||||
legacy_address_hasher.update(&self.eddsa);
|
||||
let mut legacy_address_hash = legacy_address_hasher.finish();
|
||||
legacy_address_derivation_work_function(&mut legacy_address_hash);
|
||||
legacy_address_hash[0] < LEGACY_ADDRESS_POW_THRESHOLD && legacy_address_hash[59..64].eq(&self.address.0)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,32 +330,11 @@ impl PartialEq for IdentitySecret {
|
||||
|
||||
impl Eq for IdentitySecret {}
|
||||
|
||||
impl PartialOrd for IdentitySecret {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.public.partial_cmp(&other.public)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for IdentitySecret {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.public.cmp(&other.public)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for IdentitySecret {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.public.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::IdentitySecret for IdentitySecret {
|
||||
type Public = Identity;
|
||||
type Signature = [u8; 96];
|
||||
|
||||
fn generate() -> Self {
|
||||
fn generate(_timestamp: u64) -> Self {
|
||||
let mut ecdh = X25519KeyPair::generate();
|
||||
let eddsa = Ed25519KeyPair::generate();
|
||||
let mut legacy_address_hasher = SHA512::new();
|
||||
@@ -388,7 +376,7 @@ impl crate::IdentitySecret for IdentitySecret {
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct IdentitySecretSerialized {
|
||||
a: Blob<5>,
|
||||
a: Address,
|
||||
p0: Blob<C25519_PUBLIC_KEY_SIZE>,
|
||||
s0: Blob<C25519_SECRET_KEY_SIZE>,
|
||||
p1: Blob<ED25519_PUBLIC_KEY_SIZE>,
|
||||
@@ -401,7 +389,7 @@ impl Serialize for IdentitySecret {
|
||||
S: Serializer,
|
||||
{
|
||||
IdentitySecretSerialized {
|
||||
a: self.public.address.0.into(),
|
||||
a: self.public.address,
|
||||
p0: self.public.ecdh.into(),
|
||||
s0: (*self.ecdh.secret_bytes().as_bytes()).into(),
|
||||
p1: self.public.eddsa.into(),
|
||||
@@ -417,26 +405,24 @@ impl<'de> Deserialize<'de> for IdentitySecret {
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let d = <IdentitySecretSerialized>::deserialize(deserializer)?;
|
||||
let ecdh = X25519KeyPair::from_bytes(d.p0.as_bytes(), d.s0.as_bytes());
|
||||
let eddsa = Ed25519KeyPair::from_bytes(d.p1.as_bytes(), d.s1.as_bytes());
|
||||
if ecdh.is_none() || eddsa.is_none() {
|
||||
return Err(serde::de::Error::custom(IDENTITY_ERR.0));
|
||||
if let (Some(ecdh), Some(eddsa)) = (
|
||||
X25519KeyPair::from_bytes(d.p0.as_bytes(), d.s0.as_bytes()),
|
||||
Ed25519KeyPair::from_bytes(d.p1.as_bytes(), d.s1.as_bytes()),
|
||||
) {
|
||||
let id = Self {
|
||||
public: Identity {
|
||||
address: d.a,
|
||||
ecdh: ecdh.public_bytes(),
|
||||
eddsa: eddsa.public_bytes(),
|
||||
},
|
||||
ecdh,
|
||||
eddsa,
|
||||
};
|
||||
if id.public.locally_validate() {
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
let ecdh = ecdh.unwrap();
|
||||
let eddsa = eddsa.unwrap();
|
||||
let id = Self {
|
||||
public: Identity {
|
||||
address: Address(d.a.into()),
|
||||
ecdh: ecdh.public_bytes(),
|
||||
eddsa: eddsa.public_bytes(),
|
||||
},
|
||||
ecdh,
|
||||
eddsa,
|
||||
};
|
||||
if !id.public.address.is_valid() || !id.public.locally_validate() {
|
||||
return Err(serde::de::Error::custom(IDENTITY_ERR.0));
|
||||
}
|
||||
return Ok(id);
|
||||
return Err(serde::de::Error::custom(IDENTITY_ERR.0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +492,7 @@ mod tests {
|
||||
fn generate() {
|
||||
let start = ms_monotonic();
|
||||
for _ in 0..3 {
|
||||
let secret = x25519::IdentitySecret::generate();
|
||||
let secret = x25519::IdentitySecret::generate(0);
|
||||
println!("S: {}", secret.to_string());
|
||||
println!("P: {}", secret.public.to_string());
|
||||
}
|
||||
@@ -516,7 +502,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tostring_fromstring() {
|
||||
let secret = x25519::IdentitySecret::generate();
|
||||
let secret = x25519::IdentitySecret::generate(0);
|
||||
assert!(x25519::Address::from_str(secret.public.address.to_string().as_str())
|
||||
.unwrap()
|
||||
.eq(&secret.public.address));
|
||||
@@ -526,7 +512,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tobytes_frombytes() {
|
||||
let secret = x25519::IdentitySecret::generate();
|
||||
let secret = x25519::IdentitySecret::generate(0);
|
||||
assert!(x25519::Address::from_bytes(secret.public.address.to_bytes().as_slice())
|
||||
.unwrap()
|
||||
.eq(&secret.public.address));
|
||||
|
||||
Reference in New Issue
Block a user