轻量级、无状态、可逆的资源标识派生协议 。
CKD(Channel Key Derivation)是一种轻量级、无状态、可逆的资源标识派生协议。
它允许从一个内部资源标识派生出多个不同用途的外部标识,并支持在不依赖映射表的情况下恢复原始资源标识。
CKD 的核心目标:
- 一个内部资源 ID
- 派生多个用途隔离的外部 Key
- 无数据库映射
- 可逆恢复
- 不同用途之间不可互相推导
在数学上的具体表现为:
- C → A
- C → B
- A → C
- B → C
- A 无法推导 B
- B 无法推导 A
- 不需要存储 A→C 映射
- 不需要存储 B→C 映射
- 仅依赖平台密钥即可完成派生和反向解析
- 且无法使用已知的AB来反向破解密钥
采用CKD的O(1)复杂度,相比库索引,在数值上的影响是直观的:
| 指标 | 查库 | CKD |
|---|---|---|
| 单次路由延迟 | 1~10ms | 0.01~0.1ms |
| 单机QPS | 1万~5万 | 10万~50万 |
| 横向扩容 | 受DB限制 | 近线性 |
| Redis依赖 | 需要 | 不需要 |
| MySQL依赖 | 需要 | 不需要 |
对于极纯粹的高并发技术设施,将系统从“状态驱动路由”变成“计算驱动路由”,减少两次查库,并发能力提升通常不是 2 倍、3 倍,而是一个数量级(10x)起步。
在 Cargo.toml 中添加:
[dependencies]
ckd = { git = "https://github.com/doc-war/ckd-rust" }use ckd::{self, Ckd};
fn main() -> Result<(), ckd::Error> {
// 主密钥:32 字节高熵随机字节串(CSPRNG 生成)
let secret: Vec<u8> = (0..32).collect();
let ckd = ckd::new(ckd::Config {
current_version: 1,
secrets_by_version: [(1, secret)].into(),
})?;
// 派生:内部 ID → 外部 Key
let channel_id = b"channel_12345";
let webhook_key = ckd.derive(channel_id, "webhook")?;
let sse_key = ckd.derive(channel_id, "sse")?;
println!("webhook: {}", webhook_key);
// 恢复:外部 Key → 内部 ID
let recovered = ckd.parse(&webhook_key, "webhook")?;
assert_eq!(&recovered, channel_id);
Ok(())
}| 概念 | 说明 |
|---|---|
| 资源 ID (C) | 内部资源标识,如 channel_id, user_id |
| 用途 (P) | 用途标识,如 webhook, sse, api,仅允许 [a-z0-9_-],长度 1~64 |
| 派生密钥 (K) | 外部标识,base64url 编码,含版本号和认证标签 |
| 主密钥 (S) | 平台主密钥,推荐 256-bit CSPRNG 字节串 |
pub struct Config {
pub current_version: u8, // [1, 15]
pub secrets_by_version: HashMap<u8, Vec<u8>>,
}pub trait Ckd {
fn derive(&self, resource_id: &[u8], purpose: &str) -> Result<String, Error>;
fn parse(&self, derived_key: &str, purpose: &str) -> Result<Vec<u8>, Error>;
}pub enum Error {
InvalidPurpose, // 用途标识格式不合法
InvalidDerivedKey, // 派生密钥解析失败
}parse 对认证失败、格式错误、版本不存在等情况返回统一的 InvalidDerivedKey。
let ckd = ckd::new(ckd::Config {
current_version: 2,
secrets_by_version: [
(1, old_secret), // 旧密钥,用于解析旧 Key
(2, new_secret), // 新密钥,用于新派生
].into(),
})?;
// 新派生使用 v2
let new_key = ckd.derive(channel_id, "webhook")?;
// 旧 Key 仍可被解析(v1 密钥保留在表中)
let old_id = ckd.parse(&old_key, "webhook")?;不同 purpose 派生出的 Key 不可互相推导。
| 组件 | 算法 | 标准 |
|---|---|---|
| 密钥派生 | HKDF (SHA-256) | RFC 5869 |
| 确定性 AEAD | AES-256-SIV | RFC 5297 |
| 编码 | base64url (无 padding) | RFC 4648 §5 |
3 组固定向量在 src/internal/core.rs test_known_vectors 中验证跨语言一致性(Go / Rust)。
| # | Secret | Purpose | Ver | ResourceID | K |
|---|---|---|---|---|---|
| 1 | 0001...1e1f (32B) |
webhook |
1 | channel_12345 |
AXnjdNQal5XDP66STw0pQbOzRi3AdGmiX1Xi5ZxO |
| 2 | 0001...1e1f (32B) |
sse |
1 | user_67890 |
Aez5p0zdhSjxmsgy92MfMhiMlhAbRxlldWcl |
| 3 | ffee...1100 (32B) |
api_token |
2 | channel_12345 |
ApUFLCCTYol1B57b459DrXW8SiQaJLaJRUx246YY |
cargo testMIT