1use crate::data::{ROOT_PLUS_1_SIGNING_KEY, ROOT_PLUS_2_SIGNING_KEYS};
3use crate::key_manager::KeyManager;
4use crate::key_manager::KeyType;
5use crate::TRUSTCHAIN_DATA;
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8use ssi::did::{Document, ServiceEndpoint, VerificationMethod, VerificationMethodMap};
9use ssi::jwk::JWK;
10use ssi::one_or_many::OneOrMany;
11use std::path::{Path, PathBuf};
12
13use std::sync::Once;
14
15pub fn type_of<T>(_: &T) -> String {
17 std::any::type_name::<T>().to_string()
18}
19
20struct UtilsKeyManager;
22
23impl KeyManager for UtilsKeyManager {}
24
25static INIT: Once = Once::new();
28pub fn init() {
29 INIT.call_once(|| {
30 let utils_key_manager = UtilsKeyManager;
31 let tempdir = tempfile::tempdir().unwrap();
33 std::env::set_var(TRUSTCHAIN_DATA, Path::new(tempdir.as_ref().as_os_str()));
34 drop(tempdir);
36 let root_plus_1_did_suffix = "EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A";
38 let root_plus_2_did_suffix = "EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q";
39 let root_plus_2_candidate_did_suffix = "EiCDmY0qxsde9AdIwMf2tUKOiMo4aHnoWaPBRCeGt7iMHA";
40 let root_plus_2_candidate_signing_key: &str = r#"{"kty":"EC","crv":"secp256k1","x":"WzbWcgvvq21xKDTsvANakBSI3nJKDSmNa99usFmYJ0E","y":"vAFo1gkFqgEE3QsX1xlmHcoKxs5AuDqc18kkYEGVwDk","d":"LHt66ri5ykeVqEZwbzboJevbh5UEZkT8r8etsjg3KeE"}"#;
42 let root_plus_1_signing_jwk: JWK = serde_json::from_str(ROOT_PLUS_1_SIGNING_KEY).unwrap();
43 let root_plus_2_signing_jwks: Vec<JWK> =
44 serde_json::from_str(ROOT_PLUS_2_SIGNING_KEYS).unwrap();
45 utils_key_manager
46 .save_keys(
47 root_plus_1_did_suffix,
48 KeyType::SigningKey,
49 &OneOrMany::One(root_plus_1_signing_jwk),
50 false,
51 )
52 .unwrap();
53 utils_key_manager
54 .save_keys(
55 root_plus_2_did_suffix,
56 KeyType::SigningKey,
57 &OneOrMany::Many(root_plus_2_signing_jwks),
58 false,
59 )
60 .unwrap();
61 let root_plus_2_candidate_signing_jwk: JWK = serde_json::from_str(root_plus_2_candidate_signing_key).unwrap();
62 utils_key_manager.save_keys(root_plus_2_candidate_did_suffix, KeyType::SigningKey, &OneOrMany::One(root_plus_2_candidate_signing_jwk), false).unwrap();
63 });
64}
65
66pub fn extract_keys(doc: &Document) -> Vec<JWK> {
68 let mut public_keys: Vec<JWK> = Vec::new();
69 if let Some(verification_methods) = doc.verification_method.as_ref() {
70 for verification_method in verification_methods {
71 if let VerificationMethod::Map(VerificationMethodMap {
72 public_key_jwk: Some(key),
73 ..
74 }) = verification_method
75 {
76 public_keys.push(key.clone());
77 } else {
78 continue;
79 }
80 }
81 }
82 public_keys
83}
84
85const MULTIHASH_SHA2_256_PREFIX: &[u8] = &[0x12];
87const MULTIHASH_SHA2_256_SIZE: &[u8] = &[0x20];
89fn hash_protocol_algorithm(data: &[u8]) -> (Vec<u8>, Vec<u8>) {
94 let mut hasher = Sha256::new();
95 hasher.update(data);
96 let hash = hasher.finalize().to_vec();
97 (
98 [MULTIHASH_SHA2_256_PREFIX, MULTIHASH_SHA2_256_SIZE].concat(),
99 hash,
100 )
101}
102
103fn data_encoding_scheme(data: &[u8]) -> String {
105 base64::encode_config(data, base64::URL_SAFE_NO_PAD)
106}
107
108pub fn get_operations_path() -> Result<PathBuf, Box<dyn std::error::Error>> {
110 let path: String = std::env::var(TRUSTCHAIN_DATA)?;
111 let path = Path::new(path.as_str()).join("operations");
113 std::fs::create_dir_all(&path)?;
114 Ok(path)
115}
116
117pub fn get_did_suffix(did: &str) -> &str {
119 did.split(':').last().unwrap()
120}
121
122pub fn get_did_from_suffix(did_suffix: &str, method_and_network: &str) -> String {
124 format!("did:{method_and_network}:{did_suffix}")
125}
126
127pub fn canonicalize<T: Serialize + ?Sized>(value: &T) -> Result<String, serde_json::Error> {
129 serde_jcs::to_string(value)
130}
131
132pub fn canonicalize_str<T>(s: &str) -> Result<String, serde_json::Error>
135where
136 T: Serialize + for<'a> Deserialize<'a>,
137{
138 canonicalize(&serde_json::from_str::<T>(s)?)
139}
140
141fn hash_protocol(data: &[u8]) -> Vec<u8> {
149 let (prefix, hash) = hash_protocol_algorithm(data);
150 [prefix, hash].concat()
151}
152
153pub fn hash(data: &str) -> String {
157 let hash = hash_protocol(data.as_bytes());
158 data_encoding_scheme(&hash)
159}
160
161pub fn decode_verify(jwt: &str, key: &JWK) -> Result<String, ssi::jws::Error> {
163 ssi::jwt::decode_verify(jwt, key)
164}
165
166pub fn decode(jwt: &str) -> Result<String, ssi::jws::Error> {
168 ssi::jwt::decode_unverified(jwt)
169}
170
171pub trait HasKeys {
173 fn get_keys(&self) -> Option<Vec<JWK>>;
175}
176
177pub trait HasEndpoints {
179 fn get_endpoints(&self) -> Option<Vec<ServiceEndpoint>>;
181}
182
183impl HasKeys for Document {
184 fn get_keys(&self) -> Option<Vec<JWK>> {
185 let verification_methods = match &self.verification_method {
186 Some(x) => x,
187 None => return None,
188 };
189
190 let verification_method_maps: Vec<&VerificationMethodMap> = verification_methods
191 .iter()
192 .filter_map(|verification_method| match verification_method {
193 VerificationMethod::Map(x) => Some(x),
194 _ => {
195 eprintln!("Unhandled VerificationMethod variant. Expected Map.");
196 None
197 }
198 })
199 .collect();
200
201 if verification_method_maps.is_empty() {
202 return None;
203 }
204
205 let keys: Vec<JWK> = verification_method_maps
206 .iter()
207 .filter_map(|verification_method_map| verification_method_map.public_key_jwk.to_owned())
208 .collect();
209
210 if keys.is_empty() {
211 return None;
212 }
213 Some(keys)
214 }
215}
216
217impl HasEndpoints for Document {
218 fn get_endpoints(&self) -> Option<Vec<ServiceEndpoint>> {
219 let services = match &self.service {
220 Some(x) => x,
221 None => return None,
222 };
223 let service_endpoints: Vec<ServiceEndpoint> = services
224 .iter()
225 .flat_map(|service| match service.to_owned().service_endpoint {
226 Some(endpoints) => endpoints.into_iter(),
227 None => Vec::<ServiceEndpoint>::new().into_iter(),
228 })
229 .collect();
230 if service_endpoints.is_empty() {
231 return None;
232 }
233 Some(service_endpoints)
234 }
235}
236
237pub fn json_contains(candidate: &serde_json::Value, expected: &serde_json::Value) -> bool {
239 if let serde_json::Value::Array(exp_vec) = expected {
241 return exp_vec
242 .iter()
243 .all(|exp_value| json_contains(candidate, exp_value));
244 }
245 match candidate {
246 serde_json::Value::Null => matches!(expected, serde_json::Value::Null),
247 serde_json::Value::Bool(x) => match expected {
248 serde_json::Value::Bool(y) => x == y,
249 _ => false,
250 },
251 serde_json::Value::Number(x) => match expected {
252 serde_json::Value::Number(y) => x == y,
253 _ => false,
254 },
255 serde_json::Value::String(x) => match expected {
256 serde_json::Value::String(y) => x == y,
257 _ => false,
258 },
259 serde_json::Value::Array(cand_vec) => {
260 return cand_vec.iter().any(|value| json_contains(value, expected));
262 }
263 serde_json::Value::Object(cand_map) => {
264 match expected {
265 serde_json::Value::Object(exp_map) => {
266 for exp_key in exp_map.keys() {
269 if !cand_map.contains_key(exp_key) {
270 return cand_map.keys().any(|cand_key| {
272 match cand_map.get(cand_key).unwrap() {
273 serde_json::Value::Object(..)
274 | serde_json::Value::Array(..) => {
275 return json_contains(
276 cand_map.get(cand_key).unwrap(),
277 expected,
278 )
279 }
280 _ => false,
281 }
282 });
283 } else {
284 let exp_value = exp_map.get(exp_key).unwrap();
285 let cand_value = cand_map.get(exp_key).unwrap();
286 if !json_contains(cand_value, exp_value) {
287 return false;
288 }
289 }
290 }
291 true
292 }
293 _ => {
294 return cand_map
297 .values()
298 .any(|cand_value| json_contains(cand_value, expected));
299 }
300 }
301 }
302 }
303}
304pub fn generate_key() -> JWK {
306 JWK::generate_secp256k1().expect("Could not generate key.")
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use crate::data::{
313 TEST_ROOT_JWK_PK, TEST_ROOT_PLUS_1_DOCUMENT, TEST_ROOT_PLUS_1_JWT,
314 TEST_SIDETREE_DOCUMENT_MULTIPLE_KEYS,
315 };
316 use ssi::did::Document;
317
318 #[test]
319 fn test_get_did_from_suffix() {
320 let did_suffix = "EiCClfEdkTv_aM3UnBBhlOV89LlGhpQAbfeZLFdFxVFkEg";
321 let mut method_and_network = "ion";
322 let result = get_did_from_suffix(did_suffix, method_and_network);
323 assert_eq!(
324 result,
325 "did:ion:EiCClfEdkTv_aM3UnBBhlOV89LlGhpQAbfeZLFdFxVFkEg"
326 );
327
328 method_and_network = "ion:test";
329 let result = get_did_from_suffix(did_suffix, method_and_network);
330 assert_eq!(
331 result,
332 "did:ion:test:EiCClfEdkTv_aM3UnBBhlOV89LlGhpQAbfeZLFdFxVFkEg"
333 );
334 }
335
336 #[test]
337 fn test_generate_key() {
338 let result = generate_key();
339
340 match result.params {
342 ssi::jwk::Params::EC(ecparams) => {
343 assert_eq!(ecparams.curve, Some(String::from("secp256k1")))
344 }
345 _ => panic!(),
346 }
347 }
348
349 #[test]
350 fn test_decode_verify() -> Result<(), Box<dyn std::error::Error>> {
351 let key: JWK = serde_json::from_str(TEST_ROOT_JWK_PK)?;
352 let jwt = TEST_ROOT_PLUS_1_JWT;
353 let result = decode_verify(jwt, &key);
354 assert!(result.is_ok());
355 Ok(())
356 }
357
358 #[test]
359 fn test_decode_canonicalize_hash() -> Result<(), Box<dyn std::error::Error>> {
360 let doc: Document = serde_json::from_str(TEST_ROOT_PLUS_1_DOCUMENT)?;
361 let doc_canon = canonicalize(&doc)?;
362 let actual_hash = hash(&doc_canon);
363 let jwt = TEST_ROOT_PLUS_1_JWT;
364 let expected_hash = decode(jwt)?;
365 assert_eq!(expected_hash, actual_hash);
366 Ok(())
367 }
368
369 #[test]
370 fn test_json_contains() {
371 let cand_str = r#"{"provisionalIndexFileUri":"QmfXAa2MsHspcTSyru4o1bjPQELLi62sr2pAKizFstaxSs","operations":{"create":[{"suffixData":{"deltaHash":"EiBkAX9y-Ts_siMzTzkfAzPKPIIbB033PlF0RlvF97ydJg","recoveryCommitment":"EiCymv17OGBAs7eLmm4BIXDCQBVhdOUAX5QdpIrN4SDE5w"}},{"suffixData":{"deltaHash":"EiBBkv0j587BDSTjJtIv2DJFOOHk662n9Uoh1vtBaY3JKA","recoveryCommitment":"EiClOaWycGv1m-QejUjB0L18G6DVFVeTQCZCuTRrmzCBQg"}},{"suffixData":{"deltaHash":"EiDTaFAO_ae63J4LMApAM-9VAo8ng58TTp2K-2r1nek6lQ","recoveryCommitment":"EiCy4pW16uB7H-ijA6V6jO6ddWfGCwqNcDSJpdv_USzoRA"}}]}}"#;
373 let candidate: serde_json::Value = serde_json::from_str(cand_str).unwrap();
374
375 let exp_str =
376 r#"{"provisionalIndexFileUri":"QmfXAa2MsHspcTSyru4o1bjPQELLi62sr2pAKizFstaxSs"}"#;
377 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
378 assert!(json_contains(&candidate, &expected));
379
380 let exp_str =
382 r#"{"provisionalIndeXFileUri":"QmfXAa2MsHspcTSyru4o1bjPQELLi62sr2pAKizFstaxSs"}"#;
383 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
384 assert!(!json_contains(&candidate, &expected));
385
386 let exp_str =
388 r#"{"provisionalIndexFileUri":"PmfXAa2MsHspcTSyru4o1bjPQELLi62sr2pAKizFstaxSs"}"#;
389 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
390 assert!(!json_contains(&candidate, &expected));
391
392 let array_vec = vec!["x".to_string(), "y".to_string(), "z".to_string()];
394 let candidate = serde_json::json!(array_vec);
395 assert!(json_contains(&candidate, &serde_json::json!("x")));
396 assert!(json_contains(&candidate, &serde_json::json!("y")));
397 assert!(json_contains(&candidate, &serde_json::json!("z")));
398 assert!(!json_contains(&candidate, &serde_json::json!("X")));
399
400 let candidate: serde_json::Value =
402 serde_json::from_str(TEST_SIDETREE_DOCUMENT_MULTIPLE_KEYS).unwrap();
403
404 let exp_str = r##"{"verificationMethod" : [
406 {
407 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
408 "id" : "#V9jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU85",
409 "publicKeyJwk" : {
410 "crv": "secp256k1",
411 "kty": "EC",
412 "x": "7ReQHHysGxbyuKEQmspQOjL7oQUqDTldTHuc9V3-yso",
413 "y": "kWvmS7ZOvDUhF8syO08PBzEpEk3BZMuukkvEJOKSjqE"
414 },
415 "type" : "JsonWebSignature2020"
416 },
417 {
418 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
419 "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84",
420 "publicKeyJwk" : {
421 "crv" : "secp256k1",
422 "kty" : "EC",
423 "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg",
424 "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s"
425 },
426 "type" : "JsonWebSignature2020"
427 }]
428 }"##;
429 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
430 assert!(json_contains(&candidate, &expected));
431
432 let exp_str = r##"{"verificationMethod" : [
434 {
435 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
436 "id" : "#V9jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU85",
437 "publicKeyJwk" : {
438 "crv": "secp256k1",
439 "kty": "EC",
440 "x": "7ReQHHysGxbyuKEQmspQOjL7oQUqDTldTHuc9V3-yso",
441 "z": "kWvmS7ZOvDUhF8syO08PBzEpEk3BZMuukkvEJOKSjqE"
442 },
443 "type" : "JsonWebSignature2020"
444 },
445 {
446 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
447 "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84",
448 "publicKeyJwk" : {
449 "crv" : "secp256k1",
450 "kty" : "EC",
451 "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg",
452 "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s"
453 },
454 "type" : "JsonWebSignature2020"
455 }]
456 }"##;
457 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
458 assert!(!json_contains(&candidate, &expected));
459
460 let exp_str = r##"{"verificationMethod" : [
462 {
463 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
464 "id" : "#V9jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU85",
465 "publicKeyJwk" : {
466 "crv": "secp256k1",
467 "kty": "EC",
468 "x": "7ReQHHysGxbyuKEQmspQOjL7oQUqDTldTHuc9V3-yso",
469 "y": "kWvmS7ZOvDUhF8syO08PBzEpEk3BZMuukkvEJOKSjqE"
470 },
471 "type" : "JsonWebSignature2020"
472 },
473 {
474 "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ",
475 "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84",
476 "publicKeyJwk" : {
477 "crv" : "secp256k1",
478 "kty" : "EC",
479 "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg",
480 "y" : "YcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s"
481 },
482 "type" : "JsonWebSignature2020"
483 }]
484 }"##;
485
486 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
487 assert!(!json_contains(&candidate, &expected));
488
489 let exp_str = r#"{"publicKeyJwk" : {
491 "crv": "secp256k1",
492 "kty": "EC",
493 "x": "7ReQHHysGxbyuKEQmspQOjL7oQUqDTldTHuc9V3-yso",
494 "y": "kWvmS7ZOvDUhF8syO08PBzEpEk3BZMuukkvEJOKSjqE"
495 }}"#;
496
497 let expected: serde_json::Value = serde_json::from_str(exp_str).unwrap();
498 assert!(json_contains(&candidate, &expected));
499 }
500}