use acdp_did::web::WebResolver;
use acdp_primitives::error::AcdpError;
use acdp_types::log::{LogCheckpoint, LogInclusion, LogLeaf};
pub async fn verify_log_checkpoint_value(
value: &serde_json::Value,
serving_authority: &str,
capabilities_registry_did: &str,
max_clock_skew: chrono::Duration,
resolver: &WebResolver,
) -> Result<LogCheckpoint, AcdpError> {
let checkpoint = LogCheckpoint::from_value(value)?;
checkpoint.cross_check_registry_binding(serving_authority, capabilities_registry_did)?;
checkpoint.check_timestamp_skew(chrono::Utc::now(), max_clock_skew)?;
let key_id = &checkpoint.signature.key_id;
let (did_part, fragment) = key_id.split_once('#').ok_or_else(|| {
AcdpError::InvalidLogProof(format!(
"log_checkpoint signature.key_id '{key_id}' has no fragment"
))
})?;
let doc = resolver.resolve(did_part).await?;
let method = doc.find_by_fragment(fragment).ok_or_else(|| {
AcdpError::InvalidLogProof(format!(
"registry DID document has no verification method '#{fragment}' — \
receipt keys (including retired ones) must remain in verificationMethod \
(RFC-ACDP-0010 §9)"
))
})?;
let raw_hash = LogCheckpoint::preimage_hash_of_value(value)?;
match checkpoint.signature.algorithm.as_str() {
"ed25519" => {
let key = method.ed25519_public_key_bytes().map_err(|e| {
AcdpError::InvalidLogProof(format!("checkpoint key extraction: {e}"))
})?;
checkpoint.verify_signature_against_hash(&raw_hash, Some(&key), None)?;
}
"ecdsa-p256" => {
let key = method.ecdsa_p256_public_key_sec1().map_err(|e| {
AcdpError::InvalidLogProof(format!("checkpoint key extraction: {e}"))
})?;
checkpoint.verify_signature_against_hash(&raw_hash, None, Some(&key))?;
}
other => {
return Err(AcdpError::InvalidLogProof(format!(
"log_checkpoint signature algorithm '{other}' is not supported"
)));
}
}
Ok(checkpoint)
}
pub async fn verify_log_inclusion_value(
value: &serde_json::Value,
reconstructed_leaf: &LogLeaf,
serving_authority: &str,
capabilities_registry_did: &str,
max_clock_skew: chrono::Duration,
resolver: &WebResolver,
) -> Result<LogInclusion, AcdpError> {
let inclusion = LogInclusion::from_value(value)?;
let checkpoint_value = value.get("log_checkpoint").ok_or_else(|| {
AcdpError::InvalidLogProof("log_inclusion has no log_checkpoint member".into())
})?;
verify_log_checkpoint_value(
checkpoint_value,
serving_authority,
capabilities_registry_did,
max_clock_skew,
resolver,
)
.await?;
inclusion.verify_reconstructed_leaf(reconstructed_leaf)?;
Ok(inclusion)
}