ssi_ldp/
lib.rs

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;
22// use crate::did::{VerificationMethod, VerificationMethodMap};
23use 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
36// TODO: factor out proof types
37lazy_static::lazy_static! {
38    /// JSON-LD context for Linked Data Proofs based on Tezos addresses
39    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// https://w3c-ccg.github.io/vc-http-api/#/Verifier/verifyCredential
69#[derive(Debug, Serialize, Deserialize, Clone, Default)]
70#[serde(rename_all = "camelCase")]
71/// Object summarizing a verification
72/// Reference: vc-http-api
73pub struct VerificationResult {
74    /// The checks performed
75    pub checks: Vec<Check>,
76    /// Warnings
77    pub warnings: Vec<String>,
78    /// Errors
79    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// Get current time to millisecond precision if possible
125#[deprecated = "Use now_ns instead"]
126pub fn now_ms() -> DateTime<Utc> {
127    now_ns()
128}
129
130// Get current time to nanosecond precision if possible
131pub 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    // deprecated: allow using unregistered "signTypedData" key operation value to indicate using EthereumEip712Signature2021
221    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    // deprecated: allow using unregistered "signPersonalMessage" key operation value to indicate using EthereumPersonalSignature2021
231    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
239// If a verificationMethod purpose was not provided, pick one. If one was provided,
240// verify that it is correct for the given issuer and proof purpose.
241pub 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            // No issuer: no check is done.
250            // TODO: require issuer - or invokers set for ZCap
251            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        // TODO: support non-DID issuers.
265        // Unable to verify verification relationship for non-DID issuers.
266        // Allow some for testing purposes only.
267        match issuer {
268            #[cfg(feature = "example-http-issuer")]
269            "https://example.edu/issuers/14" | "https://vc.example/issuers/5678" => {
270                // https://github.com/w3c/vc-test-suite/blob/cdc7835/test/vc-data-model-1.0/input/example-016-jwt.jsonld#L8
271                // We don't have a way to actually resolve this to anything. Just allow it for
272                // vc-test-suite for now.
273                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
290// Ensure a verification relationship exists between a given issuer and verification method for a
291// given proof purpose, and that the given JWK is matches the given verification method.
292async 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        // Try to find a VM that matches this JWK and controller.
321        match vmm.match_jwk(jwk) {
322            Ok(()) => {
323                // Found appropriate VM.
324                return Ok(vm_id);
325            }
326            Err(e) => err = e.into(),
327        }
328    }
329    // No matching VM found. Return any error encountered.
330    Err(err)
331}
332
333pub struct LinkedDataProofs;
334impl LinkedDataProofs {
335    // https://w3c-ccg.github.io/ld-proofs/#proof-algorithm
336    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        // Use type property if present
347        let suite = if let Some(ref type_) = options.type_ {
348            type_.clone()
349        }
350        // Otherwise pick proof type based on key and options.
351        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    /// Prepare to create a linked data proof. Given a linked data document, proof options, and JWS
367    /// algorithm, calculate the signing input bytes. Returns a [`ProofPreparation`] - the data for the caller to sign, along with data to reconstruct the proof.
368    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        // Use type property if present
380        let suite = if let Some(ref type_) = options.type_ {
381            type_.clone()
382        }
383        // Otherwise pick proof type based on key and options.
384        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    // https://w3c-ccg.github.io/ld-proofs/#proof-verification-algorithm
400    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
440// TODO not the best implementation
441async 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
467// TODO refactor these functions into one that accepts the hasher
468fn 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
646// Check if a linked data document has a given URI in its @context array.
647fn 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        // TODO: verify
745    }
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    /*
802    #[async_std::test]
803    async fn solvm() {
804        let mut key = JWK::generate_secp256k1().unwrap();
805        key.algorithm = Some(Algorithm::ES256KR);
806        let vm = format!("{}#SolanaMethod2021", "did:example:foo");
807        let issue_options = LinkedDataProofOptions {
808            verification_method: Some(vm),
809            ..Default::default()
810        };
811        let doc = ExampleDocument;
812        let resolver = DIDExample;
813        let _proof = LinkedDataProofs::sign(&doc, &issue_options, &resolver, &key)
814            .await
815            .unwrap();
816    }
817    */
818}