didkit/
lib.rs

1#[cfg(not(target_arch = "wasm32"))]
2pub mod c;
3mod did_methods;
4pub mod error;
5#[cfg(not(target_arch = "wasm32"))]
6pub mod jni;
7#[cfg(not(target_arch = "wasm32"))]
8pub mod runtime;
9#[cfg(not(any(target_arch = "wasm32", target_os = "windows")))]
10pub mod ssh_agent;
11
12#[macro_use]
13extern crate lazy_static;
14
15pub use crate::did_methods::DID_METHODS;
16pub use crate::error::Error;
17
18pub use ssi;
19pub use ssi::did::VerificationRelationship;
20pub use ssi::did::{
21    DIDCreate, DIDDeactivate, DIDDocumentOperation, DIDMethod, DIDRecover, DIDUpdate, Document,
22    Source, DIDURL,
23};
24pub use ssi::did_resolve::resolve_key;
25#[cfg(feature = "http-did")]
26pub use ssi::did_resolve::HTTPDIDResolver;
27pub use ssi::did_resolve::{
28    dereference, Content, ContentMetadata, DIDResolver, DereferencingInputMetadata,
29    DocumentMetadata, Metadata, ResolutionInputMetadata, ResolutionMetadata, ResolutionResult,
30    SeriesResolver,
31};
32pub use ssi::jsonld::ContextLoader;
33pub use ssi::jwk::JWK;
34pub use ssi::ldp::ProofPreparation;
35pub use ssi::tzkey::jwk_from_tezos_key;
36pub use ssi::vc::get_verification_method;
37pub use ssi::vc::Credential as VerifiableCredential;
38pub use ssi::vc::CredentialOrJWT;
39pub use ssi::vc::LinkedDataProofOptions;
40pub use ssi::vc::Presentation as VerifiablePresentation;
41pub use ssi::vc::VerificationResult;
42pub use ssi::vc::URI;
43pub use ssi::zcap::{Delegation, Invocation};
44
45use core::str::FromStr;
46use serde::{Deserialize, Serialize};
47
48#[derive(Debug, Serialize, Deserialize, Clone, Default)]
49#[non_exhaustive]
50#[serde(rename_all = "camelCase")]
51#[serde(deny_unknown_fields)]
52pub struct JWTOrLDPOptions {
53    /// Linked data proof options from vc-api (vc-http-api)
54    #[serde(flatten)]
55    pub ldp_options: LinkedDataProofOptions,
56    /// Proof format (not standard in vc-api)
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub proof_format: Option<ProofFormat>,
59}
60
61impl JWTOrLDPOptions {
62    pub fn default_for_vp() -> Self {
63        Self {
64            ldp_options: LinkedDataProofOptions {
65                proof_purpose: Some(VerificationRelationship::Authentication),
66                ..Default::default()
67            },
68            proof_format: None,
69        }
70    }
71}
72
73#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
74#[non_exhaustive]
75pub enum ProofFormat {
76    /// <https://www.w3.org/TR/vc-data-model/#linked-data-proofs>
77    #[serde(rename = "ldp")]
78    LDP,
79    /// <https://www.w3.org/TR/vc-data-model/#json-web-token>
80    #[serde(rename = "jwt")]
81    JWT,
82}
83// ProofFormat implements Display and FromStr for structopt. This should be kept in sync with the
84// serde (de)serialization (rename = ...)
85
86impl Default for ProofFormat {
87    fn default() -> Self {
88        Self::LDP
89    }
90}
91
92impl std::fmt::Display for ProofFormat {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            Self::LDP => write!(f, "ldp"),
96            Self::JWT => write!(f, "jwt"),
97        }
98    }
99}
100
101impl FromStr for ProofFormat {
102    type Err = String;
103    fn from_str(s: &str) -> Result<Self, Self::Err> {
104        match &s[..] {
105            "ldp" => Ok(Self::LDP),
106            "jwt" => Ok(Self::JWT),
107            _ => Err(format!("Unexpected proof format: {}", s))?,
108        }
109    }
110}
111
112#[derive(thiserror::Error, Debug)]
113pub enum GenerateProofError {
114    #[cfg(not(any(target_arch = "wasm32", target_os = "windows")))]
115    #[error("Unable to sign: {0}")]
116    Sign(#[from] crate::ssh_agent::SignError),
117    #[error("SSI Linked Data Proof: {0}")]
118    LDP(#[from] ssi::ldp::Error),
119    #[error("IO: {0}")]
120    IO(#[from] std::io::Error),
121    #[error("WASM support for ssh-agent is not enabled")]
122    NoWASM,
123    #[error("Windows support for ssh-agent is not enabled")]
124    NoWindows,
125}
126
127pub async fn generate_proof(
128    document: &(dyn ssi::ldp::LinkedDataDocument + Sync),
129    key: Option<&JWK>,
130    options: LinkedDataProofOptions,
131    resolver: &dyn DIDResolver,
132    context_loader: &mut ContextLoader,
133    ssh_agent_sock_path_opt: Option<&str>,
134) -> Result<ssi::ldp::Proof, GenerateProofError> {
135    use ssi::ldp::LinkedDataProofs;
136    let proof = match ssh_agent_sock_path_opt {
137        #[cfg(target_arch = "wasm32")]
138        Some(_) => {
139            return Err(GenerateProofError::NoWASM);
140        }
141        #[cfg(target_os = "windows")]
142        Some(_) => {
143            return Err(GenerateProofError::NoWindows);
144        }
145        #[cfg(not(any(target_arch = "wasm32", target_os = "windows")))]
146        Some(sock_path) => {
147            use tokio::net::UnixStream;
148            let mut ssh_agent_sock = UnixStream::connect(sock_path).await?;
149            crate::ssh_agent::generate_proof(
150                &mut ssh_agent_sock,
151                document,
152                options,
153                resolver,
154                context_loader,
155                key,
156            )
157            .await?
158        }
159        None => {
160            let jwk = key.expect("JWK, Key Path, or SSH Agent option is required.");
161            LinkedDataProofs::sign(document, &options, resolver, context_loader, &jwk, None).await?
162        }
163    };
164
165    Ok(proof)
166}