diff --git a/Cargo.lock b/Cargo.lock index 6176c88..00fb01b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3093,6 +3093,7 @@ dependencies = [ "bytes", "eyre", "hex-literal", + "ref-cast", "thiserror", ] @@ -6310,6 +6311,26 @@ 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/Cargo.toml b/Cargo.toml index ff087e5..fbdbfbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ version = "0.0.0" license = "MIT OR BSD-2-Clause-Patent OR Apache-2.0" repository = "https://github.com/NexusSocial/nexus-vr" edition = "2021" -rust-version = "1.76" +rust-version = "1.78.0" [workspace.dependencies] base64 = "0.21.7" diff --git a/crates/did-simple/Cargo.toml b/crates/did-simple/Cargo.toml index d43c8f6..c59fe05 100644 --- a/crates/did-simple/Cargo.toml +++ b/crates/did-simple/Cargo.toml @@ -12,6 +12,7 @@ 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 6c78e2a..53fa0a0 100644 --- a/crates/did-simple/src/key_algos.rs +++ b/crates/did-simple/src/key_algos.rs @@ -1,7 +1,10 @@ +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; } @@ -12,9 +15,12 @@ pub trait StaticKeyAlgo: KeyAlgo { 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 } @@ -24,17 +30,17 @@ impl KeyAlgo for T { } } +#[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; -} - -#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -pub enum DynKeyAlgo { - Ed25519, + type PubKeyArray = [u8; Self::PUB_KEY_SIZE]; } impl PartialEq for DynKeyAlgo { @@ -43,7 +49,14 @@ impl PartialEq for DynKeyAlgo { } } +#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] +pub enum DynKeyAlgo { + Ed25519, +} + impl KeyAlgo for DynKeyAlgo { + type PubKey = DynPubKey; + fn pub_key_size(&self) -> usize { match self { Self::Ed25519 => Ed25519::PUB_KEY_SIZE, @@ -56,3 +69,41 @@ impl KeyAlgo for DynKeyAlgo { } } } + +#[non_exhaustive] +pub enum DynPubKey { + Ed25519(PubKey), +} + +impl From> for DynPubKey { + fn from(value: PubKey) -> Self { + Self::Ed25519(value) + } +} + +impl AsRef<[u8]> for DynPubKey { + fn as_ref(&self) -> &[u8] { + match self { + Self::Ed25519(k) => k.0.as_ref(), + } + } +} + +#[non_exhaustive] +pub enum DynPubKeyRef<'a> { + Ed25519(&'a PubKey), +} + +impl<'a> From<&'a PubKey> for DynPubKeyRef<'a> { + fn from(value: &'a PubKey) -> Self { + Self::Ed25519(value) + } +} + +impl AsRef<[u8]> for DynPubKeyRef<'_> { + fn as_ref(&self) -> &[u8] { + match *self { + Self::Ed25519(k) => k.0.as_ref(), + } + } +} diff --git a/crates/did-simple/src/methods/key.rs b/crates/did-simple/src/methods/key.rs index 8edbe66..1cd27a1 100644 --- a/crates/did-simple/src/methods/key.rs +++ b/crates/did-simple/src/methods/key.rs @@ -2,10 +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, Ed25519, KeyAlgo, StaticKeyAlgo}, + key_algos::{DynKeyAlgo, DynPubKeyRef, Ed25519, KeyAlgo, PubKey, StaticKeyAlgo}, uri::{DidMethod, DidUri}, utf8bytes::Utf8Bytes, varint::decode_varint, @@ -14,19 +15,19 @@ use crate::{ /// An implementation of the `did:key` method. See the [module](self) docs for more /// info. #[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub struct DidKey { +pub struct DidKey { /// The string representation of the DID. s: Utf8Bytes, /// The decoded multibase portion of the DID. mb_value: Vec, - key_algo: A, + key_algo: DynKeyAlgo, /// The index into [`Self::mb_value`] that is the public key. pubkey_bytes: std::ops::RangeFrom, } pub const PREFIX: &str = "did:key:"; -impl DidKey { +impl DidKey { pub const PREFIX: &'static str = PREFIX; /// Gets the buffer representing the did:key uri as a str. @@ -44,11 +45,25 @@ impl DidKey { pub fn as_utf8_bytes(&self) -> &Utf8Bytes { &self.s } -} -impl DidKey { - pub fn key_algo(&self) -> A { - self.key_algo.clone() + pub fn key_algo(&self) -> DynKeyAlgo { + 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() + } + } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0584b09..bf1ab65 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.76" # See workspace Cargo.toml +channel = "1.78.0" # See workspace Cargo.toml components = ["rust-src"] profile = "default" targets = ["x86_64-pc-windows-msvc", "aarch64-linux-android", "x86_64-unknown-linux-gnu"]