kya_validator/
verifier.rs1use crate::resolver::{decode_multibase, resolve_key_for_method};
2use crate::types::{CryptoReport, KeyType, Manifest, ResolvedKey, ValidationConfig};
3use ed25519_dalek::{Signature as Ed25519Signature, VerifyingKey as Ed25519VerifyingKey};
4use k256::ecdsa::signature::Verifier;
5use k256::ecdsa::{Signature as SecpSignature, VerifyingKey as SecpVerifyingKey};
6
7#[cfg(not(target_arch = "wasm32"))]
8use serde_jcs::to_vec as to_jcs_vec;
9
10#[cfg(not(target_arch = "wasm32"))]
11fn canonicalize_manifest(manifest: &serde_json::Value) -> Result<Vec<u8>, String> {
12 let mut clone = manifest.clone();
13 if let serde_json::Value::Object(map) = &mut clone {
14 map.remove("proof");
15 }
16 to_jcs_vec(&clone).map_err(|err| format!("Canonicalization failed: {}", err))
17}
18
19#[cfg(target_arch = "wasm32")]
20fn canonicalize_manifest(manifest: &serde_json::Value) -> Result<Vec<u8>, String> {
21 let mut clone = manifest.clone();
23 if let serde_json::Value::Object(map) = &mut clone {
24 map.remove("proof");
25 }
26 serde_json::to_string(&clone)
28 .map(|s| s.into_bytes())
29 .map_err(|err| format!("Canonicalization failed: {}", err))
30}
31
32fn verify_ed25519(public_key: &[u8], signature: &[u8], message: &[u8]) -> Result<(), String> {
33 let key_bytes: [u8; 32] = public_key
34 .try_into()
35 .map_err(|_| "Ed25519 public key must be 32 bytes".to_string())?;
36 let key = Ed25519VerifyingKey::from_bytes(&key_bytes)
37 .map_err(|err| format!("Invalid Ed25519 key: {}", err))?;
38 let signature = Ed25519Signature::from_slice(signature)
39 .map_err(|err| format!("Invalid Ed25519 signature: {}", err))?;
40 key.verify_strict(message, &signature)
41 .map_err(|err| format!("Ed25519 verification failed: {}", err))
42}
43
44fn verify_secp256k1(public_key: &[u8], signature: &[u8], message: &[u8]) -> Result<(), String> {
45 let key = SecpVerifyingKey::from_sec1_bytes(public_key)
46 .map_err(|err| format!("Invalid Secp256k1 key: {}", err))?;
47 let signature = SecpSignature::from_slice(signature)
48 .or_else(|_| SecpSignature::from_der(signature))
49 .map_err(|err| format!("Invalid Secp256k1 signature: {}", err))?;
50 key.verify(message, &signature)
51 .map_err(|err| format!("Secp256k1 verification failed: {}", err))
52}
53
54fn verify_signature(
55 resolved: &ResolvedKey,
56 signature_bytes: &[u8],
57 message: &[u8],
58) -> Result<(), String> {
59 match resolved.key_type {
60 KeyType::Ed25519 => verify_ed25519(&resolved.public_key, signature_bytes, message),
61 KeyType::Secp256k1 => verify_secp256k1(&resolved.public_key, signature_bytes, message),
62 }
63}
64
65pub fn verify_manifest_proofs(
66 manifest: &Manifest,
67 raw_manifest: &serde_json::Value,
68 config: &ValidationConfig,
69) -> (bool, Vec<String>, CryptoReport) {
70 let mut errors = Vec::new();
71 let mut report = CryptoReport::ok();
72 let message = match canonicalize_manifest(raw_manifest) {
73 Ok(message) => message,
74 Err(err) => {
75 errors.push(err);
76 return (false, errors, report);
77 }
78 };
79
80 for proof in &manifest.proof {
81 let resolved = match resolve_key_for_method(&proof.verification_method, manifest) {
82 Ok(resolved) => resolved,
83 Err(err) => {
84 report
85 .missing_verification_methods
86 .push(proof.verification_method.clone());
87 errors.push(err);
88 continue;
89 }
90 };
91
92 if config.enforce_controller_match && resolved.controller != manifest.agent_id {
93 errors.push(format!(
94 "Verification method controller {} does not match agentId {}",
95 resolved.controller, manifest.agent_id
96 ));
97 }
98
99 report.resolved_keys.push(resolved.id.clone());
100
101 let signature_bytes = match decode_multibase(&proof.proof_value) {
102 Ok(bytes) => bytes,
103 Err(err) => {
104 report
105 .invalid_signatures
106 .push(proof.verification_method.clone());
107 errors.push(err);
108 continue;
109 }
110 };
111
112 if let Err(err) = verify_signature(&resolved, &signature_bytes, &message) {
113 report
114 .invalid_signatures
115 .push(proof.verification_method.clone());
116 errors.push(err);
117 }
118 }
119
120 (errors.is_empty(), errors, report)
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use ed25519_dalek::{Signer, SigningKey};
127 use rand::rngs::OsRng;
128 use rand::RngCore;
129
130 #[test]
131 fn verify_ed25519_signature_success() {
132 let mut secret_bytes = [0u8; 32];
133 OsRng.fill_bytes(&mut secret_bytes);
134 let signing_key = SigningKey::from_bytes(&secret_bytes);
135 let verifying_key = signing_key.verifying_key();
136 let public_key = verifying_key.to_bytes();
137 let signature = signing_key.sign(b"hello");
138 let signature_bytes = signature.to_bytes();
139 let result = verify_ed25519(&public_key, &signature_bytes, b"hello");
140 assert!(result.is_ok());
141 }
142}