use affinidi_data_integrity::crypto_suites::CryptoSuite;
use affinidi_data_integrity::signer::Signer;
use affinidi_data_integrity::{DataIntegrityError, DataIntegrityProof, SignOptions};
use serde_json::Value;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SignError {
#[error("Trust Task document must be a JSON object")]
NotAnObject,
#[error("document carries no in-band `issuer` to bind the proof to")]
MissingIssuer,
#[error(
"signer's verificationMethod is controlled by {vm_did}, not the document issuer {issuer}"
)]
IssuerMismatch {
vm_did: String,
issuer: String,
},
#[error("serialise proof: {0}")]
Serialize(#[from] serde_json::Error),
#[error("read back the emitted proof: {0}")]
ProofRoundTrip(String),
#[error(transparent)]
DataIntegrity(#[from] DataIntegrityError),
}
pub async fn sign_trust_task(
doc: &Value,
signer: &dyn Signer,
options: SignOptions,
) -> Result<Value, SignError> {
let Some(obj) = doc.as_object() else {
return Err(SignError::NotAnObject);
};
let mut unsigned = obj.clone();
unsigned.remove("proof");
let issuer = unsigned
.get("issuer")
.and_then(|v| v.as_str())
.ok_or(SignError::MissingIssuer)?;
let vm = signer.verification_method();
let vm_did = vm.split('#').next().unwrap_or(vm);
if vm_did != issuer {
return Err(SignError::IssuerMismatch {
vm_did: vm_did.to_string(),
issuer: issuer.to_string(),
});
}
let mut options = options;
if options.cryptosuite.is_none() {
options.cryptosuite = Some(CryptoSuite::EddsaJcs2022);
}
let unsigned = Value::Object(unsigned);
let proof = DataIntegrityProof::sign(&unsigned, signer, options).await?;
let Value::Object(mut signed) = unsigned else {
unreachable!("constructed as an object above");
};
signed.insert("proof".to_string(), serde_json::to_value(&proof)?);
Ok(Value::Object(signed))
}