Skip to main content

affinidi_did_key/
lib.rs

1//! Implementation of the did:key method
2//! [https://w3c-ccg.github.io/did-key-spec/]
3
4use crate::errors::Error;
5use affinidi_did_common::{
6    Document,
7    verification_method::{VerificationMethod, VerificationRelationship},
8};
9use affinidi_secrets_resolver::{
10    crypto::ed25519::ed25519_public_to_x25519_public_key,
11    multicodec::{ED25519_PUB, MultiEncoded, P256_PUB, P384_PUB, SECP256K1_PUB, X25519_PUB},
12};
13use base58::FromBase58;
14use serde_json::{Value, json};
15use std::collections::HashMap;
16use url::Url;
17
18pub mod create;
19pub mod errors;
20
21pub struct DIDKey;
22
23impl DIDKey {
24    /// Resolves a Key DID method and returns a DID Document or error
25    pub fn resolve<'a>(did: &'a str) -> Result<Document, Error<'a>> {
26        if !did.starts_with("did:key:z") {
27            return Err(Error::InvalidDid(
28                did,
29                "DID Url doesn't start with did:key:z".to_string(),
30            ));
31        }
32
33        let identifier = &did[8..]; // Strip the leading did:key: prefix
34
35        // did:key only support base58 decoding as denoted by the leading z prefix
36        let decoded = identifier[1..]
37            .from_base58()
38            .map_err(|_| Error::InvalidPublicKey("Couldn't decode base58".to_string()))?;
39
40        let multicodec = MultiEncoded::new(decoded.as_slice())
41            .map_err(|_| Error::InvalidPublicKey("Unknown multicodec value".to_string()))?;
42
43        let vm_id = Url::parse(&[did, "#", identifier].concat())
44            .map_err(|e| Error::InvalidDidUrl(did, e.to_string()))?;
45        let mut vms = Vec::new();
46
47        let mut key_agreement = Vec::new();
48
49        // Check public key type and lengths
50        match multicodec.codec() {
51            P256_PUB => {
52                if multicodec.data().len() != 33 {
53                    return Err(Error::InvalidPublicKeyLength(multicodec.data().len()));
54                }
55                key_agreement.push(VerificationRelationship::Reference(vm_id.clone()));
56            }
57            P384_PUB => {
58                if multicodec.data().len() != 49 {
59                    return Err(Error::InvalidPublicKeyLength(multicodec.data().len()));
60                }
61                key_agreement.push(VerificationRelationship::Reference(vm_id.clone()));
62            }
63            SECP256K1_PUB => {
64                if multicodec.data().len() != 33 {
65                    return Err(Error::InvalidPublicKeyLength(multicodec.data().len()));
66                }
67                key_agreement.push(VerificationRelationship::Reference(vm_id.clone()));
68            }
69            X25519_PUB => {
70                if multicodec.data().len() != 32 {
71                    return Err(Error::InvalidPublicKeyLength(multicodec.data().len()));
72                }
73                key_agreement.push(VerificationRelationship::Reference(vm_id.clone()));
74            }
75            ED25519_PUB => {
76                if multicodec.data().len() != 32 {
77                    return Err(Error::InvalidPublicKeyLength(multicodec.data().len()));
78                }
79
80                let (x25519_encoded, _) =
81                    ed25519_public_to_x25519_public_key(identifier).map_err(|e| {
82                        Error::InvalidPublicKey(format!(
83                            "Couldn't convert ed25519 to x25519 public-key: {e}"
84                        ))
85                    })?;
86
87                let mut property_set = HashMap::new();
88                property_set.insert(
89                    "publicKeyMultibase".to_string(),
90                    Value::String(x25519_encoded.clone()),
91                );
92
93                let x25519_vm_id =
94                    Url::parse(&[did, "#", &x25519_encoded].concat()).map_err(|e| {
95                        Error::InvalidPublicKey(format!(
96                            "Couldn't create valid URL ID for x25519 VerificationMethod: {e}"
97                        ))
98                    })?;
99
100                vms.push(VerificationMethod {
101                    id: x25519_vm_id.clone(),
102                    type_: "Multikey".to_string(),
103                    controller: Url::parse(did)
104                        .map_err(|e| Error::InvalidDidUrl(did, e.to_string()))?,
105                    expires: None,
106                    revoked: None,
107                    property_set,
108                });
109
110                key_agreement.push(VerificationRelationship::Reference(x25519_vm_id.clone()));
111            }
112            _ => {
113                return Err(Error::UnsupportedPublicKeyType(format!(
114                    "Unknown codec: {}",
115                    multicodec.codec()
116                )));
117            }
118        }
119
120        let mut property_set = HashMap::new();
121        property_set.insert(
122            "publicKeyMultibase".to_string(),
123            Value::String(identifier.to_string()),
124        );
125
126        vms.insert(
127            0,
128            VerificationMethod {
129                id: vm_id.clone(),
130                type_: "Multikey".to_string(),
131                controller: Url::parse(did)
132                    .map_err(|e| Error::InvalidDidUrl(did, e.to_string()))?,
133                expires: None,
134                revoked: None,
135                property_set,
136            },
137        );
138
139        let vm_relationship = VerificationRelationship::Reference(vm_id.clone());
140
141        let mut parameters_set = HashMap::new();
142        parameters_set.insert(
143            "@context".to_string(),
144            json!([
145                "https://www.w3.org/ns/did/v1".to_string(),
146                "https://w3id.org/security/multikey/v1".to_string(),
147            ]),
148        );
149
150        Ok(Document {
151            id: Url::parse(did).map_err(|e| Error::InvalidDidUrl(did, e.to_string()))?,
152            verification_method: vms,
153            authentication: vec![vm_relationship.clone()],
154            assertion_method: vec![vm_relationship.clone()],
155            key_agreement,
156            capability_invocation: vec![vm_relationship.clone()],
157            capability_delegation: vec![vm_relationship.clone()],
158            service: vec![],
159            parameters_set,
160        })
161    }
162}