diff --git a/Cargo.lock b/Cargo.lock index 434443d..abd66db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3094,7 +3094,6 @@ dependencies = [ "eyre", "hex-literal", "itertools 0.13.0", - "ref-cast", "thiserror", ] @@ -6321,26 +6320,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "regex" version = "1.10.2" diff --git a/apps/legacy_web/backend/src/main.rs b/apps/legacy_web/backend/src/main.rs index bf5bfd4..f09d555 100644 --- a/apps/legacy_web/backend/src/main.rs +++ b/apps/legacy_web/backend/src/main.rs @@ -141,7 +141,7 @@ async fn delete_avatar( async fn ensure_user_in_db(db: &mut Database, username: &str) { db.transaction(|data| { - if data.users.get(username).is_none() { + if data.users.contains_key(username) { println!("creating user: {}", username); data.users .insert(username.to_owned(), User::new(username.to_owned())); diff --git a/crates/did-chain/src/lib.rs b/crates/did-chain/src/lib.rs index 26ee125..639570c 100644 --- a/crates/did-chain/src/lib.rs +++ b/crates/did-chain/src/lib.rs @@ -30,7 +30,7 @@ use did_simple::{methods::key::DidKey, methods::DidDyn}; /// This is like an account UUID, it provides a unique identifier for the /// account. Changing it is impossible. #[derive(Debug)] -pub struct DidRoot(DidKey); +pub struct DidRoot(pub DidKey); #[derive(Debug)] pub struct DidChain { diff --git a/crates/did-simple/Cargo.toml b/crates/did-simple/Cargo.toml index 54f4407..45167ea 100644 --- a/crates/did-simple/Cargo.toml +++ b/crates/did-simple/Cargo.toml @@ -12,7 +12,6 @@ publish = false thiserror = "1.0.60" bytes = "1.6.0" bs58 = "0.5.1" -ref-cast = "1.0.23" [dev-dependencies] eyre = "0.6.12" diff --git a/crates/did-simple/src/key_algos.rs b/crates/did-simple/src/key_algos.rs index 53fa0a0..eff2b59 100644 --- a/crates/did-simple/src/key_algos.rs +++ b/crates/did-simple/src/key_algos.rs @@ -1,109 +1,38 @@ -use ref_cast::RefCast; - use crate::varint::encode_varint; -/// A key algorithm. -pub trait KeyAlgo { - type PubKey: AsRef<[u8]>; - fn pub_key_size(&self) -> usize; - fn multicodec_value(&self) -> u16; -} - -/// A key algorithm that is known statically, at compile time. -pub trait StaticKeyAlgo: KeyAlgo { - const PUB_KEY_SIZE: usize; - const MULTICODEC_VALUE: u16; - const MULTICODEC_VALUE_ENCODED: &'static [u8] = - encode_varint(Self::MULTICODEC_VALUE).as_slice(); - type PubKeyArray: AsRef<[u8]>; -} - -impl KeyAlgo for T { - type PubKey = T::PubKeyArray; - - fn pub_key_size(&self) -> usize { - Self::PUB_KEY_SIZE - } - - fn multicodec_value(&self) -> u16 { - Self::MULTICODEC_VALUE - } -} - -#[derive(RefCast)] -#[repr(transparent)] -pub struct PubKey(pub T::PubKeyArray); - #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -pub struct Ed25519; - -impl StaticKeyAlgo for Ed25519 { - const PUB_KEY_SIZE: usize = 32; - const MULTICODEC_VALUE: u16 = 0xED; - type PubKeyArray = [u8; Self::PUB_KEY_SIZE]; -} - -impl PartialEq for DynKeyAlgo { - fn eq(&self, _other: &Ed25519) -> bool { - *self == DynKeyAlgo::Ed25519 - } -} - -#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -pub enum DynKeyAlgo { +pub enum KeyAlgo { Ed25519, } -impl KeyAlgo for DynKeyAlgo { - type PubKey = DynPubKey; - - fn pub_key_size(&self) -> usize { - match self { - Self::Ed25519 => Ed25519::PUB_KEY_SIZE, - } - } - - fn multicodec_value(&self) -> u16 { +impl KeyAlgo { + pub fn pub_key_len(&self) -> usize { match self { - Self::Ed25519 => Ed25519::MULTICODEC_VALUE, + Self::Ed25519 => Ed25519::PUB_KEY_LEN, } } } -#[non_exhaustive] -pub enum DynPubKey { - Ed25519(PubKey), -} - -impl From> for DynPubKey { - fn from(value: PubKey) -> Self { - Self::Ed25519(value) - } -} +// ---- internal code ---- -impl AsRef<[u8]> for DynPubKey { - fn as_ref(&self) -> &[u8] { - match self { - Self::Ed25519(k) => k.0.as_ref(), - } - } +/// A key algorithm that is known statically, at compile time. +pub(crate) trait StaticKeyAlgo { + const PUB_KEY_LEN: usize; + const MULTICODEC_VALUE: u16; + const MULTICODEC_VALUE_ENCODED: &'static [u8] = + encode_varint(Self::MULTICODEC_VALUE).as_slice(); } -#[non_exhaustive] -pub enum DynPubKeyRef<'a> { - Ed25519(&'a PubKey), -} +#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] +pub(crate) struct Ed25519; -impl<'a> From<&'a PubKey> for DynPubKeyRef<'a> { - fn from(value: &'a PubKey) -> Self { - Self::Ed25519(value) - } +impl StaticKeyAlgo for Ed25519 { + const PUB_KEY_LEN: usize = 32; + const MULTICODEC_VALUE: u16 = 0xED; } -impl AsRef<[u8]> for DynPubKeyRef<'_> { - fn as_ref(&self) -> &[u8] { - match *self { - Self::Ed25519(k) => k.0.as_ref(), - } +impl PartialEq for KeyAlgo { + fn eq(&self, _other: &Ed25519) -> bool { + *self == KeyAlgo::Ed25519 } } diff --git a/crates/did-simple/src/lib.rs b/crates/did-simple/src/lib.rs index 2b4af14..e730eb2 100644 --- a/crates/did-simple/src/lib.rs +++ b/crates/did-simple/src/lib.rs @@ -1,22 +1,22 @@ //! A Decentralized Identifier (aka [DID][spec]), is a globally unique //! identifier that provides a general purpose way of looking up public keys -//! associated with the globally unique identifier. +//! associated with the identifier. //! -//! This means that unlike a UUID, someone can prove that they own a DID, and -//! you can encrypt messages using DIDs! This makes DIDs strictly more useful -//! than traditional UUIDs as account identifiers and are very useful for -//! building federated or decentralized services. +//! This means that ownership of a UUID can be proven and that support for +//! common cryptography operations such as signing, verifying, and encrypting +//! messages is possible. This makes DIDs strictly more useful than traditional +//! UUIDs as account identifiers and are very useful for building federated or +//! decentralized services. //! -//! Unlike traditional centralized accounts, services that use DIDs give users -//! custody over their account identity. Authentication of users can happen -//! without the need for a centralized service or database. Instead, whoever -//! holds the private keys associated with a DID will be able to authenticate as -//! the account owner. +//! Services that use DIDs give users self-custody over their account identity. +//! Authentication of users can happen without the need for a centralized +//! authentication service or user database. Instead, whoever holds the private +//! keys associated with a DID will be able to authenticate as the account owner. //! //! This gives users the ability to maintain the same account handles/identities //! across multiple separate services (or migrate homeservers in a federated -//! system) without having to create a new, different, account or identity each -//! time. +//! system) without having to create a different account or identity for each +//! service. //! //! [spec]: https://www.w3.org/TR/did-core/ @@ -24,12 +24,16 @@ use std::str::FromStr; -pub mod key_algos; +pub(crate) mod key_algos; pub mod methods; -pub mod uri; +pub mod url; pub mod utf8bytes; -pub mod varint; +mod varint; + +pub use crate::key_algos::KeyAlgo; +pub use crate::methods::DidDyn; +pub use crate::url::DidUrl; pub trait Did: FromStr { - fn uri(&self) -> self::uri::DidUri; + fn url(&self) -> self::url::DidUrl; } diff --git a/crates/did-simple/src/methods/key.rs b/crates/did-simple/src/methods/key.rs index 1cd27a1..85f7a05 100644 --- a/crates/did-simple/src/methods/key.rs +++ b/crates/did-simple/src/methods/key.rs @@ -2,12 +2,11 @@ //! //! [did:key]: https://w3c-ccg.github.io/did-method-key/ -use ref_cast::RefCast; use std::fmt::Display; use crate::{ - key_algos::{DynKeyAlgo, DynPubKeyRef, Ed25519, KeyAlgo, PubKey, StaticKeyAlgo}, - uri::{DidMethod, DidUri}, + key_algos::{Ed25519, KeyAlgo, StaticKeyAlgo}, + url::{DidMethod, DidUrl}, utf8bytes::Utf8Bytes, varint::decode_varint, }; @@ -20,7 +19,7 @@ pub struct DidKey { s: Utf8Bytes, /// The decoded multibase portion of the DID. mb_value: Vec, - key_algo: DynKeyAlgo, + key_algo: KeyAlgo, /// The index into [`Self::mb_value`] that is the public key. pubkey_bytes: std::ops::RangeFrom, } @@ -30,40 +29,33 @@ pub const PREFIX: &str = "did:key:"; impl DidKey { pub const PREFIX: &'static str = PREFIX; - /// Gets the buffer representing the did:key uri as a str. + /// Gets the buffer representing the did:key url as a str. pub fn as_str(&self) -> &str { self.s.as_str() } - /// Gets the buffer representing the did:key uri as a byte slice. + /// Gets the buffer representing the did:key url as a byte slice. pub fn as_slice(&self) -> &[u8] { self.s.as_slice() } - /// Gets the buffer representing the did:key uri as a reference counted slice + /// Gets the buffer representing the did:key url as a reference counted slice /// that is guaranteed to be utf8. pub fn as_utf8_bytes(&self) -> &Utf8Bytes { &self.s } - pub fn key_algo(&self) -> DynKeyAlgo { + pub fn key_algo(&self) -> KeyAlgo { self.key_algo } /// Gets the decoded bytes of the public key. - pub fn pub_key(&self) -> DynPubKeyRef<'_> { - match self.key_algo { - DynKeyAlgo::Ed25519 => { - // with a cast. - let bytes: &[u8] = &self.mb_value[self.pubkey_bytes.clone()]; - debug_assert_eq!(bytes.len(), Ed25519::PUB_KEY_SIZE); - // TODO: Convert to an unsafe cast behind a feature flag later. - // This is because the slice is guaranteed by our parsing logic - // to match the key algo size. - let bytes: &[u8; Ed25519::PUB_KEY_SIZE] = bytes.try_into().unwrap(); - PubKey::::ref_cast(bytes).into() - } - } + pub fn pub_key(&self) -> &[u8] { + let result = match self.key_algo { + KeyAlgo::Ed25519 => &self.mb_value[self.pubkey_bytes.clone()], + }; + debug_assert_eq!(result.len(), self.key_algo.pub_key_len()); + result } } @@ -95,13 +87,13 @@ pub enum MultibaseDecodeError { Bs58(#[from] bs58::decode::Error), } -impl TryFrom for DidKey { - type Error = FromUriError; +impl TryFrom for DidKey { + type Error = FromUrlError; - fn try_from(value: DidUri) -> Result { + fn try_from(value: DidUrl) -> Result { let m = value.method(); if m != DidMethod::Key { - return Err(FromUriError::WrongMethod(m)); + return Err(FromUrlError::WrongMethod(m)); } debug_assert_eq!( value.as_slice().len() - value.method_specific_id().as_slice().len(), @@ -113,21 +105,18 @@ impl TryFrom for DidKey { let mut decoded_multibase = Vec::new(); decode_multibase(&s, &mut decoded_multibase)?; - // TODO: Instead of comparing decoded versions which requires running the decode - // function at runtime, compare the encoded versions. We can do the encode at - // compile time. - let (multicodec_key_algo, pubkey_bytes) = decode_varint(&decoded_multibase)?; - let key_algo = match multicodec_key_algo { - Ed25519::MULTICODEC_VALUE => DynKeyAlgo::Ed25519, - _ => return Err(FromUriError::UnknownKeyAlgo(multicodec_key_algo)), + // tail bytes will end up being the pubkey bytes if everything passes validation + let (multicodec_key_algo, tail_bytes) = decode_varint(&decoded_multibase)?; + let (key_algo, pub_key_len) = match multicodec_key_algo { + Ed25519::MULTICODEC_VALUE => (KeyAlgo::Ed25519, Ed25519::PUB_KEY_LEN), + _ => return Err(FromUrlError::UnknownKeyAlgo(multicodec_key_algo)), }; - let pubkey_len = pubkey_bytes.len(); - if pubkey_len != key_algo.pub_key_size() { - return Err(FromUriError::MismatchedPubkeyLen(key_algo, pubkey_len)); + if tail_bytes.len() != pub_key_len { + return Err(FromUrlError::MismatchedPubkeyLen(key_algo, pub_key_len)); } - let pubkey_bytes = decoded_multibase.len() - pubkey_len..; + let pubkey_bytes = (decoded_multibase.len() - pub_key_len)..; Ok(Self { s, @@ -139,7 +128,7 @@ impl TryFrom for DidKey { } #[derive(thiserror::Error, Debug)] -pub enum FromUriError { +pub enum FromUrlError { #[error("Expected \"key\" method but got {0:?}")] WrongMethod(DidMethod), #[error(transparent)] @@ -148,8 +137,8 @@ pub enum FromUriError { UnknownKeyAlgo(u16), #[error(transparent)] Varint(#[from] crate::varint::DecodeError), - #[error("{0:?} requires pubkeys of length {} but got {1} bytes", .0.pub_key_size())] - MismatchedPubkeyLen(DynKeyAlgo, usize), + #[error("{0:?} requires pubkeys of length {} but got {1} bytes", .0.pub_key_len())] + MismatchedPubkeyLen(KeyAlgo, usize), } impl Display for DidKey { @@ -176,15 +165,15 @@ mod test { } #[test] - fn test_try_from_uri() -> eyre::Result<()> { + fn test_try_from_url() -> eyre::Result<()> { for &example in ed25519_examples() { - let uri = DidUri::from_str(example) - .wrap_err_with(|| format!("failed to parse DidUri from {example}"))?; - assert_eq!(example, uri.as_str()); - let key_from_uri = DidKey::try_from(uri.clone()) - .wrap_err_with(|| format!("failed to parse DidKey from {uri}"))?; - assert_eq!(example, key_from_uri.as_str()); - assert_eq!(key_from_uri.key_algo(), Ed25519); + let url = DidUrl::from_str(example) + .wrap_err_with(|| format!("failed to parse DidUrl from {example}"))?; + assert_eq!(example, url.as_str()); + let key_from_url = DidKey::try_from(url.clone()) + .wrap_err_with(|| format!("failed to parse DidKey from {url}"))?; + assert_eq!(example, key_from_url.as_str()); + assert_eq!(key_from_url.key_algo(), Ed25519); } Ok(()) } diff --git a/crates/did-simple/src/uri.rs b/crates/did-simple/src/url.rs similarity index 83% rename from crates/did-simple/src/uri.rs rename to crates/did-simple/src/url.rs index 726de29..2d93d92 100644 --- a/crates/did-simple/src/uri.rs +++ b/crates/did-simple/src/url.rs @@ -21,8 +21,8 @@ impl FromStr for DidMethod { } } -/// Helper type to access data in the method-specific-id of a [`DidUri`]. -pub struct MethodSpecificId<'a>(&'a DidUri); +/// Helper type to access data in the method-specific-id of a [`DidUrl`]. +pub struct MethodSpecificId<'a>(&'a DidUrl); impl MethodSpecificId<'_> { pub fn as_str(&self) -> &str { @@ -38,8 +38,9 @@ impl MethodSpecificId<'_> { } } +/// A Decentralized Identifier, including any path information, as a url. #[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub struct DidUri { +pub struct DidUrl { method: DidMethod, /// The string representation of the DID. s: Utf8Bytes, @@ -47,18 +48,18 @@ pub struct DidUri { method_specific_id: std::ops::RangeFrom, } -impl DidUri { - /// Gets the buffer representing the uri as a str. +impl DidUrl { + /// Gets the buffer representing the url as a str. pub fn as_str(&self) -> &str { self.s.as_str() } - /// Gets the buffer representing the uri as a byte slice. + /// Gets the buffer representing the url as a byte slice. pub fn as_slice(&self) -> &[u8] { self.s.as_slice() } - /// Gets the buffer representing the uri as a reference counted slice that + /// Gets the buffer representing the url as a reference counted slice that /// is guaranteed to be utf8. pub fn as_utf8_bytes(&self) -> &Utf8Bytes { &self.s @@ -75,7 +76,7 @@ impl DidUri { } } -impl FromStr for DidUri { +impl FromStr for DidUrl { type Err = ParseError; fn from_str(s: &str) -> Result { @@ -87,7 +88,7 @@ impl FromStr for DidUri { let method = DidMethod::from_str(method)?; let start_idx = s.len() - remaining.len(); - Ok(DidUri { + Ok(DidUrl { method, s: Utf8Bytes::from(s.to_owned()), method_specific_id: (start_idx..), @@ -95,7 +96,7 @@ impl FromStr for DidUri { } } -impl TryFrom for DidUri { +impl TryFrom for DidUrl { type Error = ParseError; fn try_from(s: String) -> Result { @@ -107,7 +108,7 @@ impl TryFrom for DidUri { let method = DidMethod::from_str(method)?; let start_idx = s.len() - remaining.len(); - Ok(DidUri { + Ok(DidUrl { method, s: Utf8Bytes::from(s), method_specific_id: (start_idx..), @@ -125,7 +126,7 @@ pub enum ParseError { UnknownMethod, } -impl Display for DidUri { +impl Display for DidUrl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.as_str().fmt(f) } @@ -136,8 +137,8 @@ mod test { use super::*; use eyre::{Result, WrapErr}; - fn common_test_cases() -> Vec { - vec![DidUri { + fn common_test_cases() -> Vec { + vec![DidUrl { method: DidMethod::Key, s: String::from("did:key:123456").into(), method_specific_id: (8..), @@ -148,8 +149,8 @@ mod test { fn test_parse() -> Result<()> { for expected in common_test_cases() { let s = expected.s.as_str().to_owned(); - let from_str = DidUri::from_str(&s).wrap_err("failed to from_str")?; - let try_from = DidUri::try_from(s).wrap_err("failed to try_from")?; + let from_str = DidUrl::from_str(&s).wrap_err("failed to from_str")?; + let try_from = DidUrl::try_from(s).wrap_err("failed to try_from")?; assert_eq!(from_str, try_from); assert_eq!(from_str, expected); } diff --git a/crates/did-simple/src/varint.rs b/crates/did-simple/src/varint.rs index 49fd7db..38b7fc5 100644 --- a/crates/did-simple/src/varint.rs +++ b/crates/did-simple/src/varint.rs @@ -17,6 +17,7 @@ pub(crate) struct VarintEncoding { impl VarintEncoding { pub const MAX_LEN: usize = 3; + #[allow(dead_code)] pub const fn as_slice(&self) -> &[u8] { self.buf.split_at(self.len as usize).0 } @@ -48,6 +49,7 @@ mod bitlength_test { /// Encodes a value as a varint. /// Returns an array as well as the length of the array to slice., along well as an array. +#[allow(dead_code)] pub(crate) const fn encode_varint(value: u16) -> VarintEncoding { let mut out_buf = [0; VarintEncoding::MAX_LEN]; let in_bit_length: u16 = bitlength(value) as u16; diff --git a/crates/replicate/client/src/instance.rs b/crates/replicate/client/src/instance.rs index 9fde696..7dbb639 100644 --- a/crates/replicate/client/src/instance.rs +++ b/crates/replicate/client/src/instance.rs @@ -79,8 +79,6 @@ //! [did:web]: https://w3c-ccg.github.io/did-method-web/ //! [ABA]: https://en.wikipedia.org/wiki/ABA_problem -use std::sync::atomic::AtomicU16; - use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; use eyre::{bail, ensure, Result, WrapErr}; use futures::{SinkExt, StreamExt}; @@ -197,7 +195,7 @@ pub enum RecvState<'a> { /// Sequence number for state messages #[derive(Debug, Default)] -pub struct StateSeq(AtomicU16); +pub struct StateSeq; async fn connect_to_url( url: &Url, diff --git a/crates/replicate/common/src/data_model/mod.rs b/crates/replicate/common/src/data_model/mod.rs index bdf6bfd..3c2a2c6 100644 --- a/crates/replicate/common/src/data_model/mod.rs +++ b/crates/replicate/common/src/data_model/mod.rs @@ -527,7 +527,7 @@ mod test_dm { }; assert_eq!(dm.data.len(), expected_states.len()); for (e, entity_data) in dm.data.iter() { - assert_eq!(entity_data.state, expected_states[&e], "entity: {e:?}"); + assert_eq!(entity_data.state, expected_states[e], "entity: {e:?}"); } } diff --git a/crates/replicate/server/src/chad/mod.rs b/crates/replicate/server/src/chad/mod.rs index 28c27ca..dfd8499 100644 --- a/crates/replicate/server/src/chad/mod.rs +++ b/crates/replicate/server/src/chad/mod.rs @@ -204,10 +204,6 @@ impl ServerCtx { } } -/// Connection state -#[derive(Debug)] -struct ConnectionCtx {} - /// Newtype on [`wtransport::Certificate`]. #[derive(Clone)] struct Certificate {