noosphere_core/data/
strings.rs

1use anyhow::Result;
2use noosphere_collections::hamt::Hash as HamtHash;
3use serde::{Deserialize, Serialize};
4use std::{fmt::Display, hash::Hash, ops::Deref, sync::Arc};
5use ucan::crypto::{did::DidParser, KeyMaterial};
6
7use crate::authority::{restore_ed25519_key, SUPPORTED_KEYS};
8
9/// A helper to stamp out trait implementations that promote coherence between
10/// Rust strings and a given wrapper type
11macro_rules! string_coherent {
12    ($wrapper:ty) => {
13        impl Deref for $wrapper {
14            type Target = String;
15
16            fn deref(&self) -> &Self::Target {
17                &self.0
18            }
19        }
20
21        impl Hash for $wrapper {
22            fn hash<H>(&self, hasher: &mut H)
23            where
24                H: std::hash::Hasher,
25            {
26                Hash::hash(&self.0, hasher)
27            }
28        }
29
30        impl HamtHash for $wrapper {
31            fn hash<H>(&self, hasher: &mut H)
32            where
33                H: std::hash::Hasher,
34            {
35                Hash::hash(&self.0, hasher)
36            }
37        }
38
39        impl From<&str> for $wrapper {
40            fn from(value: &str) -> Self {
41                Self(value.to_owned())
42            }
43        }
44
45        impl From<String> for $wrapper {
46            fn from(value: String) -> Self {
47                Self(value)
48            }
49        }
50
51        impl From<$wrapper> for String {
52            fn from(value: $wrapper) -> Self {
53                value.0
54            }
55        }
56
57        impl<'a> From<&'a $wrapper> for &'a str {
58            fn from(value: &'a $wrapper) -> Self {
59                &value.0
60            }
61        }
62
63        impl PartialEq<String> for $wrapper {
64            fn eq(&self, other: &String) -> bool {
65                &self.0 == other
66            }
67        }
68
69        impl PartialEq<$wrapper> for String {
70            fn eq(&self, other: &$wrapper) -> bool {
71                self == &other.0
72            }
73        }
74
75        impl PartialEq<str> for $wrapper {
76            fn eq(&self, other: &str) -> bool {
77                &self.0 == other
78            }
79        }
80
81        impl PartialEq<$wrapper> for str {
82            fn eq(&self, other: &$wrapper) -> bool {
83                self == &other.0
84            }
85        }
86
87        impl PartialEq<&str> for $wrapper {
88            fn eq(&self, other: &&str) -> bool {
89                &self.0 == *other
90            }
91        }
92
93        impl<'a> PartialEq<$wrapper> for &'a str {
94            fn eq(&self, other: &$wrapper) -> bool {
95                **self == *other.0
96            }
97        }
98
99        impl PartialEq for $wrapper {
100            fn eq(&self, other: &Self) -> bool {
101                self.0 == other.0
102            }
103        }
104
105        impl Eq for $wrapper {}
106
107        impl Display for $wrapper {
108            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109                Display::fmt(&self.0, f)
110            }
111        }
112
113        impl AsRef<[u8]> for $wrapper {
114            fn as_ref(&self) -> &[u8] {
115                self.0.as_ref()
116            }
117        }
118    };
119}
120
121/// A DID, aka a Decentralized Identifier, is a string that can be parsed and
122/// resolved into a so-called DID Document, usually in order to obtain PKI
123/// details related to a particular user or process.
124///
125/// See: <https://en.wikipedia.org/wiki/Decentralized_identifier>
126/// See: <https://www.w3.org/TR/did-core/>
127#[repr(transparent)]
128#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialOrd, Ord)]
129pub struct Did(pub String);
130
131string_coherent!(Did);
132
133impl Did {
134    /// Attempt to interpret the [Did] as a [KeyMaterial] and return the result;
135    /// note that the credential resolved from a [Did] is only capable of
136    /// verification, not signing
137    pub fn to_credential(&self) -> Result<Arc<Box<dyn KeyMaterial>>> {
138        let mut parser = DidParser::new(SUPPORTED_KEYS);
139        parser.parse(self)
140    }
141}
142
143/// A JWT, aka a JSON Web Token, is a specialized string-encoding of a
144/// particular format of JSON and an associated signature, commonly used for
145/// authorization flows on the web, but notably also used by the UCAN spec.
146///
147/// See: <https://jwt.io/>
148/// See: <https://ucan.xyz/>
149#[repr(transparent)]
150#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialOrd, Ord)]
151pub struct Jwt(pub String);
152
153string_coherent!(Jwt);
154
155/// A BIP39-compatible mnemonic phrase that represents the data needed to
156/// recover the private half of a cryptographic key pair.
157///
158/// See: <https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>
159#[repr(transparent)]
160#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialOrd, Ord)]
161pub struct Mnemonic(pub String);
162
163string_coherent!(Mnemonic);
164
165impl Mnemonic {
166    /// Attempt to interpret the [Did] as a [KeyMaterial] and return the result
167    /// A credential resolved from a [Mnemonic] may be used for _both_ verification
168    /// and signing
169    pub fn to_credential(&self) -> Result<Arc<Box<dyn KeyMaterial>>> {
170        Ok(Arc::new(Box::new(restore_ed25519_key(self)?)))
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use libipld_cbor::DagCborCodec;
177    use noosphere_storage::{block_deserialize, block_serialize};
178    use serde::{Deserialize, Serialize};
179
180    use crate::data::Did;
181
182    #[test]
183    fn it_serializes_a_did_transparently_as_a_string() {
184        #[derive(Serialize, Deserialize)]
185        struct FooDid {
186            foo: Did,
187        }
188
189        #[derive(Serialize, Deserialize)]
190        struct FooString {
191            foo: String,
192        }
193
194        let string_value = String::from("foobar");
195        let (did_cid, did_block) = block_serialize::<DagCborCodec, _>(&FooDid {
196            foo: Did(string_value.clone()),
197        })
198        .unwrap();
199
200        let (string_cid, string_block) = block_serialize::<DagCborCodec, _>(&FooString {
201            foo: string_value.clone(),
202        })
203        .unwrap();
204
205        assert_eq!(did_cid, string_cid);
206        assert_eq!(did_block, string_block);
207
208        let did_from_string = block_deserialize::<DagCborCodec, FooDid>(&string_block).unwrap();
209        let string_from_did = block_deserialize::<DagCborCodec, FooString>(&did_block).unwrap();
210
211        assert_eq!(did_from_string.foo, Did(string_value.clone()));
212        assert_eq!(string_from_did.foo, string_value);
213    }
214
215    #[test]
216    fn it_enables_comparison_to_string_types() {
217        let did_str = "did:key:z6MkoE19WHXJzpLqkxbGP7uXdJX38sWZNUWwyjcuCmjhPpUP";
218        let did_string = String::from(did_str);
219        let did = Did::from(did_str);
220
221        // Did <-> &str
222        assert_eq!(did, did_str);
223        assert_eq!(did_str, did);
224
225        // Did <-> String
226        assert_eq!(did, did_string);
227        assert_eq!(did_string, did);
228
229        // &Did <-> &str
230        assert_eq!(&did, did_str);
231        assert_eq!(did_str, &did);
232
233        // &Did <-> &String
234        assert_eq!(&did, &did_string);
235        assert_eq!(&did_string, &did);
236    }
237}