@agentunion/fastaun-browser 0.2.13 → 0.2.15
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/dist/auth.d.ts +4 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +16 -0
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +23 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +626 -117
- package/dist/client.js.map +1 -1
- package/dist/e2ee-group.d.ts +13 -0
- package/dist/e2ee-group.d.ts.map +1 -1
- package/dist/e2ee-group.js +200 -17
- package/dist/e2ee-group.js.map +1 -1
- package/dist/e2ee.d.ts +23 -0
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/e2ee.js +270 -33
- package/dist/e2ee.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +10 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb.d.ts +19 -0
- package/dist/keystore/indexeddb.d.ts.map +1 -1
- package/dist/keystore/indexeddb.js +83 -0
- package/dist/keystore/indexeddb.js.map +1 -1
- package/dist/namespaces/auth.d.ts +19 -0
- package/dist/namespaces/auth.d.ts.map +1 -1
- package/dist/namespaces/auth.js +237 -0
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/namespaces/meta.d.ts +106 -0
- package/dist/namespaces/meta.d.ts.map +1 -0
- package/dist/namespaces/meta.js +498 -0
- package/dist/namespaces/meta.js.map +1 -0
- package/dist/seq-tracker.d.ts +3 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +38 -2
- package/dist/seq-tracker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
// ── MetaNamespace(元数据命名空间 — 浏览器完整实现)──────────────
|
|
2
|
+
// 与 TS SDK MetaNamespace 对齐,使用 WebCrypto + IndexedDB 替代 node:crypto + fs。
|
|
3
|
+
import { ValidationError } from '../errors.js';
|
|
4
|
+
import { pemToArrayBuffer, base64ToUint8, toBufferSource, } from '../crypto.js';
|
|
5
|
+
/** 全局信任根列表端点 */
|
|
6
|
+
const AUTHORITY_ENDPOINT = 'https://trust.aun.network/.well-known/aun/trust-roots.json';
|
|
7
|
+
/** 允许的最大时钟偏移(秒) */
|
|
8
|
+
const MAX_CLOCK_SKEW = 300;
|
|
9
|
+
/**
|
|
10
|
+
* Meta 命名空间 — 心跳、状态和信任根管理。
|
|
11
|
+
*
|
|
12
|
+
* 浏览器环境适配要点:
|
|
13
|
+
* - 签名验证使用 SubtleCrypto(异步)
|
|
14
|
+
* - 指纹计算使用 SubtleCrypto SHA-256
|
|
15
|
+
* - 持久化通过 keystore(IndexedDB)的可选方法
|
|
16
|
+
* - 无文件系统访问
|
|
17
|
+
*/
|
|
18
|
+
export class MetaNamespace {
|
|
19
|
+
_client;
|
|
20
|
+
constructor(client) {
|
|
21
|
+
this._client = client;
|
|
22
|
+
}
|
|
23
|
+
get _internal() {
|
|
24
|
+
return this._client;
|
|
25
|
+
}
|
|
26
|
+
// ── RPC 直通 ──────────────────────────────────────────────
|
|
27
|
+
async ping(params) {
|
|
28
|
+
return await this._client.call('meta.ping', params ?? {});
|
|
29
|
+
}
|
|
30
|
+
async status(params) {
|
|
31
|
+
return await this._client.call('meta.status', params ?? {});
|
|
32
|
+
}
|
|
33
|
+
async trustRoots(params) {
|
|
34
|
+
return await this._client.call('meta.trust_roots', params ?? {});
|
|
35
|
+
}
|
|
36
|
+
// ── 信任根下载 ────────────────────────────────────────────
|
|
37
|
+
async downloadTrustRoots(opts) {
|
|
38
|
+
const target = this._resolveTrustRootsUrl(opts?.url, opts?.issuer, opts?.gateway_url);
|
|
39
|
+
if (!target.toLowerCase().startsWith('https://') && !target.toLowerCase().startsWith('http://')) {
|
|
40
|
+
throw new ValidationError('trust roots url must be http(s)');
|
|
41
|
+
}
|
|
42
|
+
const timeoutMs = (opts?.timeout ?? 10) * 1000;
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch(target, {
|
|
47
|
+
headers: { Accept: 'application/json' },
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new ValidationError(`trust roots download failed: HTTP ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
const payload = await response.json();
|
|
54
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
55
|
+
throw new ValidationError('trust roots endpoint returned non-object JSON');
|
|
56
|
+
}
|
|
57
|
+
return payload;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async downloadIssuerRootCert(issuer, opts) {
|
|
64
|
+
const normalizedIssuer = MetaNamespace._validateIssuer(issuer);
|
|
65
|
+
const target = opts?.url?.trim() || this._issuerRootCertUrl(normalizedIssuer);
|
|
66
|
+
if (!target.toLowerCase().startsWith('https://') && !target.toLowerCase().startsWith('http://')) {
|
|
67
|
+
throw new ValidationError('issuer root certificate url must be http(s)');
|
|
68
|
+
}
|
|
69
|
+
const timeoutMs = (opts?.timeout ?? 10) * 1000;
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(target, {
|
|
74
|
+
headers: { Accept: 'application/x-pem-file,text/plain' },
|
|
75
|
+
signal: controller.signal,
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new ValidationError(`issuer root cert download failed: HTTP ${response.status}`);
|
|
79
|
+
}
|
|
80
|
+
const certPem = (await response.text()).trim();
|
|
81
|
+
MetaNamespace._validateCertPem(certPem, normalizedIssuer);
|
|
82
|
+
return certPem + '\n';
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ── 信任根验证 ────────────────────────────────────────────
|
|
89
|
+
async verifyTrustRoots(trustList, opts) {
|
|
90
|
+
if (!trustList || typeof trustList !== 'object') {
|
|
91
|
+
throw new ValidationError('trust roots list must be a JSON object');
|
|
92
|
+
}
|
|
93
|
+
const signature = String(trustList.authority_signature ?? '').trim();
|
|
94
|
+
if (!signature && !opts?.allow_unsigned) {
|
|
95
|
+
throw new ValidationError('trust roots list missing authority_signature');
|
|
96
|
+
}
|
|
97
|
+
MetaNamespace._validateListMetadata(trustList);
|
|
98
|
+
// 签名验证(WebCrypto 异步)
|
|
99
|
+
if (signature) {
|
|
100
|
+
const publicKey = await this._loadAuthorityPublicKey(opts?.authority_cert_pem, opts?.authority_public_key_pem, trustList);
|
|
101
|
+
const signedPayload = MetaNamespace._canonicalSignedPayload(trustList);
|
|
102
|
+
const sigBytes = MetaNamespace._decodeSignature(signature);
|
|
103
|
+
const ok = await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, publicKey, toBufferSource(sigBytes), toBufferSource(signedPayload));
|
|
104
|
+
if (!ok) {
|
|
105
|
+
throw new ValidationError('trust roots authority_signature verification failed');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const roots = MetaNamespace._extractRootEntries(trustList);
|
|
109
|
+
const imported = [];
|
|
110
|
+
const skipped = [];
|
|
111
|
+
for (const item of roots) {
|
|
112
|
+
const status = String(item.status ?? 'active').trim().toLowerCase();
|
|
113
|
+
const certPem = String(item.certificate ?? item.cert_pem ?? '').trim();
|
|
114
|
+
const rootId = String(item.id ?? item.agentid ?? '').trim();
|
|
115
|
+
if (status !== 'active') {
|
|
116
|
+
skipped.push({ id: rootId, reason: `status=${status}` });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
MetaNamespace._validateCertPem(certPem, rootId || 'root');
|
|
120
|
+
const fingerprint = await MetaNamespace._certFingerprint(certPem);
|
|
121
|
+
const expectedFp = MetaNamespace._normalizeFingerprint(item.fingerprint_sha256);
|
|
122
|
+
if (expectedFp.length !== 64) {
|
|
123
|
+
throw new ValidationError(`root certificate missing or invalid fingerprint_sha256: ${rootId || fingerprint}`);
|
|
124
|
+
}
|
|
125
|
+
if (expectedFp !== fingerprint) {
|
|
126
|
+
throw new ValidationError(`root certificate fingerprint mismatch: ${rootId || fingerprint}`);
|
|
127
|
+
}
|
|
128
|
+
imported.push({ id: rootId || fingerprint, cert_pem: certPem, fingerprint_sha256: fingerprint });
|
|
129
|
+
}
|
|
130
|
+
if (imported.length === 0) {
|
|
131
|
+
throw new ValidationError('trust roots list contains no active root certificates');
|
|
132
|
+
}
|
|
133
|
+
return { imported, skipped, count: imported.length };
|
|
134
|
+
}
|
|
135
|
+
// ── 信任根导入 ────────────────────────────────────────────
|
|
136
|
+
async importTrustRoots(trustList, opts) {
|
|
137
|
+
const verified = await this.verifyTrustRoots(trustList, opts);
|
|
138
|
+
await this._enforceMonotonicVersion(trustList);
|
|
139
|
+
const ks = this._internal._keystore;
|
|
140
|
+
// 持久化:优先使用 keystore 提供的 saveTrustRoots 方法
|
|
141
|
+
let bundlePath = '';
|
|
142
|
+
if (typeof ks.saveTrustRoots === 'function') {
|
|
143
|
+
bundlePath = await ks.saveTrustRoots(trustList, verified.imported);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.warn('[MetaNamespace] keystore 没有 saveTrustRoots 方法,跳过持久化');
|
|
147
|
+
}
|
|
148
|
+
const auth = this._internal._auth;
|
|
149
|
+
const reloaded = auth.reloadTrustedRoots?.() ?? auth.reload_trusted_roots?.() ?? 0;
|
|
150
|
+
return {
|
|
151
|
+
imported: verified.count,
|
|
152
|
+
skipped: verified.skipped,
|
|
153
|
+
bundle_path: bundlePath,
|
|
154
|
+
reloaded_roots: reloaded,
|
|
155
|
+
fingerprints: verified.imported.map((i) => i.fingerprint_sha256),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async refreshTrustRoots(opts) {
|
|
159
|
+
const sourceUrl = this._resolveTrustRootsUrl(opts?.url, opts?.issuer, opts?.gateway_url);
|
|
160
|
+
const trustList = await this.downloadTrustRoots({ url: sourceUrl, timeout: opts?.timeout });
|
|
161
|
+
const result = await this.importTrustRoots(trustList, {
|
|
162
|
+
authority_cert_pem: opts?.authority_cert_pem,
|
|
163
|
+
authority_public_key_pem: opts?.authority_public_key_pem,
|
|
164
|
+
allow_unsigned: opts?.allow_unsigned,
|
|
165
|
+
});
|
|
166
|
+
result.source_url = sourceUrl;
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
async updateIssuerRootCert(issuer, opts) {
|
|
170
|
+
const normalizedIssuer = MetaNamespace._validateIssuer(issuer);
|
|
171
|
+
const sourceUrl = opts?.url?.trim() || this._issuerRootCertUrl(normalizedIssuer);
|
|
172
|
+
let rootPem = opts?.cert_pem?.trim() ? opts.cert_pem.trim() + '\n' : '';
|
|
173
|
+
if (!rootPem) {
|
|
174
|
+
rootPem = await this.downloadIssuerRootCert(normalizedIssuer, { url: sourceUrl, timeout: opts?.timeout });
|
|
175
|
+
}
|
|
176
|
+
MetaNamespace._validateCertPem(rootPem, normalizedIssuer);
|
|
177
|
+
const fingerprint = await MetaNamespace._certFingerprint(rootPem);
|
|
178
|
+
// 获取信任列表:优先参数 → 本地 → 远程下载
|
|
179
|
+
let effectiveTrustList = opts?.trust_list ?? await this._loadLocalTrustList();
|
|
180
|
+
let trustSource = 'local';
|
|
181
|
+
if (!effectiveTrustList) {
|
|
182
|
+
effectiveTrustList = await this.downloadTrustRoots({ issuer: normalizedIssuer, timeout: opts?.timeout });
|
|
183
|
+
trustSource = this._issuerTrustRootUrl(normalizedIssuer);
|
|
184
|
+
}
|
|
185
|
+
const verified = await this.verifyTrustRoots(effectiveTrustList, {
|
|
186
|
+
authority_cert_pem: opts?.authority_cert_pem,
|
|
187
|
+
authority_public_key_pem: opts?.authority_public_key_pem,
|
|
188
|
+
allow_unsigned: opts?.allow_unsigned,
|
|
189
|
+
});
|
|
190
|
+
await this._enforceMonotonicVersion(effectiveTrustList);
|
|
191
|
+
// 交叉验证:下载的证书必须在信任列表中
|
|
192
|
+
const trustedFingerprints = new Set(verified.imported.map((i) => i.fingerprint_sha256));
|
|
193
|
+
if (!trustedFingerprints.has(fingerprint)) {
|
|
194
|
+
throw new ValidationError('issuer root certificate is not in trusted root list');
|
|
195
|
+
}
|
|
196
|
+
// 持久化
|
|
197
|
+
const ks = this._internal._keystore;
|
|
198
|
+
let certPath = '';
|
|
199
|
+
let bundlePath = '';
|
|
200
|
+
if (typeof ks.saveIssuerRootCert === 'function') {
|
|
201
|
+
[certPath, bundlePath] = await ks.saveIssuerRootCert(normalizedIssuer, rootPem, fingerprint);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.warn('[MetaNamespace] keystore 没有 saveIssuerRootCert 方法,跳过持久化');
|
|
205
|
+
}
|
|
206
|
+
const auth = this._internal._auth;
|
|
207
|
+
const reloaded = auth.reloadTrustedRoots?.() ?? auth.reload_trusted_roots?.() ?? 0;
|
|
208
|
+
return {
|
|
209
|
+
issuer: normalizedIssuer,
|
|
210
|
+
fingerprint_sha256: fingerprint,
|
|
211
|
+
cert_path: certPath,
|
|
212
|
+
bundle_path: bundlePath,
|
|
213
|
+
reloaded_roots: reloaded,
|
|
214
|
+
source_url: sourceUrl,
|
|
215
|
+
trust_source: trustSource,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// ── URL 解析 ──────────────────────────────────────────────
|
|
219
|
+
_resolveTrustRootsUrl(url, issuer, gatewayUrl) {
|
|
220
|
+
const target = (url ?? '').trim();
|
|
221
|
+
if (target)
|
|
222
|
+
return target;
|
|
223
|
+
if (issuer)
|
|
224
|
+
return this._issuerTrustRootUrl(issuer);
|
|
225
|
+
const gw = (gatewayUrl ?? this._internal._gatewayUrl ?? '').trim();
|
|
226
|
+
return gw ? this._gatewayTrustRootsUrl(gw) : AUTHORITY_ENDPOINT;
|
|
227
|
+
}
|
|
228
|
+
_issuerTrustRootUrl(issuer) {
|
|
229
|
+
const authority = this._pkiAuthority(issuer);
|
|
230
|
+
return `https://${authority}/trust-root.json`;
|
|
231
|
+
}
|
|
232
|
+
_issuerRootCertUrl(issuer) {
|
|
233
|
+
const authority = this._pkiAuthority(issuer);
|
|
234
|
+
return `https://${authority}/root.crt`;
|
|
235
|
+
}
|
|
236
|
+
_pkiAuthority(issuer) {
|
|
237
|
+
const normalized = MetaNamespace._validateIssuer(issuer);
|
|
238
|
+
const port = this._internal.configModel.discoveryPort;
|
|
239
|
+
const portSuffix = port && !normalized.includes(':') ? `:${port}` : '';
|
|
240
|
+
return `pki.${normalized}${portSuffix}`;
|
|
241
|
+
}
|
|
242
|
+
_gatewayTrustRootsUrl(gatewayUrl) {
|
|
243
|
+
let parsed;
|
|
244
|
+
try {
|
|
245
|
+
parsed = new URL(gatewayUrl);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
throw new ValidationError('gateway_url must include scheme and host');
|
|
249
|
+
}
|
|
250
|
+
if (!parsed.host) {
|
|
251
|
+
throw new ValidationError('gateway_url must include scheme and host');
|
|
252
|
+
}
|
|
253
|
+
const scheme = ['wss:', 'https:'].includes(parsed.protocol) ? 'https' : 'http';
|
|
254
|
+
return `${scheme}://${parsed.host}/pki/trust-roots.json`;
|
|
255
|
+
}
|
|
256
|
+
// ── 内部辅助 ──────────────────────────────────────────────
|
|
257
|
+
/** 校验 issuer 域名格式 */
|
|
258
|
+
static _validateIssuer(issuer) {
|
|
259
|
+
const value = (issuer ?? '').trim().toLowerCase();
|
|
260
|
+
if (!value || value.includes('://') || value.includes('/') || value.includes('\\') || value.startsWith('.')) {
|
|
261
|
+
throw new ValidationError('issuer must be a domain name');
|
|
262
|
+
}
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
/** 校验信任列表元数据(版本、时间戳) */
|
|
266
|
+
static _validateListMetadata(trustList) {
|
|
267
|
+
const version = trustList.version;
|
|
268
|
+
if (typeof version !== 'number' || !Number.isInteger(version) || version < 0) {
|
|
269
|
+
throw new ValidationError('trust roots list version must be a non-negative integer');
|
|
270
|
+
}
|
|
271
|
+
const issuedAt = MetaNamespace._parseTimestamp(trustList.issued_at, 'issued_at');
|
|
272
|
+
const nextUpdate = MetaNamespace._parseTimestamp(trustList.next_update, 'next_update');
|
|
273
|
+
if (nextUpdate < issuedAt) {
|
|
274
|
+
throw new ValidationError('trust roots list next_update must not be earlier than issued_at');
|
|
275
|
+
}
|
|
276
|
+
if (issuedAt > Date.now() + MAX_CLOCK_SKEW * 1000) {
|
|
277
|
+
throw new ValidationError('trust roots list issued_at is too far in the future');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
static _parseTimestamp(value, field) {
|
|
281
|
+
const text = String(value ?? '').trim();
|
|
282
|
+
if (!text)
|
|
283
|
+
throw new ValidationError(`trust roots list missing ${field}`);
|
|
284
|
+
const ts = new Date(text).getTime();
|
|
285
|
+
if (isNaN(ts))
|
|
286
|
+
throw new ValidationError(`trust roots list ${field} must be ISO-8601`);
|
|
287
|
+
return ts;
|
|
288
|
+
}
|
|
289
|
+
/** 构建用于签名验证的规范化 JSON 载荷 */
|
|
290
|
+
static _canonicalSignedPayload(trustList) {
|
|
291
|
+
const payload = { ...trustList };
|
|
292
|
+
delete payload.authority_signature;
|
|
293
|
+
const canonical = MetaNamespace._stableStringify(payload);
|
|
294
|
+
return new TextEncoder().encode(canonical);
|
|
295
|
+
}
|
|
296
|
+
/** 递归稳定排序 JSON 序列化 */
|
|
297
|
+
static _stableStringify(obj) {
|
|
298
|
+
if (obj === null || obj === undefined)
|
|
299
|
+
return JSON.stringify(obj);
|
|
300
|
+
if (typeof obj !== 'object')
|
|
301
|
+
return JSON.stringify(obj);
|
|
302
|
+
if (Array.isArray(obj)) {
|
|
303
|
+
return '[' + obj.map(v => MetaNamespace._stableStringify(v)).join(',') + ']';
|
|
304
|
+
}
|
|
305
|
+
const keys = Object.keys(obj).sort();
|
|
306
|
+
const pairs = keys.map(k => JSON.stringify(k) + ':' + MetaNamespace._stableStringify(obj[k]));
|
|
307
|
+
return '{' + pairs.join(',') + '}';
|
|
308
|
+
}
|
|
309
|
+
/** 解码 base64 编码的签名 */
|
|
310
|
+
static _decodeSignature(signature) {
|
|
311
|
+
let value = signature.trim();
|
|
312
|
+
if (value.startsWith('base64:')) {
|
|
313
|
+
value = value.slice(7);
|
|
314
|
+
}
|
|
315
|
+
// 处理 URL-safe base64 并补齐 padding
|
|
316
|
+
const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
317
|
+
const padding = '='.repeat((4 - (normalized.length % 4)) % 4);
|
|
318
|
+
return base64ToUint8(normalized + padding);
|
|
319
|
+
}
|
|
320
|
+
/** 提取信任列表中的根 CA 条目 */
|
|
321
|
+
static _extractRootEntries(trustList) {
|
|
322
|
+
const roots = trustList.root_cas;
|
|
323
|
+
if (Array.isArray(roots)) {
|
|
324
|
+
return roots.filter(item => item && typeof item === 'object');
|
|
325
|
+
}
|
|
326
|
+
const legacy = trustList.roots;
|
|
327
|
+
if (Array.isArray(legacy)) {
|
|
328
|
+
return legacy
|
|
329
|
+
.filter(item => item && typeof item === 'object')
|
|
330
|
+
.map((item) => ({
|
|
331
|
+
id: item.agentid ?? item.id ?? item.cert_sn,
|
|
332
|
+
certificate: item.cert_pem ?? item.certificate,
|
|
333
|
+
fingerprint_sha256: item.fingerprint_sha256,
|
|
334
|
+
status: item.status ?? 'active',
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
throw new ValidationError('trust roots list missing root_cas');
|
|
338
|
+
}
|
|
339
|
+
/** 规范化指纹字符串(去除前缀和分隔符) */
|
|
340
|
+
static _normalizeFingerprint(value) {
|
|
341
|
+
let text = String(value ?? '').trim().toLowerCase();
|
|
342
|
+
if (text.startsWith('sha256:'))
|
|
343
|
+
text = text.slice(7);
|
|
344
|
+
return text.replace(/[^0-9a-f]/g, '');
|
|
345
|
+
}
|
|
346
|
+
/** 计算证书 PEM 的 SHA-256 指纹(纯 hex,不含 sha256: 前缀) */
|
|
347
|
+
static async _certFingerprint(certPem) {
|
|
348
|
+
const der = pemToArrayBuffer(certPem);
|
|
349
|
+
const hash = await crypto.subtle.digest('SHA-256', der);
|
|
350
|
+
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
351
|
+
}
|
|
352
|
+
/** 校验 PEM 格式是否包含 BEGIN CERTIFICATE */
|
|
353
|
+
static _validateCertPem(certPem, rootId) {
|
|
354
|
+
if (!certPem.includes('BEGIN CERTIFICATE')) {
|
|
355
|
+
throw new ValidationError(`root certificate missing PEM data: ${rootId}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 加载权威公钥用于签名验证。
|
|
360
|
+
* 浏览器环境:从 PEM 提取 SPKI 并导入为 CryptoKey。
|
|
361
|
+
* 支持 P-256 和 P-384 曲线。
|
|
362
|
+
*/
|
|
363
|
+
async _loadAuthorityPublicKey(authorityCertPem, authorityPublicKeyPem, trustList) {
|
|
364
|
+
// 优先使用直接提供的公钥 PEM
|
|
365
|
+
if (authorityPublicKeyPem) {
|
|
366
|
+
return this._importPublicKeyPem(authorityPublicKeyPem);
|
|
367
|
+
}
|
|
368
|
+
// 其次使用证书 PEM 中的公钥
|
|
369
|
+
const candidateCert = authorityCertPem
|
|
370
|
+
|| String(trustList?.authority_cert_pem ?? '');
|
|
371
|
+
if (!candidateCert) {
|
|
372
|
+
throw new ValidationError('authority certificate/public key is required to verify trust roots');
|
|
373
|
+
}
|
|
374
|
+
return this._importCertPublicKey(candidateCert);
|
|
375
|
+
}
|
|
376
|
+
/** 从 SPKI PEM 导入公钥(尝试 P-384,降级 P-256) */
|
|
377
|
+
async _importPublicKeyPem(pem) {
|
|
378
|
+
const der = pemToArrayBuffer(pem);
|
|
379
|
+
// 尝试 P-384(信任根权威通常用 P-384)
|
|
380
|
+
for (const curve of ['P-384', 'P-256']) {
|
|
381
|
+
try {
|
|
382
|
+
return await crypto.subtle.importKey('spki', der, { name: 'ECDSA', namedCurve: curve }, true, ['verify']);
|
|
383
|
+
}
|
|
384
|
+
catch { /* 尝试下一条曲线 */ }
|
|
385
|
+
}
|
|
386
|
+
throw new ValidationError('invalid authority public key PEM');
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 从证书 PEM 提取公钥并导入。
|
|
390
|
+
* 使用简化 ASN.1 解析提取 SPKI 段。
|
|
391
|
+
*/
|
|
392
|
+
async _importCertPublicKey(certPem) {
|
|
393
|
+
if (!certPem.includes('BEGIN CERTIFICATE')) {
|
|
394
|
+
throw new ValidationError('invalid authority certificate PEM');
|
|
395
|
+
}
|
|
396
|
+
const certDer = new Uint8Array(pemToArrayBuffer(certPem));
|
|
397
|
+
const spki = this._extractSpkiFromDer(certDer);
|
|
398
|
+
if (!spki) {
|
|
399
|
+
throw new ValidationError('invalid authority certificate PEM');
|
|
400
|
+
}
|
|
401
|
+
// 尝试 P-384 优先(信任根常用),降级 P-256
|
|
402
|
+
for (const curve of ['P-384', 'P-256']) {
|
|
403
|
+
try {
|
|
404
|
+
return await crypto.subtle.importKey('spki', spki, { name: 'ECDSA', namedCurve: curve }, true, ['verify']);
|
|
405
|
+
}
|
|
406
|
+
catch { /* 尝试下一条曲线 */ }
|
|
407
|
+
}
|
|
408
|
+
throw new ValidationError('invalid authority certificate PEM');
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 从 X.509 DER 证书中提取 SubjectPublicKeyInfo。
|
|
412
|
+
* 搜索 EC 公钥 OID (1.2.840.10045.2.1)。
|
|
413
|
+
*/
|
|
414
|
+
_extractSpkiFromDer(certDer) {
|
|
415
|
+
// EC 公钥 OID: 06 07 2a 86 48 ce 3d 02 01
|
|
416
|
+
const ecOid = [0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
|
|
417
|
+
for (let i = 0; i < certDer.length - ecOid.length; i++) {
|
|
418
|
+
let match = true;
|
|
419
|
+
for (let j = 0; j < ecOid.length; j++) {
|
|
420
|
+
if (certDer[i + j] !== ecOid[j]) {
|
|
421
|
+
match = false;
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (!match)
|
|
426
|
+
continue;
|
|
427
|
+
// 往前回溯找 SEQUENCE (0x30) 起始
|
|
428
|
+
for (let back = 1; back <= 4; back++) {
|
|
429
|
+
const seqStart = i - back;
|
|
430
|
+
if (seqStart < 0)
|
|
431
|
+
continue;
|
|
432
|
+
if (certDer[seqStart] !== 0x30)
|
|
433
|
+
continue;
|
|
434
|
+
const seqLen = this._parseDerLength(certDer, seqStart + 1);
|
|
435
|
+
if (!seqLen)
|
|
436
|
+
continue;
|
|
437
|
+
const totalLen = 1 + seqLen.lenBytes + seqLen.value;
|
|
438
|
+
if (totalLen < 50 || totalLen > 200)
|
|
439
|
+
continue;
|
|
440
|
+
return certDer.slice(seqStart, seqStart + totalLen).buffer;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
/** 解析 DER 长度字段 */
|
|
446
|
+
_parseDerLength(data, offset) {
|
|
447
|
+
if (offset >= data.length)
|
|
448
|
+
return null;
|
|
449
|
+
const first = data[offset];
|
|
450
|
+
if (first < 0x80)
|
|
451
|
+
return { value: first, lenBytes: 1 };
|
|
452
|
+
const numBytes = first & 0x7f;
|
|
453
|
+
if (numBytes === 0 || numBytes > 4 || offset + 1 + numBytes > data.length)
|
|
454
|
+
return null;
|
|
455
|
+
let value = 0;
|
|
456
|
+
for (let i = 0; i < numBytes; i++) {
|
|
457
|
+
value = (value << 8) | data[offset + 1 + i];
|
|
458
|
+
}
|
|
459
|
+
return { value, lenBytes: 1 + numBytes };
|
|
460
|
+
}
|
|
461
|
+
/** 版本单调递增检查(通过 keystore 加载已有版本) */
|
|
462
|
+
async _enforceMonotonicVersion(trustList) {
|
|
463
|
+
const version = trustList.version;
|
|
464
|
+
if (typeof version !== 'number')
|
|
465
|
+
return;
|
|
466
|
+
const ks = this._internal._keystore;
|
|
467
|
+
if (typeof ks.loadTrustRoots !== 'function')
|
|
468
|
+
return;
|
|
469
|
+
try {
|
|
470
|
+
const current = await ks.loadTrustRoots();
|
|
471
|
+
const currentVersion = current?.version;
|
|
472
|
+
if (typeof currentVersion === 'number' && version < currentVersion) {
|
|
473
|
+
throw new ValidationError('trust roots list version rollback is not allowed');
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
if (e instanceof ValidationError)
|
|
478
|
+
throw e;
|
|
479
|
+
// keystore 读取失败等情况忽略
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/** 从 keystore 加载本地已存储的信任列表 */
|
|
483
|
+
async _loadLocalTrustList() {
|
|
484
|
+
const ks = this._internal._keystore;
|
|
485
|
+
if (typeof ks.loadTrustRoots !== 'function')
|
|
486
|
+
return null;
|
|
487
|
+
try {
|
|
488
|
+
const payload = await ks.loadTrustRoots();
|
|
489
|
+
if (!payload || typeof payload !== 'object')
|
|
490
|
+
return null;
|
|
491
|
+
return payload;
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/namespaces/meta.ts"],"names":[],"mappings":"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"}
|
package/dist/seq-tracker.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export declare class SeqTracker {
|
|
|
12
12
|
onMessageSeq(ns: string, seq: number): boolean;
|
|
13
13
|
/** pull 返回后更新 tracker 状态 */
|
|
14
14
|
onPullResult(ns: string, messages: JsonObject[]): void;
|
|
15
|
+
/** P1-07: 内存保护 — 当 receivedSeqs 超过上限时,
|
|
16
|
+
* 找到最小 seq 强制推进 contiguousSeq,释放已无意义的条目。 */
|
|
17
|
+
private _forceCompact;
|
|
15
18
|
private _tryAdvance;
|
|
16
19
|
private _shouldProbe;
|
|
17
20
|
/** S2: 服务端明确告知某区间无消息(tombstone)→ 将 gap 标记为 resolved */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seq-tracker.d.ts","sourceRoot":"","sources":["../src/seq-tracker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"seq-tracker.d.ts","sourceRoot":"","sources":["../src/seq-tracker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAkC7C,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAwC;IAEzD,OAAO,CAAC,IAAI;IASZ,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAIpC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAIjC;;wBAEoB;IACpB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IASlD,uCAAuC;IACvC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IA8D9C,4BAA4B;IAC5B,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI;IAoCtD;iDAC6C;IAC7C,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,YAAY;IASpB,uDAAuD;IACvD,0BAA0B,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAU9E,+BAA+B;IAC/B,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjC;;sDAEkD;IAClD,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAmBjD,mCAAmC;IACnC,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQrC,mCAAmC;IACnC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;CASlD"}
|
package/dist/seq-tracker.js
CHANGED
|
@@ -6,11 +6,15 @@ const BACKOFF_INTERVALS = [1, 3, 10, 30, 60]; // 秒
|
|
|
6
6
|
// S2: 删除"probeCount >= 5 强制 resolved"的硬限制;仅用作 backoff 索引上限。
|
|
7
7
|
// resolved 只应由"完整补齐"或服务端明确 tombstone 驱动。
|
|
8
8
|
const MAX_PROBE_COUNT = 5;
|
|
9
|
+
// P1-07: receivedSeqs 内存保护上限,超过时触发 forceCompact 释放内存
|
|
10
|
+
const RECEIVED_SEQS_LIMIT = 5000;
|
|
9
11
|
function gapKey(start, end) {
|
|
10
12
|
return `${start}:${end}`;
|
|
11
13
|
}
|
|
14
|
+
/** 统一使用 Date.now() 返回绝对时间戳(毫秒),
|
|
15
|
+
* 避免 performance.now()(相对时间)与 Date.now()(绝对时间)混用导致语义不一致。 */
|
|
12
16
|
function nowMs() {
|
|
13
|
-
return
|
|
17
|
+
return Date.now();
|
|
14
18
|
}
|
|
15
19
|
export class SeqTracker {
|
|
16
20
|
_trackers = new Map();
|
|
@@ -66,6 +70,10 @@ export class SeqTracker {
|
|
|
66
70
|
}
|
|
67
71
|
t.receivedSeqs.add(seq);
|
|
68
72
|
t.maxSeenSeq = Math.max(t.maxSeenSeq, seq);
|
|
73
|
+
// P1-07: 内存保护 — receivedSeqs 超限时强制推进 contiguousSeq 释放内存
|
|
74
|
+
if (t.receivedSeqs.size > RECEIVED_SEQS_LIMIT) {
|
|
75
|
+
this._forceCompact(t);
|
|
76
|
+
}
|
|
69
77
|
if (seq === t.contiguousSeq + 1) {
|
|
70
78
|
t.contiguousSeq = seq;
|
|
71
79
|
t.receivedSeqs.delete(seq);
|
|
@@ -100,7 +108,7 @@ export class SeqTracker {
|
|
|
100
108
|
const t = this._get(ns);
|
|
101
109
|
const pulledSeqs = new Set();
|
|
102
110
|
for (const m of messages) {
|
|
103
|
-
const s = m.seq;
|
|
111
|
+
const s = typeof m.seq === 'number' && m.seq > 0 ? m.seq : m.event_seq;
|
|
104
112
|
if (typeof s === 'number' && s > 0)
|
|
105
113
|
pulledSeqs.add(s);
|
|
106
114
|
}
|
|
@@ -133,6 +141,34 @@ export class SeqTracker {
|
|
|
133
141
|
}
|
|
134
142
|
this._tryAdvance(t);
|
|
135
143
|
}
|
|
144
|
+
/** P1-07: 内存保护 — 当 receivedSeqs 超过上限时,
|
|
145
|
+
* 找到最小 seq 强制推进 contiguousSeq,释放已无意义的条目。 */
|
|
146
|
+
_forceCompact(t) {
|
|
147
|
+
if (t.receivedSeqs.size === 0)
|
|
148
|
+
return;
|
|
149
|
+
let minSeq = Infinity;
|
|
150
|
+
for (const s of t.receivedSeqs) {
|
|
151
|
+
if (s < minSeq)
|
|
152
|
+
minSeq = s;
|
|
153
|
+
}
|
|
154
|
+
if (minSeq === Infinity)
|
|
155
|
+
return;
|
|
156
|
+
// 强制推进到 minSeq(跳过空洞)
|
|
157
|
+
t.contiguousSeq = minSeq;
|
|
158
|
+
t.receivedSeqs.delete(minSeq);
|
|
159
|
+
// 清理被跳过区间内的 pendingGaps
|
|
160
|
+
for (const [key, probe] of t.pendingGaps) {
|
|
161
|
+
if (probe.gapEnd <= t.contiguousSeq) {
|
|
162
|
+
t.pendingGaps.delete(key);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// 清理 receivedSeqs 中 <= contiguousSeq 的条目
|
|
166
|
+
for (const s of t.receivedSeqs) {
|
|
167
|
+
if (s <= t.contiguousSeq)
|
|
168
|
+
t.receivedSeqs.delete(s);
|
|
169
|
+
}
|
|
170
|
+
this._tryAdvance(t);
|
|
171
|
+
}
|
|
136
172
|
_tryAdvance(t) {
|
|
137
173
|
// 先清理已解决的空洞
|
|
138
174
|
let changed = true;
|