baseid_did/methods/
key.rs

1//! `did:key` method implementation.
2//!
3//! `did:key` is a self-contained DID method where the DID itself encodes
4//! the public key. No network resolution is needed.
5//!
6//! Reference: <https://w3c-ccg.github.io/did-method-key/>
7
8use baseid_core::error::DidError;
9use baseid_crypto::{Jwk, PublicKey};
10
11use crate::document::{DidDocument, VerificationMethod, VerificationRelationship};
12use crate::resolution::{DidResolver, ResolutionMetadata, ResolutionResult};
13use crate::url::DidUrl;
14
15/// W3C DID context.
16const DID_CONTEXT: &str = "https://www.w3.org/ns/did/v1";
17/// Multikey verification method context.
18const MULTIKEY_CONTEXT: &str = "https://w3id.org/security/multikey/v1";
19/// JWK verification method context.
20const JWS_CONTEXT: &str = "https://w3id.org/security/suites/jws-2020/v1";
21
22/// Resolver for `did:key` method.
23pub struct DidKeyResolver;
24
25impl DidResolver for DidKeyResolver {
26    fn method(&self) -> &str {
27        "key"
28    }
29
30    async fn resolve(&self, did: &str) -> baseid_core::Result<ResolutionResult> {
31        let url = DidUrl::parse(did)?;
32        if url.method != "key" {
33            return Err(DidError::UnsupportedMethod.into());
34        }
35
36        // The method-specific identifier is the multibase-encoded public key.
37        let public_key =
38            PublicKey::from_multibase(&url.method_id).map_err(|_| DidError::ResolutionFailed)?;
39
40        let document = Self::create(&public_key)?;
41
42        Ok(ResolutionResult {
43            document: Some(document),
44            metadata: ResolutionMetadata {
45                content_type: Some("application/did+ld+json".to_string()),
46                error: None,
47            },
48        })
49    }
50}
51
52impl DidKeyResolver {
53    /// Create a new `did:key` DID Document from a public key.
54    ///
55    /// Encodes the public key as multibase, constructs a DID Document with
56    /// a Multikey verification method and a JsonWebKey2020 verification method.
57    pub fn create(public_key: &PublicKey) -> baseid_core::Result<DidDocument> {
58        let multibase = public_key.to_multibase();
59        let did = format!("did:key:{multibase}");
60
61        // Primary verification method: Multikey with publicKeyMultibase
62        let vm_id_multikey = format!("{did}#{multibase}");
63        let vm_multikey = VerificationMethod {
64            id: vm_id_multikey.clone(),
65            r#type: "Multikey".to_string(),
66            controller: did.clone(),
67            public_key_jwk: None,
68            public_key_multibase: Some(multibase),
69        };
70
71        // Secondary verification method: JsonWebKey2020 with publicKeyJwk
72        let jwk = Jwk::from_public_key(public_key)?;
73        let jwk_value = serde_json::to_value(&jwk).map_err(|_| DidError::ResolutionFailed)?;
74        let vm_id_jwk = format!("{did}#jwk");
75        let vm_jwk = VerificationMethod {
76            id: vm_id_jwk.clone(),
77            r#type: "JsonWebKey2020".to_string(),
78            controller: did.clone(),
79            public_key_jwk: Some(jwk_value),
80            public_key_multibase: None,
81        };
82
83        Ok(DidDocument {
84            id: did,
85            context: vec![
86                DID_CONTEXT.to_string(),
87                MULTIKEY_CONTEXT.to_string(),
88                JWS_CONTEXT.to_string(),
89            ],
90            verification_method: vec![vm_multikey, vm_jwk],
91            authentication: vec![VerificationRelationship::Reference(vm_id_multikey.clone())],
92            assertion_method: vec![VerificationRelationship::Reference(vm_id_multikey)],
93            key_agreement: vec![],
94            service: vec![],
95        })
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use baseid_core::types::KeyType;
103    use baseid_crypto::KeyPair;
104
105    #[test]
106    fn create_ed25519() {
107        let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
108        let doc = DidKeyResolver::create(&kp.public).unwrap();
109
110        assert!(doc.id.starts_with("did:key:z6Mk"));
111        assert_eq!(doc.context.len(), 3);
112        assert_eq!(doc.context[0], DID_CONTEXT);
113        assert_eq!(doc.verification_method.len(), 2);
114        assert_eq!(doc.verification_method[0].r#type, "Multikey");
115        assert_eq!(doc.verification_method[1].r#type, "JsonWebKey2020");
116        assert_eq!(doc.authentication.len(), 1);
117        assert_eq!(doc.assertion_method.len(), 1);
118    }
119
120    #[test]
121    fn create_p256() {
122        let kp = KeyPair::generate(KeyType::P256).unwrap();
123        let doc = DidKeyResolver::create(&kp.public).unwrap();
124
125        assert!(doc.id.starts_with("did:key:z"));
126        assert_eq!(doc.verification_method.len(), 2);
127
128        // Check that the JWK has the right curve
129        let jwk_vm = &doc.verification_method[1];
130        let jwk = jwk_vm.public_key_jwk.as_ref().unwrap();
131        assert_eq!(jwk["crv"], "P-256");
132        assert_eq!(jwk["kty"], "EC");
133    }
134
135    #[test]
136    fn create_p384() {
137        let kp = KeyPair::generate(KeyType::P384).unwrap();
138        let doc = DidKeyResolver::create(&kp.public).unwrap();
139
140        assert!(doc.id.starts_with("did:key:z"));
141        let jwk_vm = &doc.verification_method[1];
142        let jwk = jwk_vm.public_key_jwk.as_ref().unwrap();
143        assert_eq!(jwk["crv"], "P-384");
144    }
145
146    #[test]
147    fn create_secp256k1() {
148        let kp = KeyPair::generate(KeyType::Secp256k1).unwrap();
149        let doc = DidKeyResolver::create(&kp.public).unwrap();
150
151        assert!(doc.id.starts_with("did:key:z"));
152        let jwk_vm = &doc.verification_method[1];
153        let jwk = jwk_vm.public_key_jwk.as_ref().unwrap();
154        assert_eq!(jwk["crv"], "secp256k1");
155    }
156
157    #[tokio::test]
158    async fn resolve_ed25519() {
159        let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
160        let created = DidKeyResolver::create(&kp.public).unwrap();
161
162        let resolver = DidKeyResolver;
163        let result = resolver.resolve(&created.id).await.unwrap();
164        let resolved = result.document.unwrap();
165
166        assert_eq!(resolved.id, created.id);
167        assert_eq!(resolved.verification_method.len(), 2);
168        assert_eq!(
169            result.metadata.content_type.as_deref(),
170            Some("application/did+ld+json")
171        );
172    }
173
174    #[tokio::test]
175    async fn resolve_roundtrip_all_key_types() {
176        for key_type in [
177            KeyType::Ed25519,
178            KeyType::P256,
179            KeyType::P384,
180            KeyType::Secp256k1,
181        ] {
182            let kp = KeyPair::generate(key_type).unwrap();
183            let created = DidKeyResolver::create(&kp.public).unwrap();
184
185            let resolver = DidKeyResolver;
186            let result = resolver.resolve(&created.id).await.unwrap();
187            let resolved = result.document.unwrap();
188
189            assert_eq!(resolved.id, created.id);
190            assert_eq!(resolved.verification_method.len(), 2);
191
192            // Verify the multibase key in the document decodes to the original key
193            let mb = resolved.verification_method[0]
194                .public_key_multibase
195                .as_ref()
196                .unwrap();
197            let decoded = PublicKey::from_multibase(mb).unwrap();
198            assert_eq!(decoded.bytes, kp.public.bytes);
199            assert_eq!(decoded.key_type, kp.public.key_type);
200        }
201    }
202
203    #[tokio::test]
204    async fn resolve_known_test_vector() {
205        // From baseid-test-vectors
206        let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
207        let resolver = DidKeyResolver;
208        let result = resolver.resolve(did).await.unwrap();
209        let doc = result.document.unwrap();
210
211        assert_eq!(doc.id, did);
212        assert_eq!(doc.verification_method[0].r#type, "Multikey");
213        assert_eq!(
214            doc.verification_method[0].public_key_multibase.as_deref(),
215            Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK")
216        );
217    }
218
219    #[tokio::test]
220    async fn resolve_wrong_method_fails() {
221        let resolver = DidKeyResolver;
222        let result = resolver.resolve("did:web:example.com").await;
223        assert!(result.is_err());
224    }
225
226    #[tokio::test]
227    async fn resolve_invalid_did_fails() {
228        let resolver = DidKeyResolver;
229        assert!(resolver.resolve("not-a-did").await.is_err());
230    }
231
232    #[tokio::test]
233    async fn resolve_invalid_multibase_fails() {
234        let resolver = DidKeyResolver;
235        assert!(resolver.resolve("did:key:invalid!!!").await.is_err());
236    }
237
238    #[test]
239    fn document_serializes_to_json() {
240        let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
241        let doc = DidKeyResolver::create(&kp.public).unwrap();
242
243        let json = serde_json::to_string_pretty(&doc).unwrap();
244        assert!(json.contains("\"@context\""));
245        assert!(json.contains("\"verificationMethod\""));
246        assert!(json.contains("\"authentication\""));
247        assert!(json.contains("\"assertionMethod\""));
248        assert!(json.contains("\"Multikey\""));
249        assert!(json.contains("\"JsonWebKey2020\""));
250    }
251
252    #[test]
253    fn document_json_roundtrip() {
254        let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
255        let doc = DidKeyResolver::create(&kp.public).unwrap();
256
257        let json = serde_json::to_string(&doc).unwrap();
258        let deserialized: DidDocument = serde_json::from_str(&json).unwrap();
259
260        assert_eq!(deserialized.id, doc.id);
261        assert_eq!(deserialized.verification_method.len(), 2);
262    }
263
264    // --- B1: Deterministic test vector verification ---
265
266    /// Verify that a known secret key produces the expected did:key string
267    /// and that resolving it recovers the original public key bytes.
268    fn verify_did_key_vector(
269        key_type: KeyType,
270        secret: &[u8],
271        expected_public: &[u8],
272        expected_multibase: &str,
273        expected_did: &str,
274    ) {
275        // 1. Derive key pair from secret
276        let kp = KeyPair::from_bytes(key_type, secret).unwrap();
277        assert_eq!(&kp.public.bytes, expected_public, "public key mismatch");
278
279        // 2. Verify multibase encoding
280        let multibase = kp.public.to_multibase();
281        assert_eq!(multibase, expected_multibase, "multibase mismatch");
282
283        // 3. Create DID Document and verify DID string
284        let doc = DidKeyResolver::create(&kp.public).unwrap();
285        assert_eq!(doc.id, expected_did, "DID string mismatch");
286
287        // 4. Verify the document's multikey VM contains the expected multibase
288        assert_eq!(
289            doc.verification_method[0].public_key_multibase.as_deref(),
290            Some(expected_multibase),
291        );
292
293        // 5. Roundtrip: decode multibase back and verify key bytes
294        let decoded = PublicKey::from_multibase(expected_multibase).unwrap();
295        assert_eq!(decoded.key_type, key_type);
296        assert_eq!(decoded.bytes, expected_public);
297    }
298
299    #[test]
300    fn vector_ed25519() {
301        use baseid_test_vectors::did::vectors::ed25519;
302        verify_did_key_vector(
303            KeyType::Ed25519,
304            &ed25519::SECRET,
305            &ed25519::PUBLIC,
306            ed25519::MULTIBASE,
307            ed25519::DID,
308        );
309    }
310
311    #[test]
312    fn vector_p256() {
313        use baseid_test_vectors::did::vectors::p256;
314        verify_did_key_vector(
315            KeyType::P256,
316            &p256::SECRET,
317            &p256::PUBLIC,
318            p256::MULTIBASE,
319            p256::DID,
320        );
321    }
322
323    #[test]
324    fn vector_p384() {
325        use baseid_test_vectors::did::vectors::p384;
326        verify_did_key_vector(
327            KeyType::P384,
328            &p384::SECRET,
329            &p384::PUBLIC,
330            p384::MULTIBASE,
331            p384::DID,
332        );
333    }
334
335    #[test]
336    fn vector_secp256k1() {
337        use baseid_test_vectors::did::vectors::secp256k1;
338        verify_did_key_vector(
339            KeyType::Secp256k1,
340            &secp256k1::SECRET,
341            &secp256k1::PUBLIC,
342            secp256k1::MULTIBASE,
343            secp256k1::DID,
344        );
345    }
346
347    #[tokio::test]
348    async fn resolve_vector_ed25519() {
349        use baseid_test_vectors::did::vectors::ed25519;
350        let resolver = DidKeyResolver;
351        let result = resolver.resolve(ed25519::DID).await.unwrap();
352        let doc = result.document.unwrap();
353        assert_eq!(doc.id, ed25519::DID);
354
355        // Recovered key matches expected public bytes
356        let mb = doc.verification_method[0]
357            .public_key_multibase
358            .as_ref()
359            .unwrap();
360        let pk = PublicKey::from_multibase(mb).unwrap();
361        assert_eq!(pk.bytes, ed25519::PUBLIC);
362        assert_eq!(pk.key_type, KeyType::Ed25519);
363    }
364
365    #[tokio::test]
366    async fn resolve_vector_p256() {
367        use baseid_test_vectors::did::vectors::p256;
368        let resolver = DidKeyResolver;
369        let result = resolver.resolve(p256::DID).await.unwrap();
370        let doc = result.document.unwrap();
371        assert_eq!(doc.id, p256::DID);
372
373        let mb = doc.verification_method[0]
374            .public_key_multibase
375            .as_ref()
376            .unwrap();
377        let pk = PublicKey::from_multibase(mb).unwrap();
378        assert_eq!(pk.bytes, p256::PUBLIC.to_vec());
379        assert_eq!(pk.key_type, KeyType::P256);
380
381        // JWK should have correct curve
382        let jwk = doc.verification_method[1].public_key_jwk.as_ref().unwrap();
383        assert_eq!(jwk["crv"], "P-256");
384    }
385
386    #[tokio::test]
387    async fn resolve_vector_p384() {
388        use baseid_test_vectors::did::vectors::p384;
389        let resolver = DidKeyResolver;
390        let result = resolver.resolve(p384::DID).await.unwrap();
391        let doc = result.document.unwrap();
392        assert_eq!(doc.id, p384::DID);
393
394        let mb = doc.verification_method[0]
395            .public_key_multibase
396            .as_ref()
397            .unwrap();
398        let pk = PublicKey::from_multibase(mb).unwrap();
399        assert_eq!(pk.bytes, p384::PUBLIC.to_vec());
400
401        let jwk = doc.verification_method[1].public_key_jwk.as_ref().unwrap();
402        assert_eq!(jwk["crv"], "P-384");
403    }
404
405    #[tokio::test]
406    async fn resolve_vector_secp256k1() {
407        use baseid_test_vectors::did::vectors::secp256k1;
408        let resolver = DidKeyResolver;
409        let result = resolver.resolve(secp256k1::DID).await.unwrap();
410        let doc = result.document.unwrap();
411        assert_eq!(doc.id, secp256k1::DID);
412
413        let mb = doc.verification_method[0]
414            .public_key_multibase
415            .as_ref()
416            .unwrap();
417        let pk = PublicKey::from_multibase(mb).unwrap();
418        assert_eq!(pk.bytes, secp256k1::PUBLIC.to_vec());
419
420        let jwk = doc.verification_method[1].public_key_jwk.as_ref().unwrap();
421        assert_eq!(jwk["crv"], "secp256k1");
422    }
423}