From ec600f994a4a750027dd05047009b3e1cfe158db Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 3 Jan 2023 17:58:28 -0500 Subject: [PATCH] Add both Valid and Verified typestates. --- crypto/src/typestate.rs | 116 +++++++++++++++++- network-hypervisor/src/vl1/identity.rs | 4 +- network-hypervisor/src/vl1/node.rs | 10 +- network-hypervisor/src/vl1/rootset.rs | 8 +- .../src/vl2/v1/certificateofmembership.rs | 6 +- vl1-service/src/datadir.rs | 2 +- 6 files changed, 127 insertions(+), 19 deletions(-) diff --git a/crypto/src/typestate.rs b/crypto/src/typestate.rs index f8aa47f4c..d38cb865b 100644 --- a/crypto/src/typestate.rs +++ b/crypto/src/typestate.rs @@ -96,20 +96,128 @@ where { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("Verified").field(&self.0).finish() + f.debug_tuple("Valid").field(&self.0).finish() } } impl Valid { - /// 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 { + pub fn wrap(o: T) -> Self { + Self(o) + } +} + +/// Typestate indicating that a credential or other object has been externally validated. +/// +/// This is more appropriate for certificates signed by an external authority. +#[repr(transparent)] +pub struct Verified(T); + +impl AsRef for Verified { + #[inline(always)] + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl AsMut for Verified { + #[inline(always)] + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl Deref for Verified { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Verified { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Clone for Verified +where + T: Clone, +{ + #[inline(always)] + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl PartialEq for Verified +where + T: PartialEq, +{ + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl Eq for Verified where T: Eq {} + +impl Ord for Verified +where + T: Ord, +{ + #[inline(always)] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for Verified +where + T: PartialOrd, +{ + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Hash for Verified +where + T: Hash, +{ + #[inline(always)] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl Debug for Verified +where + T: Debug, +{ + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Valid").field(&self.0).finish() + } +} + +impl Verified { + #[inline(always)] + pub fn unwrap(self) -> T { + self.0 + } + + #[inline(always)] + pub fn wrap(o: T) -> Self { Self(o) } } diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index fa70071e9..8046139bd 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -206,7 +206,7 @@ impl Identity { assert!(id.upgrade().is_ok()); assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some()); - Valid::assume_verified(id) + Valid::wrap(id) } /// Upgrade older x25519-only identities to hybrid identities with both x25519 and NIST P-384 curves. @@ -321,7 +321,7 @@ impl Identity { zt_address_derivation_work_function(&mut digest); return if digest[0] < IDENTITY_POW_THRESHOLD && Address::from_bytes(&digest[59..64]).map_or(false, |a| a == self.address) { - Some(Valid::assume_verified(self)) + Some(Valid::wrap(self)) } else { None }; diff --git a/network-hypervisor/src/vl1/node.rs b/network-hypervisor/src/vl1/node.rs index c2408f7fe..0efd77d97 100644 --- a/network-hypervisor/src/vl1/node.rs +++ b/network-hypervisor/src/vl1/node.rs @@ -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::Valid; use zerotier_crypto::random; +use zerotier_crypto::typestate::{Valid, Verified}; use zerotier_utils::error::InvalidParameterError; use zerotier_utils::gate::IntervalGate; use zerotier_utils::hex; @@ -207,7 +207,7 @@ pub trait InnerProtocolLayer: Sync + Send { struct RootInfo { /// Root sets to which we are a member. - sets: HashMap>, + sets: HashMap>, /// Root peers and their statically defined endpoints (from root sets). roots: HashMap, Vec>, @@ -352,7 +352,7 @@ impl Node { /// Add a new root set or update the existing root set if the new root set is newer and otherwise matches. #[inline] - pub fn add_update_root_set(&self, rs: Valid) -> bool { + pub fn add_update_root_set(&self, rs: Verified) -> bool { let mut roots = self.roots.write().unwrap(); if let Some(entry) = roots.sets.get_mut(&rs.name) { if rs.should_replace(entry) { @@ -468,7 +468,7 @@ 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, Valid::assume_verified(m.identity.clone()), time_ticks) { + if let Some(peer) = Peer::new(&self.identity, Valid::wrap(m.identity.clone()), time_ticks) { drop(peers); new_roots.insert( self.peers @@ -967,7 +967,7 @@ impl Node { /// This will only replace an existing root set with a newer one. It won't add a new root set, which must be /// done by an authorized user or administrator not just by a root. #[allow(unused)] - pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Valid) { + pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Verified) { let mut roots = self.roots.write().unwrap(); if let Some(entry) = roots.sets.get_mut(&rs.name) { if entry.members.iter().any(|m| m.identity.eq(received_from)) && rs.should_replace(entry) { diff --git a/network-hypervisor/src/vl1/rootset.rs b/network-hypervisor/src/vl1/rootset.rs index 56d60c0bb..6949cb1b5 100644 --- a/network-hypervisor/src/vl1/rootset.rs +++ b/network-hypervisor/src/vl1/rootset.rs @@ -6,7 +6,7 @@ use std::io::Write; use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE}; use crate::vl1::Endpoint; -use zerotier_crypto::typestate::Valid; +use zerotier_crypto::typestate::{Valid, Verified}; use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::buffer::Buffer; use zerotier_utils::marshalable::{Marshalable, UnmarshalError}; @@ -91,7 +91,7 @@ impl RootSet { } /// Get the ZeroTier default root set, which contains roots run by ZeroTier Inc. - pub fn zerotier_default() -> Valid { + pub fn zerotier_default() -> Verified { let mut cursor = 0; let rs = include_bytes!("../../default-rootset/root.zerotier.com.bin"); //let rs = include_bytes!("../../default-rootset/test-root.bin"); @@ -107,7 +107,7 @@ impl RootSet { } /// Verify signatures present in this root cluster definition. - pub fn verify(self) -> Option> { + pub fn verify(self) -> Option> { if self.members.is_empty() { return None; } @@ -119,7 +119,7 @@ impl RootSet { } } - return Some(Valid::assume_verified(self)); + return Some(Verified::wrap(self)); } /// Add a member to this definition, replacing any current entry with this address. diff --git a/network-hypervisor/src/vl2/v1/certificateofmembership.rs b/network-hypervisor/src/vl2/v1/certificateofmembership.rs index 36aa705cb..bd605ab1e 100644 --- a/network-hypervisor/src/vl2/v1/certificateofmembership.rs +++ b/network-hypervisor/src/vl2/v1/certificateofmembership.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use zerotier_crypto::hash::SHA384; use zerotier_crypto::secure_eq; -use zerotier_crypto::typestate::Valid; +use zerotier_crypto::typestate::Verified; use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::blob::Blob; use zerotier_utils::error::InvalidParameterError; @@ -171,13 +171,13 @@ impl CertificateOfMembership { } /// Verify this certificate of membership. - pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option> { + pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option> { if secure_eq( &Self::v1_proto_issued_to_fingerprint(expect_issued_to), &self.issued_to_fingerprint.as_bytes()[..32], ) { if issuer.verify(&self.v1_proto_get_qualifier_bytes(), self.signature.as_bytes()) { - return Some(Valid::assume_verified(self)); + return Some(Verified::wrap(self)); } } return None; diff --git a/vl1-service/src/datadir.rs b/vl1-service/src/datadir.rs index 6bce2a160..0a12e971f 100644 --- a/vl1-service/src/datadir.rs +++ b/vl1-service/src/datadir.rs @@ -31,7 +31,7 @@ pub fn load_node_identity(base_path: &Path) -> Option> { if id_data.is_err() { return None; } - Some(Valid::assume_verified(id_data.unwrap())) + Some(Valid::wrap(id_data.unwrap())) } pub fn save_node_identity(base_path: &Path, id: &Valid) -> bool {