1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2
3use std::collections::HashMap as Map;
4
5use async_trait::async_trait;
6use chrono::prelude::*;
7pub mod proof;
8use iref::{Iri, IriBuf};
9pub use proof::{Check, LinkedDataProofOptions, Proof};
10use sha2::{Digest, Sha384};
11use static_iref::iri;
12pub mod error;
13pub use error::Error;
14pub mod context;
15pub mod soltx;
16pub use context::Context;
17
18#[cfg(feature = "eip")]
19pub mod eip712;
20
21use rdf_types::QuadRef;
22use serde::{Deserialize, Serialize};
24use serde_json::Value;
25use ssi_core::uri::URI;
26use ssi_crypto::hashes::sha256::sha256;
27use ssi_dids::did_resolve::{resolve_key, DIDResolver};
28use ssi_dids::VerificationRelationship as ProofPurpose;
29use ssi_json_ld::{rdf::DataSet, urdna2015, ContextLoader};
30use ssi_jwk::{Algorithm, Base64urlUInt, JWK};
31use ssi_jws::Header;
32
33pub mod suites;
34pub use suites::*;
35
36lazy_static::lazy_static! {
38 pub static ref TZ_CONTEXT: Value = {
40 let context_str = ssi_contexts::TZ_V2;
41 serde_json::from_str(context_str).unwrap()
42 };
43 pub static ref TZVM_CONTEXT: Value = {
44 let context_str = ssi_contexts::TZVM_V1;
45 serde_json::from_str(context_str).unwrap()
46 };
47 pub static ref TZJCSVM_CONTEXT: Value = {
48 let context_str = ssi_contexts::TZJCSVM_V1;
49 serde_json::from_str(context_str).unwrap()
50 };
51 pub static ref EIP712VM_CONTEXT: Value = {
52 let context_str = ssi_contexts::EIP712VM;
53 serde_json::from_str(context_str).unwrap()
54 };
55 pub static ref EPSIG_CONTEXT: Value = {
56 let context_str = ssi_contexts::EPSIG_V0_1;
57 serde_json::from_str(context_str).unwrap()
58 };
59 pub static ref SOLVM_CONTEXT: Value = {
60 let context_str = ssi_contexts::SOLVM;
61 serde_json::from_str(context_str).unwrap()
62 };
63 pub static ref ALEOVM_CONTEXT: Value = {
64 let context_str = ssi_contexts::ALEOVM;
65 serde_json::from_str(context_str).unwrap()
66 };
67}
68#[derive(Debug, Serialize, Deserialize, Clone, Default)]
70#[serde(rename_all = "camelCase")]
71pub struct VerificationResult {
74 pub checks: Vec<Check>,
76 pub warnings: Vec<String>,
78 pub errors: Vec<String>,
80}
81
82impl VerificationResult {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn error(err: &str) -> Self {
88 Self {
89 checks: vec![],
90 warnings: vec![],
91 errors: vec![err.to_string()],
92 }
93 }
94
95 pub fn append(&mut self, other: &mut Self) {
96 self.checks.append(&mut other.checks);
97 self.warnings.append(&mut other.warnings);
98 self.errors.append(&mut other.errors);
99 }
100
101 pub fn with_error(mut self, error: String) -> Self {
102 self.errors.push(error);
103 self
104 }
105}
106
107impl From<Result<VerificationWarnings, Error>> for VerificationResult {
108 fn from(res: Result<VerificationWarnings, Error>) -> Self {
109 match res {
110 Ok(warnings) => Self {
111 checks: vec![],
112 warnings,
113 errors: vec![],
114 },
115 Err(error) => Self {
116 checks: vec![],
117 warnings: vec![],
118 errors: vec![error.to_string()],
119 },
120 }
121 }
122}
123
124#[deprecated = "Use now_ns instead"]
126pub fn now_ms() -> DateTime<Utc> {
127 now_ns()
128}
129
130pub fn now_ns() -> DateTime<Utc> {
132 let datetime = Utc::now();
133 let ns = datetime.timestamp_subsec_nanos();
134 datetime.with_nanosecond(ns).unwrap_or(datetime)
135}
136
137#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
138#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
139pub trait LinkedDataDocument {
140 fn get_contexts(&self) -> Result<Option<String>, Error>;
141 fn to_value(&self) -> Result<Value, Error>;
142 fn get_default_proof_purpose(&self) -> Option<ProofPurpose> {
143 None
144 }
145 fn get_issuer(&self) -> Option<&str> {
146 None
147 }
148 async fn to_dataset_for_signing(
149 &self,
150 parent: Option<&(dyn LinkedDataDocument + Sync)>,
151 context_loader: &mut ContextLoader,
152 ) -> Result<DataSet, Error>;
153}
154
155#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
156#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
157pub trait ProofSuite {
158 async fn sign(
159 &self,
160 document: &(dyn LinkedDataDocument + Sync),
161 options: &LinkedDataProofOptions,
162 resolver: &dyn DIDResolver,
163 context_loader: &mut ContextLoader,
164 key: &JWK,
165 extra_proof_properties: Option<Map<String, Value>>,
166 ) -> Result<Proof, Error>;
167
168 async fn prepare(
169 &self,
170 document: &(dyn LinkedDataDocument + Sync),
171 options: &LinkedDataProofOptions,
172 resolver: &dyn DIDResolver,
173 context_loader: &mut ContextLoader,
174 public_key: &JWK,
175 extra_proof_properties: Option<Map<String, Value>>,
176 ) -> Result<ProofPreparation, Error>;
177
178 async fn complete(
179 &self,
180 preparation: &ProofPreparation,
181 signature: &str,
182 ) -> Result<Proof, Error>;
183
184 async fn verify(
185 &self,
186 proof: &Proof,
187 document: &(dyn LinkedDataDocument + Sync),
188 resolver: &dyn DIDResolver,
189 context_loader: &mut ContextLoader,
190 ) -> Result<VerificationWarnings, Error>;
191}
192
193pub use ssi_jws::VerificationWarnings;
194
195#[derive(Debug, Serialize, Deserialize, Clone)]
196#[serde(rename_all = "camelCase")]
197pub struct ProofPreparation {
198 pub proof: Proof,
199 pub jws_header: Option<Header>,
200 pub signing_input: SigningInput,
201}
202
203#[derive(Debug, Serialize, Deserialize, Clone)]
204#[serde(untagged)]
205#[non_exhaustive]
206pub enum SigningInput {
207 Bytes(Base64urlUInt),
208 #[cfg(feature = "eip")]
209 TypedData(eip712::TypedData),
210 #[serde(rename_all = "camelCase")]
211 EthereumPersonalMessage {
212 ethereum_personal_message: String,
213 },
214 Micheline {
215 micheline: String,
216 },
217}
218
219fn use_eip712sig(key: &JWK) -> bool {
220 if let Some(ref key_ops) = key.key_operations {
222 if key_ops.contains(&"signTypedData".to_string()) {
223 return true;
224 }
225 }
226 false
227}
228
229fn use_epsig(key: &JWK) -> bool {
230 if let Some(ref key_ops) = key.key_operations {
232 if key_ops.contains(&"signPersonalMessage".to_string()) {
233 return true;
234 }
235 }
236 false
237}
238
239pub async fn ensure_or_pick_verification_relationship(
242 options: &mut LinkedDataProofOptions,
243 document: &(dyn LinkedDataDocument + Sync),
244 key: &JWK,
245 resolver: &dyn DIDResolver,
246) -> Result<(), Error> {
247 let issuer = match document.get_issuer() {
248 None => {
249 return Ok(());
252 }
253 Some(issuer) => issuer,
254 };
255 if options.proof_purpose.is_none() {
256 options.proof_purpose = document.get_default_proof_purpose();
257 }
258 let proof_purpose = options
259 .proof_purpose
260 .as_ref()
261 .ok_or(Error::MissingProofPurpose)?
262 .clone();
263 if !issuer.starts_with("did:") {
264 match issuer {
268 #[cfg(feature = "example-http-issuer")]
269 "https://example.edu/issuers/14" | "https://vc.example/issuers/5678" => {
270 return Ok(());
274 }
275 _ => {
276 return Err(Error::UnsupportedNonDIDIssuer(issuer.to_string()));
277 }
278 }
279 }
280 if let Some(URI::String(ref vm_id)) = options.verification_method {
281 ensure_verification_relationship(issuer, proof_purpose, vm_id, key, resolver).await?;
282 } else {
283 options.verification_method = Some(URI::String(
284 pick_default_vm(issuer, proof_purpose, key, resolver).await?,
285 ))
286 }
287 Ok(())
288}
289
290async fn ensure_verification_relationship(
293 issuer: &str,
294 proof_purpose: ProofPurpose,
295 vm: &str,
296 jwk: &JWK,
297 resolver: &dyn DIDResolver,
298) -> Result<(), Error> {
299 let vmms =
300 ssi_dids::did_resolve::get_verification_methods(issuer, proof_purpose.clone(), resolver)
301 .await?;
302 let vmm = vmms.get(vm).ok_or_else(|| {
303 Error::MissingVerificationRelationship(issuer.to_string(), proof_purpose, vm.to_string())
304 })?;
305 vmm.match_jwk(jwk)?;
306 Ok(())
307}
308
309async fn pick_default_vm(
310 issuer: &str,
311 proof_purpose: ProofPurpose,
312 jwk: &JWK,
313 resolver: &dyn DIDResolver,
314) -> Result<String, Error> {
315 let vm_ids =
316 ssi_dids::did_resolve::get_verification_methods(issuer, proof_purpose.clone(), resolver)
317 .await?;
318 let mut err = Error::MissingKey;
319 for (vm_id, vmm) in vm_ids {
320 match vmm.match_jwk(jwk) {
322 Ok(()) => {
323 return Ok(vm_id);
325 }
326 Err(e) => err = e.into(),
327 }
328 }
329 Err(err)
331}
332
333pub struct LinkedDataProofs;
334impl LinkedDataProofs {
335 pub async fn sign(
337 document: &(dyn LinkedDataDocument + Sync),
338 options: &LinkedDataProofOptions,
339 resolver: &dyn DIDResolver,
340 context_loader: &mut ContextLoader,
341 key: &JWK,
342 extra_proof_properties: Option<Map<String, Value>>,
343 ) -> Result<Proof, Error> {
344 let mut options = options.clone();
345 ensure_or_pick_verification_relationship(&mut options, document, key, resolver).await?;
346 let suite = if let Some(ref type_) = options.type_ {
348 type_.clone()
349 }
350 else {
352 ProofSuiteType::pick(key, options.verification_method.as_ref())?
353 };
354 suite
355 .sign(
356 document,
357 &options,
358 resolver,
359 context_loader,
360 key,
361 extra_proof_properties,
362 )
363 .await
364 }
365
366 pub async fn prepare(
369 document: &(dyn LinkedDataDocument + Sync),
370 options: &LinkedDataProofOptions,
371 resolver: &dyn DIDResolver,
372 context_loader: &mut ContextLoader,
373 public_key: &JWK,
374 extra_proof_properties: Option<Map<String, Value>>,
375 ) -> Result<ProofPreparation, Error> {
376 let mut options = options.clone();
377 ensure_or_pick_verification_relationship(&mut options, document, public_key, resolver)
378 .await?;
379 let suite = if let Some(ref type_) = options.type_ {
381 type_.clone()
382 }
383 else {
385 ProofSuiteType::pick(public_key, options.verification_method.as_ref())?
386 };
387 suite
388 .prepare(
389 document,
390 &options,
391 resolver,
392 context_loader,
393 public_key,
394 extra_proof_properties,
395 )
396 .await
397 }
398
399 pub async fn verify(
401 proof: &Proof,
402 document: &(dyn LinkedDataDocument + Sync),
403 resolver: &dyn DIDResolver,
404 context_loader: &mut ContextLoader,
405 ) -> Result<VerificationWarnings, Error> {
406 let suite = &proof.type_;
407 suite
408 .verify(proof, document, resolver, context_loader)
409 .await
410 }
411}
412
413async fn to_jws_payload(
414 document: &(dyn LinkedDataDocument + Sync),
415 proof: &Proof,
416 context_loader: &mut ContextLoader,
417) -> Result<Vec<u8>, Error> {
418 let (doc_normalized, sigopts_normalized) =
419 urdna2015_normalize(document, proof, context_loader).await?;
420 sha256_normalized(doc_normalized, sigopts_normalized)
421}
422
423async fn urdna2015_normalize(
424 document: &(dyn LinkedDataDocument + Sync),
425 proof: &Proof,
426 context_loader: &mut ContextLoader,
427) -> Result<(String, String), Error> {
428 let sigopts_dataset = proof
429 .to_dataset_for_signing(Some(document), context_loader)
430 .await?;
431 let doc_dataset = document
432 .to_dataset_for_signing(None, context_loader)
433 .await?;
434 let doc_normalized = urdna2015::normalize(doc_dataset.quads().map(QuadRef::from)).into_nquads();
435 let sigopts_normalized =
436 urdna2015::normalize(sigopts_dataset.quads().map(QuadRef::from)).into_nquads();
437 Ok((doc_normalized, sigopts_normalized))
438}
439
440async fn jcs_normalize(
442 document: &(dyn LinkedDataDocument + Sync),
443 proof: &Proof,
444) -> Result<(String, String), Error> {
445 let mut document = document.to_value()?;
446 let document = document.as_object_mut().unwrap();
447 document.remove("proof");
448 let doc_normalized = serde_jcs::to_string(&document)?;
449 let mut proof = proof.clone();
450 proof.jws = None;
451 proof.proof_value = None;
452 let sigopts_normalized = serde_jcs::to_string(&proof)?;
453 Ok((doc_normalized, sigopts_normalized))
454}
455
456fn sha256_normalized(doc_normalized: String, sigopts_normalized: String) -> Result<Vec<u8>, Error> {
457 let sigopts_digest = sha256(sigopts_normalized.as_bytes());
458 let doc_digest = sha256(doc_normalized.as_bytes());
459 let data = [
460 sigopts_digest.as_ref().to_vec(),
461 doc_digest.as_ref().to_vec(),
462 ]
463 .concat();
464 Ok(data)
465}
466
467fn sha384_normalized(doc_normalized: String, sigopts_normalized: String) -> Result<Vec<u8>, Error> {
469 let mut hasher = Sha384::new();
470 hasher.update(sigopts_normalized.as_bytes());
471 let sigopts_digest = hasher.finalize_reset();
472 hasher.update(doc_normalized.as_bytes());
473 let doc_digest = hasher.finalize();
474 let data = [sigopts_digest.to_vec(), doc_digest.to_vec()].concat();
475 Ok(data)
476}
477
478async fn sign(
479 document: &(dyn LinkedDataDocument + Sync),
480 options: &LinkedDataProofOptions,
481 context_loader: &mut ContextLoader,
482 key: &JWK,
483 type_: ProofSuiteType,
484 algorithm: Algorithm,
485 extra_proof_properties: Option<Map<String, Value>>,
486) -> Result<Proof, Error> {
487 if let Some(key_algorithm) = key.algorithm {
488 if key_algorithm != algorithm {
489 return Err(Error::JWS(ssi_jws::Error::AlgorithmMismatch));
490 }
491 }
492 let proof = Proof::new(type_)
493 .with_options(options)
494 .with_properties(extra_proof_properties);
495 sign_proof(document, proof, key, algorithm, context_loader).await
496}
497
498async fn sign_proof(
499 document: &(dyn LinkedDataDocument + Sync),
500 mut proof: Proof,
501 key: &JWK,
502 algorithm: Algorithm,
503 context_loader: &mut ContextLoader,
504) -> Result<Proof, Error> {
505 let message = to_jws_payload(document, &proof, context_loader).await?;
506 let jws = ssi_jws::detached_sign_unencoded_payload(algorithm, &message, key)?;
507 proof.jws = Some(jws);
508 Ok(proof)
509}
510
511#[allow(clippy::too_many_arguments)]
512async fn sign_nojws(
513 document: &(dyn LinkedDataDocument + Sync),
514 options: &LinkedDataProofOptions,
515 context_loader: &mut ContextLoader,
516 key: &JWK,
517 type_: ProofSuiteType,
518 algorithm: Algorithm,
519 context_uri: Iri<'_>,
520 extra_proof_properties: Option<Map<String, Value>>,
521) -> Result<Proof, Error> {
522 if let Some(key_algorithm) = key.algorithm {
523 if key_algorithm != algorithm {
524 return Err(Error::JWS(ssi_jws::Error::AlgorithmMismatch));
525 }
526 }
527 let mut proof = Proof::new(type_)
528 .with_options(options)
529 .with_properties(extra_proof_properties);
530 if !document_has_context(document, context_uri)? {
531 proof.context = serde_json::json!([context_uri]);
532 }
533 let message = to_jws_payload(document, &proof, context_loader).await?;
534 let sig = ssi_jws::sign_bytes(algorithm, &message, key)?;
535 let sig_multibase = multibase::encode(multibase::Base::Base58Btc, sig);
536 proof.proof_value = Some(sig_multibase);
537 Ok(proof)
538}
539
540async fn prepare(
541 document: &(dyn LinkedDataDocument + Sync),
542 options: &LinkedDataProofOptions,
543 context_loader: &mut ContextLoader,
544 public_key: &JWK,
545 type_: ProofSuiteType,
546 algorithm: Algorithm,
547 extra_proof_properties: Option<Map<String, Value>>,
548) -> Result<ProofPreparation, Error> {
549 if let Some(key_algorithm) = public_key.algorithm {
550 if key_algorithm != algorithm {
551 return Err(Error::JWS(ssi_jws::Error::AlgorithmMismatch));
552 }
553 }
554 let proof = Proof::new(type_)
555 .with_options(options)
556 .with_properties(extra_proof_properties);
557 prepare_proof(document, proof, algorithm, context_loader).await
558}
559
560async fn prepare_proof(
561 document: &(dyn LinkedDataDocument + Sync),
562 proof: Proof,
563 algorithm: Algorithm,
564 context_loader: &mut ContextLoader,
565) -> Result<ProofPreparation, Error> {
566 let message = to_jws_payload(document, &proof, context_loader).await?;
567 let (jws_header, signing_input) =
568 ssi_jws::prepare_detached_unencoded_payload(algorithm, &message)?;
569 Ok(ProofPreparation {
570 proof,
571 jws_header: Some(jws_header),
572 signing_input: SigningInput::Bytes(Base64urlUInt(signing_input)),
573 })
574}
575
576#[allow(clippy::too_many_arguments)]
577async fn prepare_nojws(
578 document: &(dyn LinkedDataDocument + Sync),
579 options: &LinkedDataProofOptions,
580 context_loader: &mut ContextLoader,
581 public_key: &JWK,
582 type_: ProofSuiteType,
583 algorithm: Algorithm,
584 context_uri: Iri<'_>,
585 extra_proof_properties: Option<Map<String, Value>>,
586) -> Result<ProofPreparation, Error> {
587 if let Some(key_algorithm) = public_key.algorithm {
588 if key_algorithm != algorithm {
589 return Err(Error::JWS(ssi_jws::Error::AlgorithmMismatch));
590 }
591 }
592 let mut proof = Proof::new(type_)
593 .with_options(options)
594 .with_properties(extra_proof_properties);
595 if !document_has_context(document, context_uri)? {
596 proof.context = serde_json::json!([context_uri]);
597 }
598 let message = to_jws_payload(document, &proof, context_loader).await?;
599 Ok(ProofPreparation {
600 proof,
601 jws_header: None,
602 signing_input: SigningInput::Bytes(Base64urlUInt(message)),
603 })
604}
605
606async fn verify(
607 proof: &Proof,
608 document: &(dyn LinkedDataDocument + Sync),
609 resolver: &dyn DIDResolver,
610 context_loader: &mut ContextLoader,
611) -> Result<VerificationWarnings, Error> {
612 let jws = proof.jws.as_ref().ok_or(Error::MissingProofSignature)?;
613 let verification_method = proof
614 .verification_method
615 .as_ref()
616 .ok_or(Error::MissingVerificationMethod)?;
617 let key = resolve_key(verification_method, resolver).await?;
618 let message = to_jws_payload(document, proof, context_loader).await?;
619 ssi_jws::detached_verify(jws, &message, &key)?;
620 Ok(Default::default())
621}
622
623async fn verify_nojws(
624 proof: &Proof,
625 document: &(dyn LinkedDataDocument + Sync),
626 resolver: &dyn DIDResolver,
627 context_loader: &mut ContextLoader,
628 algorithm: Algorithm,
629) -> Result<VerificationWarnings, Error> {
630 let proof_value = proof
631 .proof_value
632 .as_ref()
633 .ok_or(Error::MissingProofSignature)?;
634 let verification_method = proof
635 .verification_method
636 .as_ref()
637 .ok_or(Error::MissingVerificationMethod)?;
638 let key = resolve_key(verification_method, resolver).await?;
639 let message = to_jws_payload(document, proof, context_loader).await?;
640 let (_base, sig) = multibase::decode(proof_value)?;
641 Ok(ssi_jws::verify_bytes_warnable(
642 algorithm, &message, &key, &sig,
643 )?)
644}
645
646fn document_has_context(
648 document: &(dyn LinkedDataDocument + Sync),
649 context_uri: Iri,
650) -> Result<bool, Error> {
651 let contexts_string = document.get_contexts()?.ok_or(Error::MissingContext)?;
652 let contexts: ssi_core::one_or_many::OneOrMany<Context> =
653 serde_json::from_str(&contexts_string)?;
654 Ok(contexts
655 .into_iter()
656 .any(|c| matches!(c, Context::URI(URI::String(u)) if u == context_uri.as_str())))
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662 use rdf_types::BlankIdBuf;
663 use ssi_dids::example::DIDExample;
664 use ssi_json_ld::CREDENTIALS_V1_CONTEXT;
665
666 struct ExampleDocument;
667
668 #[async_trait]
669 impl LinkedDataDocument for ExampleDocument {
670 fn get_contexts(&self) -> Result<Option<String>, Error> {
671 Ok(Some(serde_json::to_string(&CREDENTIALS_V1_CONTEXT)?))
672 }
673 async fn to_dataset_for_signing(
674 &self,
675 _parent: Option<&(dyn LinkedDataDocument + Sync)>,
676 _context_loader: &mut ContextLoader,
677 ) -> Result<DataSet, Error> {
678 let mut dataset = DataSet::default();
679 let statement = rdf_types::Quad(
680 rdf_types::Subject::Blank(BlankIdBuf::from_suffix("c14n0").unwrap()),
681 iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").to_owned(),
682 rdf_types::Object::Iri(iri!("http://example.org/vocab#Foo").to_owned()),
683 None,
684 );
685 dataset.insert(statement);
686 Ok(dataset)
687 }
688
689 fn to_value(&self) -> Result<Value, Error> {
690 Err(Error::MissingAlgorithm)
691 }
692 }
693
694 #[cfg(feature = "w3c")]
695 #[async_std::test]
696 async fn eip712vm() {
697 let mut key = JWK::generate_secp256k1().unwrap();
698 key.algorithm = Some(Algorithm::ES256KR);
699 let vm = format!("{}#Recovery2020", "did:example:foo");
700 let issue_options = LinkedDataProofOptions {
701 verification_method: Some(URI::String(vm)),
702 ..Default::default()
703 };
704 let resolver = DIDExample;
705 let mut context_loader = ssi_json_ld::ContextLoader::default();
706 let doc = ExampleDocument;
707 let _proof = LinkedDataProofs::sign(
708 &doc,
709 &issue_options,
710 &resolver,
711 &mut context_loader,
712 &key,
713 None,
714 )
715 .await
716 .unwrap();
717 }
718
719 #[async_std::test]
720 #[cfg(feature = "tezos")]
721 async fn tezos_vm_tz1() {
722 let mut key = JWK::generate_ed25519().unwrap();
723 key.algorithm = Some(Algorithm::EdBlake2b);
724 let vm = format!("{}#TezosMethod2021", "did:example:foo");
725 let issue_options = LinkedDataProofOptions {
726 type_: Some(ProofSuiteType::TezosSignature2021),
727 verification_method: Some(URI::String(vm)),
728 ..Default::default()
729 };
730 let doc = ExampleDocument;
731 let resolver = DIDExample;
732 let mut context_loader = ssi_json_ld::ContextLoader::default();
733 let proof = LinkedDataProofs::sign(
734 &doc,
735 &issue_options,
736 &resolver,
737 &mut context_loader,
738 &key,
739 None,
740 )
741 .await
742 .unwrap();
743 println!("{}", serde_json::to_string(&proof).unwrap());
744 }
746
747 #[async_std::test]
748 #[cfg(feature = "tezos")]
749 async fn tezos_vm_tz2() {
750 let mut key = JWK::generate_secp256k1().unwrap();
751 key.algorithm = Some(Algorithm::ESBlake2bK);
752 let vm = format!("{}#TezosMethod2021", "did:example:foo");
753 let issue_options = LinkedDataProofOptions {
754 type_: Some(ProofSuiteType::TezosSignature2021),
755 verification_method: Some(URI::String(vm)),
756 ..Default::default()
757 };
758 let doc = ExampleDocument;
759 let resolver = DIDExample;
760 let mut context_loader = ssi_json_ld::ContextLoader::default();
761 let proof = LinkedDataProofs::sign(
762 &doc,
763 &issue_options,
764 &resolver,
765 &mut context_loader,
766 &key,
767 None,
768 )
769 .await
770 .unwrap();
771 println!("{}", serde_json::to_string(&proof).unwrap());
772 }
773
774 #[async_std::test]
775 #[cfg(feature = "tezos")]
776 async fn tezos_jcs_vm_tz2() {
777 let mut key = JWK::generate_secp256k1().unwrap();
778 key.algorithm = Some(Algorithm::ESBlake2bK);
779 let vm = format!("{}#TezosMethod2021", "did:example:foo");
780 let issue_options = LinkedDataProofOptions {
781 type_: Some(ProofSuiteType::TezosJcsSignature2021),
782 verification_method: Some(URI::String(vm)),
783 ..Default::default()
784 };
785 let doc = ExampleDocument;
786 let resolver = DIDExample;
787 let mut context_loader = ssi_json_ld::ContextLoader::default();
788 let proof = LinkedDataProofs::sign(
789 &doc,
790 &issue_options,
791 &resolver,
792 &mut context_loader,
793 &key,
794 None,
795 )
796 .await
797 .unwrap();
798 println!("{}", serde_json::to_string(&proof).unwrap());
799 }
800
801 }