use atproto_identity::key::{KeyData, sign, validate};
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use serde_json::json;
use crate::errors::VerificationError;
pub async fn create(
key_data: &KeyData,
record: &serde_json::Value,
repository: &str,
collection: &str,
signature_object: serde_json::Value,
) -> Result<serde_json::Value, VerificationError> {
if let Some(record_map) = signature_object.as_object() {
if !record_map.contains_key("issuer") {
return Err(VerificationError::SignatureObjectMissingField {
field: "issuer".to_string(),
});
}
} else {
return Err(VerificationError::InvalidSignatureObjectType);
};
let mut sig = signature_object.clone();
if let Some(record_map) = sig.as_object_mut() {
record_map.insert("repository".to_string(), json!(repository));
record_map.insert("collection".to_string(), json!(collection));
record_map.insert(
"$type".to_string(),
json!("community.lexicon.attestation.signature"),
);
}
let mut signing_record = record.clone();
if let Some(record_map) = signing_record.as_object_mut() {
record_map.remove("signatures");
record_map.remove("$sig");
record_map.insert("$sig".to_string(), sig);
}
let serialized_signing_record = serde_ipld_dagcbor::to_vec(&signing_record)?;
let signature: Vec<u8> = sign(key_data, &serialized_signing_record)?;
let encoded_signature = URL_SAFE_NO_PAD.encode(&signature);
let mut proof = signature_object.clone();
if let Some(record_map) = proof.as_object_mut() {
record_map.remove("repository");
record_map.remove("collection");
record_map.insert(
"signature".to_string(),
json!({"$bytes": json!(encoded_signature)}),
);
record_map.insert(
"$type".to_string(),
json!("community.lexicon.attestation.signature"),
);
}
let mut signed_record = record.clone();
if let Some(record_map) = signed_record.as_object_mut() {
let mut signatures: Vec<serde_json::Value> = record
.get("signatures")
.and_then(|v| v.as_array().cloned())
.unwrap_or_default();
signatures.push(proof);
record_map.remove("$sig");
record_map.remove("signatures");
record_map.insert("signatures".to_string(), json!(signatures));
}
Ok(signed_record)
}
pub async fn verify(
issuer: &str,
key_data: &KeyData,
record: serde_json::Value,
repository: &str,
collection: &str,
) -> Result<(), VerificationError> {
let signatures = record
.get("sigs")
.or_else(|| record.get("signatures"))
.and_then(|v| v.as_array())
.ok_or(VerificationError::NoSignaturesField)?;
for sig_obj in signatures {
let signature_issuer = sig_obj
.get("issuer")
.and_then(|v| v.as_str())
.ok_or(VerificationError::MissingIssuerField)?;
let signature_value = sig_obj
.get("signature")
.and_then(|v| v.as_str())
.ok_or(VerificationError::MissingSignatureField)?;
if issuer != signature_issuer {
continue;
}
let mut sig_variable = sig_obj.clone();
if let Some(sig_map) = sig_variable.as_object_mut() {
sig_map.remove("signature");
sig_map.insert("repository".to_string(), json!(repository));
sig_map.insert("collection".to_string(), json!(collection));
}
let mut signed_record = record.clone();
if let Some(record_map) = signed_record.as_object_mut() {
record_map.remove("signatures");
record_map.insert("$sig".to_string(), sig_variable);
}
let serialized_record = serde_ipld_dagcbor::to_vec(&signed_record)
.map_err(|error| VerificationError::RecordSerializationFailed { error })?;
let signature_bytes = URL_SAFE_NO_PAD
.decode(signature_value)
.map_err(|error| VerificationError::SignatureDecodingFailed { error })?;
validate(key_data, &signature_bytes, &serialized_record)
.map_err(|error| VerificationError::CryptographicValidationFailed { error })?;
return Ok(());
}
Err(VerificationError::NoValidSignatureForIssuer {
issuer: issuer.to_string(),
})
}