@agentunion/fastaun-browser 0.2.17 → 0.2.19

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 (58) hide show
  1. package/dist/auth.d.ts +12 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +370 -215
  4. package/dist/auth.js.map +1 -1
  5. package/dist/client.d.ts +24 -1
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +1307 -849
  8. package/dist/client.js.map +1 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +3 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/discovery.d.ts +3 -0
  13. package/dist/discovery.d.ts.map +1 -1
  14. package/dist/discovery.js +15 -1
  15. package/dist/discovery.js.map +1 -1
  16. package/dist/e2ee-group.d.ts +4 -0
  17. package/dist/e2ee-group.d.ts.map +1 -1
  18. package/dist/e2ee-group.js +327 -201
  19. package/dist/e2ee-group.js.map +1 -1
  20. package/dist/e2ee.d.ts +4 -0
  21. package/dist/e2ee.d.ts.map +1 -1
  22. package/dist/e2ee.js +196 -117
  23. package/dist/e2ee.js.map +1 -1
  24. package/dist/events.d.ts +3 -0
  25. package/dist/events.d.ts.map +1 -1
  26. package/dist/events.js +4 -1
  27. package/dist/events.js.map +1 -1
  28. package/dist/keystore/index.d.ts +11 -0
  29. package/dist/keystore/index.d.ts.map +1 -1
  30. package/dist/keystore/indexeddb.d.ts +38 -0
  31. package/dist/keystore/indexeddb.d.ts.map +1 -1
  32. package/dist/keystore/indexeddb.js +245 -98
  33. package/dist/keystore/indexeddb.js.map +1 -1
  34. package/dist/logger.d.ts +37 -0
  35. package/dist/logger.d.ts.map +1 -0
  36. package/dist/logger.js +112 -0
  37. package/dist/logger.js.map +1 -0
  38. package/dist/namespaces/auth.d.ts +13 -3
  39. package/dist/namespaces/auth.d.ts.map +1 -1
  40. package/dist/namespaces/auth.js +284 -106
  41. package/dist/namespaces/auth.js.map +1 -1
  42. package/dist/namespaces/custody.d.ts +3 -0
  43. package/dist/namespaces/custody.d.ts.map +1 -1
  44. package/dist/namespaces/custody.js +147 -75
  45. package/dist/namespaces/custody.js.map +1 -1
  46. package/dist/namespaces/meta.d.ts +3 -0
  47. package/dist/namespaces/meta.d.ts.map +1 -1
  48. package/dist/namespaces/meta.js +94 -43
  49. package/dist/namespaces/meta.js.map +1 -1
  50. package/dist/secret-store/indexeddb-store.d.ts +3 -0
  51. package/dist/secret-store/indexeddb-store.d.ts.map +1 -1
  52. package/dist/secret-store/indexeddb-store.js +57 -29
  53. package/dist/secret-store/indexeddb-store.js.map +1 -1
  54. package/dist/transport.d.ts +3 -0
  55. package/dist/transport.d.ts.map +1 -1
  56. package/dist/transport.js +74 -4
  57. package/dist/transport.js.map +1 -1
  58. package/package.json +37 -37
@@ -3,6 +3,10 @@
3
3
  import { E2EEError, E2EEGroupSecretMissingError, } from './errors.js';
4
4
  import { uint8ToBase64, base64ToUint8, pemToArrayBuffer, toBufferSource } from './crypto.js';
5
5
  import { SUITE, _concatBytes as concatBytes, _certificateSha256Fingerprint as certificateSha256Fingerprint, _ecdsaSignDer as ecdsaSignDer, _ecdsaVerifyDer as ecdsaVerifyDer, _hkdfDerive as hkdfDerive, _aesGcmEncrypt as aesGcmEncrypt, _aesGcmDecrypt as aesGcmDecrypt, _randomNonce as randomNonce, _uuidV4 as uuidV4, _importCertPublicKeyEcdsa as importCertPublicKeyEcdsa, _importPrivateKeyEcdsa as importPrivateKeyEcdsa, } from './e2ee.js';
6
+ const _noopLog = { error: () => { }, warn: () => { }, info: () => { }, debug: () => { } };
7
+ // 顶层函数共享的模块 logger(client 构造时通过 setModuleLogger 注入)
8
+ let _moduleLog = _noopLog;
9
+ export function setModuleLogger(log) { _moduleLog = log; }
6
10
  import { isJsonObject, } from './types.js';
7
11
  const _encoder = new TextEncoder();
8
12
  const _decoder = new TextDecoder();
@@ -472,7 +476,7 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
472
476
  return null;
473
477
  const groupSecret = groupSecrets.get(epoch);
474
478
  if (!groupSecret) {
475
- console.warn('[aun_core.e2ee-group] 群消息解密失败:找不到对应 epoch 的密钥');
479
+ _moduleLog.warn('[aun_core.e2ee-group] group message decrypt failed: epoch key not found');
476
480
  return null;
477
481
  }
478
482
  try {
@@ -488,18 +492,18 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
488
492
  aadFrom = (aad.from ?? '');
489
493
  // 外层路由字段与 AAD 绑定校验
490
494
  if (outerGroupId && groupId !== outerGroupId) {
491
- console.warn('[aun_core.e2ee-group] AAD group_id 与外层路由不匹配');
495
+ _moduleLog.warn('[aun_core.e2ee-group] AAD group_id mismatches outer route');
492
496
  return null;
493
497
  }
494
498
  if (aadFrom) {
495
499
  const outerFrom = (message.from ?? '');
496
500
  const outerSender = String(message.sender_aid ?? '');
497
501
  if (outerFrom && outerFrom !== aadFrom) {
498
- console.warn('[aun_core.e2ee-group] AAD from 与外层 from 不匹配');
502
+ _moduleLog.warn('[aun_core.e2ee-group] AAD from mismatches outer from');
499
503
  return null;
500
504
  }
501
505
  if (outerSender && outerSender !== aadFrom) {
502
- console.warn('[aun_core.e2ee-group] AAD sender_aid 与外层 sender_aid 不匹配');
506
+ _moduleLog.warn('[aun_core.e2ee-group] AAD sender_aid mismatches outer sender_aid');
503
507
  return null;
504
508
  }
505
509
  }
@@ -509,7 +513,7 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
509
513
  messageId = (message.message_id ?? '');
510
514
  }
