Skip to main content

platform/aid/credential/
validator.rs

1//! AId Credential 验证器
2//!
3//! 基于 Ed25519 签名验证 AId Credential:
4//! - claims 是明文 proto bytes,由 AIS 的 Ed25519 私钥签名
5//! - 验证方通过 `key_cache` 按 key_id 查找对应 VerifyingKey 完成验证
6
7use super::error::AidError;
8use super::verifier::AIdCredentialVerifier;
9use crate::aid::key_cache::KeyCache;
10use actr_protocol::{AIdCredential, IdentityClaims};
11use ed25519_dalek::VerifyingKey;
12use once_cell::sync::OnceCell;
13use std::sync::Arc;
14
15static KEY_CACHE: OnceCell<Arc<KeyCache>> = OnceCell::new();
16
17/// AId Token 验证器 - 静态方法验证 AIdCredential(Ed25519 签名)
18pub struct AIdCredentialValidator;
19
20impl AIdCredentialValidator {
21    /// 初始化全局 KeyCache 实例(通常在服务启动时调用一次)
22    ///
23    /// 可多次调用,首次调用有效;后续调用被忽略(幂等)。
24    pub async fn init(sqlite_path: &std::path::Path) -> Result<(), AidError> {
25        if KEY_CACHE.get().is_some() {
26            return Ok(());
27        }
28        let cache_db = sqlite_path.join("signaling_key_cache.db");
29        let cache = KeyCache::new(&cache_db).await?;
30        // Best-effort set; concurrent callers may race but both produce valid caches
31        let _ = KEY_CACHE.set(Arc::new(cache));
32        Ok(())
33    }
34
35    /// 获取全局 KeyCache 实例
36    fn get_cache() -> Result<Arc<KeyCache>, AidError> {
37        KEY_CACHE.get().cloned().ok_or(AidError::InvalidFormat)
38    }
39
40    /// 校验 AIdCredential:Ed25519 签名验证 + realm_id 检查
41    ///
42    /// # Returns
43    /// `Ok((IdentityClaims, false))` — Ed25519 模式无容忍期概念,第二个值恒为 `false`
44    pub async fn check(
45        credential: &AIdCredential,
46        realm_id: u32,
47    ) -> Result<(IdentityClaims, bool), AidError> {
48        let cache = Self::get_cache()?;
49        let key_id = credential.key_id;
50
51        let (verifying_key, _expires_at) = cache
52            .get_cached_key(key_id)
53            .await?
54            .ok_or(AidError::InvalidFormat)?; // key_id not found in cache
55
56        let claims = AIdCredentialVerifier::verify(credential, &verifying_key)?;
57
58        if claims.realm_id != realm_id {
59            return Err(AidError::InvalidFormat);
60        }
61
62        Ok((claims, false))
63    }
64
65    /// 返回 key_id 对应的 Ed25519 公钥字节(用于 GetSigningKeyRequest 响应)
66    pub async fn get_key_bytes(key_id: u32) -> Result<Option<Vec<u8>>, AidError> {
67        let cache = Self::get_cache()?;
68        let result = cache.get_cached_key(key_id).await?;
69        Ok(result.map(|(verifying_key, _expires_at)| verifying_key.as_bytes().to_vec()))
70    }
71
72    /// 将 Ed25519 verifying key 写入全局 KeyCache(由 AIS 签发器调用)
73    ///
74    /// AIS 在加载或刷新 signing key 后调用此方法,让 signaling 等服务能验证凭证。
75    /// 若 KeyCache 尚未初始化(`init` 未调用),此方法是无操作(non-fatal)。
76    pub async fn populate_key(
77        key_id: u32,
78        verifying_key: &VerifyingKey,
79        expires_at: u64,
80    ) -> Result<(), AidError> {
81        let cache = Self::get_cache()?;
82        cache.cache_key(key_id, verifying_key, expires_at).await
83    }
84
85    /// 将 Ed25519 verifying key 持久化写入 SQLite DB,无需 KeyCache 已初始化。
86    ///
87    /// 供 AIS 签发器在 `init` 调用前写入密钥,确保后续 `init` 能从 DB 读取到该密钥。
88    /// 若全局 KeyCache 已初始化,同时更新内存缓存。
89    pub async fn persist_key(
90        sqlite_path: &std::path::Path,
91        key_id: u32,
92        verifying_key: &VerifyingKey,
93        expires_at: u64,
94    ) -> Result<(), AidError> {
95        let cache_db = sqlite_path.join("signaling_key_cache.db");
96        let cache = KeyCache::new(&cache_db).await?;
97        cache.cache_key(key_id, verifying_key, expires_at).await?;
98
99        // 如果全局缓存已初始化,同步更新内存
100        if let Some(global_cache) = KEY_CACHE.get() {
101            let _ = global_cache
102                .cache_key(key_id, verifying_key, expires_at)
103                .await;
104        }
105
106        Ok(())
107    }
108
109    /// 同步校验(用于非 async 上下文,如 TURN 认证)
110    pub fn check_sync(
111        credential: &AIdCredential,
112        realm_id: u32,
113    ) -> Result<IdentityClaims, AidError> {
114        tokio::task::block_in_place(|| {
115            let handle =
116                tokio::runtime::Handle::try_current().map_err(|_| AidError::InvalidFormat)?;
117            let (claims, _) = handle.block_on(Self::check(credential, realm_id))?;
118            Ok(claims)
119        })
120    }
121}