iop_keyvault/ed25519/
morpheus.rs

1use super::*;
2use crate::multicipher::{MPrivateKey, MPublicKey};
3
4/// There can be several usages of DIDs differentiated inside the wallet
5/// invisible externally, e.g. on a blockchain.
6/// Each represents a separate subtree under the Morpheus root node of the HD wallet.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8#[repr(i32)]
9pub enum DidKind {
10    /// A certain aspect of your personal life worth separating.
11    /// E.g. business, family, education, hobby, dating, etc.
12    Persona = 0,
13    /// A handle of a persona paired with a single device only.
14    /// Useful for using the same persona on multiple devices.
15    Device = 1,
16    /// An identifier intrinsically handled by multiple personas.
17    /// E.g. legal entities, organizations, families, etc.
18    Group = 2,
19    /// An identifier that represents some resource or property in the real world.
20    /// E.g. smart lock, real estate, NFT, etc.
21    Resource = 3,
22}
23
24impl DidKind {
25    /// The canonical BIP32 derivation path of the node that stores all identifiers of the given kind.
26    pub fn bip32_path(self) -> bip32::Path {
27        Morpheus.bip32_path().append(ChildIndex::Hardened(self as i32))
28    }
29
30    /// All variants of the enumeration
31    pub fn all() -> &'static [DidKind] {
32        &[DidKind::Persona, DidKind::Device, DidKind::Group, DidKind::Resource]
33    }
34}
35
36impl FromStr for DidKind {
37    type Err = anyhow::Error;
38
39    fn from_str(input: &str) -> Result<Self> {
40        match input.to_lowercase().as_ref() {
41            "persona" => Ok(DidKind::Persona),
42            "device" => Ok(DidKind::Device),
43            "group" => Ok(DidKind::Group),
44            "resource" => Ok(DidKind::Resource),
45            _ => bail!("Unknown DID kind {}", input),
46        }
47    }
48}
49
50/// Configuration of Bip32 key derivation for Morpheus.
51pub struct MorpheusSubtree;
52
53impl Subtree for MorpheusSubtree {
54    type Suite = Ed25519;
55
56    fn name(&self) -> &'static str {
57        "morpheus"
58    }
59    fn master(&self, seed: &Seed) -> EdExtPrivateKey {
60        Ed25519::master(seed)
61    }
62    fn key_id(&self, pk: &EdPublicKey) -> EdKeyId {
63        pk.key_id()
64    }
65}
66
67#[derive(Clone, Copy, Debug)]
68/// Starting point for deriving all Morpheus related keys.
69pub struct Morpheus;
70
71impl Morpheus {
72    /// Morpheus is not a coin as defined by Bip44, so it needs a separate Bip43 purpose
73    /// to derive its root node.
74    pub const BIP43_PURPOSE: i32 = 0x1F4A4;
75
76    /// Calculate the root node of the Morpheus subtree in the HD wallet.
77    pub fn root(self, seed: &Seed) -> Result<MorpheusRoot> {
78        let node = Bip32.master(seed, &MorpheusSubtree).derive_hardened(Self::BIP43_PURPOSE)?;
79        Ok(MorpheusRoot { node })
80    }
81
82    /// The canonical BIP32 derivation path of the root Morpheus node.
83    pub fn bip32_path(self) -> bip32::Path {
84        Bip43Path::purpose(Morpheus::BIP43_PURPOSE).bip32_path()
85    }
86}
87
88#[derive(Clone, Debug)]
89/// Representation of the root node of the Morpheus subtree in the HD wallet.
90pub struct MorpheusRoot {
91    node: Bip32Node<Ed25519>,
92}
93
94impl MorpheusRoot {
95    /// To match the `node.path().bip32_path()` pattern, the `Morpheus` sizeless struct has the `bip32_path` method and this method returns
96    /// a reference to `Morpheus`.
97    pub fn path(&self) -> &Morpheus {
98        &Morpheus
99    }
100
101    /// Returns a reference to the underlying BIP32 node.
102    pub fn node(&self) -> &Bip32Node<ed25519::Ed25519> {
103        &self.node
104    }
105
106    /// Creates an admin API for the Morpheus root node.
107    pub fn admin(&self) -> MorpheusVaultAdmin {
108        MorpheusVaultAdmin { parent: self.clone() }
109    }
110
111    /// Alias for kind(Persona).
112    pub fn personas(&self) -> Result<MorpheusKind> {
113        self.kind(DidKind::Persona)
114    }
115
116    /// Alias for kind(Device).
117    pub fn devices(&self) -> Result<MorpheusKind> {
118        self.kind(DidKind::Device)
119    }
120
121    /// Alias for kind(Group).
122    pub fn groups(&self) -> Result<MorpheusKind> {
123        self.kind(DidKind::Group)
124    }
125
126    /// Alias for kind(Resource).
127    pub fn resources(&self) -> Result<MorpheusKind> {
128        self.kind(DidKind::Resource)
129    }
130
131    /// Derive a separate HD wallet subtree of the given kind.
132    pub fn kind(&self, kind: DidKind) -> Result<MorpheusKind> {
133        let node = self.node.derive_hardened(kind as i32)?;
134        Ok(MorpheusKind { kind, node })
135    }
136}
137
138#[derive(Clone, Debug)]
139/// The admin node for the Morpheus root node will be used for self-encrypting administrative data on a storage for data not derived from the seed itself.
140pub struct MorpheusVaultAdmin {
141    parent: MorpheusRoot,
142}
143
144impl MorpheusVaultAdmin {
145    /// To match the `node.path().bip32_path()` pattern, the `Morpheus` sizeless struct has the `bip32_path` method and this method returns
146    /// a reference to `Morpheus`.
147    pub fn path(&self) -> &Morpheus {
148        &Morpheus
149    }
150
151    /// Returns a reference to the underlying BIP32 node.
152    pub fn node(&self) -> &Bip32Node<ed25519::Ed25519> {
153        self.parent.node()
154    }
155}
156
157#[derive(Clone, Debug)]
158/// The admin node for a Morpheus kind node will be used for self-encrypting administrative data on a storage for the collection of identifiers of that kind.
159pub struct MorpheusKindAdmin {
160    parent: MorpheusKind,
161}
162
163impl MorpheusKindAdmin {
164    /// Accessor for the kind of subtree this node is an admin for.
165    pub fn path(&self) -> DidKind {
166        self.parent.path()
167    }
168
169    /// Returns a reference to the underlying BIP32 node.
170    pub fn node(&self) -> &Bip32Node<ed25519::Ed25519> {
171        self.parent.node()
172    }
173}
174
175/// Root node of a specific kind of DIDs. The kind is invisible outside the wallet.
176#[derive(Clone, Debug)]
177pub struct MorpheusKind {
178    kind: DidKind,
179    node: Bip32Node<Ed25519>,
180}
181
182impl MorpheusKind {
183    /// Accessor for the kind of identifiers generated by this node.
184    pub fn path(&self) -> DidKind {
185        self.kind
186    }
187
188    /// Returns a reference to the underlying BIP32 node.
189    pub fn node(&self) -> &Bip32Node<ed25519::Ed25519> {
190        &self.node
191    }
192
193    /// Creates an admin API for the Morpheus kind node.
194    pub fn admin(&self) -> MorpheusKindAdmin {
195        MorpheusKindAdmin { parent: self.clone() }
196    }
197
198    /// The private key of the child node with given index under this subtree.
199    /// E.g. 5th persona, 3rd device, or 0th group, etc.
200    pub fn key(&self, idx: i32) -> Result<MorpheusPrivateKey> {
201        let path = MorpheusKeyPath { kind: self.kind, idx };
202        let node = self.node.derive_hardened(idx)?;
203        Ok(MorpheusPrivateKey { path, node })
204    }
205}
206
207#[derive(Clone, Debug)]
208/// A Morpheus path describing a position of a node in the HD wallet without being bound to a given seed. Will be useful for hardware wallet
209/// integrations in the future.
210pub struct MorpheusKeyPath {
211    kind: DidKind,
212    idx: i32,
213}
214
215impl MorpheusKeyPath {
216    /// The kind of identifiers in this subtree
217    pub fn kind(&self) -> DidKind {
218        self.kind
219    }
220
221    /// The index of the identifier
222    pub fn idx(&self) -> i32 {
223        self.idx
224    }
225
226    /// The canonical BIP32 derivation path of the identifier.
227    pub fn bip32_path(&self) -> bip32::Path {
228        self.kind.bip32_path().append(ChildIndex::Hardened(self.idx))
229    }
230}
231
232#[derive(Clone, Debug)]
233/// The operations on an identifier that require the private key to be available in memory.
234pub struct MorpheusPrivateKey {
235    path: MorpheusKeyPath,
236    node: Bip32Node<ed25519::Ed25519>,
237}
238
239impl MorpheusPrivateKey {
240    /// Creates the public interface of the node that does not need the private key in memory.
241    pub fn neuter(&self) -> MorpheusPublicKey {
242        let node = self.node.neuter();
243        MorpheusPublicKey { path: self.path.clone(), node }
244    }
245
246    /// Returns the Morpheus path for this identifier.
247    pub fn path(&self) -> &MorpheusKeyPath {
248        &self.path
249    }
250
251    /// Returns a reference to the underlying BIP32 node,
252    pub fn node(&self) -> &Bip32Node<ed25519::Ed25519> {
253        &self.node
254    }
255
256    /// Returns the multicipher private key that belongs to this identifier.
257    pub fn private_key(&self) -> MPrivateKey {
258        MPrivateKey::from(self.node.private_key())
259    }
260}
261
262#[derive(Clone, Debug)]
263/// The operations on an identifier that do not require the private key to be available in memory.
264pub struct MorpheusPublicKey {
265    path: MorpheusKeyPath,
266    node: Bip32PublicNode<ed25519::Ed25519>,
267}
268
269impl MorpheusPublicKey {
270    /// Returns the Morpheus path for this identifier.
271    pub fn path(&self) -> &MorpheusKeyPath {
272        &self.path
273    }
274
275    /// Returns a reference to the underlying BIP32 public node.
276    pub fn node(&self) -> &Bip32PublicNode<ed25519::Ed25519> {
277        &self.node
278    }
279
280    /// Returns the multicipher public key that belongs to this identifier.
281    pub fn public_key(&self) -> MPublicKey {
282        MPublicKey::from(self.node.public_key())
283    }
284}
285
286#[cfg(test)]
287mod test {
288    use super::*;
289
290    // TODO Should we rename node() to bip32_node() in this module and Bip44 as well?
291    #[test]
292    fn api_usage() -> Result<()> {
293        let seed = Bip39::new().phrase(Seed::DEMO_PHRASE)?.password(Seed::PASSWORD);
294        let morpheus = Morpheus.root(&seed)?;
295        assert_eq!(morpheus.path().bip32_path(), "m/128164'".parse()?);
296        assert_eq!(morpheus.node().path(), &"m/128164'".parse()?);
297
298        let vault_admin = morpheus.admin();
299        assert_eq!(vault_admin.path().bip32_path(), "m/128164'".parse()?);
300        assert_eq!(vault_admin.node().path(), &"m/128164'".parse()?);
301
302        let groups = morpheus.groups()?;
303        assert_eq!(groups.path(), DidKind::Group);
304        assert_eq!(groups.path().bip32_path(), "m/128164'/2'".parse()?);
305        assert_eq!(groups.node().path(), &"m/128164'/2'".parse()?);
306
307        let groups_admin = groups.admin();
308        assert_eq!(groups_admin.path().bip32_path(), "m/128164'/2'".parse()?);
309        assert_eq!(groups_admin.node().path(), &"m/128164'/2'".parse()?);
310
311        let group0 = groups.key(0)?;
312        assert_eq!(group0.path().bip32_path(), "m/128164'/2'/0'".parse()?);
313        assert_eq!(group0.node().path(), &"m/128164'/2'/0'".parse()?);
314        assert_eq!(group0.path().kind(), DidKind::Group);
315        assert_eq!(group0.path().idx(), 0);
316
317        let group0_pub = group0.neuter();
318        assert_eq!(group0_pub.path().bip32_path(), "m/128164'/2'/0'".parse()?);
319        assert_eq!(group0_pub.node().path(), &"m/128164'/2'/0'".parse()?);
320        let group0_ed_pk = group0_pub.node().public_key();
321        assert_eq!(
322            hex::encode(group0_ed_pk.to_bytes()),
323            "10634a63a4ab84d40170079f2c538d969d4693de9e399f32b8bd29e4583e2e42"
324        );
325        let group0_pk = group0_pub.public_key();
326        assert_eq!(group0_pk.to_string(), "pez26yLWBBR78PjHvMVWZWJK8BC8fQ4KyUMmvNVdpvdKCN5");
327
328        let group0_ed_sk = group0.node().private_key();
329        assert_eq!(
330            hex::encode(group0_ed_sk.to_bytes()),
331            "9f3dbcd653e8825e765c5c2c40b105e73f1eb088ec18247550ff12f2a0733947"
332        );
333        let group0_sk = group0.private_key();
334        assert_eq!(
335            group0_sk.public_key().to_string(),
336            "pez26yLWBBR78PjHvMVWZWJK8BC8fQ4KyUMmvNVdpvdKCN5"
337        );
338
339        // TODO vault_admin.encrypt(plain_data_vec);
340        // TODO vault_admin.decrypt(ecnrypted_data_vec);
341        // TODO group_admin.encrypt(plain_data_vec);
342        // TODO group_admin.decrypt(ecnrypted_data_vec);
343
344        Ok(())
345    }
346}