1use ssi_dids_core::{
2 document::{
3 self,
4 representation::{self, MediaType},
5 verification_method::DIDVerificationMethod,
6 VerificationRelationships,
7 },
8 resolution::{DIDMethodResolver, Error, Metadata, Options, Output},
9 DIDBuf, DIDMethod, DIDURLBuf, Document, RelativeDIDURLBuf,
10};
11use ssi_jwk::JWK;
12use ssi_verification_methods::ProofPurposes;
13
14mod vm;
15pub use vm::*;
16
17pub struct VerificationMethod {
19 pub type_: VerificationMethodType,
20
21 pub id: DIDURLBuf,
23
24 pub controller: DIDBuf,
26
27 pub public_key: PublicKey,
29}
30
31impl VerificationMethod {
32 pub fn new(
33 type_: VerificationMethodType,
34 id: DIDURLBuf,
35 controller: DIDBuf,
36 public_key: PublicKey,
37 ) -> Self {
38 Self {
39 type_,
40 id,
41 controller,
42 public_key,
43 }
44 }
45}
46
47impl From<VerificationMethod> for DIDVerificationMethod {
48 fn from(value: VerificationMethod) -> Self {
49 DIDVerificationMethod::new(
50 value.id,
51 value.type_.name().to_owned(),
52 value.controller,
53 [(
54 value.public_key.property().to_owned(),
55 value.public_key.into_json(),
56 )]
57 .into_iter()
58 .collect(),
59 )
60 }
61}
62
63pub struct DIDJWK;
65
66impl DIDJWK {
67 pub fn generate(key: &JWK) -> DIDBuf {
89 let key = key.to_public();
90 let normalized = serde_jcs::to_string(&key).unwrap();
91 let method_id = multibase::Base::Base64Url.encode(normalized);
92 DIDBuf::new(format!("did:jwk:{method_id}").into_bytes()).unwrap()
93 }
94
95 pub fn generate_url(key: &JWK) -> DIDURLBuf {
97 let key = key.to_public();
98 let normalized = serde_jcs::to_string(&key).unwrap();
99 let method_id = multibase::Base::Base64Url.encode(normalized);
100 DIDURLBuf::new(format!("did:jwk:{method_id}#0").into_bytes()).unwrap()
101 }
102}
103
104impl DIDMethod for DIDJWK {
105 const DID_METHOD_NAME: &'static str = "jwk";
106}
107
108impl DIDMethodResolver for DIDJWK {
109 async fn resolve_method_representation<'a>(
110 &'a self,
111 method_specific_id: &'a str,
112 options: Options,
113 ) -> Result<Output<Vec<u8>>, Error> {
114 resolve_method_representation(method_specific_id, options)
115 }
116}
117
118fn resolve_method_representation(
119 method_specific_id: &str,
120 options: Options,
121) -> Result<Output<Vec<u8>>, Error> {
122 let data = multibase::Base::decode(&multibase::Base::Base64Url, method_specific_id)
123 .map_err(|_| Error::InvalidMethodSpecificId(method_specific_id.to_string()))?;
124
125 let jwk: JWK = serde_json::from_slice(&data)
126 .map_err(|_| Error::InvalidMethodSpecificId(method_specific_id.to_string()))?;
127
128 let public_jwk = jwk.to_public();
129
130 if public_jwk != jwk {
131 return Err(Error::InvalidMethodSpecificId(
132 method_specific_id.to_string(),
133 ));
134 }
135
136 let did = DIDBuf::new(format!("did:jwk:{method_specific_id}").into_bytes()).unwrap();
137
138 let vm_type = match options.parameters.public_key_format {
139 Some(name) => VerificationMethodType::from_name(&name).ok_or_else(|| {
140 Error::Internal(format!(
141 "verification method type `{name}` unsupported by did:jwk"
142 ))
143 })?,
144 None => VerificationMethodType::Multikey,
145 };
146
147 let public_key = vm_type.encode_public_key(jwk)?;
148
149 let document = Document {
150 verification_method: vec![VerificationMethod::new(
151 vm_type,
152 DIDURLBuf::new(format!("did:jwk:{method_specific_id}#0").into_bytes()).unwrap(),
153 did.clone(),
154 public_key,
155 )
156 .into()],
157 verification_relationships: VerificationRelationships::from_reference(
158 RelativeDIDURLBuf::new(b"#0".to_vec()).unwrap().into(),
159 ProofPurposes::all(),
160 ),
161 ..Document::new(did)
162 };
163
164 let represented = document.into_representation(representation::Options::from_media_type(
165 options.accept.unwrap_or(MediaType::JsonLd),
166 || representation::json_ld::Options {
167 context: representation::json_ld::Context::array(
168 representation::json_ld::DIDContext::V1,
169 vec![vm_type.context_entry()],
170 ),
171 },
172 ));
173
174 Ok(Output::new(
175 represented.to_bytes(),
176 document::Metadata::default(),
177 Metadata::from_content_type(Some(represented.media_type().to_string())),
178 ))
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use ssi_dids_core::{resolution, DIDResolver, DIDURL};
185
186 #[async_std::test]
187 async fn p256_roundtrip() {
188 let jwk = JWK::generate_p256();
189
190 let expected_public_key = VerificationMethodType::Multikey
191 .encode_public_key(jwk.clone())
192 .unwrap()
193 .into_json();
194
195 let did_url = DIDJWK::generate_url(&jwk);
196 let resolved = DIDJWK.dereference(&did_url).await.unwrap();
197
198 let vm = resolved.content.as_verification_method().unwrap();
199
200 let public_key = vm.properties.get("publicKeyMultibase").unwrap();
201
202 assert_eq!(*public_key, expected_public_key);
203 }
204
205 #[async_std::test]
206 async fn from_p256() {
207 let did_url = DIDURL::new(b"did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0").unwrap();
208 let resolved = DIDJWK.dereference(did_url).await.unwrap();
209
210 let vm = resolved.content.as_verification_method().unwrap();
211
212 let public_key = vm.properties.get("publicKeyMultibase").unwrap();
213
214 assert_eq!(vm.id, did_url);
215 assert_eq!(vm.controller, did_url.did());
216
217 let jwk = serde_json::from_value(serde_json::json!({
218 "kty": "EC",
219 "crv": "P-256",
220 "x": "acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0",
221 "y": "_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE"
222 }))
223 .unwrap();
224
225 let expected_public_key = VerificationMethodType::Multikey
226 .encode_public_key(jwk)
227 .unwrap()
228 .into_json();
229
230 assert_eq!(*public_key, expected_public_key);
231 }
232
233 #[async_std::test]
234 async fn to_p256() {
235 let jwk: ssi_jwk::JWK = serde_json::from_value(serde_json::json!({
236 "crv": "P-256",
237 "kty": "EC",
238 "x": "acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0",
239 "y": "_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE"
240 }))
241 .unwrap();
242
243 let expected_public_key = VerificationMethodType::Multikey
244 .encode_public_key(jwk.clone())
245 .unwrap()
246 .into_json();
247
248 let expected = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9";
249 let did = DIDJWK::generate(&jwk);
250 assert_eq!(did, expected);
251
252 let resolved = DIDJWK
253 .resolve_with(&did, resolution::Options::default())
254 .await
255 .unwrap();
256
257 let vm = resolved.document.verification_method.first().unwrap();
258
259 let public_key = vm.properties.get("publicKeyMultibase").unwrap();
260
261 assert_eq!(*public_key, expected_public_key);
262 }
263
264 #[async_std::test]
265 async fn from_x25519() {
266 let did_url = DIDURL::new(b"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9#0").unwrap();
267
268 let mut options = resolution::Options::default();
269 options.parameters.public_key_format = Some("JsonWebKey2020".to_owned());
270
271 let resolved = DIDJWK.dereference_with(did_url, options).await.unwrap();
272
273 let vm = resolved.content.as_verification_method().unwrap();
274
275 let public_key = vm.properties.get("publicKeyJwk").unwrap();
276
277 assert_eq!(vm.id, did_url);
278 assert_eq!(vm.controller, did_url.did());
279
280 let jwk = serde_json::from_value(serde_json::json!({
281 "kty": "OKP",
282 "crv": "X25519",
283 "use": "enc",
284 "x": "3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"
285 }))
286 .unwrap();
287
288 let expected_public_key = VerificationMethodType::JsonWebKey2020
289 .encode_public_key(jwk)
290 .unwrap()
291 .into_json();
292
293 assert_eq!(*public_key, expected_public_key);
294 }
295
296 #[async_std::test]
297 async fn to_x25519() {
298 let jwk: JWK = serde_json::from_value(serde_json::json!({
299 "kty": "OKP",
300 "crv": "X25519",
301 "use": "enc",
302 "x": "3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"
303 }))
304 .unwrap();
305
306 let expected_public_key = VerificationMethodType::JsonWebKey2020
307 .encode_public_key(jwk.clone())
308 .unwrap()
309 .into_json();
310
311 let expected = "did:jwk:eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9";
312 let did = DIDJWK::generate(&jwk);
313 assert_eq!(did, expected);
314
315 let mut options = resolution::Options::default();
316 options.parameters.public_key_format = Some("JsonWebKey2020".to_owned());
317
318 let resolved = DIDJWK.resolve_with(&did, options).await.unwrap();
319
320 let vm = resolved.document.verification_method.first().unwrap();
321
322 let public_key = vm.properties.get("publicKeyJwk").unwrap();
323
324 assert_eq!(*public_key, expected_public_key);
325 }
326
327 #[async_std::test]
328 async fn deny_private_key() {
329 let jwk = JWK::generate_ed25519().unwrap();
330 let json = serde_jcs::to_string(&jwk).unwrap();
331 let json_encoded = multibase::Base::Base64Url.encode(&json);
332 let did = DIDBuf::new(format!("did:jwk:{}", json_encoded).into_bytes()).unwrap();
333 assert!(matches!(
334 DIDJWK.resolve(&did).await.unwrap_err(),
335 Error::InvalidMethodSpecificId(_)
336 ),);
337 }
338}