Skip to main content

platform/aid/credential/
verifier.rs

1//! AId Credential 验证器
2//!
3//! 负责验证 AId Credential 的 Ed25519 签名,并解码 IdentityClaims。
4//! 相比旧的 ECIES 解密方式,新方案使用非对称签名验证:
5//! - claims 是明文 proto bytes,由 AIS 的 Ed25519 私钥签名
6//! - 验证方只需持有对应的 verifying key(公钥)即可完成验证
7
8use super::error::AidError;
9use actr_protocol::prost::Message as ProstMessage;
10use actr_protocol::{AIdCredential, IdentityClaims};
11use ed25519_dalek::{Signature, Verifier, VerifyingKey};
12
13/// AId Credential 验证器
14///
15/// 提供纯函数式的 Ed25519 签名验证,无内部状态。
16/// verifying_key 由调用方提供,通常来自 AIS 注册响应中的 signing_pubkey 或本地密钥缓存。
17pub struct AIdCredentialVerifier;
18
19impl AIdCredentialVerifier {
20    /// 验证 AIdCredential 的 Ed25519 签名并返回 IdentityClaims
21    ///
22    /// # 验证流程
23    /// 1. 从 credential.signature 解析 64 字节 Ed25519 签名
24    /// 2. 用 verifying_key 对 credential.claims(明文 proto bytes)验签
25    /// 3. proto decode IdentityClaims
26    /// 4. 检查 expires_at 是否已过期
27    ///
28    /// # Arguments
29    /// * `credential` - 待验证的 AIdCredential,字段为 key_id / claims / signature
30    /// * `verifying_key` - Ed25519 公钥,由调用方提供(来自 AIS 注册响应或密钥缓存)
31    ///
32    /// # Returns
33    /// * `Ok(IdentityClaims)` - 验签通过且未过期,返回解码后的身份声明
34    /// * `Err(AidError)` - 签名无效、格式错误或 token 已过期
35    pub fn verify(
36        credential: &AIdCredential,
37        verifying_key: &VerifyingKey,
38    ) -> Result<IdentityClaims, AidError> {
39        crate::recording::debug!(
40            "验证 AIdCredential Ed25519 签名:key_id={}, claims_len={}",
41            credential.key_id,
42            credential.claims.len()
43        );
44
45        // 1. 解析 signature(必须恰好 64 bytes)
46        let sig_bytes: [u8; 64] = credential.signature[..].try_into().map_err(|_| {
47            AidError::InvalidSignature(format!(
48                "signature 长度无效:期望 64 bytes,实际 {} bytes",
49                credential.signature.len()
50            ))
51        })?;
52        let signature = Signature::from_bytes(&sig_bytes);
53
54        // 2. 验证签名(claims 是被签名的明文 bytes)
55        verifying_key
56            .verify(&credential.claims[..], &signature)
57            .map_err(|e| AidError::InvalidSignature(format!("Ed25519 签名验证失败:{e}")))?;
58
59        // 3. proto decode IdentityClaims
60        let claims = IdentityClaims::decode(&credential.claims[..])
61            .map_err(|e| AidError::DecodeFailure(format!("IdentityClaims proto 解码失败:{e}")))?;
62
63        // 4. 检查过期
64        let now = std::time::SystemTime::now()
65            .duration_since(std::time::UNIX_EPOCH)
66            .unwrap_or_default()
67            .as_secs();
68
69        if claims.expires_at <= now {
70            return Err(AidError::Expired);
71        }
72
73        crate::recording::debug!(
74            "AIdCredential 验证通过:key_id={}, actor_id={}, expires_at={}",
75            credential.key_id,
76            claims.actor_id,
77            claims.expires_at
78        );
79
80        Ok(claims)
81    }
82}