CKD(Channel Key Derivation)的 JavaScript SDK。
对应 Go 版本:github.com/doc-war/ckd
对应 Rust 版本:github.com/doc-war/ckd-rust
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)起步。
npm install channel-key-derivationimport { CKD } from 'channel-key-derivation';
const secret = Buffer.from([
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
]);
const ckd = new CKD({
currentVersion: 1,
secretsByVersion: { 1: secret },
});
const channelId = Buffer.from('channel_12345');
const webhookKey = await ckd.derive(channelId, 'webhook');
const sseKey = await ckd.derive(channelId, 'sse');
const recovered = await ckd.parse(webhookKey, 'webhook');
console.log(recovered.toString()); // 'channel_12345'| 参数 | 类型 | 说明 |
|---|---|---|
config.currentVersion |
number |
当前生效的密钥版本号 [1, 15] |
config.secretsByVersion |
object |
多版本密钥映射表 { 1: secretBuffer } |
| 参数 | 类型 | 说明 |
|---|---|---|
resourceId |
Uint8Array |
内部资源标识 |
purpose |
string |
用途标识 [a-z0-9_-]{1,64} |
返回 Promise<string> base64url 编码的派生密钥。
| 参数 | 类型 | 说明 |
|---|---|---|
derivedKey |
string |
base64url 编码的派生密钥 |
purpose |
string |
用途标识 |
返回 Promise<Buffer> 原始资源 ID。
import { CkdError, ErrInvalidPurpose, ErrInvalidDerivedKey } from 'channel-key-derivation';
try {
await ckd.parse('!!!', 'webhook');
} catch (err) {
if (err === ErrInvalidDerivedKey) {
// 格式错误 / 认证失败 / 版本不存在
}
}const ckd = new CKD({
currentVersion: 2,
secretsByVersion: {
1: oldSecret, // 保留,用于解析旧 Key
2: newSecret, // 新派生使用此密钥
},
});
const newKey = await ckd.derive(channelId, 'webhook'); // 使用 v2
const recovered = await ckd.parse(oldKey, 'webhook'); // v1 仍可解析| # | 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 |
Go、Rust、JS 三端产出完全一致。
npm testMIT