mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-15 17:03:45 +02:00
Cleanup and implement some serde handlers for some common core data structures.
This commit is contained in:
parent
b346f3ff07
commit
6e55419581
8 changed files with 208 additions and 25 deletions
|
@ -18,9 +18,7 @@ lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-
|
|||
dashmap = "^4"
|
||||
parking_lot = "^0"
|
||||
lazy_static = "^1"
|
||||
serde = "^1"
|
||||
serde_derive = "^1"
|
||||
serde_json = "^1"
|
||||
serde = { version = "^1", features = ["derive"], default-features = false }
|
||||
|
||||
[target."cfg(not(windows))".dependencies]
|
||||
libc = "^0"
|
||||
|
|
|
@ -10,14 +10,15 @@ use std::hash::{Hash, Hasher};
|
|||
use std::num::NonZeroU64;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::buffer::Buffer;
|
||||
use crate::util::hex::HEX_CHARS;
|
||||
use crate::vl1::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
/// A unique address on the global ZeroTier VL1 network.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Address(NonZeroU64);
|
||||
|
||||
|
@ -91,3 +92,57 @@ impl Hash for Address {
|
|||
state.write_u64(self.0.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Address {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.to_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
||||
type Value = Address;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a ZeroTier address")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.len() == ADDRESS_SIZE {
|
||||
Address::from_bytes(v).map_or_else(|| Err(E::custom("object too large")), |a| Ok(a))
|
||||
} else {
|
||||
Err(E::custom("object too large"))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Address::from_str(v).map_err(|e| E::custom(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Address {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Address, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(AddressVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(AddressVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::str::FromStr;
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use zerotier_core_crypto::c25519::*;
|
||||
use zerotier_core_crypto::hash::*;
|
||||
|
@ -45,12 +45,14 @@ pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
|||
pub const MAX_MARSHAL_SIZE: usize =
|
||||
ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
||||
|
||||
/// Secret keys associated with NIST P-384 public keys.
|
||||
#[derive(Clone)]
|
||||
pub struct IdentityP384Secret {
|
||||
pub ecdh: P384KeyPair,
|
||||
pub ecdsa: P384KeyPair,
|
||||
}
|
||||
|
||||
/// NIST P-384 public keys and signatures binding them bidirectionally to V0 c25519 keys.
|
||||
#[derive(Clone)]
|
||||
pub struct IdentityP384Public {
|
||||
pub ecdh: P384PublicKey,
|
||||
|
@ -59,6 +61,7 @@ pub struct IdentityP384Public {
|
|||
pub ed25519_self_signature: [u8; ED25519_SIGNATURE_SIZE],
|
||||
}
|
||||
|
||||
/// Secret keys associated with an identity.
|
||||
#[derive(Clone)]
|
||||
pub struct IdentitySecret {
|
||||
pub c25519: C25519KeyPair,
|
||||
|
@ -66,6 +69,14 @@ pub struct IdentitySecret {
|
|||
pub p384: Option<IdentityP384Secret>,
|
||||
}
|
||||
|
||||
/// A unique identity on the global VL1 network.
|
||||
///
|
||||
/// Identity implements serde Serialize and Deserialize. Identities are serialized as strings
|
||||
/// for human-readable formats and binary otherwise.
|
||||
///
|
||||
/// SECURITY NOTE: for security reasons secret keys are NOT exported by default by to_string()
|
||||
/// or by the serde serializer. If you want secrets you must use to_string_with_options() or
|
||||
/// marshal(). This is to prevent accidental leakage of secrets by naive code.
|
||||
#[derive(Clone)]
|
||||
pub struct Identity {
|
||||
pub address: Address,
|
||||
|
@ -76,9 +87,6 @@ pub struct Identity {
|
|||
pub fingerprint: [u8; SHA512_HASH_SIZE],
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub struct NetworkId(pub u64);
|
||||
|
||||
#[inline(always)]
|
||||
fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A], b: &[u8; B]) -> [u8; S] {
|
||||
assert_eq!(A + B, S);
|
||||
|
@ -726,6 +734,65 @@ impl Hash for Identity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serialize for Identity {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false).as_str())
|
||||
} else {
|
||||
let mut tmp: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
||||
assert!(self.marshal(&mut tmp, IDENTITY_ALGORITHM_ALL, false).is_ok());
|
||||
serializer.serialize_bytes(tmp.as_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentityVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
||||
type Value = Identity;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a ZeroTier identity")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.len() <= MAX_MARSHAL_SIZE {
|
||||
let mut tmp: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
||||
let _ = tmp.append_bytes(v);
|
||||
let mut cursor = 0;
|
||||
Identity::unmarshal(&tmp, &mut cursor).map_err(|e| E::custom(e.to_string()))
|
||||
} else {
|
||||
Err(E::custom("object too large"))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Identity::from_str(v).map_err(|e| E::custom(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Identity {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Identity, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(IdentityVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(IdentityVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ADDRESS_DERIVATION_HASH_MEMORY_SIZE: usize = 2097152;
|
||||
|
||||
/// This is a compound hasher used for the work function that derives an address.
|
||||
|
|
|
@ -11,9 +11,12 @@ use std::hash::{Hash, Hasher};
|
|||
use std::num::NonZeroU64;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::buffer::Buffer;
|
||||
|
||||
/// An Ethernet MAC address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct MAC(NonZeroU64);
|
||||
|
@ -87,3 +90,57 @@ impl Hash for MAC {
|
|||
state.write_u64(self.0.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for MAC {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.to_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MACVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for MACVisitor {
|
||||
type Value = MAC;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a ZeroTier address")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.len() == 6 {
|
||||
MAC::from_bytes(v).map_or_else(|| Err(E::custom("object too large")), |a| Ok(a))
|
||||
} else {
|
||||
Err(E::custom("object too large"))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
MAC::from_str(v).map_err(|e| E::custom(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for MAC {
|
||||
fn deserialize<D>(deserializer: D) -> Result<MAC, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(MACVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(MACVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -509,7 +509,7 @@ impl Peer {
|
|||
// Set legacy poly1305 MAC in packet header. Newer nodes check HMAC-SHA512 but older ones only use this.
|
||||
let (_, mut poly) = salsa_poly_create(&self.identity_symmetric_key, packet.struct_at::<PacketHeader>(0).unwrap(), packet.len());
|
||||
poly.update(packet.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
|
||||
packet.as_mut_range_fixed::<HEADER_MAC_FIELD_INDEX, { HEADER_MAC_FIELD_INDEX + 8 }>().copy_from_slice(&poly.finish()[0..8]);
|
||||
packet.as_mut()[HEADER_MAC_FIELD_INDEX..HEADER_MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
|
||||
|
||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
self.total_bytes_sent.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
||||
|
|
|
@ -48,13 +48,13 @@ pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY: u8 = b'e';
|
|||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
||||
|
||||
/// Maximum number of times to use an ephemeral secret before trying to replace it.
|
||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: u32 = 536870912; // 1/4 the NIST/FIPS security bound of 2^31
|
||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: usize = 536870912; // 1/4 the NIST/FIPS security bound of 2^31
|
||||
|
||||
/// Ephemeral secret reject after time.
|
||||
pub const EPHEMERAL_SECRET_REJECT_AFTER_TIME: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME * 2;
|
||||
|
||||
/// Ephemeral secret reject after uses.
|
||||
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: u32 = 2147483648; // NIST/FIPS security bound
|
||||
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: usize = 2147483648; // NIST/FIPS security bound
|
||||
|
||||
pub const SESSION_METADATA_INSTANCE_ID: &'static str = "i";
|
||||
pub const SESSION_METADATA_CLOCK: &'static str = "t";
|
||||
|
@ -211,7 +211,7 @@ pub fn compress_packet(src: &[u8], dest: &mut Buffer<{ PACKET_SIZE_MAX }>) -> bo
|
|||
///
|
||||
/// This is the header for a complete packet. If the fragmented flag is set, it will
|
||||
/// arrive with one or more fragments that must be assembled to complete it.
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct PacketHeader {
|
||||
pub id: [u8; 8],
|
||||
pub dest: [u8; 5],
|
||||
|
@ -275,7 +275,7 @@ impl PacketHeader {
|
|||
/// is normally illegal since addresses can't begin with that. Fragmented packets
|
||||
/// will arrive with the first fragment carrying a normal header with the fragment
|
||||
/// bit set and remaining fragments being these.
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct FragmentHeader {
|
||||
pub id: [u8; 8], // (outer) packet ID
|
||||
pub dest: [u8; 5], // destination address
|
||||
|
@ -324,7 +324,7 @@ impl FragmentHeader {
|
|||
pub(crate) mod message_component_structs {
|
||||
use crate::util::buffer::RawObject;
|
||||
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct OkHeader {
|
||||
pub in_re_verb: u8,
|
||||
pub in_re_message_id: [u8; 8],
|
||||
|
@ -332,7 +332,7 @@ pub(crate) mod message_component_structs {
|
|||
|
||||
unsafe impl RawObject for OkHeader {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct ErrorHeader {
|
||||
pub in_re_verb: u8,
|
||||
pub in_re_message_id: [u8; 8],
|
||||
|
@ -341,7 +341,7 @@ pub(crate) mod message_component_structs {
|
|||
|
||||
unsafe impl RawObject for ErrorHeader {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct HelloFixedHeaderFields {
|
||||
pub verb: u8,
|
||||
pub version_proto: u8,
|
||||
|
@ -353,7 +353,7 @@ pub(crate) mod message_component_structs {
|
|||
|
||||
unsafe impl RawObject for HelloFixedHeaderFields {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[repr(C, packed)]
|
||||
pub struct OkHelloFixedHeaderFields {
|
||||
pub timestamp_echo: [u8; 8], // u64
|
||||
pub version_proto: u8,
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use zerotier_core_crypto::aes_gmac_siv::AesGmacSiv;
|
||||
use zerotier_core_crypto::kbkdf::*;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
|
@ -20,7 +21,7 @@ pub(crate) struct AesGmacSivPoolFactory(Secret<32>, Secret<32>);
|
|||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||
#[inline(always)]
|
||||
fn create(&self) -> AesGmacSiv {
|
||||
AesGmacSiv::new(&self.0 .0, &self.1 .0)
|
||||
AesGmacSiv::new(self.0.as_bytes(), self.1.as_bytes())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -33,16 +34,23 @@ impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
|||
///
|
||||
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
||||
pub(crate) struct SymmetricSecret {
|
||||
/// Master key from which other keys are derived.
|
||||
pub key: Secret<64>,
|
||||
|
||||
/// Key used for HMAC extended validation on packets like HELLO.
|
||||
pub packet_hmac_key: Secret<64>,
|
||||
|
||||
/// Key used with ephemeral keying/re-keying.
|
||||
pub ephemeral_ratchet_key: Secret<64>,
|
||||
|
||||
/// Pool of keyed AES-GMAC-SIV engines (pooled to avoid AES re-init every time).
|
||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||
}
|
||||
|
||||
impl PartialEq for SymmetricSecret {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.key.0.eq(&other.key.0)
|
||||
self.key == other.key
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,8 +78,8 @@ pub(crate) struct EphemeralSymmetricSecret {
|
|||
pub rekey_time: i64,
|
||||
pub expire_time: i64,
|
||||
pub ratchet_count: u64,
|
||||
pub encrypt_uses: AtomicU32,
|
||||
pub decrypt_uses: AtomicU32,
|
||||
pub encrypt_uses: AtomicUsize,
|
||||
pub decrypt_uses: AtomicUsize,
|
||||
pub fips_compliant_exchange: bool,
|
||||
}
|
||||
|
||||
|
|
2
zerotier-system-service/Cargo.lock
generated
2
zerotier-system-service/Cargo.lock
generated
|
@ -868,8 +868,6 @@ dependencies = [
|
|||
"lz4_flex",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"winapi",
|
||||
"zerotier-core-crypto",
|
||||
]
|
||||
|
|
Loading…
Add table
Reference in a new issue