@agentunion/fastaun-browser 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.
- package/CHANGELOG.md +50 -26
- package/_packed_docs/CHANGELOG.md +50 -26
- 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
- package/_packed_docs/protocol/index.md +13 -3
- package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +39 -16
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +90 -39
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +20 -3
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +14300 -0
- package/dist/client.d.ts +179 -187
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3007 -4012
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +0 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -4
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +8 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +114 -1
- package/dist/crypto.js.map +1 -1
- package/dist/e2ee.d.ts +5 -210
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/e2ee.js +4 -1379
- package/dist/e2ee.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/namespaces/auth.d.ts.map +1 -1
- package/dist/namespaces/auth.js +5 -4
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/protected-headers.d.ts +14 -0
- package/dist/protected-headers.d.ts.map +1 -0
- package/dist/protected-headers.js +47 -0
- package/dist/protected-headers.js.map +1 -0
- package/dist/seq-tracker.d.ts +7 -2
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +31 -10
- package/dist/seq-tracker.js.map +1 -1
- package/dist/v2/crypto/aead.d.ts +26 -0
- package/dist/v2/crypto/aead.d.ts.map +1 -0
- package/dist/v2/crypto/aead.js +63 -0
- package/dist/v2/crypto/aead.js.map +1 -0
- package/dist/v2/crypto/canonical.d.ts +21 -0
- package/dist/v2/crypto/canonical.d.ts.map +1 -0
- package/dist/v2/crypto/canonical.js +111 -0
- package/dist/v2/crypto/canonical.js.map +1 -0
- package/dist/v2/crypto/dh-path.d.ts +21 -0
- package/dist/v2/crypto/dh-path.d.ts.map +1 -0
- package/dist/v2/crypto/dh-path.js +50 -0
- package/dist/v2/crypto/dh-path.js.map +1 -0
- package/dist/v2/crypto/ecdh.d.ts +19 -0
- package/dist/v2/crypto/ecdh.d.ts.map +1 -0
- package/dist/v2/crypto/ecdh.js +101 -0
- package/dist/v2/crypto/ecdh.js.map +1 -0
- package/dist/v2/crypto/ecdsa.d.ts +16 -0
- package/dist/v2/crypto/ecdsa.d.ts.map +1 -0
- package/dist/v2/crypto/ecdsa.js +52 -0
- package/dist/v2/crypto/ecdsa.js.map +1 -0
- package/dist/v2/crypto/hkdf.d.ts +21 -0
- package/dist/v2/crypto/hkdf.d.ts.map +1 -0
- package/dist/v2/crypto/hkdf.js +32 -0
- package/dist/v2/crypto/hkdf.js.map +1 -0
- package/dist/v2/crypto/index.d.ts +9 -0
- package/dist/v2/crypto/index.d.ts.map +1 -0
- package/dist/v2/crypto/index.js +8 -0
- package/dist/v2/crypto/index.js.map +1 -0
- package/dist/v2/crypto/recipients.d.ts +43 -0
- package/dist/v2/crypto/recipients.d.ts.map +1 -0
- package/dist/v2/crypto/recipients.js +188 -0
- package/dist/v2/crypto/recipients.js.map +1 -0
- package/dist/v2/e2ee/decrypt.d.ts +13 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -0
- package/dist/v2/e2ee/decrypt.js +176 -0
- package/dist/v2/e2ee/decrypt.js.map +1 -0
- package/dist/v2/e2ee/encrypt-group.d.ts +14 -0
- package/dist/v2/e2ee/encrypt-group.d.ts.map +1 -0
- package/dist/v2/e2ee/encrypt-group.js +196 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -0
- package/dist/v2/e2ee/encrypt-p2p.d.ts +15 -0
- package/dist/v2/e2ee/encrypt-p2p.d.ts.map +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js +240 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
- package/dist/v2/e2ee/index.d.ts +9 -0
- package/dist/v2/e2ee/index.d.ts.map +1 -0
- package/dist/v2/e2ee/index.js +9 -0
- package/dist/v2/e2ee/index.js.map +1 -0
- package/dist/v2/e2ee/metadata-auth.d.ts +9 -0
- package/dist/v2/e2ee/metadata-auth.d.ts.map +1 -0
- package/dist/v2/e2ee/metadata-auth.js +60 -0
- package/dist/v2/e2ee/metadata-auth.js.map +1 -0
- package/dist/v2/e2ee/types.d.ts +57 -0
- package/dist/v2/e2ee/types.d.ts.map +1 -0
- package/dist/v2/e2ee/types.js +7 -0
- package/dist/v2/e2ee/types.js.map +1 -0
- package/dist/v2/session/index.d.ts +4 -0
- package/dist/v2/session/index.d.ts.map +1 -0
- package/dist/v2/session/index.js +3 -0
- package/dist/v2/session/index.js.map +1 -0
- package/dist/v2/session/keystore.d.ts +48 -0
- package/dist/v2/session/keystore.d.ts.map +1 -0
- package/dist/v2/session/keystore.js +184 -0
- package/dist/v2/session/keystore.js.map +1 -0
- package/dist/v2/session/session.d.ts +98 -0
- package/dist/v2/session/session.d.ts.map +1 -0
- package/dist/v2/session/session.js +270 -0
- package/dist/v2/session/session.js.map +1 -0
- package/dist/v2/state/commitment.d.ts +10 -0
- package/dist/v2/state/commitment.d.ts.map +1 -0
- package/dist/v2/state/commitment.js +86 -0
- package/dist/v2/state/commitment.js.map +1 -0
- package/dist/v2/state/index.d.ts +2 -0
- package/dist/v2/state/index.d.ts.map +1 -0
- package/dist/v2/state/index.js +2 -0
- package/dist/v2/state/index.js.map +1 -0
- package/package.json +8 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/v2/e2ee/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,UAAU,EAAG,8BAAuC,CAAC;AAElE,aAAa;AACb,MAAM,WAAW,MAAM;IACrB,eAAe;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,gCAAgC;IAChC,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,eAAe;AACf,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,OAAO,EAAE,UAAU,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kBAAkB;AAClB,MAAM,WAAW,SAAS;IACxB,cAAc;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB;IACjB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,cAAc;AACd,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,yCAAyC;AACzC,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/v2/e2ee/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,8BAAuC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { V2KeyStore, V2_DB_NAME, V2_DB_VERSION, V2_STORE_NAME } from './keystore';
|
|
2
|
+
export { V2Session, PEER_KEY_CACHE_TTL_MS, DESTROY_DELAY_MS, RECENT_GENERATIONS, } from './session';
|
|
3
|
+
export type { CallFn, SenderIdentity, DecryptKeys } from './session';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/v2/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUN E2EE V2: 设备密钥存储(IndexedDB 持久化)
|
|
3
|
+
*
|
|
4
|
+
* 与 Python `aun_core.v2.keystore.V2KeyStore` 行为对齐:
|
|
5
|
+
* - 每个 `(device_id)` 持有一对 IK 与若干 SPK(按 spk_id 索引)
|
|
6
|
+
* - SPK 按 created_at 排序,支持 `loadCurrentSPK` / `listRecentSPKIds(n)`
|
|
7
|
+
*
|
|
8
|
+
* 浏览器目标:所有 IO 是 async(IndexedDB 事务)。
|
|
9
|
+
*/
|
|
10
|
+
export declare const V2_DB_NAME = "aun_v2";
|
|
11
|
+
export declare const V2_DB_VERSION = 1;
|
|
12
|
+
export declare const V2_STORE_NAME = "v2_device_keys";
|
|
13
|
+
export declare const V2_INDEX_BY_DEVICE_TYPE_CREATED = "by_device_type_created";
|
|
14
|
+
/**
|
|
15
|
+
* V2 设备密钥持久化存储。复合主键 [device_id, key_type, key_id]。
|
|
16
|
+
*
|
|
17
|
+
* 使用 IndexedDB;在浏览器内置 indexedDB 不可用时(如 jsdom)请提前安装
|
|
18
|
+
* `fake-indexeddb/auto` 作为 polyfill(见 `tests/setup.ts`)。
|
|
19
|
+
*/
|
|
20
|
+
export declare class V2KeyStore {
|
|
21
|
+
private readonly db;
|
|
22
|
+
constructor(db: IDBDatabase);
|
|
23
|
+
/** 打开/创建 V2 keystore 数据库。 */
|
|
24
|
+
static open(dbName?: string): Promise<V2KeyStore>;
|
|
25
|
+
/** 关闭数据库连接(测试或释放资源时使用)。 */
|
|
26
|
+
close(): void;
|
|
27
|
+
private store;
|
|
28
|
+
saveSPK(deviceId: string, spkId: string, priv: Uint8Array, pubDer: Uint8Array): Promise<void>;
|
|
29
|
+
loadSPK(deviceId: string, spkId: string): Promise<Uint8Array | null>;
|
|
30
|
+
/** 取最新 SPK(按 created_at DESC LIMIT 1)。 */
|
|
31
|
+
loadCurrentSPK(deviceId: string): Promise<{
|
|
32
|
+
spkId: string;
|
|
33
|
+
priv: Uint8Array;
|
|
34
|
+
pubDer: Uint8Array;
|
|
35
|
+
} | null>;
|
|
36
|
+
deleteSPK(deviceId: string, spkId: string): Promise<void>;
|
|
37
|
+
/** 返回最近 N 代 SPK 的 spk_id(按 created_at DESC)。 */
|
|
38
|
+
listRecentSPKIds(deviceId: string, n: number): Promise<string[]>;
|
|
39
|
+
listExpiredSPKIds(deviceId: string, maxAgeMs: number): Promise<string[]>;
|
|
40
|
+
saveIK(deviceId: string, priv: Uint8Array, pubDer: Uint8Array): Promise<void>;
|
|
41
|
+
loadIK(deviceId: string): Promise<{
|
|
42
|
+
priv: Uint8Array;
|
|
43
|
+
pubDer: Uint8Array;
|
|
44
|
+
} | null>;
|
|
45
|
+
/** 测试用:清空 store。 */
|
|
46
|
+
_clear(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=keystore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keystore.d.ts","sourceRoot":"","sources":["../../../src/v2/session/keystore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,UAAU,WAAW,CAAC;AACnC,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,aAAa,mBAAmB,CAAC;AAC9C,eAAO,MAAM,+BAA+B,2BAA2B,CAAC;AAaxE;;;;;GAKG;AACH,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAE5C,6BAA6B;WAChB,IAAI,CAAC,MAAM,GAAE,MAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBnE,2BAA2B;IAC3B,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,KAAK;IAMP,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC;IAgBV,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAY1E,0CAA0C;IACpC,cAAc,CAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;IAyBpE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,gDAAgD;IAC1C,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAuBhE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BxE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7E,MAAM,CACV,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;IAe3D,oBAAoB;IACd,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAO9B"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUN E2EE V2: 设备密钥存储(IndexedDB 持久化)
|
|
3
|
+
*
|
|
4
|
+
* 与 Python `aun_core.v2.keystore.V2KeyStore` 行为对齐:
|
|
5
|
+
* - 每个 `(device_id)` 持有一对 IK 与若干 SPK(按 spk_id 索引)
|
|
6
|
+
* - SPK 按 created_at 排序,支持 `loadCurrentSPK` / `listRecentSPKIds(n)`
|
|
7
|
+
*
|
|
8
|
+
* 浏览器目标:所有 IO 是 async(IndexedDB 事务)。
|
|
9
|
+
*/
|
|
10
|
+
export const V2_DB_NAME = 'aun_v2';
|
|
11
|
+
export const V2_DB_VERSION = 1;
|
|
12
|
+
export const V2_STORE_NAME = 'v2_device_keys';
|
|
13
|
+
export const V2_INDEX_BY_DEVICE_TYPE_CREATED = 'by_device_type_created';
|
|
14
|
+
/**
|
|
15
|
+
* V2 设备密钥持久化存储。复合主键 [device_id, key_type, key_id]。
|
|
16
|
+
*
|
|
17
|
+
* 使用 IndexedDB;在浏览器内置 indexedDB 不可用时(如 jsdom)请提前安装
|
|
18
|
+
* `fake-indexeddb/auto` 作为 polyfill(见 `tests/setup.ts`)。
|
|
19
|
+
*/
|
|
20
|
+
export class V2KeyStore {
|
|
21
|
+
db;
|
|
22
|
+
constructor(db) {
|
|
23
|
+
this.db = db;
|
|
24
|
+
}
|
|
25
|
+
/** 打开/创建 V2 keystore 数据库。 */
|
|
26
|
+
static async open(dbName = V2_DB_NAME) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const req = indexedDB.open(dbName, V2_DB_VERSION);
|
|
29
|
+
req.onupgradeneeded = () => {
|
|
30
|
+
const db = req.result;
|
|
31
|
+
if (!db.objectStoreNames.contains(V2_STORE_NAME)) {
|
|
32
|
+
const store = db.createObjectStore(V2_STORE_NAME, {
|
|
33
|
+
keyPath: ['device_id', 'key_type', 'key_id'],
|
|
34
|
+
});
|
|
35
|
+
store.createIndex(V2_INDEX_BY_DEVICE_TYPE_CREATED, ['device_id', 'key_type', 'created_at']);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
req.onsuccess = () => resolve(new V2KeyStore(req.result));
|
|
39
|
+
req.onerror = () => reject(req.error);
|
|
40
|
+
req.onblocked = () => reject(new Error('V2KeyStore.open: blocked'));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** 关闭数据库连接(测试或释放资源时使用)。 */
|
|
44
|
+
close() {
|
|
45
|
+
this.db.close();
|
|
46
|
+
}
|
|
47
|
+
store(mode) {
|
|
48
|
+
return this.db.transaction(V2_STORE_NAME, mode).objectStore(V2_STORE_NAME);
|
|
49
|
+
}
|
|
50
|
+
// ---------- SPK ----------
|
|
51
|
+
async saveSPK(deviceId, spkId, priv, pubDer) {
|
|
52
|
+
const record = {
|
|
53
|
+
device_id: deviceId,
|
|
54
|
+
key_type: 'spk',
|
|
55
|
+
key_id: spkId,
|
|
56
|
+
private_key: priv,
|
|
57
|
+
public_key: pubDer,
|
|
58
|
+
created_at: Date.now(),
|
|
59
|
+
};
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const req = this.store('readwrite').put(record);
|
|
62
|
+
req.onsuccess = () => resolve();
|
|
63
|
+
req.onerror = () => reject(req.error);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async loadSPK(deviceId, spkId) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const req = this.store('readonly').get([deviceId, 'spk', spkId]);
|
|
69
|
+
req.onsuccess = () => {
|
|
70
|
+
const r = req.result;
|
|
71
|
+
// 用 new Uint8Array(...) 拷贝,规避 fake-indexeddb / 跨 realm 实例对象
|
|
72
|
+
resolve(r ? new Uint8Array(r.private_key) : null);
|
|
73
|
+
};
|
|
74
|
+
req.onerror = () => reject(req.error);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** 取最新 SPK(按 created_at DESC LIMIT 1)。 */
|
|
78
|
+
async loadCurrentSPK(deviceId) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const idx = this.store('readonly').index(V2_INDEX_BY_DEVICE_TYPE_CREATED);
|
|
81
|
+
const range = IDBKeyRange.bound([deviceId, 'spk', -Infinity], [deviceId, 'spk', Infinity]);
|
|
82
|
+
const req = idx.openCursor(range, 'prev');
|
|
83
|
+
req.onsuccess = () => {
|
|
84
|
+
const cursor = req.result;
|
|
85
|
+
if (!cursor) {
|
|
86
|
+
resolve(null);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const r = cursor.value;
|
|
90
|
+
resolve({
|
|
91
|
+
spkId: r.key_id,
|
|
92
|
+
priv: new Uint8Array(r.private_key),
|
|
93
|
+
pubDer: new Uint8Array(r.public_key),
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
req.onerror = () => reject(req.error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async deleteSPK(deviceId, spkId) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const req = this.store('readwrite').delete([deviceId, 'spk', spkId]);
|
|
102
|
+
req.onsuccess = () => resolve();
|
|
103
|
+
req.onerror = () => reject(req.error);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** 返回最近 N 代 SPK 的 spk_id(按 created_at DESC)。 */
|
|
107
|
+
async listRecentSPKIds(deviceId, n) {
|
|
108
|
+
if (n <= 0)
|
|
109
|
+
return [];
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const idx = this.store('readonly').index(V2_INDEX_BY_DEVICE_TYPE_CREATED);
|
|
112
|
+
const range = IDBKeyRange.bound([deviceId, 'spk', -Infinity], [deviceId, 'spk', Infinity]);
|
|
113
|
+
const req = idx.openCursor(range, 'prev');
|
|
114
|
+
const out = [];
|
|
115
|
+
req.onsuccess = () => {
|
|
116
|
+
const cursor = req.result;
|
|
117
|
+
if (cursor && out.length < n) {
|
|
118
|
+
out.push(cursor.value.key_id);
|
|
119
|
+
cursor.continue();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
resolve(out);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
req.onerror = () => reject(req.error);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async listExpiredSPKIds(deviceId, maxAgeMs) {
|
|
129
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const idx = this.store('readonly').index(V2_INDEX_BY_DEVICE_TYPE_CREATED);
|
|
132
|
+
const range = IDBKeyRange.bound([deviceId, 'spk', -Infinity], [deviceId, 'spk', cutoff], false, true);
|
|
133
|
+
const req = idx.openCursor(range);
|
|
134
|
+
const out = [];
|
|
135
|
+
req.onsuccess = () => {
|
|
136
|
+
const cursor = req.result;
|
|
137
|
+
if (cursor) {
|
|
138
|
+
out.push(cursor.value.key_id);
|
|
139
|
+
cursor.continue();
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
resolve(out);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
req.onerror = () => reject(req.error);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// ---------- IK ----------
|
|
149
|
+
async saveIK(deviceId, priv, pubDer) {
|
|
150
|
+
const record = {
|
|
151
|
+
device_id: deviceId,
|
|
152
|
+
key_type: 'ik',
|
|
153
|
+
key_id: '',
|
|
154
|
+
private_key: priv,
|
|
155
|
+
public_key: pubDer,
|
|
156
|
+
created_at: Date.now(),
|
|
157
|
+
};
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const req = this.store('readwrite').put(record);
|
|
160
|
+
req.onsuccess = () => resolve();
|
|
161
|
+
req.onerror = () => reject(req.error);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async loadIK(deviceId) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const req = this.store('readonly').get([deviceId, 'ik', '']);
|
|
167
|
+
req.onsuccess = () => {
|
|
168
|
+
const r = req.result;
|
|
169
|
+
resolve(r ? { priv: new Uint8Array(r.private_key), pubDer: new Uint8Array(r.public_key) } : null);
|
|
170
|
+
};
|
|
171
|
+
req.onerror = () => reject(req.error);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// ---------- 测试用 ----------
|
|
175
|
+
/** 测试用:清空 store。 */
|
|
176
|
+
async _clear() {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
const req = this.store('readwrite').clear();
|
|
179
|
+
req.onsuccess = () => resolve();
|
|
180
|
+
req.onerror = () => reject(req.error);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=keystore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keystore.js","sourceRoot":"","sources":["../../../src/v2/session/keystore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUN E2EE V2 Session Manager(浏览器版,全 async)。
|
|
3
|
+
*
|
|
4
|
+
* 与 Python `aun_core.v2.session.V2Session` 行为对齐:
|
|
5
|
+
* - IK = AID 长期密钥(多设备共享 AID 身份),由构造函数注入,不独立生成
|
|
6
|
+
* - SPK 设备级 P-256 密钥对,IK 签名背书
|
|
7
|
+
* - SPK 销毁三重条件:
|
|
8
|
+
* contig_seq >= 该 SPK 引用的最大 seq
|
|
9
|
+
* && now - last_seen >= 7 小时
|
|
10
|
+
* && 不在最近 7 代保留窗口内
|
|
11
|
+
* - 对端 IK 公钥缓存 TTL 1 小时
|
|
12
|
+
* - SPK 注册:`callFn("message.v2.put_peer_pk", ...)`
|
|
13
|
+
*
|
|
14
|
+
* 浏览器目标:所有 store 调用均 `await`,签名走 noble(确定性 ECDSA)。
|
|
15
|
+
*/
|
|
16
|
+
import { V2KeyStore } from './keystore';
|
|
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
|
+
/** SPK 180 天硬上限。 */
|
|
24
|
+
export declare const HARD_LIMIT_MS: number;
|
|
25
|
+
/** 服务端 RPC 调用函数签名(与 Python `call_fn` 等价)。 */
|
|
26
|
+
export type CallFn = (method: string, params: Record<string, unknown>) => Promise<Record<string, unknown> | unknown>;
|
|
27
|
+
/** 加密所需的发送方身份。 */
|
|
28
|
+
export interface SenderIdentity {
|
|
29
|
+
aid: string;
|
|
30
|
+
deviceId: string;
|
|
31
|
+
ikPriv: Uint8Array;
|
|
32
|
+
ikPubDer: Uint8Array;
|
|
33
|
+
}
|
|
34
|
+
/** 解密所需的私钥。 */
|
|
35
|
+
export interface DecryptKeys {
|
|
36
|
+
ikPriv: Uint8Array;
|
|
37
|
+
spkPriv?: Uint8Array;
|
|
38
|
+
}
|
|
39
|
+
export declare class V2Session {
|
|
40
|
+
private readonly _store;
|
|
41
|
+
private readonly _deviceId;
|
|
42
|
+
private readonly _aid;
|
|
43
|
+
private readonly _ikPriv;
|
|
44
|
+
private readonly _ikPubDer;
|
|
45
|
+
private _spkId;
|
|
46
|
+
private _spkPriv?;
|
|
47
|
+
private _spkPubDer?;
|
|
48
|
+
private _registered;
|
|
49
|
+
private _peerIKCache;
|
|
50
|
+
private _verifiedSPKs;
|
|
51
|
+
private _oldSPKMaxSeq;
|
|
52
|
+
private _nowFn;
|
|
53
|
+
constructor(store: V2KeyStore, deviceId: string, aid: string, ikPriv: Uint8Array, ikPubDer: Uint8Array);
|
|
54
|
+
/** 测试用:注入虚拟时钟。 */
|
|
55
|
+
_setNowFn(fn: () => number): void;
|
|
56
|
+
get deviceId(): string;
|
|
57
|
+
get aid(): string;
|
|
58
|
+
get currentSpkId(): string;
|
|
59
|
+
get currentIkPubDer(): Uint8Array;
|
|
60
|
+
/** 暴露 store 以便测试(与 Python 同等私有约定)。 */
|
|
61
|
+
get _storeForTest(): V2KeyStore;
|
|
62
|
+
/** 加载或生成当前 SPK;IK 由构造函数注入,无需加载。 */
|
|
63
|
+
ensureKeys(): Promise<void>;
|
|
64
|
+
private _generateNewSPK;
|
|
65
|
+
/** SPK 由 AID 私钥(IK)签名背书并上报到 message.v2.put_peer_pk。 */
|
|
66
|
+
private _registerSPK;
|
|
67
|
+
/** 注册本设备 SPK 到服务端。IK = AID 长期密钥,无需注册。幂等。 */
|
|
68
|
+
ensureRegistered(callFn: CallFn): Promise<void>;
|
|
69
|
+
/** 返回加密所需的 sender 结构。 */
|
|
70
|
+
getSenderIdentity(): Promise<SenderIdentity>;
|
|
71
|
+
/**
|
|
72
|
+
* 返回解密所需的私钥。
|
|
73
|
+
* - spkId 空:1DH(仅 IK)
|
|
74
|
+
* - spkId == 当前 SPK:当前 spkPriv
|
|
75
|
+
* - 否则:从 store 加载旧 SPK 私钥(可能 undefined = 已销毁)
|
|
76
|
+
*/
|
|
77
|
+
getDecryptKeys(spkId: string | null | undefined): Promise<DecryptKeys>;
|
|
78
|
+
/** 判断 spkId 是否命中当前活跃 SPK。 */
|
|
79
|
+
isCurrentSPK(spkId: string | null | undefined): boolean;
|
|
80
|
+
/** 跟踪每个旧 SPK 引用的最大 seq(用于销毁判定)。 */
|
|
81
|
+
trackOldSPKMaxSeq(spkId: string, seq: number): void;
|
|
82
|
+
/**
|
|
83
|
+
* contig_seq 已覆盖、超过 7h 安全窗口、且不在最近 7 代保留窗口内时销毁。
|
|
84
|
+
*
|
|
85
|
+
* 销毁条件(全部满足才销毁):
|
|
86
|
+
* - contig_seq >= 该 SPK 引用的最大 seq
|
|
87
|
+
* - 自最后一次见到该 spk_id 引用 >= 7 小时
|
|
88
|
+
* - 不在最近 7 代 SPK 保留窗口内
|
|
89
|
+
*/
|
|
90
|
+
maybeDestroyOldSPKs(contigSeq: number): Promise<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
|
+
}
|
|
98
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/v2/session/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUN E2EE V2 Session Manager(浏览器版,全 async)。
|
|
3
|
+
*
|
|
4
|
+
* 与 Python `aun_core.v2.session.V2Session` 行为对齐:
|
|
5
|
+
* - IK = AID 长期密钥(多设备共享 AID 身份),由构造函数注入,不独立生成
|
|
6
|
+
* - SPK 设备级 P-256 密钥对,IK 签名背书
|
|
7
|
+
* - SPK 销毁三重条件:
|
|
8
|
+
* contig_seq >= 该 SPK 引用的最大 seq
|
|
9
|
+
* && now - last_seen >= 7 小时
|
|
10
|
+
* && 不在最近 7 代保留窗口内
|
|
11
|
+
* - 对端 IK 公钥缓存 TTL 1 小时
|
|
12
|
+
* - SPK 注册:`callFn("message.v2.put_peer_pk", ...)`
|
|
13
|
+
*
|
|
14
|
+
* 浏览器目标:所有 store 调用均 `await`,签名走 noble(确定性 ECDSA)。
|
|
15
|
+
*/
|
|
16
|
+
import { generateP256Keypair, ecdsaSignRaw } from '../crypto/index';
|
|
17
|
+
/** 对端 IK 公钥缓存 TTL(毫秒)。 */
|
|
18
|
+
export const PEER_KEY_CACHE_TTL_MS = 60 * 60 * 1000; // 1h
|
|
19
|
+
/** SPK 销毁安全窗口(毫秒)。 */
|
|
20
|
+
export const DESTROY_DELAY_MS = 7 * 60 * 60 * 1000; // 7h
|
|
21
|
+
/** SPK 销毁时保留的最近代数。 */
|
|
22
|
+
export const RECENT_GENERATIONS = 7;
|
|
23
|
+
/** SPK 180 天硬上限。 */
|
|
24
|
+
export const HARD_LIMIT_MS = 180 * 24 * 60 * 60 * 1000;
|
|
25
|
+
async function sha256Hex(data) {
|
|
26
|
+
const buf = await crypto.subtle.digest('SHA-256', data.slice().buffer);
|
|
27
|
+
const arr = new Uint8Array(buf);
|
|
28
|
+
let hex = '';
|
|
29
|
+
for (let i = 0; i < arr.length; i++)
|
|
30
|
+
hex += arr[i].toString(16).padStart(2, '0');
|
|
31
|
+
return hex;
|
|
32
|
+
}
|
|
33
|
+
function bytesToBase64(b) {
|
|
34
|
+
let bin = '';
|
|
35
|
+
for (let i = 0; i < b.length; i++)
|
|
36
|
+
bin += String.fromCharCode(b[i]);
|
|
37
|
+
return btoa(bin);
|
|
38
|
+
}
|
|
39
|
+
function concatBytes(...parts) {
|
|
40
|
+
let total = 0;
|
|
41
|
+
for (const p of parts)
|
|
42
|
+
total += p.length;
|
|
43
|
+
const out = new Uint8Array(total);
|
|
44
|
+
let offset = 0;
|
|
45
|
+
for (const p of parts) {
|
|
46
|
+
out.set(p, offset);
|
|
47
|
+
offset += p.length;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
export class V2Session {
|
|
52
|
+
_store;
|
|
53
|
+
_deviceId;
|
|
54
|
+
_aid;
|
|
55
|
+
_ikPriv;
|
|
56
|
+
_ikPubDer;
|
|
57
|
+
_spkId = '';
|
|
58
|
+
_spkPriv;
|
|
59
|
+
_spkPubDer;
|
|
60
|
+
_registered = false;
|
|
61
|
+
_peerIKCache = new Map();
|
|
62
|
+
_verifiedSPKs = new Set();
|
|
63
|
+
_oldSPKMaxSeq = new Map();
|
|
64
|
+
_nowFn = () => Date.now();
|
|
65
|
+
constructor(store, deviceId, aid, ikPriv, ikPubDer) {
|
|
66
|
+
if (!ikPriv || !ikPubDer) {
|
|
67
|
+
throw new Error('V2Session requires AID priv/pub keys (IK = AID identity)');
|
|
68
|
+
}
|
|
69
|
+
this._store = store;
|
|
70
|
+
this._deviceId = deviceId;
|
|
71
|
+
this._aid = aid;
|
|
72
|
+
this._ikPriv = ikPriv;
|
|
73
|
+
this._ikPubDer = ikPubDer;
|
|
74
|
+
}
|
|
75
|
+
/** 测试用:注入虚拟时钟。 */
|
|
76
|
+
_setNowFn(fn) {
|
|
77
|
+
this._nowFn = fn;
|
|
78
|
+
}
|
|
79
|
+
get deviceId() {
|
|
80
|
+
return this._deviceId;
|
|
81
|
+
}
|
|
82
|
+
get aid() {
|
|
83
|
+
return this._aid;
|
|
84
|
+
}
|
|
85
|
+
get currentSpkId() {
|
|
86
|
+
return this._spkId;
|
|
87
|
+
}
|
|
88
|
+
get currentIkPubDer() {
|
|
89
|
+
return this._ikPubDer;
|
|
90
|
+
}
|
|
91
|
+
/** 暴露 store 以便测试(与 Python 同等私有约定)。 */
|
|
92
|
+
get _storeForTest() {
|
|
93
|
+
return this._store;
|
|
94
|
+
}
|
|
95
|
+
/** 加载或生成当前 SPK;IK 由构造函数注入,无需加载。 */
|
|
96
|
+
async ensureKeys() {
|
|
97
|
+
if (this._spkPriv)
|
|
98
|
+
return;
|
|
99
|
+
const cur = await this._store.loadCurrentSPK(this._deviceId);
|
|
100
|
+
if (cur) {
|
|
101
|
+
this._spkId = cur.spkId;
|
|
102
|
+
this._spkPriv = cur.priv;
|
|
103
|
+
this._spkPubDer = cur.pubDer;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
await this._generateNewSPK();
|
|
107
|
+
}
|
|
108
|
+
async _generateNewSPK() {
|
|
109
|
+
const [priv, pubDer] = await generateP256Keypair();
|
|
110
|
+
const hex = await sha256Hex(pubDer);
|
|
111
|
+
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
112
|
+
await this._store.saveSPK(this._deviceId, spkId, priv, pubDer);
|
|
113
|
+
this._spkId = spkId;
|
|
114
|
+
this._spkPriv = priv;
|
|
115
|
+
this._spkPubDer = pubDer;
|
|
116
|
+
}
|
|
117
|
+
/** SPK 由 AID 私钥(IK)签名背书并上报到 message.v2.put_peer_pk。 */
|
|
118
|
+
async _registerSPK(callFn) {
|
|
119
|
+
const spkTimestamp = Math.floor(this._nowFn() / 1000);
|
|
120
|
+
const enc = new TextEncoder();
|
|
121
|
+
const signData = concatBytes(this._spkPubDer, enc.encode(this._spkId), enc.encode(String(spkTimestamp)));
|
|
122
|
+
const signature = await ecdsaSignRaw(this._ikPriv, signData);
|
|
123
|
+
await callFn('message.v2.put_peer_pk', {
|
|
124
|
+
peer_aid: this._aid,
|
|
125
|
+
key_source: 'peer_device_prekey',
|
|
126
|
+
spk_id: this._spkId,
|
|
127
|
+
spk_pk: bytesToBase64(this._spkPubDer),
|
|
128
|
+
spk_signature: bytesToBase64(signature),
|
|
129
|
+
spk_timestamp: spkTimestamp,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** 注册本设备 SPK 到服务端。IK = AID 长期密钥,无需注册。幂等。 */
|
|
133
|
+
async ensureRegistered(callFn) {
|
|
134
|
+
if (this._registered)
|
|
135
|
+
return;
|
|
136
|
+
await this.ensureKeys();
|
|
137
|
+
await this._registerSPK(callFn);
|
|
138
|
+
this._registered = true;
|
|
139
|
+
}
|
|
140
|
+
/** 返回加密所需的 sender 结构。 */
|
|
141
|
+
async getSenderIdentity() {
|
|
142
|
+
await this.ensureKeys();
|
|
143
|
+
return {
|
|
144
|
+
aid: this._aid,
|
|
145
|
+
deviceId: this._deviceId,
|
|
146
|
+
ikPriv: this._ikPriv,
|
|
147
|
+
ikPubDer: this._ikPubDer,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 返回解密所需的私钥。
|
|
152
|
+
* - spkId 空:1DH(仅 IK)
|
|
153
|
+
* - spkId == 当前 SPK:当前 spkPriv
|
|
154
|
+
* - 否则:从 store 加载旧 SPK 私钥(可能 undefined = 已销毁)
|
|
155
|
+
*/
|
|
156
|
+
async getDecryptKeys(spkId) {
|
|
157
|
+
await this.ensureKeys();
|
|
158
|
+
if (!spkId)
|
|
159
|
+
return { ikPriv: this._ikPriv };
|
|
160
|
+
if (spkId === this._spkId)
|
|
161
|
+
return { ikPriv: this._ikPriv, spkPriv: this._spkPriv };
|
|
162
|
+
const oldSPK = await this._store.loadSPK(this._deviceId, spkId);
|
|
163
|
+
if (!oldSPK)
|
|
164
|
+
return { ikPriv: this._ikPriv };
|
|
165
|
+
return { ikPriv: this._ikPriv, spkPriv: oldSPK };
|
|
166
|
+
}
|
|
167
|
+
/** 判断 spkId 是否命中当前活跃 SPK。 */
|
|
168
|
+
isCurrentSPK(spkId) {
|
|
169
|
+
return Boolean(spkId) && spkId === this._spkId;
|
|
170
|
+
}
|
|
171
|
+
/** 跟踪每个旧 SPK 引用的最大 seq(用于销毁判定)。 */
|
|
172
|
+
trackOldSPKMaxSeq(spkId, seq) {
|
|
173
|
+
if (!spkId || spkId === this._spkId)
|
|
174
|
+
return;
|
|
175
|
+
const cur = this._oldSPKMaxSeq.get(spkId);
|
|
176
|
+
const curSeq = cur ? cur.seq : 0;
|
|
177
|
+
if (seq > curSeq) {
|
|
178
|
+
this._oldSPKMaxSeq.set(spkId, { seq, lastSeenAt: this._nowFn() });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* contig_seq 已覆盖、超过 7h 安全窗口、且不在最近 7 代保留窗口内时销毁。
|
|
183
|
+
*
|
|
184
|
+
* 销毁条件(全部满足才销毁):
|
|
185
|
+
* - contig_seq >= 该 SPK 引用的最大 seq
|
|
186
|
+
* - 自最后一次见到该 spk_id 引用 >= 7 小时
|
|
187
|
+
* - 不在最近 7 代 SPK 保留窗口内
|
|
188
|
+
*/
|
|
189
|
+
async maybeDestroyOldSPKs(contigSeq) {
|
|
190
|
+
const destroyed = [];
|
|
191
|
+
const now = this._nowFn();
|
|
192
|
+
let recentKeep;
|
|
193
|
+
try {
|
|
194
|
+
recentKeep = new Set(await this._store.listRecentSPKIds(this._deviceId, RECENT_GENERATIONS));
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// 列表失败时退化为空集,保持销毁行为可继续推进
|
|
198
|
+
recentKeep = new Set();
|
|
199
|
+
}
|
|
200
|
+
for (const [spkId, info] of Array.from(this._oldSPKMaxSeq.entries())) {
|
|
201
|
+
if (spkId === this._spkId)
|
|
202
|
+
continue;
|
|
203
|
+
if (contigSeq < info.seq)
|
|
204
|
+
continue;
|
|
205
|
+
if (now - info.lastSeenAt < DESTROY_DELAY_MS)
|
|
206
|
+
continue;
|
|
207
|
+
if (recentKeep.has(spkId))
|
|
208
|
+
continue;
|
|
209
|
+
try {
|
|
210
|
+
await this._store.deleteSPK(this._deviceId, spkId);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
// 销毁失败时记录到控制台并跳过本轮,下次再重试
|
|
214
|
+
// eslint-disable-next-line no-console
|
|
215
|
+
console.warn('[V2Session] deleteSPK failed', { spkId, err });
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
this._oldSPKMaxSeq.delete(spkId);
|
|
219
|
+
destroyed.push(spkId);
|
|
220
|
+
}
|
|
221
|
+
// 180 天硬上限:无论是否被引用,超龄 SPK 强制销毁
|
|
222
|
+
try {
|
|
223
|
+
const expired = await this._store.listExpiredSPKIds(this._deviceId, HARD_LIMIT_MS);
|
|
224
|
+
for (const spkId of expired) {
|
|
225
|
+
if (spkId === this._spkId)
|
|
226
|
+
continue;
|
|
227
|
+
try {
|
|
228
|
+
await this._store.deleteSPK(this._deviceId, spkId);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
this._oldSPKMaxSeq.delete(spkId);
|
|
234
|
+
if (!destroyed.includes(spkId))
|
|
235
|
+
destroyed.push(spkId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch { /* ignore */ }
|
|
239
|
+
return destroyed;
|
|
240
|
+
}
|
|
241
|
+
/** 轮换 SPK:生成新 SPK 并上报到服务端。旧 SPK 保留本地用于解密。 */
|
|
242
|
+
async rotateSPK(callFn) {
|
|
243
|
+
await this._generateNewSPK();
|
|
244
|
+
await this._registerSPK(callFn);
|
|
245
|
+
}
|
|
246
|
+
cachePeerIK(peerAid, deviceId, ikPubDer) {
|
|
247
|
+
this._peerIKCache.set(`${peerAid}#${deviceId}`, {
|
|
248
|
+
pubDer: ikPubDer,
|
|
249
|
+
cachedAt: this._nowFn(),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
getPeerIK(peerAid, deviceId) {
|
|
253
|
+
const key = `${peerAid}#${deviceId}`;
|
|
254
|
+
const entry = this._peerIKCache.get(key);
|
|
255
|
+
if (!entry)
|
|
256
|
+
return null;
|
|
257
|
+
if (this._nowFn() - entry.cachedAt >= PEER_KEY_CACHE_TTL_MS) {
|
|
258
|
+
this._peerIKCache.delete(key);
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
return entry.pubDer;
|
|
262
|
+
}
|
|
263
|
+
isPeerSPKVerified(peerAid, deviceId, spkId) {
|
|
264
|
+
return this._verifiedSPKs.has(`${peerAid}#${deviceId}#${spkId}`);
|
|
265
|
+
}
|
|
266
|
+
markPeerSPKVerified(peerAid, deviceId, spkId) {
|
|
267
|
+
this._verifiedSPKs.add(`${peerAid}#${deviceId}#${spkId}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=session.js.map
|