Add both Valid and Verified typestates.

This commit is contained in:
Adam Ierymenko 2023-01-03 17:58:28 -05:00
parent 382688d251
commit ec600f994a
6 changed files with 127 additions and 19 deletions

View file

@ -96,20 +96,128 @@ where
{ {
#[inline] #[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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<T> Valid<T> { impl<T> Valid<T> {
/// Strip the Verified typestate off this object.
#[inline(always)] #[inline(always)]
pub fn unwrap(self) -> T { pub fn unwrap(self) -> T {
self.0 self.0
} }
/// Set Verified typestate on an object.
#[inline(always)] #[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>(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;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Verified<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
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("Valid").field(&self.0).finish()
}
}
impl<T> Verified<T> {
#[inline(always)]
pub fn unwrap(self) -> T {
self.0
}
#[inline(always)]
pub fn wrap(o: T) -> Self {
Self(o) Self(o)
} }
} }

View file

@ -206,7 +206,7 @@ impl Identity {
assert!(id.upgrade().is_ok()); assert!(id.upgrade().is_ok());
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some()); 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. /// 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); 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) { 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 { } else {
None None
}; };

View file

@ -17,9 +17,9 @@ use crate::vl1::identity::Identity;
use crate::vl1::path::{Path, PathServiceResult}; use crate::vl1::path::{Path, PathServiceResult};
use crate::vl1::peer::Peer; use crate::vl1::peer::Peer;
use crate::vl1::rootset::RootSet; use crate::vl1::rootset::RootSet;
use crate::vl1::Valid;
use zerotier_crypto::random; use zerotier_crypto::random;
use zerotier_crypto::typestate::{Valid, Verified};
use zerotier_utils::error::InvalidParameterError; use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::gate::IntervalGate; use zerotier_utils::gate::IntervalGate;
use zerotier_utils::hex; use zerotier_utils::hex;
@ -207,7 +207,7 @@ pub trait InnerProtocolLayer: Sync + Send {
struct RootInfo { struct RootInfo {
/// Root sets to which we are a member. /// Root sets to which we are a member.
sets: HashMap<String, Valid<RootSet>>, sets: HashMap<String, Verified<RootSet>>,
/// Root peers and their statically defined endpoints (from root sets). /// Root peers and their statically defined endpoints (from root sets).
roots: HashMap<Arc<Peer>, Vec<Endpoint>>, roots: HashMap<Arc<Peer>, Vec<Endpoint>>,
@ -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. /// Add a new root set or update the existing root set if the new root set is newer and otherwise matches.
#[inline] #[inline]
pub fn add_update_root_set(&self, rs: Valid<RootSet>) -> bool { pub fn add_update_root_set(&self, rs: Verified<RootSet>) -> bool {
let mut roots = self.roots.write().unwrap(); let mut roots = self.roots.write().unwrap();
if let Some(entry) = roots.sets.get_mut(&rs.name) { if let Some(entry) = roots.sets.get_mut(&rs.name) {
if rs.should_replace(entry) { if rs.should_replace(entry) {
@ -468,7 +468,7 @@ impl Node {
if let Some(peer) = peers.get(&m.identity.address) { if let Some(peer) = peers.get(&m.identity.address) {
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect()); new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
} else { } 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); drop(peers);
new_roots.insert( new_roots.insert(
self.peers 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 /// 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. /// done by an authorized user or administrator not just by a root.
#[allow(unused)] #[allow(unused)]
pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Valid<RootSet>) { pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Verified<RootSet>) {
let mut roots = self.roots.write().unwrap(); let mut roots = self.roots.write().unwrap();
if let Some(entry) = roots.sets.get_mut(&rs.name) { 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) { if entry.members.iter().any(|m| m.identity.eq(received_from)) && rs.should_replace(entry) {

View file

@ -6,7 +6,7 @@ use std::io::Write;
use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE}; use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE};
use crate::vl1::Endpoint; use crate::vl1::Endpoint;
use zerotier_crypto::typestate::Valid; use zerotier_crypto::typestate::{Valid, Verified};
use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::buffer::Buffer; use zerotier_utils::buffer::Buffer;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError}; 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. /// Get the ZeroTier default root set, which contains roots run by ZeroTier Inc.
pub fn zerotier_default() -> Valid<Self> { pub fn zerotier_default() -> Verified<Self> {
let mut cursor = 0; let mut cursor = 0;
let rs = include_bytes!("../../default-rootset/root.zerotier.com.bin"); let rs = include_bytes!("../../default-rootset/root.zerotier.com.bin");
//let rs = include_bytes!("../../default-rootset/test-root.bin"); //let rs = include_bytes!("../../default-rootset/test-root.bin");
@ -107,7 +107,7 @@ impl RootSet {
} }
/// Verify signatures present in this root cluster definition. /// Verify signatures present in this root cluster definition.
pub fn verify(self) -> Option<Valid<Self>> { pub fn verify(self) -> Option<Verified<Self>> {
if self.members.is_empty() { if self.members.is_empty() {
return None; 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. /// Add a member to this definition, replacing any current entry with this address.

View file

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use zerotier_crypto::hash::SHA384; use zerotier_crypto::hash::SHA384;
use zerotier_crypto::secure_eq; use zerotier_crypto::secure_eq;
use zerotier_crypto::typestate::Valid; use zerotier_crypto::typestate::Verified;
use zerotier_utils::arrayvec::ArrayVec; use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::blob::Blob; use zerotier_utils::blob::Blob;
use zerotier_utils::error::InvalidParameterError; use zerotier_utils::error::InvalidParameterError;
@ -171,13 +171,13 @@ impl CertificateOfMembership {
} }
/// Verify this certificate of membership. /// Verify this certificate of membership.
pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option<Valid<Self>> { pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option<Verified<Self>> {
if secure_eq( if secure_eq(
&Self::v1_proto_issued_to_fingerprint(expect_issued_to), &Self::v1_proto_issued_to_fingerprint(expect_issued_to),
&self.issued_to_fingerprint.as_bytes()[..32], &self.issued_to_fingerprint.as_bytes()[..32],
) { ) {
if issuer.verify(&self.v1_proto_get_qualifier_bytes(), self.signature.as_bytes()) { 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; return None;

View file

@ -31,7 +31,7 @@ pub fn load_node_identity(base_path: &Path) -> Option<Valid<Identity>> {
if id_data.is_err() { if id_data.is_err() {
return None; 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<Identity>) -> bool { pub fn save_node_identity(base_path: &Path, id: &Valid<Identity>) -> bool {