mod resolver;
pub use resolver::CachedDidResolver;
use std::sync::Arc;
use affinidi_data_integrity::{
DataIntegrityError, DataIntegrityProof, DidKeyResolver, SignatureFailure,
VerificationMethodResolver, VerifyOptions,
};
use async_trait::async_trait;
use serde::Serialize;
use serde_json::Value;
use trust_tasks_rs::{ProofVerifier, TrustTask, VerificationError};
pub use affinidi_data_integrity::DidKeyResolver as AffinidiDidKeyResolver;
pub struct Verifier {
resolver: Arc<dyn VerificationMethodResolver>,
options: VerifyOptions,
}
impl Verifier {
pub fn for_did_key() -> Self {
Self::with_resolver(Arc::new(DidKeyResolver))
}
pub fn with_resolver(resolver: Arc<dyn VerificationMethodResolver>) -> Self {
Self {
resolver,
options: VerifyOptions::default(),
}
}
pub fn with_options(mut self, options: VerifyOptions) -> Self {
self.options = options;
self
}
}
#[async_trait]
impl ProofVerifier for Verifier {
async fn verify<P>(&self, doc: &TrustTask<P>) -> Result<(), VerificationError>
where
P: Serialize + Send + Sync,
{
let Some(proof) = &doc.proof else {
return Err(VerificationError::MalformedProof(
"document carries no proof member".to_string(),
));
};
let proof_value = serde_json::to_value(proof)
.map_err(|e| VerificationError::MalformedProof(format!("serialise proof: {e}")))?;
let parsed_proof: DataIntegrityProof = serde_json::from_value(proof_value)
.map_err(|e| VerificationError::MalformedProof(format!("parse proof: {e}")))?;
let mut doc_value = serde_json::to_value(doc).map_err(|e| {
VerificationError::Other(format!("serialise TrustTask for verification: {e}"))
})?;
if let Some(obj) = doc_value.as_object_mut() {
obj.remove("proof");
}
match doc_value.get("issuer").and_then(|v| v.as_str()) {
None => {
return Err(VerificationError::IssuerMismatch(
"document carries a proof but no in-band issuer to bind it to".to_string(),
));
}
Some(issuer) => {
let vm_did = proof
.verification_method
.split('#')
.next()
.unwrap_or(&proof.verification_method);
if vm_did != issuer {
return Err(VerificationError::IssuerMismatch(format!(
"verificationMethod is controlled by {vm_did}, not the document issuer {issuer}"
)));
}
}
}
parsed_proof
.verify(&doc_value, &*self.resolver, self.options.clone())
.await
.map_err(map_error)?;
Ok(())
}
}
fn map_error(err: DataIntegrityError) -> VerificationError {
match err {
DataIntegrityError::UnsupportedCryptoSuite { name } => {
VerificationError::UnsupportedCryptosuite(name)
}
DataIntegrityError::KeyTypeMismatch {
expected,
actual,
suite,
} => VerificationError::IssuerMismatch(format!(
"key type {actual:?} does not match cryptosuite {suite:?} (expected {expected:?})"
)),
DataIntegrityError::InvalidSignature { reason, .. } => match reason {
SignatureFailure::Malformed | SignatureFailure::Invalid => {
VerificationError::SignatureInvalid
}
_ => VerificationError::SignatureInvalid,
},
DataIntegrityError::InvalidPublicKey { reason, .. } => {
VerificationError::MalformedProof(format!("public key: {reason}"))
}
DataIntegrityError::Canonicalization(reason) => {
VerificationError::Other(format!("canonicalisation: {reason}"))
}
DataIntegrityError::MalformedProof(reason) => VerificationError::MalformedProof(reason),
other => VerificationError::Other(other.to_string()),
}
}
pub fn parse_data_integrity_proof(value: &Value) -> Result<DataIntegrityProof, VerificationError> {
serde_json::from_value(value.clone())
.map_err(|e| VerificationError::MalformedProof(format!("parse DataIntegrityProof: {e}")))
}