@agentunion/fastaun 0.2.20 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +46 -23
  2. package/_packed_docs/CHANGELOG.md +46 -23
  3. package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +419 -0
  4. package/_packed_docs/protocol/index.md +13 -3
  5. package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
  6. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +39 -16
  7. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +90 -39
  8. package/dist/auth.js +24 -7
  9. package/dist/auth.js.map +1 -1
  10. package/dist/client.d.ts +115 -166
  11. package/dist/client.js +2006 -3427
  12. package/dist/client.js.map +1 -1
  13. package/dist/config.d.ts +0 -4
  14. package/dist/config.js +0 -4
  15. package/dist/config.js.map +1 -1
  16. package/dist/e2ee.d.ts +5 -139
  17. package/dist/e2ee.js +4 -1151
  18. package/dist/e2ee.js.map +1 -1
  19. package/dist/errors.d.ts +0 -8
  20. package/dist/errors.js +0 -14
  21. package/dist/errors.js.map +1 -1
  22. package/dist/index.d.ts +9 -5
  23. package/dist/index.js +6 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/keystore/aid-db.d.ts +12 -61
  26. package/dist/keystore/aid-db.js +41 -539
  27. package/dist/keystore/aid-db.js.map +1 -1
  28. package/dist/keystore/file.d.ts +5 -41
  29. package/dist/keystore/file.js +8 -64
  30. package/dist/keystore/file.js.map +1 -1
  31. package/dist/keystore/index.d.ts +1 -49
  32. package/dist/namespaces/auth.js +4 -2
  33. package/dist/namespaces/auth.js.map +1 -1
  34. package/dist/protected-headers.d.ts +13 -0
  35. package/dist/protected-headers.js +47 -0
  36. package/dist/protected-headers.js.map +1 -0
  37. package/dist/seq-tracker.d.ts +7 -2
  38. package/dist/seq-tracker.js +31 -10
  39. package/dist/seq-tracker.js.map +1 -1
  40. package/dist/types.d.ts +0 -56
  41. package/dist/v2/crypto/aead.d.ts +20 -0
  42. package/dist/v2/crypto/aead.js +59 -0
  43. package/dist/v2/crypto/aead.js.map +1 -0
  44. package/dist/v2/crypto/canonical.d.ts +20 -0
  45. package/dist/v2/crypto/canonical.js +119 -0
  46. package/dist/v2/crypto/canonical.js.map +1 -0
  47. package/dist/v2/crypto/dh-path.d.ts +39 -0
  48. package/dist/v2/crypto/dh-path.js +55 -0
  49. package/dist/v2/crypto/dh-path.js.map +1 -0
  50. package/dist/v2/crypto/ecdh.d.ts +29 -0
  51. package/dist/v2/crypto/ecdh.js +122 -0
  52. package/dist/v2/crypto/ecdh.js.map +1 -0
  53. package/dist/v2/crypto/ecdsa.d.ts +29 -0
  54. package/dist/v2/crypto/ecdsa.js +120 -0
  55. package/dist/v2/crypto/ecdsa.js.map +1 -0
  56. package/dist/v2/crypto/hkdf.d.ts +19 -0
  57. package/dist/v2/crypto/hkdf.js +47 -0
  58. package/dist/v2/crypto/hkdf.js.map +1 -0
  59. package/dist/v2/crypto/index.d.ts +8 -0
  60. package/dist/v2/crypto/index.js +8 -0
  61. package/dist/v2/crypto/index.js.map +1 -0
  62. package/dist/v2/crypto/recipients.d.ts +32 -0
  63. package/dist/v2/crypto/recipients.js +183 -0
  64. package/dist/v2/crypto/recipients.js.map +1 -0
  65. package/dist/v2/e2ee/decrypt.d.ts +29 -0
  66. package/dist/v2/e2ee/decrypt.js +159 -0
  67. package/dist/v2/e2ee/decrypt.js.map +1 -0
  68. package/dist/v2/e2ee/encrypt-group.d.ts +17 -0
  69. package/dist/v2/e2ee/encrypt-group.js +143 -0
  70. package/dist/v2/e2ee/encrypt-group.js.map +1 -0
  71. package/dist/v2/e2ee/encrypt-p2p.d.ts +31 -0
  72. package/dist/v2/e2ee/encrypt-p2p.js +190 -0
  73. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
  74. package/dist/v2/e2ee/index.d.ts +9 -0
  75. package/dist/v2/e2ee/index.js +9 -0
  76. package/dist/v2/e2ee/index.js.map +1 -0
  77. package/dist/v2/e2ee/metadata-auth.d.ts +15 -0
  78. package/dist/v2/e2ee/metadata-auth.js +50 -0
  79. package/dist/v2/e2ee/metadata-auth.js.map +1 -0
  80. package/dist/v2/e2ee/types.d.ts +57 -0
  81. package/dist/v2/e2ee/types.js +7 -0
  82. package/dist/v2/e2ee/types.js.map +1 -0
  83. package/dist/v2/session/index.d.ts +4 -0
  84. package/dist/v2/session/index.js +3 -0
  85. package/dist/v2/session/index.js.map +1 -0
  86. package/dist/v2/session/keystore.d.ts +41 -0
  87. package/dist/v2/session/keystore.js +103 -0
  88. package/dist/v2/session/keystore.js.map +1 -0
  89. package/dist/v2/session/session.d.ts +97 -0
  90. package/dist/v2/session/session.js +242 -0
  91. package/dist/v2/session/session.js.map +1 -0
  92. package/dist/v2/state/commitment.d.ts +58 -0
  93. package/dist/v2/state/commitment.js +85 -0
  94. package/dist/v2/state/commitment.js.map +1 -0
  95. package/dist/v2/state/index.d.ts +2 -0
  96. package/dist/v2/state/index.js +2 -0
  97. package/dist/v2/state/index.js.map +1 -0
  98. package/package.json +4 -3
