(1) Implement typestate Verified for identity, (2) FileDatabase in controller now works.

This commit is contained in:
Adam Ierymenko 2022-11-16 21:41:08 -05:00
parent 68021b8a7f
commit 19d973cfd4
33 changed files with 994 additions and 219 deletions

View file

@ -35,7 +35,7 @@ pub struct Controller {
reaper: Reaper,
runtime: tokio::runtime::Handle,
database: Arc<dyn Database>,
local_identity: Identity,
local_identity: Verified<Identity>,
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
multicast_authority: MulticastAuthority,
@ -61,7 +61,7 @@ impl Controller {
reaper: Reaper::new(&runtime),
runtime,
database: database.clone(),
local_identity,
local_identity: local_identity,
multicast_authority: MulticastAuthority::new(),
daemons: Mutex::new(Vec::with_capacity(2)),
recently_authorized: RwLock::new(HashMap::new()),
@ -76,9 +76,8 @@ impl Controller {
/// Set the service and HostSystem implementation for this controller and start daemons.
///
/// This must be called once the service that uses this handler is up or the controller
/// won't actually do anything. The reference the handler holds is weak to prevent
/// a circular reference, so if the VL1Service is dropped this must be called again to
/// tell the controller handler about a new instance.
/// won't actually do anything. The controller holds a weak reference to VL1Service so
/// be sure it's not dropped.
pub async fn start(&self, service: &Arc<VL1Service<dyn Database, Self, Self>>) {
*self.service.write().unwrap() = Arc::downgrade(service);
@ -106,7 +105,7 @@ impl Controller {
// Create background task to expire multicast subscriptions and recent authorizations.
let self2 = self.self_ref.clone();
self.daemons.lock().unwrap().push(self.runtime.spawn(async move {
let sleep_duration = Duration::from_millis((protocol::VL2_DEFAULT_MULTICAST_LIKE_EXPIRE / 2).min(5000) as u64);
let sleep_duration = Duration::from_millis((protocol::VL2_DEFAULT_MULTICAST_LIKE_EXPIRE / 2).min(2500) as u64);
loop {
tokio::time::sleep(sleep_duration).await;
@ -234,7 +233,7 @@ impl Controller {
/// reason is returned with None or an acceptance reason with a network configuration is returned.
async fn get_network_config(
self: &Arc<Self>,
source_identity: &Identity,
source_identity: &Verified<Identity>,
network_id: NetworkId,
now: i64,
) -> Result<(AuthorizationResult, Option<NetworkConfig>, Option<Vec<vl2::v1::Revocation>>), Box<dyn Error + Send + Sync>> {
@ -287,7 +286,7 @@ impl Controller {
if !member_authorized {
if member.is_none() {
if network.learn_members.unwrap_or(true) {
let _ = member.insert(Member::new_with_identity(source_identity.clone(), network_id));
let _ = member.insert(Member::new_with_identity(source_identity.as_ref().clone(), network_id));
member_changed = true;
} else {
return Ok((AuthorizationResult::Rejected, None, None));
@ -407,8 +406,10 @@ impl Controller {
} else {
return Ok((AuthorizationResult::RejectedDueToError, None, None));
}
} else {
// TODO: create V2 type credential for V2-only networks
}
if source_identity.p384.is_some() {
// TODO: create V2 type credential for V2 nodes
}
// Log this member in the recently authorized cache, which is currently just used to filter whether we should
@ -578,14 +579,14 @@ impl InnerProtocol for Controller {
impl VL1AuthProvider for Controller {
#[inline(always)]
fn should_respond_to(&self, _: &Identity) -> bool {
fn should_respond_to(&self, _: &Verified<Identity>) -> bool {
// Controllers always have to establish sessions to process requests. We don't really know if
// a member is relevant until we have looked up both the network and the member, since whether
// or not to "learn" unknown members is a network level option.
true
}
fn has_trust_relationship(&self, id: &Identity) -> bool {
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool {
self.recently_authorized
.read()
.unwrap()

View file

@ -9,7 +9,7 @@ use zerotier_utils::tokio::sync::broadcast::Receiver;
use crate::model::*;
/// Database change relevant to the controller and that was NOT initiated by the controller.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Change {
NetworkCreated(Network),
NetworkChanged(Network, Network),

View file

@ -8,11 +8,9 @@ use async_trait::async_trait;
use notify::{RecursiveMode, Watcher};
use serde::de::DeserializeOwned;
use zerotier_network_hypervisor::vl1::{Address, Identity, NodeStorage};
use zerotier_network_hypervisor::vl1::{Address, Identity, NodeStorage, Verified};
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::io::{fs_restrict_permissions, read_limit};
use zerotier_utils::reaper::Reaper;
use zerotier_utils::tokio::fs;
use zerotier_utils::tokio::runtime::Handle;
@ -60,11 +58,6 @@ impl FileDatabase {
tasks: Reaper::new(&runtime2),
cache: Cache::new(),
daemon: runtime2.spawn(async move {
while db_weak.lock().unwrap().upgrade().is_none() {
// Wait for parent to finish constructing and start up, then create watcher.
sleep(Duration::from_millis(10)).await;
}
let mut watcher = notify::recommended_watcher(move |event: notify::Result<notify::event::Event>| {
if let Ok(event) = event {
match event.kind {
@ -121,7 +114,6 @@ impl FileDatabase {
}
if deleted.is_some() {
println!("DELETED: {}", deleted.unwrap().as_os_str().to_string_lossy());
match record_type {
RecordType::Network => {
if let Some((network, mut members)) =
@ -147,7 +139,6 @@ impl FileDatabase {
}
if let Some(changed) = changed {
println!("CHANGED: {}", changed.as_os_str().to_string_lossy());
match record_type {
RecordType::Network => {
if let Ok(Some(new_network)) =
@ -258,7 +249,7 @@ impl FileDatabase {
/// Get record type and also the number after it: network number or address.
fn record_type_from_path(controller_address: Address, p: &Path) -> Option<(RecordType, NetworkId, Option<Address>)> {
let parent = p.parent()?.to_string_lossy();
let parent = p.parent()?.file_name()?.to_string_lossy();
if parent.len() == 7 && (parent.starts_with("N") || parent.starts_with('n')) {
let network_id = NetworkId::from_controller_and_network_no(controller_address, u64::from_str_radix(&parent[1..], 16).ok()?)?;
if let Some(file_name) = p.file_name().map(|p| p.to_string_lossy().to_lowercase()) {
@ -284,7 +275,7 @@ impl Drop for FileDatabase {
}
impl NodeStorage for FileDatabase {
fn load_node_identity(&self) -> Option<Identity> {
fn load_node_identity(&self) -> Option<Verified<Identity>> {
let id_data = read_limit(self.base_path.join(IDENTITY_SECRET_FILENAME), 16384);
if id_data.is_err() {
return None;
@ -293,10 +284,10 @@ impl NodeStorage for FileDatabase {
if id_data.is_err() {
return None;
}
Some(id_data.unwrap())
Some(Verified::assume_verified(id_data.unwrap()))
}
fn save_node_identity(&self, id: &Identity) {
fn save_node_identity(&self, id: &Verified<Identity>) {
assert!(id.secret.is_some());
let id_secret_str = id.to_secret_string();
let secret_path = self.base_path.join(IDENTITY_SECRET_FILENAME);
@ -422,17 +413,35 @@ mod tests {
println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap());
let _ = std::fs::remove_dir_all(&test_dir);
let controller_id = Identity::generate();
let db = Arc::new(FileDatabase::new(tokio_runtime.handle().clone(), test_dir).await.expect("new db"));
db.save_node_identity(&controller_id);
assert!(db.load_node_identity().is_some());
let db2 = db.clone();
tokio_runtime.spawn(async move {
let mut change_receiver = db2.changes().await.unwrap();
loop {
if let Ok(change) = change_receiver.recv().await {
//println!("[FileDatabase] {:#?}", change);
} else {
break;
}
}
});
let mut test_network = Network::new(network_id);
db.save_network(test_network.clone()).await.expect("network save error");
let db = FileDatabase::new(tokio_runtime.handle().clone(), test_dir).await.expect("new db");
let mut test_member = Member::new_without_identity(node_id, network_id);
for x in 0..3 {
test_member.name = x.to_string();
db.save_member(test_member.clone()).await.expect("member save ok");
db.save_member(test_member.clone()).await.expect("member save error");
sleep(Duration::from_millis(100)).await;
let test_member2 = db.get_member(network_id, node_id).await.unwrap().unwrap();
//println!("{}", test_member.to_string());
//println!("{}", test_member2.to_string());
assert!(test_member == test_member2);
}
});

View file

@ -9,7 +9,7 @@ use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::blob::Blob;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct Member {
#[serde(rename = "address")]
pub node_id: Address,
@ -119,9 +119,3 @@ impl Hash for Member {
self.network_id.hash(state);
}
}
impl ToString for Member {
fn to_string(&self) -> String {
zerotier_utils::json::to_json_pretty(self)
}
}

View file

@ -15,7 +15,7 @@ use zerotier_network_hypervisor::vl2::networkconfig::NetworkConfig;
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::blob::Blob;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecordType {
Network,
Member,
@ -23,13 +23,13 @@ pub enum RecordType {
}
/// A complete network with all member configuration information for import/export or blob storage.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NetworkExport {
pub network: Network,
pub members: HashMap<Address, Member>,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum AuthorizationResult {
#[serde(rename = "r")]
@ -87,7 +87,7 @@ impl ToString for AuthorizationResult {
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RequestLogItem {
#[serde(rename = "nw")]
pub network_id: NetworkId,

View file

@ -15,12 +15,12 @@ use crate::model::Member;
pub const CREDENTIAL_WINDOW_SIZE_DEFAULT: u64 = 1000 * 60 * 60;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Debug)]
pub struct Ipv4AssignMode {
pub zt: bool,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Debug)]
pub struct Ipv6AssignMode {
pub zt: bool,
pub rfc4193: bool,
@ -28,7 +28,7 @@ pub struct Ipv6AssignMode {
pub _6plane: bool,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Debug)]
pub struct IpAssignmentPool {
#[serde(rename = "ipRangeStart")]
ip_range_start: InetAddress,
@ -40,7 +40,7 @@ pub struct IpAssignmentPool {
///
/// This contains only fields of relevance to the controller. Other fields can be tracked by various
/// database implementations such as row last modified, creation time, ownership in an admin panel, etc.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct Network {
pub id: NetworkId,
@ -134,18 +134,32 @@ impl Hash for Network {
}
}
impl ToString for Network {
fn to_string(&self) -> String {
zerotier_utils::json::to_json_pretty(self)
}
}
#[inline(always)]
fn troo() -> bool {
true
}
impl Network {
pub fn new(id: NetworkId) -> Self {
Network {
id,
name: String::new(),
multicast_limit: None,
enable_broadcast: None,
v4_assign_mode: None,
v6_assign_mode: None,
ip_assignment_pools: HashSet::new(),
ip_routes: HashSet::new(),
dns: HashMap::new(),
rules: Vec::new(),
credential_ttl: None,
min_supported_version: None,
mtu: None,
private: true,
learn_members: None,
}
}
/// Check member IP assignments and return 'true' if IP assignments were created or modified.
pub async fn assign_ip_addresses<DatabaseImpl: Database + ?Sized>(&self, database: &DatabaseImpl, member: &mut Member) -> bool {
let mut modified = false;

View file

@ -11,7 +11,7 @@ ed25519-dalek = { version = "1.0.1", features = ["std", "u64_backend"], default-
foreign-types = "0.3.1"
lazy_static = "^1"
openssl = { version = "^0", features = [], default-features = false }
poly1305 = { version = "0.7.2", features = [], default-features = false }
poly1305 = { version = "0.8.0", features = [], default-features = false }
pqc_kyber = { path = "../third_party/kyber", features = ["kyber1024", "reference"], default-features = false }
#pqc_kyber = { version = "^0", features = ["kyber1024", "reference"], default-features = false }
rand_core = "0.5.1"
@ -19,9 +19,6 @@ rand_core_062 = { package = "rand_core", version = "0.6.2" }
subtle = "2.4.1"
x25519-dalek = { version = "1.2.0", features = ["std", "u64_backend"], default-features = false }
[target."cfg(not(any(target_os = \"macos\", target_os = \"ios\")))".dependencies]
openssl = "^0"
[dev-dependencies]
criterion = "0.3"
sha2 = "^0"

View file

@ -4,4 +4,4 @@
Most of this library is just glue to provide a simple safe API around things like OpenSSL or OS-specific crypto APIs.
It also contains ZSSP, the V2 ZeroTier Secure Session Protocol.
It also contains [ZSSP](ZSSP.md), the V2 ZeroTier Secure Session Protocol.

View file

@ -1,6 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use poly1305::universal_hash::NewUniversalHash;
use poly1305::universal_hash::KeyInit;
/// The poly1305 message authentication function.
pub struct Poly1305(poly1305::Poly1305, [u8; 16], usize);
@ -12,7 +12,6 @@ pub const POLY1305_MAC_SIZE: usize = 16;
pub fn compute(one_time_key: &[u8], message: &[u8]) -> [u8; POLY1305_MAC_SIZE] {
poly1305::Poly1305::new(poly1305::Key::from_slice(one_time_key))
.compute_unpadded(message)
.into_bytes()
.into()
}

View file

@ -1,15 +1,32 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
/// A zero-overhead wrapper that signals that a credential is verified.
/// A zero-overhead typestate indicating that a credential has been verified as valid.
///
/// This is used when a function expects to receive an object that is already verified to
/// make code more self-documenting and make it semantically harder to accidentally use
/// an untrusted object.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
/// What this means is obviously specific to the credential.
///
/// The purpose of this is to make code more self-documenting and make it harder to accidentally
/// use an unverified/unvalidated credential (or other security critical object) where a verified
/// one is required.
#[repr(transparent)]
pub struct Verified<T>(pub T);
pub struct Verified<T>(T);
impl<T> AsRef<T> for Verified<T> {
#[inline(always)]
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for Verified<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> Deref for Verified<T> {
type Target = T;
@ -27,9 +44,78 @@ impl<T> DerefMut for Verified<T> {
}
}
impl<T> Clone for Verified<T>
where
T: Clone,
{
#[inline(always)]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> PartialEq for Verified<T>
where
T: PartialEq,
{
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<T> Eq for Verified<T> where T: Eq {}
impl<T> Ord for Verified<T>
where
T: Ord,
{
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T> PartialOrd for Verified<T>
where
T: PartialOrd,
{
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T> Hash for Verified<T>
where
T: Hash,
{
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> Debug for Verified<T>
where
T: Debug,
{
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Verified").field(&self.0).finish()
}
}
impl<T> Verified<T> {
/// Strip the Verified typestate off this object.
#[inline(always)]
pub fn unwrap(self) -> T {
self.0
}
/// Set Verified typestate on an object.
#[inline(always)]
pub fn assume_verified(o: T) -> Self {
Self(o)
}
}

View file

@ -1,5 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::num::NonZeroU64;
use std::str::FromStr;
@ -12,7 +13,7 @@ use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
/// A unique address on the global ZeroTier VL1 network.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Address(NonZeroU64);
@ -92,6 +93,13 @@ impl Hash for Address {
}
}
impl Debug for Address {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -2,6 +2,7 @@
use std::cmp::Ordering;
use std::convert::TryInto;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::io::Write;
use std::str::FromStr;
@ -22,6 +23,7 @@ use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use crate::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
use crate::vl1::Address;
use crate::vl1::Verified;
/// Current maximum size for an identity signature.
pub const IDENTITY_MAX_SIGNATURE_SIZE: usize = P384_ECDSA_SIGNATURE_SIZE + 1;
@ -164,7 +166,7 @@ impl Identity {
const FLAG_INCLUDES_SECRETS: u8 = 0x80;
/// Generate a new identity.
pub fn generate() -> Self {
pub fn generate() -> Verified<Self> {
// First generate an identity with just x25519 keys and derive its address.
let mut sha = SHA512::new();
let ed25519 = Ed25519KeyPair::generate();
@ -204,7 +206,7 @@ impl Identity {
assert!(id.upgrade().is_ok());
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
id
Verified::assume_verified(id)
}
/// Upgrade older x25519-only identities to hybrid identities with both x25519 and NIST P-384 curves.
@ -288,7 +290,7 @@ impl Identity {
/// Locally check the validity of this identity.
///
/// This is somewhat time consuming due to the memory-intensive work algorithm.
pub fn validate_identity(&self) -> bool {
pub fn validate(self) -> Option<Verified<Self>> {
if let Some(p384) = self.p384.as_ref() {
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(
ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE,
@ -301,12 +303,12 @@ impl Identity {
let _ = self_sign_buf.write_all(p384.ecdsa.as_bytes());
if !p384.ecdsa.verify(self_sign_buf.as_slice(), &p384.ecdsa_self_signature) {
return false;
return None;
}
let _ = self_sign_buf.write_all(&p384.ecdsa_self_signature);
if !ed25519_verify(&self.ed25519, &p384.ed25519_self_signature, self_sign_buf.as_slice()) {
return false;
return None;
}
}
@ -318,7 +320,11 @@ impl Identity {
let mut digest = sha.finish();
zt_address_derivation_work_function(&mut digest);
return digest[0] < IDENTITY_POW_THRESHOLD && Address::from_bytes(&digest[59..64]).map_or(false, |a| a == self.address);
return if digest[0] < IDENTITY_POW_THRESHOLD && Address::from_bytes(&digest[59..64]).map_or(false, |a| a == self.address) {
Some(Verified::assume_verified(self))
} else {
None
};
}
/// Returns true if this identity was upgraded from another older version.
@ -337,7 +343,7 @@ impl Identity {
/// For new identities with P-384 keys a hybrid agreement is performed using both X25519 and NIST P-384 ECDH.
/// The final key is derived as HMAC(x25519 secret, p-384 secret) to yield a FIPS-compliant key agreement with
/// the X25519 secret being used as a "salt" as far as FIPS is concerned.
pub fn agree(&self, other: &Identity) -> Option<Secret<64>> {
pub fn agree(&self, other: &Verified<Identity>) -> Option<Secret<64>> {
if let Some(secret) = self.secret.as_ref() {
let c25519_secret: Secret<64> = Secret(SHA512::hash(&secret.x25519.agree(&other.x25519).0));
@ -822,6 +828,13 @@ impl Hash for Identity {
}
}
impl Debug for Identity {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl Serialize for Identity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -887,8 +900,7 @@ mod tests {
let id = Identity::from_str(
"728efdb79d:0:3077ed0084d8d48a3ac628af6b45d9351e823bff34bc4376cddfc77a3d73a966c7d347bdcc1244d0e99e1b9c961ff5e963092e90ca43b47ff58c114d2d699664:2afaefcd1dca336ed59957eb61919b55009850b0b7088af3ee142672b637d1d49cc882b30a006f9eee42f2211ef8fe1cbe99a16a4436737fc158ce2243c15f12",
)
.unwrap();
assert!(id.validate_identity());
.unwrap().validate().unwrap();
let self_agree = id.agree(&id).unwrap();
assert!(self_agree_expected.as_slice().eq(&self_agree.as_bytes()[..48]));
@ -920,7 +932,6 @@ mod tests {
fn marshal_unmarshal_sign_verify_agree() {
let gen = Identity::generate();
assert!(gen.agree(&gen).is_some());
assert!(gen.validate_identity());
let bytes = gen.to_secret_bytes().unwrap();
let string = gen.to_secret_string();
assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen));
@ -938,57 +949,53 @@ mod tests {
assert!(Identity::from_str(string.as_str()).unwrap().secret.is_some());
let gen2 = Identity::generate();
assert!(gen2.validate_identity());
assert!(gen2.agree(&gen).unwrap().eq(&gen.agree(&gen2).unwrap()));
for id_str in GOOD_V0_IDENTITIES {
let mut id = Identity::from_str(id_str).unwrap();
let mut id = Identity::from_str(id_str).unwrap().validate().unwrap();
assert_eq!(id.to_secret_string().as_str(), id_str);
assert!(id.validate_identity());
assert!(id.p384.is_none());
let idb = id.to_secret_bytes().unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap().validate().unwrap();
assert!(id == id_unmarshal);
assert!(id_unmarshal.secret.is_some());
let idb2 = id_unmarshal.to_bytes();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap().validate().unwrap();
assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id);
assert!(id_unmarshal2.secret.is_none());
let ids = id.to_string();
assert!(Identity::from_str(ids.as_str()).unwrap() == id);
assert!(Identity::from_str(ids.as_str()).unwrap() == *id);
assert!(id.upgrade().is_ok());
assert!(id.validate_identity());
assert!(id.p384.is_some());
assert!(id.secret.as_ref().unwrap().p384.is_some());
let ids = id.to_string();
assert!(Identity::from_str(ids.as_str()).unwrap() == id);
assert!(Identity::from_str(ids.as_str()).unwrap() == *id);
}
for id_str in GOOD_V1_IDENTITIES {
let id = Identity::from_str(id_str).unwrap();
let id = Identity::from_str(id_str).unwrap().validate().unwrap();
assert_eq!(id.to_secret_string().as_str(), id_str);
assert!(id.validate_identity());
assert!(id.p384.is_some());
assert!(id.secret.as_ref().unwrap().p384.is_some());
let idb = id.to_secret_bytes().unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap();
let id_unmarshal = Identity::from_bytes(idb.as_bytes()).unwrap().validate().unwrap();
assert!(id == id_unmarshal);
let idb2 = id_unmarshal.to_bytes();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap();
let id_unmarshal2 = Identity::from_bytes(&idb2).unwrap().validate().unwrap();
assert!(id_unmarshal2 == id_unmarshal);
assert!(id_unmarshal2 == id);
let ids = id.to_string();
assert!(Identity::from_str(ids.as_str()).unwrap() == id);
assert!(Identity::from_str(ids.as_str()).unwrap() == *id);
}
}
}

View file

@ -222,7 +222,7 @@ impl TryInto<SocketAddrV6> for InetAddress {
impl TryInto<SocketAddrV6> for &InetAddress {
type Error = InvalidParameterError;
#[inline(always)]
#[inline]
fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
unsafe {
match self.sa.sa_family {
@ -239,7 +239,7 @@ impl TryInto<SocketAddrV6> for &InetAddress {
}
impl From<&IpAddr> for InetAddress {
#[inline(always)]
#[inline]
fn from(ip: &IpAddr) -> Self {
match ip {
IpAddr::V4(ip4) => Self::from(ip4),
@ -284,7 +284,7 @@ impl From<Ipv6Addr> for InetAddress {
}
impl From<&SocketAddr> for InetAddress {
#[inline(always)]
#[inline]
fn from(sa: &SocketAddr) -> Self {
match sa {
SocketAddr::V4(sa4) => Self::from(sa4),
@ -353,6 +353,7 @@ impl std::fmt::Debug for InetAddress {
const TEMP_SERIALIZE_BUFFER_SIZE: usize = 24;
impl Serialize for InetAddress {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
@ -372,10 +373,12 @@ struct InetAddressVisitor;
impl<'de> serde::de::Visitor<'de> for InetAddressVisitor {
type Value = InetAddress;
#[inline]
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an InetAddress")
}
#[inline]
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
@ -390,6 +393,7 @@ impl<'de> serde::de::Visitor<'de> for InetAddressVisitor {
}
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
@ -399,6 +403,7 @@ impl<'de> serde::de::Visitor<'de> for InetAddressVisitor {
}
impl<'de> Deserialize<'de> for InetAddress {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<InetAddress, D::Error>
where
D: Deserializer<'de>,
@ -420,7 +425,7 @@ impl InetAddress {
/// Construct from IP and port.
/// If the IP is not either 4 or 16 bytes in length, a nil/0 InetAddress is returned.
#[inline(always)]
#[inline]
pub fn from_ip_port(ip: &[u8], port: u16) -> InetAddress {
unsafe {
let mut c = MaybeUninit::<InetAddress>::uninit().assume_init(); // gets zeroed in set()
@ -570,6 +575,7 @@ impl InetAddress {
}
/// Get the IP port for this InetAddress.
#[inline]
pub fn port(&self) -> u16 {
unsafe {
u16::from_be(match self.sa.sa_family as AddressFamilyType {

View file

@ -17,12 +17,6 @@ use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
#[repr(transparent)]
pub struct MAC(NonZeroU64);
impl Debug for MAC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_string())
}
}
impl MAC {
#[inline(always)]
pub fn from_u64(i: u64) -> Option<MAC> {
@ -122,6 +116,12 @@ impl Hash for MAC {
}
}
impl Debug for MAC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_string())
}
}
impl Serialize for MAC {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -24,6 +24,8 @@ pub use path::Path;
pub use peer::Peer;
pub use rootset::{Root, RootSet};
pub use zerotier_crypto::verified::Verified;
#[cfg(feature = "debug_events")]
#[allow(unused_macros)]
#[macro_export]

View file

@ -17,9 +17,9 @@ use crate::vl1::identity::Identity;
use crate::vl1::path::{Path, PathServiceResult};
use crate::vl1::peer::Peer;
use crate::vl1::rootset::RootSet;
use crate::vl1::Verified;
use zerotier_crypto::random;
use zerotier_crypto::verified::Verified;
use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::gate::IntervalGate;
use zerotier_utils::hex;
@ -36,14 +36,14 @@ pub trait VL1AuthProvider: Sync + Send {
///
/// If this returns false, the node simply drops messages on the floor and refuses
/// to init V2 sessions.
fn should_respond_to(&self, id: &Identity) -> bool;
fn should_respond_to(&self, id: &Verified<Identity>) -> bool;
/// Check if this node has any trust relationship with the provided identity.
///
/// This should return true if there is any special trust relationship such as mutual
/// membership in a network or for controllers the peer's membership in any network
/// they control.
fn has_trust_relationship(&self, id: &Identity) -> bool;
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool;
}
/// Trait to be implemented by outside code to provide object storage to VL1
@ -52,10 +52,10 @@ pub trait VL1AuthProvider: Sync + Send {
/// implementers of HostSystem to break this out and allow a user to specify it.
pub trait NodeStorage: Sync + Send {
/// Load this node's identity from the data store.
fn load_node_identity(&self) -> Option<Identity>;
fn load_node_identity(&self) -> Option<Verified<Identity>>;
/// Save this node's identity to the data store.
fn save_node_identity(&self, id: &Identity);
fn save_node_identity(&self, id: &Verified<Identity>);
}
/// Trait implemented by external code to handle events and provide an interface to the system or application.
@ -112,7 +112,7 @@ pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
#[allow(unused_variables)]
fn should_use_physical_path<HostSystemImpl: HostSystem + ?Sized>(
&self,
id: &Identity,
id: &Verified<Identity>,
endpoint: &Endpoint,
local_socket: Option<&HostSystemImpl::LocalSocket>,
local_interface: Option<&HostSystemImpl::LocalInterface>,
@ -126,7 +126,7 @@ pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
#[allow(unused_variables)]
fn get_path_hints<HostSystemImpl: HostSystem + ?Sized>(
&self,
id: &Identity,
id: &Verified<Identity>,
) -> Option<
Vec<(
Endpoint,
@ -272,7 +272,7 @@ pub struct Node {
pub instance_id: [u8; 16],
/// This node's identity and permanent keys.
pub identity: Identity,
pub identity: Verified<Identity>,
/// Interval latches for periodic background tasks.
intervals: Mutex<BackgroundTaskIntervals>,
@ -306,7 +306,7 @@ impl Node {
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
} else {
let id = Identity::generate();
host_system.event(Event::IdentityAutoGenerated(id.clone()));
host_system.event(Event::IdentityAutoGenerated(id.as_ref().clone()));
host_system.storage().save_node_identity(&id);
id
}
@ -319,7 +319,7 @@ impl Node {
let old = id.clone();
if id.upgrade()? {
host_system.storage().save_node_identity(&id);
host_system.event(Event::IdentityAutoUpgraded(old, id.clone()));
host_system.event(Event::IdentityAutoUpgraded(old.unwrap(), id.as_ref().clone()));
}
}
@ -477,7 +477,7 @@ impl Node {
let (mut old_root_identities, address_collisions, new_roots, bad_identities, my_root_sets) = {
let roots = self.roots.read().unwrap();
let old_root_identities: Vec<Identity> = roots.roots.iter().map(|(p, _)| p.identity.clone()).collect();
let old_root_identities: Vec<Identity> = roots.roots.iter().map(|(p, _)| p.identity.as_ref().clone()).collect();
let mut new_roots = HashMap::new();
let mut bad_identities = Vec::new();
let mut my_root_sets: Option<Vec<u8>> = None;
@ -497,7 +497,7 @@ impl Node {
.read()
.unwrap()
.get(&m.identity.address)
.map_or(false, |p| !p.identity.eq(&m.identity))
.map_or(false, |p| !p.identity.as_ref().eq(&m.identity))
|| address_collision_check
.insert(m.identity.address, &m.identity)
.map_or(false, |old_id| !old_id.eq(&m.identity))
@ -522,7 +522,8 @@ impl Node {
if let Some(peer) = peers.get(&m.identity.address) {
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
} else {
if let Some(peer) = Peer::new(&self.identity, m.identity.clone(), time_ticks) {
if let Some(peer) = Peer::new(&self.identity, Verified::assume_verified(m.identity.clone()), time_ticks)
{
drop(peers);
new_roots.insert(
self.peers
@ -557,7 +558,7 @@ impl Node {
)));
}
let mut new_root_identities: Vec<Identity> = new_roots.iter().map(|(p, _)| p.identity.clone()).collect();
let mut new_root_identities: Vec<Identity> = new_roots.iter().map(|(p, _)| p.identity.as_ref().clone()).collect();
old_root_identities.sort_unstable();
new_root_identities.sort_unstable();
@ -991,7 +992,7 @@ impl Node {
authoritative: bool,
) {
if authoritative {
if received_identity.validate_identity() {
if let Some(received_identity) = received_identity.validate() {
let mut whois_queue = self.whois_queue.lock().unwrap();
if let Some(qi) = whois_queue.get_mut(&received_identity.address) {
let address = received_identity.address;
@ -1124,12 +1125,12 @@ impl InnerProtocol for DummyInnerProtocol {}
impl VL1AuthProvider for DummyInnerProtocol {
#[inline(always)]
fn should_respond_to(&self, id: &Identity) -> bool {
fn should_respond_to(&self, _: &Verified<Identity>) -> bool {
true
}
#[inline(always)]
fn has_trust_relationship(&self, id: &Identity) -> bool {
fn has_trust_relationship(&self, _: &Verified<Identity>) -> bool {
true
}
}

View file

@ -18,13 +18,14 @@ use crate::protocol::*;
use crate::vl1::address::Address;
use crate::vl1::debug_event;
use crate::vl1::node::*;
use crate::vl1::Verified;
use crate::vl1::{Endpoint, Identity, Path};
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
pub(crate) const SERVICE_INTERVAL_MS: i64 = 10000;
pub struct Peer {
pub identity: Identity,
pub identity: Verified<Identity>,
v1_proto_static_secret: v1::SymmetricSecret,
paths: Mutex<Vec<PeerPath>>,
@ -61,7 +62,7 @@ impl Peer {
///
/// This only returns None if this_node_identity does not have its secrets or if some
/// fatal error occurs performing key agreement between the two identities.
pub(crate) fn new(this_node_identity: &Identity, id: Identity, time_ticks: i64) -> Option<Self> {
pub(crate) fn new(this_node_identity: &Verified<Identity>, id: Verified<Identity>, time_ticks: i64) -> Option<Self> {
this_node_identity.agree(&id).map(|static_secret| -> Self {
Self {
identity: id,

View file

@ -119,7 +119,7 @@ impl RootSet {
}
}
return Some(Verified(self));
return Some(Verified::assume_verified(self));
}
/// Add a member to this definition, replacing any current entry with this address.

View file

@ -21,7 +21,6 @@ pub struct MulticastAuthority {
}
impl MulticastAuthority {
#[inline]
pub fn new() -> Self {
Self { subscriptions: RwLock::new(HashMap::new()) }
}

View file

@ -1,5 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
@ -34,6 +35,20 @@ impl Hash for MulticastGroup {
state.write_u32(self.adi);
}
}
impl ToString for MulticastGroup {
fn to_string(&self) -> String {
format!("{}/{}", self.mac.to_string(), self.adi.to_string())
}
}
impl Debug for MulticastGroup {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl Serialize for MulticastGroup {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -17,7 +17,7 @@ use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::marshalable::Marshalable;
/// Network configuration object sent to nodes by network controllers.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct NetworkConfig {
/// Network ID
pub network_id: NetworkId,
@ -420,7 +420,7 @@ mod proto_v1_field_name {
}
/// SSO authentication configuration object.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SSOAuthConfiguration {
pub version: u32,
pub authentication_url: String,
@ -435,7 +435,7 @@ pub struct SSOAuthConfiguration {
///
/// These are also handed out to V2 nodes to use when communicating with V1 nodes on
/// networks that support older protocol versions.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct V1Credentials {
pub revision: u64,
pub max_delta: u64,
@ -447,7 +447,7 @@ pub struct V1Credentials {
}
/// Statically pushed L3 IP routes included with a network configuration.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct IpRoute {
pub target: InetAddress,
#[serde(skip_serializing_if = "Option::is_none")]

View file

@ -1,5 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::num::NonZeroU64;
use std::str::FromStr;
@ -97,6 +98,13 @@ impl ToString for NetworkId {
}
}
impl Debug for NetworkId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl FromStr for NetworkId {
type Err = InvalidFormatError;

View file

@ -1,4 +1,6 @@
use std::fmt::Debug;
use std::mem::{size_of, zeroed};
use std::net::Ipv6Addr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -159,44 +161,44 @@ union RuleValue {
/// Trait to implement in order to evaluate rules.
pub trait RuleVisitor {
fn action_drop(self) -> bool;
fn action_accept(self) -> bool;
fn action_tee(self, address: Address, flags: u32, length: u16) -> bool;
fn action_watch(self, address: Address, flags: u32, length: u16) -> bool;
fn action_redirect(self, address: Address, flags: u32, length: u16) -> bool;
fn action_break(self) -> bool;
fn action_priority(self, qos_bucket: u8) -> bool;
fn action_drop(&mut self) -> bool;
fn action_accept(&mut self) -> bool;
fn action_tee(&mut self, address: Address, flags: u32, length: u16) -> bool;
fn action_watch(&mut self, address: Address, flags: u32, length: u16) -> bool;
fn action_redirect(&mut self, address: Address, flags: u32, length: u16) -> bool;
fn action_break(&mut self) -> bool;
fn action_priority(&mut self, qos_bucket: u8) -> bool;
fn invalid_rule(self) -> bool;
fn invalid_rule(&mut self) -> bool;
fn match_source_zerotier_address(self, not: bool, or: bool, address: Address);
fn match_dest_zerotier_address(self, not: bool, or: bool, address: Address);
fn match_vlan_id(self, not: bool, or: bool, id: u16);
fn match_vlan_pcp(self, not: bool, or: bool, pcp: u8);
fn match_vlan_dei(self, not: bool, or: bool, dei: u8);
fn match_mac_source(self, not: bool, or: bool, mac: MAC);
fn match_mac_dest(self, not: bool, or: bool, mac: MAC);
fn match_ipv4_source(self, not: bool, or: bool, ip: &[u8; 4], mask: u8);
fn match_ipv4_dest(self, not: bool, or: bool, ip: &[u8; 4], mask: u8);
fn match_ipv6_source(self, not: bool, or: bool, ip: &[u8; 16], mask: u8);
fn match_ipv6_dest(self, not: bool, or: bool, ip: &[u8; 16], mask: u8);
fn match_ip_tos(self, not: bool, or: bool, mask: u8, start: u8, end: u8);
fn match_ip_protocol(self, not: bool, or: bool, protocol: u8);
fn match_ethertype(self, not: bool, or: bool, ethertype: u16);
fn match_icmp(self, not: bool, or: bool, _type: u8, code: u8, flags: u8);
fn match_ip_source_port_range(self, not: bool, or: bool, start: u16, end: u16);
fn match_ip_dest_port_range(self, not: bool, or: bool, start: u16, end: u16);
fn match_characteristics(self, not: bool, or: bool, characteristics: u64);
fn match_frame_size_range(self, not: bool, or: bool, start: u16, end: u16);
fn match_random(self, not: bool, or: bool, probability: u32);
fn match_tags_difference(self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_and(self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_or(self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_xor(self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_equal(self, not: bool, or: bool, id: u32, value: u32);
fn match_tag_sender(self, not: bool, or: bool, id: u32, value: u32);
fn match_tag_receiver(self, not: bool, or: bool, id: u32, value: u32);
fn match_integer_range(self, not: bool, or: bool, start: u64, end: u64, idx: u16, format: u8);
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: Address);
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: Address);
fn match_vlan_id(&mut self, not: bool, or: bool, id: u16);
fn match_vlan_pcp(&mut self, not: bool, or: bool, pcp: u8);
fn match_vlan_dei(&mut self, not: bool, or: bool, dei: u8);
fn match_mac_source(&mut self, not: bool, or: bool, mac: MAC);
fn match_mac_dest(&mut self, not: bool, or: bool, mac: MAC);
fn match_ipv4_source(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8);
fn match_ipv4_dest(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8);
fn match_ipv6_source(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8);
fn match_ipv6_dest(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8);
fn match_ip_tos(&mut self, not: bool, or: bool, mask: u8, start: u8, end: u8);
fn match_ip_protocol(&mut self, not: bool, or: bool, protocol: u8);
fn match_ethertype(&mut self, not: bool, or: bool, ethertype: u16);
fn match_icmp(&mut self, not: bool, or: bool, _type: u8, code: u8, flags: u8);
fn match_ip_source_port_range(&mut self, not: bool, or: bool, start: u16, end: u16);
fn match_ip_dest_port_range(&mut self, not: bool, or: bool, start: u16, end: u16);
fn match_characteristics(&mut self, not: bool, or: bool, characteristics: u64);
fn match_frame_size_range(&mut self, not: bool, or: bool, start: u16, end: u16);
fn match_random(&mut self, not: bool, or: bool, probability: u32);
fn match_tags_difference(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_and(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_or(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_bitwise_xor(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tags_equal(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tag_sender(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_tag_receiver(&mut self, not: bool, or: bool, id: u32, value: u32);
fn match_integer_range(&mut self, not: bool, or: bool, start: u64, end: u64, idx: u16, format: u8);
}
#[repr(C, packed)]
@ -220,8 +222,8 @@ impl Rule {
}
/// Execute the visitor, returning the result of action methods and true for condition methods.
#[inline(always)]
pub fn visit<V: RuleVisitor>(&self, v: V) -> bool {
#[inline]
pub fn visit<V: RuleVisitor>(&self, v: &mut V) -> bool {
unsafe {
let t = self.t;
let not = (t & 0x80) != 0;
@ -576,6 +578,7 @@ impl Marshalable for Rule {
}
impl PartialEq for Rule {
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe { (*(self as *const Self).cast::<[u8; size_of::<Self>()]>()).eq(&*(other as *const Self).cast::<[u8; size_of::<Self>()]>()) }
}
@ -583,6 +586,21 @@ impl PartialEq for Rule {
impl Eq for Rule {}
impl ToString for Rule {
fn to_string(&self) -> String {
let mut s = RuleStringer(String::with_capacity(32));
self.visit(&mut s);
s.0
}
}
impl Debug for Rule {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl Serialize for Rule {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -675,7 +693,7 @@ static HR_NAME_TO_RULE_TYPE: phf::Map<&'static str, u8> = phf_map! {
"MATCH_TAGS_EQUAL" => match_cond::TAGS_EQUAL,
"MATCH_TAG_SENDER" => match_cond::TAG_SENDER,
"MATCH_TAG_RECEIVER" => match_cond::TAG_RECEIVER,
"INTEGER_RANGE" => match_cond::INTEGER_RANGE,
"MATCH_INTEGER_RANGE" => match_cond::INTEGER_RANGE,
};
/// A "bag of fields" used to serialize/deserialize rules in human readable form e.g. JSON.
@ -860,21 +878,22 @@ impl<'a> HumanReadableRule<'a> {
fn from_rule(r: &Rule) -> Self {
let mut hr = HumanReadableRule::default();
r.visit(MakeHumanReadable(&mut hr));
r.visit(&mut MakeHumanReadable(&mut hr));
hr
}
}
#[repr(transparent)]
struct MakeHumanReadable<'a>(&'a mut HumanReadableRule<'static>);
impl<'a> MakeHumanReadable<'a> {
fn do_tag(self, t: &'static str, not: bool, or: bool, id: u32, value: u32) {
fn do_tag(&mut self, t: &'static str, not: bool, or: bool, id: u32, value: u32) {
let _ = self.0.id.insert(id);
let _ = self.0.value.insert(value);
self.do_cond(t, not, or);
}
fn do_cond(self, t: &'static str, not: bool, or: bool) {
fn do_cond(&mut self, t: &'static str, not: bool, or: bool) {
self.0._type = t;
if not {
let _ = self.0.not.insert(not);
@ -887,19 +906,19 @@ impl<'a> MakeHumanReadable<'a> {
impl<'a> RuleVisitor for MakeHumanReadable<'a> {
#[inline(always)]
fn action_drop(self) -> bool {
fn action_drop(&mut self) -> bool {
self.0._type = "ACTION_DROP";
true
}
#[inline(always)]
fn action_accept(self) -> bool {
fn action_accept(&mut self) -> bool {
self.0._type = "ACTION_ACCEPT";
true
}
#[inline(always)]
fn action_tee(self, address: Address, flags: u32, length: u16) -> bool {
fn action_tee(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_TEE";
let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags);
@ -908,7 +927,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
}
#[inline(always)]
fn action_watch(self, address: Address, flags: u32, length: u16) -> bool {
fn action_watch(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_WATCH";
let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags);
@ -917,7 +936,7 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
}
#[inline(always)]
fn action_redirect(self, address: Address, flags: u32, length: u16) -> bool {
fn action_redirect(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0._type = "ACTION_REDIRECT";
let _ = self.0.address.insert(address);
let _ = self.0.flags.insert(flags);
@ -926,91 +945,91 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
}
#[inline(always)]
fn action_break(self) -> bool {
fn action_break(&mut self) -> bool {
self.0._type = "ACTION_BREAK";
true
}
#[inline(always)]
fn action_priority(self, qos_bucket: u8) -> bool {
fn action_priority(&mut self, qos_bucket: u8) -> bool {
self.0._type = "ACTION_PRIORITY";
let _ = self.0.qosBucket.insert(qos_bucket);
true
}
#[inline(always)]
fn invalid_rule(self) -> bool {
fn invalid_rule(&mut self) -> bool {
false
}
#[inline(always)]
fn match_source_zerotier_address(self, not: bool, or: bool, address: Address) {
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: Address) {
let _ = self.0.zt.insert(address);
self.do_cond("MATCH_SOURCE_ZEROTIER_ADDRESS", not, or);
}
#[inline(always)]
fn match_dest_zerotier_address(self, not: bool, or: bool, address: Address) {
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: Address) {
let _ = self.0.zt.insert(address);
self.do_cond("MATCH_DEST_ZEROTIER_ADDRESS", not, or);
}
#[inline(always)]
fn match_vlan_id(self, not: bool, or: bool, id: u16) {
fn match_vlan_id(&mut self, not: bool, or: bool, id: u16) {
let _ = self.0.vlanId.insert(id);
self.do_cond("MATCH_VLAN_ID", not, or);
}
#[inline(always)]
fn match_vlan_pcp(self, not: bool, or: bool, pcp: u8) {
fn match_vlan_pcp(&mut self, not: bool, or: bool, pcp: u8) {
let _ = self.0.vlanPcp.insert(pcp);
self.do_cond("MATCH_VLAN_PCP", not, or);
}
#[inline(always)]
fn match_vlan_dei(self, not: bool, or: bool, dei: u8) {
fn match_vlan_dei(&mut self, not: bool, or: bool, dei: u8) {
let _ = self.0.vlanDei.insert(dei);
self.do_cond("MATCH_VLAN_DEI", not, or);
}
#[inline(always)]
fn match_mac_source(self, not: bool, or: bool, mac: MAC) {
fn match_mac_source(&mut self, not: bool, or: bool, mac: MAC) {
let _ = self.0.mac.insert(mac);
self.do_cond("MATCH_MAC_SOURCE", not, or);
}
#[inline(always)]
fn match_mac_dest(self, not: bool, or: bool, mac: MAC) {
fn match_mac_dest(&mut self, not: bool, or: bool, mac: MAC) {
let _ = self.0.mac.insert(mac);
self.do_cond("MATCH_MAC_DEST", not, or);
}
#[inline(always)]
fn match_ipv4_source(self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
fn match_ipv4_source(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
let _ = self.0.ip.insert(InetAddress::from_ip_port(ip, mask as u16));
self.do_cond("MATCH_IPV4_SOURCE", not, or);
}
#[inline(always)]
fn match_ipv4_dest(self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
fn match_ipv4_dest(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
let _ = self.0.ip.insert(InetAddress::from_ip_port(ip, mask as u16));
self.do_cond("MATCH_IPV4_DEST", not, or);
}
#[inline(always)]
fn match_ipv6_source(self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
fn match_ipv6_source(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
let _ = self.0.ip.insert(InetAddress::from_ip_port(ip, mask as u16));
self.do_cond("MATCH_IPV6_SOURCE", not, or);
}
#[inline(always)]
fn match_ipv6_dest(self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
fn match_ipv6_dest(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
let _ = self.0.ip.insert(InetAddress::from_ip_port(ip, mask as u16));
self.do_cond("MATCH_IPV6_DEST", not, or);
}
#[inline(always)]
fn match_ip_tos(self, not: bool, or: bool, mask: u8, start: u8, end: u8) {
fn match_ip_tos(&mut self, not: bool, or: bool, mask: u8, start: u8, end: u8) {
let _ = self.0.mask.insert(mask as u64);
let _ = self.0.start.insert(start as u64);
let _ = self.0.end.insert(end as u64);
@ -1018,19 +1037,19 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
}
#[inline(always)]
fn match_ip_protocol(self, not: bool, or: bool, protocol: u8) {
fn match_ip_protocol(&mut self, not: bool, or: bool, protocol: u8) {
let _ = self.0.ipProtocol.insert(protocol);
self.do_cond("MATCH_IP_PROTOCOL", not, or);
}
#[inline(always)]
fn match_ethertype(self, not: bool, or: bool, ethertype: u16) {
fn match_ethertype(&mut self, not: bool, or: bool, ethertype: u16) {
let _ = self.0.etherType.insert(ethertype);
self.do_cond("MATCH_ETHERTYPE", not, or);
}
#[inline(always)]
fn match_icmp(self, not: bool, or: bool, _type: u8, code: u8, flags: u8) {
fn match_icmp(&mut self, not: bool, or: bool, _type: u8, code: u8, flags: u8) {
let _ = self.0.icmpType.insert(_type);
if (flags & 0x01) != 0 {
let _ = self.0.icmpCode.insert(code);
@ -1039,80 +1058,662 @@ impl<'a> RuleVisitor for MakeHumanReadable<'a> {
}
#[inline(always)]
fn match_ip_source_port_range(self, not: bool, or: bool, start: u16, end: u16) {
fn match_ip_source_port_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
let _ = self.0.start.insert(start as u64);
let _ = self.0.end.insert(end as u64);
self.do_cond("MATCH_IP_SOURCE_PORT_RANGE", not, or);
}
#[inline(always)]
fn match_ip_dest_port_range(self, not: bool, or: bool, start: u16, end: u16) {
fn match_ip_dest_port_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
let _ = self.0.start.insert(start as u64);
let _ = self.0.end.insert(end as u64);
self.do_cond("MATCH_IP_DEST_PORT_RANGE", not, or);
}
#[inline(always)]
fn match_characteristics(self, not: bool, or: bool, characteristics: u64) {
fn match_characteristics(&mut self, not: bool, or: bool, characteristics: u64) {
let _ = self.0.mask.insert(characteristics);
self.do_cond("MATCH_CHARACTERISTICS", not, or);
}
#[inline(always)]
fn match_frame_size_range(self, not: bool, or: bool, start: u16, end: u16) {
fn match_frame_size_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
let _ = self.0.start.insert(start as u64);
let _ = self.0.end.insert(end as u64);
self.do_cond("MATCH_FRAME_SIZE_RANGE", not, or);
}
#[inline(always)]
fn match_random(self, not: bool, or: bool, probability: u32) {
fn match_random(&mut self, not: bool, or: bool, probability: u32) {
let _ = self.0.probability.insert(probability);
self.do_cond("MATCH_RANDOM", not, or);
}
#[inline(always)]
fn match_tags_difference(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tags_difference(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAGS_DIFFERENCE", not, or, id, value);
}
#[inline(always)]
fn match_tags_bitwise_and(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tags_bitwise_and(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAGS_BITWISE_AND", not, or, id, value);
}
#[inline(always)]
fn match_tags_bitwise_or(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tags_bitwise_or(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAGS_BITWISE_OR", not, or, id, value);
}
#[inline(always)]
fn match_tags_bitwise_xor(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tags_bitwise_xor(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAGS_BITWISE_XOR", not, or, id, value);
}
#[inline(always)]
fn match_tags_equal(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tags_equal(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAGS_EQUAL", not, or, id, value);
}
#[inline(always)]
fn match_tag_sender(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tag_sender(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAG_SENDER", not, or, id, value);
}
#[inline(always)]
fn match_tag_receiver(self, not: bool, or: bool, id: u32, value: u32) {
fn match_tag_receiver(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.do_tag("MATCH_TAG_RECEIVER", not, or, id, value);
}
#[inline(always)]
fn match_integer_range(self, not: bool, or: bool, start: u64, end: u64, idx: u16, format: u8) {
fn match_integer_range(&mut self, not: bool, or: bool, start: u64, end: u64, idx: u16, format: u8) {
let _ = self.0.start.insert(start);
let _ = self.0.end.insert(end);
let _ = self.0.idx.insert(idx);
let _ = self.0.little.insert((format & 0x80) != 0);
let _ = self.0.bits.insert((format & 63) + 1);
self.do_cond("INTEGER_RANGE", not, or);
self.do_cond("MATCH_INTEGER_RANGE", not, or);
}
}
#[repr(transparent)]
struct RuleStringer(String);
impl RuleVisitor for RuleStringer {
#[inline(always)]
fn action_drop(&mut self) -> bool {
self.0.push_str("ACTION_DROP");
true
}
#[inline(always)]
fn action_accept(&mut self) -> bool {
self.0.push_str("ACTION_ACCEPT");
true
}
#[inline(always)]
fn action_tee(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_TEE({}, {}, {})", address.to_string(), flags, length);
true
}
#[inline(always)]
fn action_watch(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_WATCH({}, {}, {})", address.to_string(), flags, length);
true
}
#[inline(always)]
fn action_redirect(&mut self, address: Address, flags: u32, length: u16) -> bool {
self.0 = format!("ACTION_REDIRECT({}, {}, {})", address.to_string(), flags, length);
true
}
#[inline(always)]
fn action_break(&mut self) -> bool {
self.0.push_str("ACTION_BREAK");
true
}
#[inline(always)]
fn action_priority(&mut self, qos_bucket: u8) -> bool {
self.0 = format!("ACTION_PRIORITY({})", qos_bucket);
true
}
#[inline(always)]
fn invalid_rule(&mut self) -> bool {
self.0.push_str("(INVALID RULE)");
false
}
#[inline(always)]
fn match_source_zerotier_address(&mut self, not: bool, or: bool, address: Address) {
self.0 = format!(
"MATCH_SOURCE_ZEROTIER_ADDRESS({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
address.to_string()
);
}
#[inline(always)]
fn match_dest_zerotier_address(&mut self, not: bool, or: bool, address: Address) {
self.0 = format!(
"MATCH_DEST_ZEROTIER_ADDRESS({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
address.to_string()
);
}
#[inline(always)]
fn match_vlan_id(&mut self, not: bool, or: bool, id: u16) {
self.0 = format!(
"MATCH_VLAN_ID({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id
);
}
#[inline(always)]
fn match_vlan_pcp(&mut self, not: bool, or: bool, pcp: u8) {
self.0 = format!(
"MATCH_VLAN_PCP({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
pcp
);
}
#[inline(always)]
fn match_vlan_dei(&mut self, not: bool, or: bool, dei: u8) {
self.0 = format!(
"MATCH_VLAN_DEI({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
dei
);
}
#[inline(always)]
fn match_mac_source(&mut self, not: bool, or: bool, mac: MAC) {
self.0 = format!(
"MATCH_MAC_SOURCE({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
mac.to_string()
);
}
#[inline(always)]
fn match_mac_dest(&mut self, not: bool, or: bool, mac: MAC) {
self.0 = format!(
"MATCH_MAC_DEST({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
mac.to_string()
);
}
#[inline(always)]
fn match_ipv4_source(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
self.0 = format!(
"MATCH_IPV4_SOURCE({}{}{}.{}.{}.{}/{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
ip[0],
ip[1],
ip[2],
ip[3],
mask
);
}
#[inline(always)]
fn match_ipv4_dest(&mut self, not: bool, or: bool, ip: &[u8; 4], mask: u8) {
self.0 = format!(
"MATCH_IPV4_DEST({}{}{}.{}.{}.{}/{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
ip[0],
ip[1],
ip[2],
ip[3],
mask
);
}
fn match_ipv6_source(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
self.0 = format!(
"MATCH_IPV6_SOURCE({}{}{}/{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
Ipv6Addr::from(*ip).to_string(),
mask
);
}
fn match_ipv6_dest(&mut self, not: bool, or: bool, ip: &[u8; 16], mask: u8) {
self.0 = format!(
"MATCH_IPV6_DEST({}{}{}/{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
Ipv6Addr::from(*ip).to_string(),
mask
);
}
#[inline(always)]
fn match_ip_tos(&mut self, not: bool, or: bool, mask: u8, start: u8, end: u8) {
self.0 = format!(
"MATCH_IP_TOS({}{}{}&{}-{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
mask,
start,
end
);
}
#[inline(always)]
fn match_ip_protocol(&mut self, not: bool, or: bool, protocol: u8) {
self.0 = format!(
"MATCH_IP_PROTOCOL({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
protocol
);
}
#[inline(always)]
fn match_ethertype(&mut self, not: bool, or: bool, ethertype: u16) {
self.0 = format!(
"MATCH_ETHERTYPE({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
ethertype
);
}
#[inline(always)]
fn match_icmp(&mut self, not: bool, or: bool, _type: u8, code: u8, flags: u8) {
self.0 = format!(
"MATCH_ICMP({}{} {}, {}, {})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
_type,
code,
flags
);
}
#[inline(always)]
fn match_ip_source_port_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
self.0 = format!(
"MATCH_IP_SOURCE_PORT_RANGE({}{}{}-{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
start,
end
);
}
#[inline(always)]
fn match_ip_dest_port_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
self.0 = format!(
"MATCH_IP_DEST_PORT_RANGE({}{}{}-{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
start,
end
);
}
#[inline(always)]
fn match_characteristics(&mut self, not: bool, or: bool, characteristics: u64) {
self.0 = format!(
"MATCH_IP_CHARACTERISTICS({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
zerotier_utils::hex::to_string_u64(characteristics, false)
);
}
#[inline(always)]
fn match_frame_size_range(&mut self, not: bool, or: bool, start: u16, end: u16) {
self.0 = format!(
"MATCH_FRAME_SIZE_RANGE({}{}{}-{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
start,
end
);
}
#[inline(always)]
fn match_random(&mut self, not: bool, or: bool, probability: u32) {
self.0 = format!(
"MATCH_RANDOM({}{}{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
probability
);
}
#[inline(always)]
fn match_tags_difference(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAGS_DIFFERENCE({}{}{}<>{})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tags_bitwise_and(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAGS_BITWISE_AND({}{}{}&{}!=0)",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tags_bitwise_or(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAGS_BITWISE_OR({}{}{}|{}!=0)",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tags_bitwise_xor(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAGS_BITWISE_XOR({}{}{}^{}!=0)",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tags_equal(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAGS_EQUAL({}{}{}=={})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tag_sender(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAG_SENDER({}{}{}=={})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_tag_receiver(&mut self, not: bool, or: bool, id: u32, value: u32) {
self.0 = format!(
"MATCH_TAG_RECEIVER({}{}{}=={})",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
id,
value
);
}
#[inline(always)]
fn match_integer_range(&mut self, not: bool, or: bool, start: u64, end: u64, idx: u16, format: u8) {
self.0 = format!(
"MATCH_INTEGER_RANGE({}{}({}, {}, {}, {}))",
if or {
"|"
} else {
""
},
if not {
"!"
} else {
""
},
start,
end,
idx,
format
);
}
}

View file

@ -21,7 +21,7 @@ use zerotier_utils::memory;
/// This was done to permit some things such as geo-fencing that were never implemented, so it's
/// a bit of a case of YAGNI. In V2 this is deprecated in favor of a more standard sort of
/// certificate.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CertificateOfMembership {
pub network_id: NetworkId,
pub timestamp: i64,
@ -173,7 +173,7 @@ impl CertificateOfMembership {
pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option<Verified<Self>> {
if Self::v1_proto_issued_to_fingerprint(expect_issued_to).eq(&self.issued_to_fingerprint.as_bytes()[..32]) {
if issuer.verify(&self.v1_proto_get_qualifier_bytes(), self.signature.as_bytes()) {
return Some(Verified(self));
return Some(Verified::assume_verified(self));
}
}
return None;

View file

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::error::InvalidParameterError;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Thing {
Ipv4([u8; 4]),
Ipv6([u8; 16]),
@ -27,7 +27,7 @@ impl Thing {
}
}
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CertificateOfOwnership {
pub network_id: NetworkId,
pub timestamp: i64,

View file

@ -3,6 +3,7 @@ mod certificateofownership;
mod revocation;
mod tag;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum CredentialType {
Null = 0u8,

View file

@ -10,7 +10,7 @@ use crate::vl1::{Address, Identity};
use crate::vl2::v1::CredentialType;
use crate::vl2::NetworkId;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Revocation {
pub id: u32,
pub network_id: NetworkId,

View file

@ -10,7 +10,7 @@ use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::blob::Blob;
use zerotier_utils::error::InvalidParameterError;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Tag {
pub network_id: NetworkId,
pub timestamp: i64,

View file

@ -42,8 +42,10 @@ pub fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, Strin
Identity::from_str(s).map_or_else(
|e| Err(format!("invalid identity: {}", e.to_string())),
|id| {
if !validate || id.validate_identity() {
if !validate {
Ok(id)
} else if let Some(id) = id.validate() {
Ok(id.unwrap())
} else {
Err(String::from("invalid identity: local validation failed"))
}

View file

@ -1,5 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::io::Write;
use std::mem::{size_of, MaybeUninit};
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
@ -77,6 +78,20 @@ impl<T: Clone, const C: usize, const S: usize> From<[T; S]> for ArrayVec<T, C> {
}
}
impl<const C: usize> ToString for ArrayVec<u8, C> {
#[inline]
fn to_string(&self) -> String {
crate::hex::to_string(self.as_bytes())
}
}
impl<const C: usize> Debug for ArrayVec<u8, C> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl<const C: usize> Write for ArrayVec<u8, C> {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {

View file

@ -1,5 +1,7 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use serde::ser::SerializeTuple;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -64,6 +66,13 @@ impl<const L: usize> ToString for Blob<L> {
}
}
impl<const L: usize> Debug for Blob<L> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
}
impl<const L: usize> Serialize for Blob<L> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

View file

@ -8,7 +8,7 @@ use serde::de::DeserializeOwned;
use serde::Serialize;
use zerotier_crypto::random::next_u32_secure;
use zerotier_network_hypervisor::vl1::{Identity, NodeStorage};
use zerotier_network_hypervisor::vl1::{Identity, NodeStorage, Verified};
use zerotier_utils::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT};
use zerotier_utils::json::to_json_pretty;
@ -27,7 +27,7 @@ pub struct DataDir<Config: PartialEq + Eq + Clone + Send + Sync + Default + Seri
}
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> NodeStorage for DataDir<Config> {
fn load_node_identity(&self) -> Option<Identity> {
fn load_node_identity(&self) -> Option<Verified<Identity>> {
let id_data = read_limit(self.base_path.join(IDENTITY_SECRET_FILENAME), 4096);
if id_data.is_err() {
return None;
@ -36,10 +36,10 @@ impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + Deseri
if id_data.is_err() {
return None;
}
Some(id_data.unwrap())
Some(Verified::assume_verified(id_data.unwrap()))
}
fn save_node_identity(&self, id: &Identity) {
fn save_node_identity(&self, id: &Verified<Identity>) {
assert!(id.secret.is_some());
let id_secret_str = id.to_secret_string();
let id_public_str = id.to_string();

View file

@ -328,12 +328,12 @@ impl<
> NodeStorage for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
{
#[inline(always)]
fn load_node_identity(&self) -> Option<Identity> {
fn load_node_identity(&self) -> Option<Verified<Identity>> {
self.storage.load_node_identity()
}
#[inline(always)]
fn save_node_identity(&self, id: &Identity) {
fn save_node_identity(&self, id: &Verified<Identity>) {
self.storage.save_node_identity(id)
}
}
@ -345,12 +345,12 @@ impl<
> VL1AuthProvider for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
{
#[inline(always)]
fn should_respond_to(&self, id: &Identity) -> bool {
fn should_respond_to(&self, id: &Verified<Identity>) -> bool {
self.vl1_auth_provider.should_respond_to(id)
}
#[inline(always)]
fn has_trust_relationship(&self, id: &Identity) -> bool {
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool {
self.vl1_auth_provider.has_trust_relationship(id)
}
}