511
515
  if (!groupId || !messageId) {
512
- console.warn('[aun_core.e2ee-group] 群消息解密失败:缺少 groupId messageId');
516
+ _moduleLog.warn('[aun_core.e2ee-group] group message decrypt failed: missing groupId or messageId');
513
517
  return null;
514
518
  }
515
519
  const msgKey = await deriveGroupMsgKey(groupSecret, groupId, messageId);
@@ -549,16 +553,16 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
549
553
  if (requireSignature) {
550
554
  // 零信任模式:必须有签名且有证书可验证
551
555
  if (!senderSigB64) {
552
- console.warn(`拒绝无发送方签名的群消息(require_signature=true): group=${groupId} from=${aadFrom}`);
556
+ _moduleLog.warn(`reject group msg without sender signature (require_signature=true): group=${groupId} from=${aadFrom}`);
553
557
  return null;
554
558
  }
555
559
  if (!senderCertPem) {
556
- console.warn(`拒绝群消息:有签名但无发送方证书可验证(零信任模式禁止跳过验签): group=${groupId} from=${aadFrom}`);
560
+ _moduleLog.warn(`拒绝群消息:有签名但无发送方证书可验证(零信任模式禁止跳过验签): group=${groupId} from=${aadFrom}`);
557
561
  return null;
558
562
  }
559
563
  const verified = await _verifySenderSigGroup(senderCertPem, senderSigB64, ciphertext, tag, aadBytes);
560
564
  if (!verified) {
561
- console.warn(`群消息发送方签名验证失败: group=${groupId} from=${aadFrom}`);
565
+ _moduleLog.warn(`group msg sender signature verify failed: group=${groupId} from=${aadFrom}`);
562
566
  return null;
563
567
  }
564
568
  if (isJsonObject(result.e2ee))
@@ -567,12 +571,12 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
567
571
  else if (senderCertPem) {
568
572
  // 非零信任模式但提供了证书:有证书时强制验签
569
573
  if (!senderSigB64) {
570
- console.warn(`拒绝无发送方签名的群消息: group=${groupId} from=${aadFrom}`);
574
+ _moduleLog.warn(`reject group msg without sender signature: group=${groupId} from=${aadFrom}`);
571
575
  return null;
572
576
  }
573
577
  const verified = await _verifySenderSigGroup(senderCertPem, senderSigB64, ciphertext, tag, aadBytes);
574
578
  if (!verified) {
575
- console.warn(`群消息发送方签名验证失败: group=${groupId} from=${aadFrom}`);
579
+ _moduleLog.warn(`group msg sender signature verify failed: group=${groupId} from=${aadFrom}`);
576
580
  return null;
577
581
  }
578
582
  if (isJsonObject(result.e2ee))
@@ -581,7 +585,7 @@ export async function decryptGroupMessage(message, groupSecrets, senderCertPem,
581
585
  return result;
582
586
  }
583
587
  catch (exc) {
584
- console.warn('[aun_core.e2ee-group] 群消息解密异常:', exc instanceof Error ? (exc.stack || exc.message) : String(exc));
588
+ _moduleLog.warn('[aun_core.e2ee-group] group message decrypt exception:', exc instanceof Error ? (exc.stack || exc.message) : String(exc));
585
589
  return null;
586
590
  }
587
591
  }
@@ -857,7 +861,7 @@ async function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitmen
857
861
  const rid = rotationId.trim();
858
862
  const rotator = rotatorAid.trim();
859
863
  if (rid && !chain) {
860
- console.warn(`[aun_core.e2ee-group] 拒绝缺少 epoch_chain 的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
864
+ _moduleLog.warn(`[aun_core.e2ee-group] reject missing epoch_chain new rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
861
865
  return { ok: false };
862
866
  }
863
867
  const current = await loadGroupSecret(keystore, aid, groupId);
@@ -868,7 +872,7 @@ async function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitmen
868
872
  return { ok: true };
869
873
  if (rid && chain && currentChain && currentChain !== chain) {
870
874
  if (!(currentPendingRotationId && currentPendingRotationId !== rid)) {
871
- console.warn(`[aun_core.e2ee-group] 拒绝同 epoch 分叉 chain source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
875
+ _moduleLog.warn(`[aun_core.e2ee-group] reject same epoch forked chain source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
872
876
  return { ok: false };
873
877
  }
874
878
  }
@@ -881,17 +885,17 @@ async function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitmen
881
885
  return { ok: true, unverified: true, reason: 'missing_prev_chain' };
882
886
  if (!rotator) {
883
887
  if (rid) {
884
- console.warn(`[aun_core.e2ee-group] 拒绝缺少 rotator_aid 的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
888
+ _moduleLog.warn(`[aun_core.e2ee-group] reject missing rotator_aid new rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
885
889
  return { ok: false };
886
890
  }
887
891
  return { ok: true, unverified: true, reason: 'missing_rotator_aid' };
888
892
  }
889
893
  if (!await verifyEpochChain(chain, prevChain, epoch, commitment, rotator)) {
890
894
  if (rid) {
891
- console.warn(`[aun_core.e2ee-group] 拒绝 epoch_chain 验证失败的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
895
+ _moduleLog.warn(`[aun_core.e2ee-group] reject epoch_chain verify failed new rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
892
896
  return { ok: false };
893
897
  }
894
- console.warn(`[aun_core.e2ee-group] epoch_chain 验证失败,按兼容档接收并标记未验证 source=${source} group=${groupId} epoch=${epoch}`);
898
+ _moduleLog.warn(`[aun_core.e2ee-group] epoch_chain verify failed, accept in compat mode and mark unverified source=${source} group=${groupId} epoch=${epoch}`);
895
899
  return { ok: true, unverified: true, reason: 'chain_mismatch_legacy' };
896
900
  }
897
901
  if (!rid)
@@ -975,12 +979,12 @@ export async function handleKeyDistribution(message, keystore, aid, initiatorCer
975
979
  const manifest = isJsonObject(payload.manifest) ? payload.manifest : undefined;
976
980
  if (initiatorCertPem) {
977
981
  if (!manifest) {
978
- console.warn(`拒绝无 manifest 的密钥分发: group=${groupId} epoch=${epoch}`);
982
+ _moduleLog.warn(`rejectno manifest keydistribute: group=${groupId} epoch=${epoch}`);
979
983
  return false;
980
984
  }
981
985
  const valid = await verifyMembershipManifest(manifest, initiatorCertPem);
982
986
  if (!valid) {
983
- console.warn(`group key distribution manifest 签名验证失败: group=${groupId} epoch=${epoch}`);
987
+ _moduleLog.warn(`group key distribution manifest signature verifyfailed: group=${groupId} epoch=${epoch}`);
984
988
  return false;
985
989
  }
986
990
  if (manifest.group_id !== groupId || manifest.epoch !== epoch)
@@ -1195,6 +1199,8 @@ export class GroupKeyRequestThrottle {
1195
1199
  * 所有密码学操作均为异步。
1196
1200
  */
1197
1201
  export class GroupE2EEManager {
1202
+ _log = _noopLog;
1203
+ setLogger(log) { this._log = log; }
1198
1204
  _identityFn;
1199
1205
  _keystoreRef;
1200
1206
  _replayGuard;
@@ -1223,69 +1229,96 @@ export class GroupE2EEManager {
1223
1229
  }
1224
1230
  /** 创建首个 epoch。返回 {epoch, commitment, distributions: [{to, payload}]}。 */
1225
1231
  async createEpoch(groupId, memberAids) {
1226
- const aid = this._currentAid();
1227
- const gs = generateGroupSecret();
1228
- const epoch = 1;
1229
- const commitment = await computeMembershipCommitment(memberAids, epoch, groupId, gs);
1230
- const epochChain = await computeEpochChain(null, epoch, commitment, aid);
1231
- await storeGroupSecret(this._keystoreRef, aid, groupId, epoch, gs, commitment, memberAids, epochChain);
1232
- const manifest = await this._signManifest(buildMembershipManifest(groupId, epoch, null, memberAids, { initiatorAid: aid }));
1233
- const distPayload = await buildKeyDistribution(groupId, epoch, gs, memberAids, aid, manifest, epochChain);
1234
- return {
1235
- epoch,
1236
- commitment,
1237
- distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1238
- };
1232
+ const tStart = Date.now();
1233
+ this._log.debug(`createEpoch enter: group_id=${groupId} members=${memberAids.length}`);
1234
+ try {
1235
+ const aid = this._currentAid();
1236
+ const gs = generateGroupSecret();
1237
+ const epoch = 1;
1238
+ const commitment = await computeMembershipCommitment(memberAids, epoch, groupId, gs);
1239
+ const epochChain = await computeEpochChain(null, epoch, commitment, aid);
1240
+ await storeGroupSecret(this._keystoreRef, aid, groupId, epoch, gs, commitment, memberAids, epochChain);
1241
+ const manifest = await this._signManifest(buildMembershipManifest(groupId, epoch, null, memberAids, { initiatorAid: aid }));
1242
+ const distPayload = await buildKeyDistribution(groupId, epoch, gs, memberAids, aid, manifest, epochChain);
1243
+ this._log.debug(`createEpoch exit: elapsed=${Date.now() - tStart}ms group_id=${groupId} epoch=${epoch}`);
1244
+ return {
1245
+ epoch,
1246
+ commitment,
1247
+ distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1248
+ };
1249
+ }
1250
+ catch (err) {
1251
+ this._log.debug(`createEpoch exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1252
+ throw err;
1253
+ }
1239
1254
  }
1240
1255
  /** 轮换 epoch(踢人/退出后调用) */
1241
1256
  async rotateEpoch(groupId, memberAids) {
1242
- const aid = this._currentAid();
1243
- const current = await loadGroupSecret(this._keystoreRef, aid, groupId);
1244
- const prevEpoch = current ? current.epoch : null;
1245
- const prevChain = current?.epoch_chain ?? null;
1246
- const newEpoch = (prevEpoch ?? 0) + 1;
1247
- const gs = generateGroupSecret();
1248
- const commitment = await computeMembershipCommitment(memberAids, newEpoch, groupId, gs);
1249
- const epochChain = await computeEpochChain(prevChain, newEpoch, commitment, aid);
1250
- const stored = await storeGroupSecret(this._keystoreRef, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain);
1251
- if (!stored) {
1252
- throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1257
+ const tStart = Date.now();
1258
+ this._log.debug(`rotateEpoch enter: group_id=${groupId} members=${memberAids.length}`);
1259
+ try {
1260
+ const aid = this._currentAid();
1261
+ const current = await loadGroupSecret(this._keystoreRef, aid, groupId);
1262
+ const prevEpoch = current ? current.epoch : null;
1263
+ const prevChain = current?.epoch_chain ?? null;
1264
+ const newEpoch = (prevEpoch ?? 0) + 1;
1265
+ const gs = generateGroupSecret();
1266
+ const commitment = await computeMembershipCommitment(memberAids, newEpoch, groupId, gs);
1267
+ const epochChain = await computeEpochChain(prevChain, newEpoch, commitment, aid);
1268
+ const stored = await storeGroupSecret(this._keystoreRef, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain);
1269
+ if (!stored) {
1270
+ throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1271
+ }
1272
+ const manifest = await this._signManifest(buildMembershipManifest(groupId, newEpoch, prevEpoch, memberAids, { initiatorAid: aid }));
1273
+ const distPayload = await buildKeyDistribution(groupId, newEpoch, gs, memberAids, aid, manifest, epochChain);
1274
+ this._log.debug(`rotateEpoch exit: elapsed=${Date.now() - tStart}ms group_id=${groupId} epoch=${newEpoch}`);
1275
+ return {
1276
+ epoch: newEpoch,
1277
+ commitment,
1278
+ distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1279
+ };
1280
+ }
1281
+ catch (err) {
1282
+ this._log.debug(`rotateEpoch exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1283
+ throw err;
1253
1284
  }
1254
- const manifest = await this._signManifest(buildMembershipManifest(groupId, newEpoch, prevEpoch, memberAids, { initiatorAid: aid }));
1255
- const distPayload = await buildKeyDistribution(groupId, newEpoch, gs, memberAids, aid, manifest, epochChain);
1256
- return {
1257
- epoch: newEpoch,
1258
- commitment,
1259
- distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1260
- };
1261
1285
  }
1262
1286
  /** 指定目标 epoch 号轮换(配合服务端 CAS 使用) */
1263
1287
  async rotateEpochTo(groupId, targetEpoch, memberAids, opts) {
1264
- const aid = this._currentAid();
1265
- const current = await loadGroupSecret(this._keystoreRef, aid, groupId, targetEpoch - 1)
1266
- ?? await loadGroupSecret(this._keystoreRef, aid, groupId);
1267
- let prevChain = current?.epoch_chain ?? null;
1268
- if (!prevChain && opts?.prevChainHint) {
1269
- prevChain = opts.prevChainHint;
1270
- }
1271
- const gs = generateGroupSecret();
1272
- const commitment = await computeMembershipCommitment(memberAids, targetEpoch, groupId, gs);
1273
- const epochChain = await computeEpochChain(prevChain, targetEpoch, commitment, aid);
1274
- const rotationId = opts?.rotationId ?? '';
1275
- const stored = await storeGroupSecret(this._keystoreRef, aid, groupId, targetEpoch, gs, commitment, memberAids, epochChain, rotationId);
1276
- if (!stored) {
1277
- throw new Error(`group ${groupId} epoch ${targetEpoch} secret already exists or is newer; abort distribution`);
1288
+ const tStart = Date.now();
1289
+ this._log.debug(`rotateEpochTo enter: group_id=${groupId} target_epoch=${targetEpoch} members=${memberAids.length} rotation_id=${opts?.rotationId ?? ''}`);
1290
+ try {
1291
+ const aid = this._currentAid();
1292
+ const current = await loadGroupSecret(this._keystoreRef, aid, groupId, targetEpoch - 1)
1293
+ ?? await loadGroupSecret(this._keystoreRef, aid, groupId);
1294
+ let prevChain = current?.epoch_chain ?? null;
1295
+ if (!prevChain && opts?.prevChainHint) {
1296
+ prevChain = opts.prevChainHint;
1297
+ }
1298
+ const gs = generateGroupSecret();
1299
+ const commitment = await computeMembershipCommitment(memberAids, targetEpoch, groupId, gs);
1300
+ const epochChain = await computeEpochChain(prevChain, targetEpoch, commitment, aid);
1301
+ const rotationId = opts?.rotationId ?? '';
1302
+ const stored = await storeGroupSecret(this._keystoreRef, aid, groupId, targetEpoch, gs, commitment, memberAids, epochChain, rotationId);
1303
+ if (!stored) {
1304
+ throw new Error(`group ${groupId} epoch ${targetEpoch} secret already exists or is newer; abort distribution`);
1305
+ }
1306
+ const manifest = await this._signManifest(buildMembershipManifest(groupId, targetEpoch, targetEpoch - 1, memberAids, { initiatorAid: aid }));
1307
+ const distPayload = await buildKeyDistribution(groupId, targetEpoch, gs, memberAids, aid, manifest, epochChain);
1308
+ if (rotationId) {
1309
+ distPayload.rotation_id = rotationId;
1310
+ }
1311
+ this._log.debug(`rotateEpochTo exit: elapsed=${Date.now() - tStart}ms group_id=${groupId} epoch=${targetEpoch}`);
1312
+ return {
1313
+ epoch: targetEpoch,
1314
+ commitment,
1315
+ distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1316
+ };
1278
1317
  }
1279
- const manifest = await this._signManifest(buildMembershipManifest(groupId, targetEpoch, targetEpoch - 1, memberAids, { initiatorAid: aid }));
1280
- const distPayload = await buildKeyDistribution(groupId, targetEpoch, gs, memberAids, aid, manifest, epochChain);
1281
- if (rotationId) {
1282
- distPayload.rotation_id = rotationId;
1318
+ catch (err) {
1319
+ this._log.debug(`rotateEpochTo exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1320
+ throw err;
1283
1321
  }
1284
- return {
1285
- epoch: targetEpoch,
1286
- commitment,
1287
- distributions: memberAids.filter(m => m !== aid).map(m => ({ to: m, payload: distPayload })),
1288
- };
1289
1322
  }
1290
1323
  /** 手动存储 group_secret。返回 false 表示 epoch 降级被拒。 */
1291
1324
  async storeSecret(groupId, epoch, groupSecretBytes, commitment, memberAids, epochChain) {
@@ -1306,46 +1339,66 @@ export class GroupE2EEManager {
1306
1339
  // ── 加解密 ────────────────────────────────────────
1307
1340
  /** 加密群消息(含发送方签名)。无密钥时抛 E2EEGroupSecretMissingError。 */
1308
1341
  async encrypt(groupId, payload, opts) {
1309
- const aid = this._currentAid();
1310
- const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId);
1311
- if (!secretData) {
1312
- throw new E2EEGroupSecretMissingError(`no group secret for ${groupId}`);
1342
+ const tStart = Date.now();
1343
+ this._log.debug(`encrypt enter: group_id=${groupId} mid=${opts?.messageId ?? '<auto>'}`);
1344
+ try {
1345
+ const aid = this._currentAid();
1346
+ const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId);
1347
+ if (!secretData) {
1348
+ throw new E2EEGroupSecretMissingError(`no group secret for ${groupId}`);
1349
+ }
1350
+ const identity = this._identityFn();
1351
+ const senderPkPem = identity?.private_key_pem ?? null;
1352
+ const senderCertPem = identity?.cert ?? null;
1353
+ const result = await encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1354
+ fromAid: aid,
1355
+ messageId: opts?.messageId ?? `gm-${uuidV4()}`,
1356
+ timestamp: opts?.timestamp ?? Date.now(),
1357
+ senderPrivateKeyPem: senderPkPem,
1358
+ senderCertPem,
1359
+ protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1360
+ context: opts?.context ?? null,
1361
+ });
1362
+ this._log.debug(`encrypt exit: elapsed=${Date.now() - tStart}ms group_id=${groupId} epoch=${secretData.epoch}`);
1363
+ return result;
1364
+ }
1365
+ catch (err) {
1366
+ this._log.debug(`encrypt exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1367
+ throw err;
1313
1368
  }
1314
- const identity = this._identityFn();
1315
- const senderPkPem = identity?.private_key_pem ?? null;
1316
- const senderCertPem = identity?.cert ?? null;
1317
- return encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1318
- fromAid: aid,
1319
- messageId: opts?.messageId ?? `gm-${uuidV4()}`,
1320
- timestamp: opts?.timestamp ?? Date.now(),
1321
- senderPrivateKeyPem: senderPkPem,
1322
- senderCertPem,
1323
- protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1324
- context: opts?.context ?? null,
1325
- });
1326
1369
  }
1327
1370
  /** 使用指定 epoch 加密群消息。 */
1328
1371
  async encryptWithEpoch(groupId, epoch, payload, opts) {
1329
- const aid = this._currentAid();
1330
- const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId, epoch);
1331
- if (!secretData) {
1332
- throw new E2EEGroupSecretMissingError(`no group secret for ${groupId} epoch ${epoch}`);
1372
+ const tStart = Date.now();
1373
+ this._log.debug(`encryptWithEpoch enter: group_id=${groupId} epoch=${epoch} mid=${opts?.messageId ?? '<auto>'}`);
1374
+ try {
1375
+ const aid = this._currentAid();
1376
+ const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId, epoch);
1377
+ if (!secretData) {
1378
+ throw new E2EEGroupSecretMissingError(`no group secret for ${groupId} epoch ${epoch}`);
1379
+ }
1380
+ const identity = this._identityFn();
1381
+ const senderPkPem = identity?.private_key_pem ?? null;
1382
+ if (!senderPkPem) {
1383
+ throw new E2EEError('sender identity private key unavailable for group message signing');
1384
+ }
1385
+ const senderCertPem = identity?.cert ?? null;
1386
+ const result = await encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1387
+ fromAid: aid,
1388
+ messageId: opts?.messageId ?? `gm-${uuidV4()}`,
1389
+ timestamp: opts?.timestamp ?? Date.now(),
1390
+ senderPrivateKeyPem: senderPkPem,
1391
+ senderCertPem,
1392
+ protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1393
+ context: opts?.context ?? null,
1394
+ });
1395
+ this._log.debug(`encryptWithEpoch exit: elapsed=${Date.now() - tStart}ms group_id=${groupId} epoch=${secretData.epoch}`);
1396
+ return result;
1333
1397
  }
1334
- const identity = this._identityFn();
1335
- const senderPkPem = identity?.private_key_pem ?? null;
1336
- if (!senderPkPem) {
1337
- throw new E2EEError('sender identity private key unavailable for group message signing');
1398
+ catch (err) {
1399
+ this._log.debug(`encryptWithEpoch exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1400
+ throw err;
1338
1401
  }
1339
- const senderCertPem = identity?.cert ?? null;
1340
- return encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1341
- fromAid: aid,
1342
- messageId: opts?.messageId ?? `gm-${uuidV4()}`,
1343
- timestamp: opts?.timestamp ?? Date.now(),
1344
- senderPrivateKeyPem: senderPkPem,
1345
- senderCertPem,
1346
- protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1347
- context: opts?.context ?? null,
1348
- });
1349
1402
  }
1350
1403
  /**
1351
1404
  * 解密单条群消息(异步)。内置防重放 + 发送方验签 + 外层字段校验。
@@ -1353,51 +1406,78 @@ export class GroupE2EEManager {
1353
1406
  * opts.skipReplay: 跳过防重放检查(用于 group.pull 场景)。
1354
1407
  */
1355
1408
  async decrypt(message, opts) {
1356
- const payload = isJsonObject(message.payload) ? message.payload : null;
1357
- if (payload === null || payload.type !== 'e2ee.group_encrypted') {
1358
- return message;
1359
- }
1360
- const groupId = String(message.group_id ?? '');
1361
- const sender = String(message.from ?? message.sender_aid ?? '');
1362
- const skipReplay = opts?.skipReplay ?? false;
1363
- // 防重放预检:优先使用 AAD message_id
1364
- const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
1365
- const aadMsgId = aad ? (aad.message_id ?? '') : '';
1366
- const msgId = aadMsgId || (message.message_id ?? '');
1367
- if (!skipReplay && groupId && sender && msgId) {
1368
- // 返回原消息(不含 e2ee 字段),调用方可通过缺失 e2ee 识别 replay
1369
- if (this._replayGuard.isSeen(groupId, sender, msgId))
1409
+ const tStart = Date.now();
1410
+ const groupIdInit = String(message.group_id ?? '');
1411
+ const senderInit = String(message.from ?? message.sender_aid ?? '');
1412
+ const midInit = String(message.message_id ?? '');
1413
+ this._log.debug(`decrypt enter: group_id=${groupIdInit} from=${senderInit} mid=${midInit} skip_replay=${!!opts?.skipReplay}`);
1414
+ try {
1415
+ const payload = isJsonObject(message.payload) ? message.payload : null;
1416
+ if (payload === null || payload.type !== 'e2ee.group_encrypted') {
1417
+ this._log.debug(`decrypt exit: elapsed=${Date.now() - tStart}ms result=passthrough_not_encrypted`);
1370
1418
  return message;
1371
- }
1372
- // 解析发送方证书(零信任:无证书则拒绝)
1373
- let senderCertPem = null;
1374
- if (this._senderCertResolver && sender) {
1375
- senderCertPem = this._senderCertResolver(sender);
1376
- }
1377
- if (!senderCertPem) {
1378
- console.warn(`拒绝群消息:无法获取发送方 ${sender} 的证书(零信任模式禁止跳过验签): group=${groupId}`);
1379
- return null;
1380
- }
1381
- const allSecrets = await loadAllGroupSecrets(this._keystoreRef, this._currentAid(), groupId);
1382
- if (!allSecrets.size)
1383
- return null;
1384
- const result = await decryptGroupMessage(message, allSecrets, senderCertPem);
1385
- // 解密成功后记录防重放
1386
- if (result !== null) {
1387
- const finalMsgId = aadMsgId || (message.message_id ?? '');
1388
- if (!skipReplay && groupId && sender && finalMsgId) {
1389
- this._replayGuard.record(groupId, sender, finalMsgId);
1390
1419
  }
1420
+ const groupId = String(message.group_id ?? '');
1421
+ const sender = String(message.from ?? message.sender_aid ?? '');
1422
+ const skipReplay = opts?.skipReplay ?? false;
1423
+ // 防重放预检:优先使用 AAD 内 message_id
1424
+ const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
1425
+ const aadMsgId = aad ? (aad.message_id ?? '') : '';
1426
+ const msgId = aadMsgId || (message.message_id ?? '');
1427
+ if (!skipReplay && groupId && sender && msgId) {
1428
+ // 返回原消息(不含 e2ee 字段),调用方可通过缺失 e2ee 识别 replay
1429
+ if (this._replayGuard.isSeen(groupId, sender, msgId)) {
1430
+ this._log.debug(`decrypt exit: elapsed=${Date.now() - tStart}ms result=replay_skipped group_id=${groupId}`);
1431
+ return message;
1432
+ }
1433
+ }
1434
+ // 解析发送方证书(零信任:无证书则拒绝)
1435
+ let senderCertPem = null;
1436
+ if (this._senderCertResolver && sender) {
1437
+ senderCertPem = this._senderCertResolver(sender);
1438
+ }
1439
+ if (!senderCertPem) {
1440
+ this._log.warn(`拒绝群消息:无法获取发送方 ${sender} 的证书(零信任模式禁止跳过验签): group=${groupId}`);
1441
+ this._log.debug(`decrypt exit: elapsed=${Date.now() - tStart}ms result=rejected_no_sender_cert`);
1442
+ return null;
1443
+ }
1444
+ const allSecrets = await loadAllGroupSecrets(this._keystoreRef, this._currentAid(), groupId);
1445
+ if (!allSecrets.size) {
1446
+ this._log.debug(`decrypt exit: elapsed=${Date.now() - tStart}ms result=no_secrets group_id=${groupId}`);
1447
+ return null;
1448
+ }
1449
+ const result = await decryptGroupMessage(message, allSecrets, senderCertPem);
1450
+ // 解密成功后记录防重放
1451
+ if (result !== null) {
1452
+ const finalMsgId = aadMsgId || (message.message_id ?? '');
1453
+ if (!skipReplay && groupId && sender && finalMsgId) {
1454
+ this._replayGuard.record(groupId, sender, finalMsgId);
1455
+ }
1456
+ }
1457
+ this._log.debug(`decrypt exit: elapsed=${Date.now() - tStart}ms result=${result !== null ? 'ok' : 'failed'} group_id=${groupId}`);
1458
+ return result;
1459
+ }
1460
+ catch (err) {
1461
+ this._log.debug(`decrypt exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1462
+ throw err;
1391
1463
  }
1392
- return result;
1393
1464
  }
1394
1465
  /** 批量解密 */
1395
1466
  async decryptBatch(messages, opts) {
1396
- const results = [];
1397
- for (const m of messages) {
1398
- results.push((await this.decrypt(m, opts)) ?? m);
1467
+ const tStart = Date.now();
1468
+ this._log.debug(`decryptBatch enter: count=${messages.length} skip_replay=${!!opts?.skipReplay}`);
1469
+ try {
1470
+ const results = [];
1471
+ for (const m of messages) {
1472
+ results.push((await this.decrypt(m, opts)) ?? m);
1473
+ }
1474
+ this._log.debug(`decryptBatch exit: elapsed=${Date.now() - tStart}ms count=${results.length}`);
1475
+ return results;
1476
+ }
1477
+ catch (err) {
1478
+ this._log.debug(`decryptBatch exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1479
+ throw err;
1399
1480
  }
1400
- return results;
1401
1481
  }
1402
1482
  // ── 密钥协议消息处理 ──────────────────────────────
1403
1483
  /**
@@ -1408,60 +1488,89 @@ export class GroupE2EEManager {
1408
1488
  * 返回 null 表示不是密钥消息。
1409
1489
  */
1410
1490
  async handleIncoming(payload) {
1491
+ const tStart = Date.now();
1411
1492
  const msgType = (payload.type ?? '');
1412
- const aid = this._currentAid();
1413
- if (msgType === 'e2ee.group_key_distribution') {
1414
- // 解析发起者证书用于 manifest 验证
1415
- let initiatorCert = null;
1416
- const distributedBy = (payload.distributed_by ?? '');
1417
- if (this._initiatorCertResolver && distributedBy) {
1418
- initiatorCert = this._initiatorCertResolver(distributedBy);
1493
+ this._log.debug(`handleIncoming enter: type=${msgType} group_id=${String(payload.group_id ?? '')}`);
1494
+ try {
1495
+ const aid = this._currentAid();
1496
+ if (msgType === 'e2ee.group_key_distribution') {
1497
+ // 解析发起者证书用于 manifest 验证
1498
+ let initiatorCert = null;
1499
+ const distributedBy = (payload.distributed_by ?? '');
1500
+ if (this._initiatorCertResolver && distributedBy) {
1501
+ initiatorCert = this._initiatorCertResolver(distributedBy);
1502
+ }
1503
+ const ok = await handleKeyDistribution(payload, this._keystoreRef, aid, initiatorCert);
1504
+ const r = ok ? 'distribution' : 'distribution_rejected';
1505
+ this._log.debug(`handleIncoming exit: elapsed=${Date.now() - tStart}ms result=${r}`);
1506
+ return r;
1419
1507
  }
1420
- const ok = await handleKeyDistribution(payload, this._keystoreRef, aid, initiatorCert);
1421
- return ok ? 'distribution' : 'distribution_rejected';
1422
- }
1423
- if (msgType === 'e2ee.group_key_response') {
1424
- const pendingKey = `${String(payload.group_id ?? '')}:${String(payload.epoch ?? '')}:${String(payload.request_id ?? '')}`;
1425
- const expected = this._pendingKeyRequests.get(pendingKey) ?? null;
1426
- if (expected === null)
1427
- return 'response_rejected';
1428
- const responderAid = String(payload.responder_aid ?? '');
1429
- const responderCertPem = responderAid && this._initiatorCertResolver
1430
- ? this._initiatorCertResolver(responderAid)
1431
- : null;
1432
- const ok = await handleKeyResponse(payload, this._keystoreRef, aid, {
1433
- expectedRequest: expected,
1434
- responderCertPem,
1435
- currentMembers: await this.getMemberAids(String(payload.group_id ?? '')),
1436
- strict: true,
1437
- });
1438
- if (ok && expected)
1439
- this._pendingKeyRequests.delete(pendingKey);
1440
- return ok ? 'response' : 'response_rejected';
1508
+ if (msgType === 'e2ee.group_key_response') {
1509
+ const pendingKey = `${String(payload.group_id ?? '')}:${String(payload.epoch ?? '')}:${String(payload.request_id ?? '')}`;
1510
+ const expected = this._pendingKeyRequests.get(pendingKey) ?? null;
1511
+ if (expected === null) {
1512
+ this._log.debug(`handleIncoming exit: elapsed=${Date.now() - tStart}ms result=response_rejected reason=no_pending`);
1513
+ return 'response_rejected';
1514
+ }
1515
+ const responderAid = String(payload.responder_aid ?? '');
1516
+ const responderCertPem = responderAid && this._initiatorCertResolver
1517
+ ? this._initiatorCertResolver(responderAid)
1518
+ : null;
1519
+ const ok = await handleKeyResponse(payload, this._keystoreRef, aid, {
1520
+ expectedRequest: expected,
1521
+ responderCertPem,
1522
+ currentMembers: await this.getMemberAids(String(payload.group_id ?? '')),
1523
+ strict: true,
1524
+ });
1525
+ if (ok && expected)
1526
+ this._pendingKeyRequests.delete(pendingKey);
1527
+ const r = ok ? 'response' : 'response_rejected';
1528
+ this._log.debug(`handleIncoming exit: elapsed=${Date.now() - tStart}ms result=${r}`);
1529
+ return r;
1530
+ }
1531
+ if (msgType === 'e2ee.group_key_request') {
1532
+ this._log.debug(`handleIncoming exit: elapsed=${Date.now() - tStart}ms result=request`);
1533
+ return 'request';
1534
+ }
1535
+ this._log.debug(`handleIncoming exit: elapsed=${Date.now() - tStart}ms result=null`);
1536
+ return null;
1441
1537
  }
1442
- if (msgType === 'e2ee.group_key_request') {
1443
- return 'request';
1538
+ catch (err) {
1539
+ this._log.debug(`handleIncoming exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1540
+ throw err;
1444
1541
  }
1445
- return null;
1446
1542
  }
1447
1543
  /** 构建恢复请求。返回 {to, payload} 或 null(限流/无目标)。 */
1448
1544
  async buildRecoveryRequest(groupId, epoch, opts) {
1449
- const aid = this._currentAid();
1450
- if (!this._requestThrottle.allow(`request:${groupId}:${epoch}`))
1451
- return null;
1452
- let candidates = [];
1453
- const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId);
1454
- if (secretData?.member_aids?.length) {
1455
- candidates = secretData.member_aids.filter(m => m !== aid);
1545
+ const tStart = Date.now();
1546
+ this._log.debug(`buildRecoveryRequest enter: group_id=${groupId} epoch=${epoch} sender_hint=${opts?.senderAid ?? ''}`);
1547
+ try {
1548
+ const aid = this._currentAid();
1549
+ if (!this._requestThrottle.allow(`request:${groupId}:${epoch}`)) {
1550
+ this._log.debug(`buildRecoveryRequest exit: elapsed=${Date.now() - tStart}ms result=throttled`);
1551
+ return null;
1552
+ }
1553
+ let candidates = [];
1554
+ const secretData = await loadGroupSecret(this._keystoreRef, aid, groupId);
1555
+ if (secretData?.member_aids?.length) {
1556
+ candidates = secretData.member_aids.filter(m => m !== aid);
1557
+ }
1558
+ if (!candidates.length && opts?.senderAid && opts.senderAid !== aid) {
1559
+ candidates = [opts.senderAid];
1560
+ }
1561
+ if (!candidates.length) {
1562
+ this._log.debug(`buildRecoveryRequest exit: elapsed=${Date.now() - tStart}ms result=no_candidates`);
1563
+ return null;
1564
+ }
1565
+ const payload = buildKeyRequest(groupId, epoch, aid);
1566
+ this.rememberKeyRequest(payload, candidates[0]);
1567
+ this._log.debug(`buildRecoveryRequest exit: elapsed=${Date.now() - tStart}ms target=${candidates[0]}`);
1568
+ return { to: candidates[0], payload };
1456
1569
  }
1457
- if (!candidates.length && opts?.senderAid && opts.senderAid !== aid) {
1458
- candidates = [opts.senderAid];
1570
+ catch (err) {
1571
+ this._log.debug(`buildRecoveryRequest exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1572
+ throw err;
1459
1573
  }
1460
- if (!candidates.length)
1461
- return null;
1462
- const payload = buildKeyRequest(groupId, epoch, aid);
1463
- this.rememberKeyRequest(payload, candidates[0]);
1464
- return { to: candidates[0], payload };
1465
1574
  }
1466
1575
  rememberKeyRequest(payload, expectedResponderAid) {
1467
1576
  if (payload.type !== 'e2ee.group_key_request')
@@ -1473,21 +1582,38 @@ export class GroupE2EEManager {
1473
1582
  }
1474
1583
  /** 处理密钥请求(受频率限制 + 成员资格验证) */
1475
1584
  async handleKeyRequestMsg(requestPayload, currentMembers) {
1585
+ const tStart = Date.now();
1476
1586
  const requester = (requestPayload.requester_aid ?? '');
1477
1587
  const groupId = (requestPayload.group_id ?? '');
1478
- if (!requester || !groupId)
1479
- return null;
1480
- if (!currentMembers.includes(requester)) {
1481
- console.warn(`拒绝密钥恢复请求:${requester} 不在群 ${groupId} 的当前成员列表中`);
1482
- return null;
1588
+ this._log.debug(`handleKeyRequestMsg enter: group_id=${groupId} requester=${requester} members=${currentMembers.length}`);
1589
+ try {
1590
+ if (!requester || !groupId) {
1591
+ this._log.debug(`handleKeyRequestMsg exit: elapsed=${Date.now() - tStart}ms result=invalid_payload`);
1592
+ return null;
1593
+ }
1594
+ if (!currentMembers.includes(requester)) {
1595
+ this._log.warn(`reject key recover request: ${requester} not in group ${groupId} current member list`);
1596
+ this._log.debug(`handleKeyRequestMsg exit: elapsed=${Date.now() - tStart}ms result=not_member`);
1597
+ return null;
1598
+ }
1599
+ if (!this._responseThrottle.allow(`response:${groupId}:${requester}`)) {
1600
+ this._log.debug(`handleKeyRequestMsg exit: elapsed=${Date.now() - tStart}ms result=throttled`);
1601
+ return null;
1602
+ }
1603
+ const identity = this._identityFn();
1604
+ const privateKeyPem = identity?.private_key_pem;
1605
+ if (!privateKeyPem) {
1606
+ this._log.debug(`handleKeyRequestMsg exit: elapsed=${Date.now() - tStart}ms result=no_private_key`);
1607
+ return null;
1608
+ }
1609
+ const result = await handleKeyRequest(requestPayload, this._keystoreRef, this._currentAid(), currentMembers, privateKeyPem);
1610
+ this._log.debug(`handleKeyRequestMsg exit: elapsed=${Date.now() - tStart}ms result=${result !== null ? 'ok' : 'null'}`);
1611
+ return result;
1612
+ }
1613
+ catch (err) {
1614
+ this._log.debug(`handleKeyRequestMsg exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
1615
+ throw err;
1483
1616
  }
1484
- if (!this._responseThrottle.allow(`response:${groupId}:${requester}`))
1485
- return null;
1486
- const identity = this._identityFn();
1487
- const privateKeyPem = identity?.private_key_pem;
1488
- if (!privateKeyPem)
1489
- return null;
1490
- return handleKeyRequest(requestPayload, this._keystoreRef, this._currentAid(), currentMembers, privateKeyPem);
1491
1617
  }
1492
1618
  // ── 状态查询 ──────────────────────────────────────
1493
1619
  async hasSecret(groupId) {