@@ -0,0 +1,50 @@
1
+ /**
2
+ * AUN E2EE V2: protected_headers / context HMAC 签名
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p._with_metadata_auth` 对齐。
5
+ * 用 master_key 派生 HMAC key,对 metadata body 做签名,生成 `_auth` 字段。
6
+ */
7
+ import { createHmac } from 'node:crypto';
8
+ import { canonicalJson } from '../crypto/canonical.js';
9
+ export const METADATA_KEY_DOMAIN = Buffer.from('aun-envelope-metadata-key-v1', 'utf-8');
10
+ export const PROTECTED_HEADERS_DOMAIN = Buffer.from('aun-protected-headers-v1', 'utf-8');
11
+ export const PROTECTED_CONTEXT_DOMAIN = Buffer.from('aun-protected-context-v1', 'utf-8');
12
+ /**
13
+ * 计算 metadata HMAC 签名 tag。
14
+ *
15
+ * metadata_key = HMAC-SHA256(key, METADATA_KEY_DOMAIN)
16
+ * sign_input = domain + "\0" + canonical_json(body)
17
+ * tag = HMAC-SHA256(metadata_key, sign_input)
18
+ */
19
+ function metadataAuthTag(key, domain, body) {
20
+ const metadataKey = createHmac('sha256', key).update(METADATA_KEY_DOMAIN).digest();
21
+ const bodyBytes = canonicalJson(body);
22
+ return createHmac('sha256', metadataKey)
23
+ .update(domain)
24
+ .update(Buffer.from([0]))
25
+ .update(Buffer.from(bodyBytes))
26
+ .digest();
27
+ }
28
+ /**
29
+ * 为 metadata 对象添加 HMAC 签名(_auth 字段)。
30
+ *
31
+ * 如果 body(去除 _auth 后)为空,返回空对象。
32
+ */
33
+ export function withMetadataAuth(metadata, key, domain) {
34
+ const body = {};
35
+ for (const [k, v] of Object.entries(metadata)) {
36
+ if (k !== '_auth')
37
+ body[k] = v;
38
+ }
39
+ if (Object.keys(body).length === 0)
40
+ return {};
41
+ const tag = metadataAuthTag(key, domain, body);
42
+ return {
43
+ ...body,
44
+ _auth: {
45
+ alg: 'HMAC-SHA256',
46
+ tag: tag.toString('base64'),
47
+ },
48
+ };
49
+ }
50
+ //# sourceMappingURL=metadata-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-auth.js","sourceRoot":"","sources":["../../../src/v2/e2ee/metadata-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;AACxF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;AACzF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;AAEzF;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,GAAe,EAAE,MAAc,EAAE,IAA6B;IACrF,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC;IACnF,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;SACrC,MAAM,CAAC,MAAM,CAAC;SACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SAC9B,MAAM,EAAE,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAiC,EACjC,GAAe,EACf,MAAc;IAEd,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC,KAAK,OAAO;YAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,IAAI;QACP,KAAK,EAAE;YACL,GAAG,EAAE,aAAa;YAClB,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC5B;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎类型定义
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p` / `encrypt_group` / `decrypt` 对齐。
5
+ */
6
+ import type { ProtectedHeadersInput } from '../../protected-headers.js';
7
+ export declare const SUITE_NAME: "P256_HKDF_SHA256_AES_256_GCM";
8
+ /** 发送方身份。 */
9
+ export interface Sender {
10
+ /** 发送方 AID。 */
11
+ aid: string;
12
+ /** 发送方 device_id。 */
13
+ deviceId: string;
14
+ /** 32 字节 P-256 私钥标量(AID 主私钥)。 */
15
+ ikPriv: Uint8Array;
16
+ /** SPKI DER 编码的公钥(用于签名指纹计算)。 */
17
+ ikPubDer: Uint8Array;
18
+ }
19
+ /** 接收方目标设备。 */
20
+ export interface Target {
21
+ aid: string;
22
+ deviceId: string;
23
+ /** "peer" | "member" | "self_sync" | "audit" 等。 */
24
+ role: string;
25
+ /** "peer_device_prekey" | "group_device_prekey" | "aid_master"。 */
26
+ keySource: string;
27
+ /** 接收方 IK 公钥(DER SPKI)。 */
28
+ ikPkDer: Uint8Array;
29
+ /** 接收方 SPK 公钥(DER SPKI);undefined 表示走 1DH 路径。 */
30
+ spkPkDer?: Uint8Array;
31
+ /** SPK 标识;3DH 时为非空字符串,1DH 时为空串/未定义。 */
32
+ spkId?: string;
33
+ }
34
+ /** 接收方集合(P2P)。 */
35
+ export interface TargetSet {
36
+ /** 普通接收设备。 */
37
+ targets: Target[];
38
+ /** 监管方设备(可选)。 */
39
+ auditRecipients?: Target[];
40
+ }
41
+ /** 加密可选参数。 */
42
+ export interface EncryptOptions {
43
+ /** 消息 ID;不传则自动生成 `m-{uuid4 hex}`。 */
44
+ messageId?: string;
45
+ /** 时间戳(毫秒);不传则用 Date.now()。 */
46
+ timestamp?: number;
47
+ /** 端到端保护的信封元数据(HMAC 签名,不进 AAD)。 */
48
+ protectedHeaders?: ProtectedHeadersInput;
49
+ /** 端到端保护的上下文元数据(HMAC 签名,不进 AAD)。 */
50
+ context?: Record<string, unknown>;
51
+ }
52
+ /** Group AAD 中的 state_commitment 子结构。 */
53
+ export interface StateCommitmentAAD {
54
+ state_version: number;
55
+ state_hash: string;
56
+ state_chain: string;
57
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎类型定义
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p` / `encrypt_group` / `decrypt` 对齐。
5
+ */
6
+ export const SUITE_NAME = 'P256_HKDF_SHA256_AES_256_GCM';
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/v2/e2ee/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,CAAC,MAAM,UAAU,GAAG,8BAAuC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { V2KeyStore, V2_DEVICE_KEYS_DDL } from './keystore.js';
2
+ export type { SqliteLike } from './keystore.js';
3
+ export { V2Session, PEER_KEY_CACHE_TTL_MS, DESTROY_DELAY_MS, RECENT_GENERATIONS, } from './session.js';
4
+ export type { CallFn, SenderIdentity } from './session.js';
@@ -0,0 +1,3 @@
1
+ export { V2KeyStore, V2_DEVICE_KEYS_DDL } from './keystore.js';
2
+ export { V2Session, PEER_KEY_CACHE_TTL_MS, DESTROY_DELAY_MS, RECENT_GENERATIONS, } from './session.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * V2 E2EE 设备密钥存储。
3
+ *
4
+ * 每个 (aid, device_id) 持有一对 IK 和若干 SPK(按 spk_id 索引)。
5
+ * 表结构与 Python / Go SDK 完全一致(v2_device_keys)。
6
+ *
7
+ * 实现说明:
8
+ * - TS SDK 统一使用 Node 22+ 内置的 `node:sqlite`(与 `keystore/aid-db.ts` 保持一致)
9
+ * - 接受任意 sqlite-like 句柄(exec / prepare),便于复用 AIDDatabase 的连接
10
+ * - BLOB 字段返回 Uint8Array(node:sqlite 默认行为)
11
+ */
12
+ interface SqliteStatement {
13
+ run(...params: unknown[]): unknown;
14
+ get(...params: unknown[]): unknown;
15
+ all(...params: unknown[]): unknown;
16
+ }
17
+ export interface SqliteLike {
18
+ exec(sql: string): unknown;
19
+ prepare(sql: string): SqliteStatement;
20
+ }
21
+ export declare const V2_DEVICE_KEYS_DDL = "\nCREATE TABLE IF NOT EXISTS v2_device_keys (\n device_id TEXT NOT NULL,\n key_type TEXT NOT NULL,\n key_id TEXT NOT NULL DEFAULT '',\n private_key BLOB NOT NULL,\n public_key BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (device_id, key_type, key_id)\n)";
22
+ export declare class V2KeyStore {
23
+ private db;
24
+ constructor(db: SqliteLike);
25
+ saveIK(deviceId: string, priv: Uint8Array, pubDer: Uint8Array): void;
26
+ loadIK(deviceId: string): {
27
+ priv: Uint8Array;
28
+ pubDer: Uint8Array;
29
+ } | null;
30
+ saveSPK(deviceId: string, spkId: string, priv: Uint8Array, pubDer: Uint8Array): void;
31
+ loadSPK(deviceId: string, spkId: string): Uint8Array | null;
32
+ loadCurrentSPK(deviceId: string): {
33
+ spkId: string;
34
+ priv: Uint8Array;
35
+ pubDer: Uint8Array;
36
+ } | null;
37
+ deleteSPK(deviceId: string, spkId: string): void;
38
+ listRecentSPKIds(deviceId: string, n: number): string[];
39
+ listExpiredSPKIds(deviceId: string, maxAgeMs: number): string[];
40
+ }
41
+ export {};
@@ -0,0 +1,103 @@
1
+ /**
2
+ * V2 E2EE 设备密钥存储。
3
+ *
4
+ * 每个 (aid, device_id) 持有一对 IK 和若干 SPK(按 spk_id 索引)。
5
+ * 表结构与 Python / Go SDK 完全一致(v2_device_keys)。
6
+ *
7
+ * 实现说明:
8
+ * - TS SDK 统一使用 Node 22+ 内置的 `node:sqlite`(与 `keystore/aid-db.ts` 保持一致)
9
+ * - 接受任意 sqlite-like 句柄(exec / prepare),便于复用 AIDDatabase 的连接
10
+ * - BLOB 字段返回 Uint8Array(node:sqlite 默认行为)
11
+ */
12
+ export const V2_DEVICE_KEYS_DDL = `
13
+ CREATE TABLE IF NOT EXISTS v2_device_keys (
14
+ device_id TEXT NOT NULL,
15
+ key_type TEXT NOT NULL,
16
+ key_id TEXT NOT NULL DEFAULT '',
17
+ private_key BLOB NOT NULL,
18
+ public_key BLOB NOT NULL,
19
+ created_at INTEGER NOT NULL,
20
+ PRIMARY KEY (device_id, key_type, key_id)
21
+ )`;
22
+ function asBuffer(value) {
23
+ if (value instanceof Uint8Array)
24
+ return value;
25
+ if (Buffer.isBuffer(value))
26
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
27
+ if (Array.isArray(value))
28
+ return new Uint8Array(value);
29
+ throw new Error(`unexpected blob type: ${typeof value}`);
30
+ }
31
+ /** node:sqlite 不接受裸 Uint8Array,需要 Buffer 包装才会绑定为 BLOB。 */
32
+ function toSqliteBlob(value) {
33
+ return Buffer.isBuffer(value) ? value : Buffer.from(value.buffer, value.byteOffset, value.byteLength);
34
+ }
35
+ export class V2KeyStore {
36
+ db;
37
+ constructor(db) {
38
+ this.db = db;
39
+ this.db.exec(V2_DEVICE_KEYS_DDL);
40
+ }
41
+ saveIK(deviceId, priv, pubDer) {
42
+ this.db
43
+ .prepare(`INSERT OR REPLACE INTO v2_device_keys (device_id, key_type, key_id, private_key, public_key, created_at)
44
+ VALUES (?, 'ik', '', ?, ?, ?)`)
45
+ .run(deviceId, toSqliteBlob(priv), toSqliteBlob(pubDer), Date.now());
46
+ }
47
+ loadIK(deviceId) {
48
+ const row = this.db
49
+ .prepare(`SELECT private_key, public_key FROM v2_device_keys WHERE device_id=? AND key_type='ik' AND key_id=''`)
50
+ .get(deviceId);
51
+ if (!row)
52
+ return null;
53
+ return { priv: asBuffer(row.private_key), pubDer: asBuffer(row.public_key) };
54
+ }
55
+ saveSPK(deviceId, spkId, priv, pubDer) {
56
+ this.db
57
+ .prepare(`INSERT OR REPLACE INTO v2_device_keys (device_id, key_type, key_id, private_key, public_key, created_at)
58
+ VALUES (?, 'spk', ?, ?, ?, ?)`)
59
+ .run(deviceId, spkId, toSqliteBlob(priv), toSqliteBlob(pubDer), Date.now());
60
+ }
61
+ loadSPK(deviceId, spkId) {
62
+ const row = this.db
63
+ .prepare(`SELECT private_key FROM v2_device_keys WHERE device_id=? AND key_type='spk' AND key_id=?`)
64
+ .get(deviceId, spkId);
65
+ return row ? asBuffer(row.private_key) : null;
66
+ }
67
+ loadCurrentSPK(deviceId) {
68
+ const row = this.db
69
+ .prepare(`SELECT key_id, private_key, public_key FROM v2_device_keys
70
+ WHERE device_id=? AND key_type='spk' ORDER BY created_at DESC, key_id DESC LIMIT 1`)
71
+ .get(deviceId);
72
+ if (!row)
73
+ return null;
74
+ return {
75
+ spkId: row.key_id,
76
+ priv: asBuffer(row.private_key),
77
+ pubDer: asBuffer(row.public_key),
78
+ };
79
+ }
80
+ deleteSPK(deviceId, spkId) {
81
+ this.db
82
+ .prepare(`DELETE FROM v2_device_keys WHERE device_id=? AND key_type='spk' AND key_id=?`)
83
+ .run(deviceId, spkId);
84
+ }
85
+ listRecentSPKIds(deviceId, n) {
86
+ if (n <= 0)
87
+ return [];
88
+ const rows = this.db
89
+ .prepare(`SELECT key_id FROM v2_device_keys
90
+ WHERE device_id=? AND key_type='spk' ORDER BY created_at DESC, key_id DESC LIMIT ?`)
91
+ .all(deviceId, n);
92
+ return rows.map((r) => r.key_id);
93
+ }
94
+ listExpiredSPKIds(deviceId, maxAgeMs) {
95
+ const cutoff = Date.now() / 1000 - maxAgeMs / 1000;
96
+ const rows = this.db
97
+ .prepare(`SELECT key_id FROM v2_device_keys
98
+ WHERE device_id=? AND key_type='spk' AND created_at < ?`)
99
+ .all(deviceId, cutoff);
100
+ return rows.map((r) => r.key_id);
101
+ }
102
+ }
103
+ //# sourceMappingURL=keystore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keystore.js","sourceRoot":"","sources":["../../../src/v2/session/keystore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;EAShC,CAAC;AAEH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACpG,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,KAAiB,CAAC,CAAC;IACnE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,SAAS,YAAY,CAAC,KAAiB;IACrC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;AACxG,CAAC;AAED,MAAM,OAAO,UAAU;IACb,EAAE,CAAa;IAEvB,YAAY,EAAc;QACxB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,QAAgB,EAAE,IAAgB,EAAE,MAAkB;QAC3D,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;uCAC+B,CAChC;aACA,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,CAAC,QAAgB;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN,sGAAsG,CACvG;aACA,GAAG,CAAC,QAAQ,CAA8D,CAAC;QAC9E,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;IAC/E,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAgB,EAAE,MAAkB;QAC3E,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;uCAC+B,CAChC;aACA,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,KAAa;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN,0FAA0F,CAC3F;aACA,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAyC,CAAC;QAChE,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN;4FACoF,CACrF;aACA,GAAG,CAAC,QAAQ,CAA8E,CAAC;QAC9F,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO;YACL,KAAK,EAAE,GAAG,CAAC,MAAM;YACjB,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;YAC/B,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,KAAa;QACvC,IAAI,CAAC,EAAE;aACJ,OAAO,CAAC,8EAA8E,CAAC;aACvF,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,gBAAgB,CAAC,QAAgB,EAAE,CAAS;QAC1C,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN;4FACoF,CACrF;aACA,GAAG,CAAC,QAAQ,EAAE,CAAC,CAA8B,CAAC;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN;iEACyD,CAC1D;aACA,GAAG,CAAC,QAAQ,EAAE,MAAM,CAA8B,CAAC;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * V2 E2EE Session Manager。
3
+ *
4
+ * 管理本设备的 IK/SPK 生命周期、服务端注册、加解密密钥获取。
5
+ *
6
+ * 设计要点:
7
+ * - IK = AID 长期密钥(多设备共享 AID 身份),不独立生成
8
+ * - SPK 设备级 P-256 密钥对,IK 签名背书
9
+ * - SPK 销毁三重条件:
10
+ * - contig_seq >= 该 SPK 引用的最大 seq
11
+ * - 自最后一次见到该 spk_id >= 7 小时
12
+ * - 不在最近 7 代保留窗口内
13
+ * - 对端 IK 公钥缓存 TTL 1 小时
14
+ * - SPK 注册:调 callFn("message.v2.put_peer_pk", ...)
15
+ */
16
+ import { V2KeyStore } from './keystore.js';
17
+ /** 对端 IK 公钥缓存 TTL(毫秒)。 */
18
+ export declare const PEER_KEY_CACHE_TTL_MS: number;
19
+ /** SPK 销毁安全窗口(毫秒)。 */
20
+ export declare const DESTROY_DELAY_MS: number;
21
+ /** SPK 销毁时保留的最近代数。 */
22
+ export declare const RECENT_GENERATIONS = 7;
23
+ export declare const HARD_LIMIT_MS: number;
24
+ /** 服务端 RPC 调用函数签名(与 Python `call_fn` 等价)。 */
25
+ export type CallFn = (method: string, params: Record<string, unknown>) => Promise<Record<string, unknown> | unknown>;
26
+ /** 加密所需的发送方身份。 */
27
+ export interface SenderIdentity {
28
+ aid: string;
29
+ deviceId: string;
30
+ ikPriv: Uint8Array;
31
+ ikPubDer: Uint8Array;
32
+ }
33
+ export declare class V2Session {
34
+ private readonly _store;
35
+ private readonly _deviceId;
36
+ private readonly _aid;
37
+ private readonly _ikPriv;
38
+ private readonly _ikPubDer;
39
+ private _spkId;
40
+ private _spkPriv?;
41
+ private _spkPubDer?;
42
+ private _registered;
43
+ private _peerIKCache;
44
+ private _verifiedSPKs;
45
+ private _oldSPKMaxSeq;
46
+ private _nowFn;
47
+ constructor(store: V2KeyStore, deviceId: string, aid: string, ikPriv: Uint8Array, ikPubDer: Uint8Array);
48
+ /** 测试用:注入虚拟时钟。 */
49
+ _setNowFn(fn: () => number): void;
50
+ get deviceId(): string;
51
+ get aid(): string;
52
+ get currentSpkId(): string;
53
+ get currentIkPubDer(): Uint8Array;
54
+ /** 暴露 store 便于测试(与 Python 同等私有约定)。 */
55
+ get _storeForTest(): V2KeyStore;
56
+ /** 加载或生成当前 SPK;IK 由构造函数注入,无需加载。 */
57
+ ensureKeys(): void;
58
+ private _generateNewSPK;
59
+ /** 注册本设备 SPK 到服务端。IK = AID 长期密钥,无需注册。 */
60
+ ensureRegistered(callFn: CallFn): Promise<void>;
61
+ /** SPK 由 AID 私钥(IK)签名背书,并上报到 message.v2.put_peer_pk。 */
62
+ private _registerSPK;
63
+ /** 返回加密所需的 sender 结构。 */
64
+ getSenderIdentity(): SenderIdentity;
65
+ /**
66
+ * 返回解密所需的私钥。
67
+ * - spkId 空:1DH(仅 IK)
68
+ * - spkId == 当前 SPK:当前 spkPriv
69
+ * - 否则:从 store 加载旧 SPK 私钥(可能为 null = 已销毁)
70
+ */
71
+ getDecryptKeys(spkId: string | null | undefined): {
72
+ ikPriv: Uint8Array;
73
+ spkPriv?: Uint8Array;
74
+ };
75
+ /** 判断 spkId 是否命中当前活跃 SPK。 */
76
+ isCurrentSPK(spkId: string | null | undefined): boolean;
77
+ /** 跟踪每个旧 SPK 引用的最大 seq(用于销毁判定)。 */
78
+ trackOldSPKMaxSeq(spkId: string, seq: number): void;
79
+ /**
80
+ * contig_seq 已覆盖、超过 7h 安全窗口、且不在最近 7 代保留窗口内时销毁。
81
+ *
82
+ * 销毁条件(全部满足才销毁):
83
+ * - contig_seq >= 该 SPK 引用的最大 seq(接收方已消费完所有引用此 SPK 的消息)
84
+ * - 自最后一次见到该 spk_id 引用 >= 7 小时
85
+ * - 不在最近 7 代 SPK 保留窗口内
86
+ *
87
+ * 7h + 7 代双兜底:低频群即便 contig_seq 已覆盖也至少留 7 代或 7h,
88
+ * 避免发送方陈旧 bootstrap 缓存导致新消息加密失败。
89
+ */
90
+ maybeDestroyOldSPKs(contigSeq: number): string[];
91
+ /** 轮换 SPK:生成新 SPK 并上报到服务端。旧 SPK 保留本地用于解密。 */
92
+ rotateSPK(callFn: CallFn): Promise<void>;
93
+ cachePeerIK(peerAid: string, deviceId: string, ikPubDer: Uint8Array): void;
94
+ getPeerIK(peerAid: string, deviceId: string): Uint8Array | null;
95
+ isPeerSPKVerified(peerAid: string, deviceId: string, spkId: string): boolean;
96
+ markPeerSPKVerified(peerAid: string, deviceId: string, spkId: string): void;
97
+ }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * V2 E2EE Session Manager。
3
+ *
4
+ * 管理本设备的 IK/SPK 生命周期、服务端注册、加解密密钥获取。
5
+ *
6
+ * 设计要点:
7
+ * - IK = AID 长期密钥(多设备共享 AID 身份),不独立生成
8
+ * - SPK 设备级 P-256 密钥对,IK 签名背书
9
+ * - SPK 销毁三重条件:
10
+ * - contig_seq >= 该 SPK 引用的最大 seq
11
+ * - 自最后一次见到该 spk_id >= 7 小时
12
+ * - 不在最近 7 代保留窗口内
13
+ * - 对端 IK 公钥缓存 TTL 1 小时
14
+ * - SPK 注册:调 callFn("message.v2.put_peer_pk", ...)
15
+ */
16
+ import { createHash } from 'node:crypto';
17
+ import { generateP256Keypair } from '../crypto/ecdh.js';
18
+ import { ecdsaSignRaw } from '../crypto/ecdsa.js';
19
+ /** 对端 IK 公钥缓存 TTL(毫秒)。 */
20
+ export const PEER_KEY_CACHE_TTL_MS = 60 * 60 * 1000; // 1h
21
+ /** SPK 销毁安全窗口(毫秒)。 */
22
+ export const DESTROY_DELAY_MS = 7 * 60 * 60 * 1000; // 7h
23
+ /** SPK 销毁时保留的最近代数。 */
24
+ export const RECENT_GENERATIONS = 7;
25
+ export const HARD_LIMIT_MS = 180 * 24 * 60 * 60 * 1000; // 180 天
26
+ export class V2Session {
27
+ _store;
28
+ _deviceId;
29
+ _aid;
30
+ _ikPriv;
31
+ _ikPubDer;
32
+ _spkId = '';
33
+ _spkPriv;
34
+ _spkPubDer;
35
+ _registered = false;
36
+ _peerIKCache = new Map();
37
+ _verifiedSPKs = new Set();
38
+ _oldSPKMaxSeq = new Map();
39
+ _nowFn = () => Date.now();
40
+ constructor(store, deviceId, aid, ikPriv, ikPubDer) {
41
+ if (!ikPriv || !ikPubDer) {
42
+ throw new Error('V2Session requires AID priv/pub keys (IK = AID identity)');
43
+ }
44
+ this._store = store;
45
+ this._deviceId = deviceId;
46
+ this._aid = aid;
47
+ this._ikPriv = ikPriv;
48
+ this._ikPubDer = ikPubDer;
49
+ }
50
+ /** 测试用:注入虚拟时钟。 */
51
+ _setNowFn(fn) {
52
+ this._nowFn = fn;
53
+ }
54
+ get deviceId() {
55
+ return this._deviceId;
56
+ }
57
+ get aid() {
58
+ return this._aid;
59
+ }
60
+ get currentSpkId() {
61
+ return this._spkId;
62
+ }
63
+ get currentIkPubDer() {
64
+ return this._ikPubDer;
65
+ }
66
+ /** 暴露 store 便于测试(与 Python 同等私有约定)。 */
67
+ get _storeForTest() {
68
+ return this._store;
69
+ }
70
+ /** 加载或生成当前 SPK;IK 由构造函数注入,无需加载。 */
71
+ ensureKeys() {
72
+ if (this._spkPriv)
73
+ return;
74
+ const cur = this._store.loadCurrentSPK(this._deviceId);
75
+ if (cur) {
76
+ this._spkId = cur.spkId;
77
+ this._spkPriv = cur.priv;
78
+ this._spkPubDer = cur.pubDer;
79
+ return;
80
+ }
81
+ this._generateNewSPK();
82
+ }
83
+ _generateNewSPK() {
84
+ const [priv, pubDer] = generateP256Keypair();
85
+ const hashHex = createHash('sha256').update(pubDer).digest('hex');
86
+ const spkId = `sha256:${hashHex.substring(0, 16)}`;
87
+ this._store.saveSPK(this._deviceId, spkId, priv, pubDer);
88
+ this._spkId = spkId;
89
+ this._spkPriv = priv;
90
+ this._spkPubDer = pubDer;
91
+ }
92
+ /** 注册本设备 SPK 到服务端。IK = AID 长期密钥,无需注册。 */
93
+ async ensureRegistered(callFn) {
94
+ if (this._registered)
95
+ return;
96
+ this.ensureKeys();
97
+ await this._registerSPK(callFn);
98
+ this._registered = true;
99
+ }
100
+ /** SPK 由 AID 私钥(IK)签名背书,并上报到 message.v2.put_peer_pk。 */
101
+ async _registerSPK(callFn) {
102
+ const spkTimestamp = Math.floor(this._nowFn() / 1000);
103
+ const signData = Buffer.concat([
104
+ Buffer.from(this._spkPubDer),
105
+ Buffer.from(this._spkId, 'utf-8'),
106
+ Buffer.from(String(spkTimestamp), 'utf-8'),
107
+ ]);
108
+ const signature = ecdsaSignRaw(this._ikPriv, signData);
109
+ await callFn('message.v2.put_peer_pk', {
110
+ peer_aid: this._aid,
111
+ key_source: 'peer_device_prekey',
112
+ spk_id: this._spkId,
113
+ spk_pk: Buffer.from(this._spkPubDer).toString('base64'),
114
+ spk_signature: Buffer.from(signature).toString('base64'),
115
+ spk_timestamp: spkTimestamp,
116
+ });
117
+ }
118
+ /** 返回加密所需的 sender 结构。 */
119
+ getSenderIdentity() {
120
+ this.ensureKeys();
121
+ return {
122
+ aid: this._aid,
123
+ deviceId: this._deviceId,
124
+ ikPriv: this._ikPriv,
125
+ ikPubDer: this._ikPubDer,
126
+ };
127
+ }
128
+ /**
129
+ * 返回解密所需的私钥。
130
+ * - spkId 空:1DH(仅 IK)
131
+ * - spkId == 当前 SPK:当前 spkPriv
132
+ * - 否则:从 store 加载旧 SPK 私钥(可能为 null = 已销毁)
133
+ */
134
+ getDecryptKeys(spkId) {
135
+ this.ensureKeys();
136
+ if (!spkId)
137
+ return { ikPriv: this._ikPriv };
138
+ if (spkId === this._spkId)
139
+ return { ikPriv: this._ikPriv, spkPriv: this._spkPriv };
140
+ const oldSPK = this._store.loadSPK(this._deviceId, spkId);
141
+ if (!oldSPK)
142
+ return { ikPriv: this._ikPriv };
143
+ return { ikPriv: this._ikPriv, spkPriv: oldSPK };
144
+ }
145
+ /** 判断 spkId 是否命中当前活跃 SPK。 */
146
+ isCurrentSPK(spkId) {
147
+ return Boolean(spkId) && spkId === this._spkId;
148
+ }
149
+ /** 跟踪每个旧 SPK 引用的最大 seq(用于销毁判定)。 */
150
+ trackOldSPKMaxSeq(spkId, seq) {
151
+ if (!spkId || spkId === this._spkId)
152
+ return;
153
+ const cur = this._oldSPKMaxSeq.get(spkId);
154
+ const curSeq = cur ? cur.seq : 0;
155
+ if (seq > curSeq) {
156
+ this._oldSPKMaxSeq.set(spkId, { seq, lastSeenAt: this._nowFn() });
157
+ }
158
+ }
159
+ /**
160
+ * contig_seq 已覆盖、超过 7h 安全窗口、且不在最近 7 代保留窗口内时销毁。
161
+ *
162
+ * 销毁条件(全部满足才销毁):
163
+ * - contig_seq >= 该 SPK 引用的最大 seq(接收方已消费完所有引用此 SPK 的消息)
164
+ * - 自最后一次见到该 spk_id 引用 >= 7 小时
165
+ * - 不在最近 7 代 SPK 保留窗口内
166
+ *
167
+ * 7h + 7 代双兜底:低频群即便 contig_seq 已覆盖也至少留 7 代或 7h,
168
+ * 避免发送方陈旧 bootstrap 缓存导致新消息加密失败。
169
+ */
170
+ maybeDestroyOldSPKs(contigSeq) {
171
+ const destroyed = [];
172
+ const now = this._nowFn();
173
+ let recentKeep;
174
+ try {
175
+ recentKeep = new Set(this._store.listRecentSPKIds(this._deviceId, RECENT_GENERATIONS));
176
+ }
177
+ catch {
178
+ recentKeep = new Set();
179
+ }
180
+ for (const [spkId, info] of Array.from(this._oldSPKMaxSeq.entries())) {
181
+ if (spkId === this._spkId)
182
+ continue;
183
+ if (contigSeq < info.seq)
184
+ continue;
185
+ if (now - info.lastSeenAt < DESTROY_DELAY_MS)
186
+ continue;
187
+ if (recentKeep.has(spkId))
188
+ continue;
189
+ try {
190
+ this._store.deleteSPK(this._deviceId, spkId);
191
+ }
192
+ catch {
193
+ // 忽略 delete 失败,但 _oldSPKMaxSeq 仍清理避免重复尝试
194
+ }
195
+ this._oldSPKMaxSeq.delete(spkId);
196
+ destroyed.push(spkId);
197
+ }
198
+ // 180 天硬上限:无论是否被引用,超龄 SPK 强制销毁
199
+ try {
200
+ const expired = this._store.listExpiredSPKIds(this._deviceId, HARD_LIMIT_MS);
201
+ for (const spkId of expired) {
202
+ if (spkId === this._spkId)
203
+ continue;
204
+ try {
205
+ this._store.deleteSPK(this._deviceId, spkId);
206
+ }
207
+ catch { /* ignore */ }
208
+ this._oldSPKMaxSeq.delete(spkId);
209
+ if (!destroyed.includes(spkId))
210
+ destroyed.push(spkId);
211
+ }
212
+ }
213
+ catch { /* ignore */ }
214
+ return destroyed;
215
+ }
216
+ /** 轮换 SPK:生成新 SPK 并上报到服务端。旧 SPK 保留本地用于解密。 */
217
+ async rotateSPK(callFn) {
218
+ this._generateNewSPK();
219
+ await this._registerSPK(callFn);
220
+ }
221
+ cachePeerIK(peerAid, deviceId, ikPubDer) {
222
+ this._peerIKCache.set(`${peerAid}#${deviceId}`, { pubDer: ikPubDer, cachedAt: this._nowFn() });
223
+ }
224
+ getPeerIK(peerAid, deviceId) {
225
+ const key = `${peerAid}#${deviceId}`;
226
+ const entry = this._peerIKCache.get(key);
227
+ if (!entry)
228
+ return null;
229
+ if (this._nowFn() - entry.cachedAt >= PEER_KEY_CACHE_TTL_MS) {
230
+ this._peerIKCache.delete(key);
231
+ return null;
232
+ }
233
+ return entry.pubDer;
234
+ }
235
+ isPeerSPKVerified(peerAid, deviceId, spkId) {
236
+ return this._verifiedSPKs.has(`${peerAid}#${deviceId}#${spkId}`);
237
+ }
238
+ markPeerSPKVerified(peerAid, deviceId, spkId) {
239
+ this._verifiedSPKs.add(`${peerAid}#${deviceId}#${spkId}`);
240
+ }
241
+ }
242
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/v2/session/session.ts"],"names":[],"mappings":"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"}