use crate::error::AcdpError;
use sha2::{Digest, Sha256};
pub fn fingerprint_ed25519(public_key: &[u8; 32]) -> String {
format!("sha256:{}", hex::encode(Sha256::digest(public_key)))
}
pub fn fingerprint_p256_sec1(sec1: &[u8]) -> Result<String, AcdpError> {
let vk = p256::ecdsa::VerifyingKey::from_sec1_bytes(sec1)
.map_err(|e| AcdpError::KeyResolution(format!("p256 SEC1 parse: {e}")))?;
let compressed = vk.to_encoded_point(true);
Ok(format!(
"sha256:{}",
hex::encode(Sha256::digest(compressed.as_bytes()))
))
}
pub fn fingerprint_did_key_material(
material: &crate::did::key::DidKeyMaterial,
) -> Result<String, AcdpError> {
match material {
crate::did::key::DidKeyMaterial::Ed25519(pk) => Ok(fingerprint_ed25519(pk)),
crate::did::key::DidKeyMaterial::EcdsaP256(sec1) => fingerprint_p256_sec1(sec1),
}
}
pub fn fingerprint_verification_method(
method: &crate::did::document::VerificationMethod,
algorithm: &str,
) -> Result<String, AcdpError> {
match algorithm {
"ed25519" => Ok(fingerprint_ed25519(&method.ed25519_public_key_bytes()?)),
"ecdsa-p256" => fingerprint_p256_sec1(&method.ecdsa_p256_public_key_sec1()?),
other => Err(AcdpError::UnsupportedAlgorithm(format!(
"cannot fingerprint keys for algorithm '{other}'"
))),
}
}
#[cfg(feature = "client")]
pub async fn fingerprint_for_key_id(
key_id: &str,
algorithm: &str,
resolver: &crate::did::web::WebResolver,
) -> Result<String, AcdpError> {
let (did_part, fragment) = key_id
.split_once('#')
.ok_or_else(|| AcdpError::KeyResolution(format!("key_id '{key_id}' has no '#fragment'")))?;
if did_part.starts_with("did:key:") {
let material = crate::did::key::resolve_did_key_url(key_id)?;
return fingerprint_did_key_material(&material);
}
let doc = resolver.resolve(did_part).await?;
let method = doc.find_by_fragment(fragment).ok_or_else(|| {
AcdpError::KeyResolution(format!(
"no verification method with fragment '#{fragment}'"
))
})?;
fingerprint_verification_method(method, algorithm)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fp_001_ed25519_zero_seed() {
let key = crate::crypto::SigningKey::from_bytes(&[0u8; 32]);
let fp = fingerprint_ed25519(&key.verifying_key_bytes());
assert_eq!(
fp,
"sha256:139e3940e64b5491722088d9a0d741628fc826e09475d341a780acde3c4b8070"
);
}
#[test]
fn p256_compressed_and_uncompressed_agree() {
let key = crate::crypto::sign::P256SigningKey::generate();
let uncompressed = key.verifying_key_sec1();
let vk = p256::ecdsa::VerifyingKey::from_sec1_bytes(&uncompressed).unwrap();
let compressed = vk.to_encoded_point(true);
let fp_a = fingerprint_p256_sec1(&uncompressed).unwrap();
let fp_b = fingerprint_p256_sec1(compressed.as_bytes()).unwrap();
assert_eq!(fp_a, fp_b);
}
#[test]
fn did_key_material_matches_raw() {
let key = crate::crypto::SigningKey::from_bytes(&[3u8; 32]);
let did = crate::did::key::did_key_from_ed25519(&key.verifying_key_bytes());
let material = crate::did::key::resolve_did_key(&did).unwrap();
assert_eq!(
fingerprint_did_key_material(&material).unwrap(),
fingerprint_ed25519(&key.verifying_key_bytes())
);
}
}