@agentunion/fastaun 0.2.16 → 0.2.18

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 (56) hide show
  1. package/dist/auth.d.ts +3 -0
  2. package/dist/auth.js +314 -217
  3. package/dist/auth.js.map +1 -1
  4. package/dist/client.d.ts +7 -0
  5. package/dist/client.js +1137 -740
  6. package/dist/client.js.map +1 -1
  7. package/dist/config.d.ts +2 -0
  8. package/dist/config.js +2 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/discovery.d.ts +3 -0
  11. package/dist/discovery.js +29 -2
  12. package/dist/discovery.js.map +1 -1
  13. package/dist/e2ee-group.d.ts +7 -2
  14. package/dist/e2ee-group.js +216 -61
  15. package/dist/e2ee-group.js.map +1 -1
  16. package/dist/e2ee.d.ts +3 -0
  17. package/dist/e2ee.js +34 -11
  18. package/dist/e2ee.js.map +1 -1
  19. package/dist/events.d.ts +3 -0
  20. package/dist/events.js +11 -1
  21. package/dist/events.js.map +1 -1
  22. package/dist/group-id.d.ts +23 -0
  23. package/dist/group-id.js +94 -0
  24. package/dist/group-id.js.map +1 -0
  25. package/dist/keystore/aid-db.d.ts +8 -1
  26. package/dist/keystore/aid-db.js +14 -3
  27. package/dist/keystore/aid-db.js.map +1 -1
  28. package/dist/keystore/file.d.ts +5 -0
  29. package/dist/keystore/file.js +19 -10
  30. package/dist/keystore/file.js.map +1 -1
  31. package/dist/keystore/index.d.ts +2 -0
  32. package/dist/keystore/sqlite-backup.d.ts +5 -1
  33. package/dist/keystore/sqlite-backup.js +9 -6
  34. package/dist/keystore/sqlite-backup.js.map +1 -1
  35. package/dist/logger.d.ts +28 -3
  36. package/dist/logger.js +170 -37
  37. package/dist/logger.js.map +1 -1
  38. package/dist/namespaces/auth.d.ts +1 -0
  39. package/dist/namespaces/auth.js +289 -146
  40. package/dist/namespaces/auth.js.map +1 -1
  41. package/dist/namespaces/custody.d.ts +1 -0
  42. package/dist/namespaces/custody.js +138 -56
  43. package/dist/namespaces/custody.js.map +1 -1
  44. package/dist/namespaces/meta.d.ts +1 -0
  45. package/dist/namespaces/meta.js +26 -0
  46. package/dist/namespaces/meta.js.map +1 -1
  47. package/dist/secret-store/file-store.d.ts +4 -0
  48. package/dist/secret-store/file-store.js +7 -3
  49. package/dist/secret-store/file-store.js.map +1 -1
  50. package/dist/secret-store/index.d.ts +3 -0
  51. package/dist/secret-store/index.js +2 -2
  52. package/dist/secret-store/index.js.map +1 -1
  53. package/dist/transport.d.ts +3 -0
  54. package/dist/transport.js +91 -2
  55. package/dist/transport.js.map +1 -1
  56. package/package.json +1 -1
@@ -7,6 +7,7 @@
7
7
  import * as crypto from 'node:crypto';
8
8
  import { E2EEError, E2EEGroupSecretMissingError, } from './errors.js';
9
9
  import { isJsonObject, } from './types.js';
10
+ const _noopLogger = { error: () => { }, warn: () => { }, info: () => { }, debug: () => { } };
10
11
  // ── 辅助:证书 SHA-256 指纹 ──────────────────────────────────────
11
12
  /** PEM 证书 → sha256:{hex} 指纹(与 Python/Go/JS SDK 一致) */
12
13
  function certSha256Fingerprint(certPem) {
@@ -459,17 +460,26 @@ export function encryptGroupMessage(groupId, epoch, groupSecret, payload, opts)
459
460
  */
460
461
  export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts) {
461
462
  const requireSignature = opts?.requireSignature ?? true;
463
+ const logger = opts?.logger;
462
464
  const payload = isJsonObject(message.payload) ? message.payload : null;
463
- if (payload === null)
465
+ if (payload === null) {
466
+ logger?.warn(`decryptGroupMessage rejected: payload is not an object mid=${String(message.message_id ?? '')}`);
464
467
  return null;
465
- if (payload.type !== 'e2ee.group_encrypted')
468
+ }
469
+ if (payload.type !== 'e2ee.group_encrypted') {
470
+ logger?.warn(`decryptGroupMessage rejected: unexpected payload.type=${String(payload.type)} mid=${String(message.message_id ?? '')}`);
466
471
  return null;
472
+ }
467
473
  const epoch = payload.epoch;
468
- if (epoch == null)
474
+ if (epoch == null) {
475
+ logger?.warn(`decryptGroupMessage rejected: missing epoch mid=${String(message.message_id ?? '')}`);
469
476
  return null;
477
+ }
470
478
  const groupSecret = groupSecrets.get(epoch);
471
- if (!groupSecret)
479
+ if (!groupSecret) {
480
+ logger?.warn(`decryptGroupMessage rejected: no group secret for epoch=${epoch} mid=${String(message.message_id ?? '')}`);
472
481
  return null;
482
+ }
473
483
  try {
474
484
  // 优先从 AAD 读取 group_id 和 message_id
475
485
  const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
@@ -482,34 +492,44 @@ export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts)
482
492
  messageId = aad.message_id || message.message_id || '';
483
493
  aadFrom = aad.from || '';
484
494
  // 外层路由字段与 AAD 绑定校验
485
- if (outerGroupId && groupId !== outerGroupId)
495
+ if (outerGroupId && groupId !== outerGroupId) {
496
+ logger?.warn(`decryptGroupMessage rejected: outer group_id does not match aad.group_id outer=${outerGroupId} aad=${groupId}`);
486
497
  return null;
498
+ }
487
499
  if (aadFrom) {
488
500
  const outerFrom = message.from || '';
489
501
  const outerSender = message.sender_aid || '';
490
- if (outerFrom && outerFrom !== aadFrom)
502
+ if (outerFrom && outerFrom !== aadFrom) {
503
+ logger?.warn(`decryptGroupMessage rejected: outer from does not match aad.from outer=${outerFrom} aad=${aadFrom}`);
491
504
  return null;
492
- if (outerSender && outerSender !== aadFrom)
505
+ }
506
+ if (outerSender && outerSender !== aadFrom) {
507
+ logger?.warn(`decryptGroupMessage rejected: outer sender_aid does not match aad.from outer=${outerSender} aad=${aadFrom}`);
493
508
  return null;
509
+ }
494
510
  }
495
511
  }
496
512
  else {
497
513
  groupId = outerGroupId;
498
514
  messageId = message.message_id || '';
499
515
  }
500
- if (!groupId || !messageId)
516
+ if (!groupId || !messageId) {
517
+ logger?.warn(`decryptGroupMessage rejected: missing group_id or message_id group=${groupId} mid=${messageId}`);
501
518
  return null;
519
+ }
502
520
  const msgKey = deriveGroupMsgKey(groupSecret, groupId, messageId);
503
521
  const nonce = Buffer.from(payload.nonce, 'base64');
504
522
  const ciphertext = Buffer.from(payload.ciphertext, 'base64');
505
523
  const tag = Buffer.from(payload.tag, 'base64');
506
524
  if (!verifyEnvelopeMetadataAuth(payload, msgKey)) {
525
+ logger?.warn(`decryptGroupMessage rejected: envelope metadata auth failed group=${groupId} mid=${messageId}`);
507
526
  return null;
508
527
  }
509
528
  const aadBytes = aad ? aadBytesGroup(aad) : Buffer.alloc(0);
510
529
  const plaintext = aesGcmDecrypt(msgKey, ciphertext, tag, nonce, aadBytes);
511
530
  const decoded = JSON.parse(plaintext.toString('utf-8'));
512
531
  if (!validateDecryptedEnvelopeMetadata(decoded, payload, message)) {
532
+ logger?.warn(`decryptGroupMessage rejected: decrypted envelope metadata validation failed group=${groupId} mid=${messageId}`);
513
533
  return null;
514
534
  }
515
535
  const e2ee = {
@@ -533,43 +553,56 @@ export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts)
533
553
  // 发送方签名验证
534
554
  const senderSigB64 = payload.sender_signature;
535
555
  if (requireSignature) {
536
- if (!senderSigB64)
556
+ if (!senderSigB64) {
557
+ logger?.warn(`decryptGroupMessage rejected: sender signature required but missing group=${groupId} mid=${messageId}`);
537
558
  return null;
538
- if (!senderCertPem)
559
+ }
560
+ if (!senderCertPem) {
561
+ logger?.warn(`decryptGroupMessage rejected: sender signature required but senderCertPem missing group=${groupId} mid=${messageId}`);
539
562
  return null;
563
+ }
540
564
  try {
541
565
  const senderPub = pemToCertPublicKey(senderCertPem);
542
566
  const sigBytes = Buffer.from(senderSigB64, 'base64');
543
567
  const verifyPayload = Buffer.concat([ciphertext, tag, aadBytes]);
544
- if (!ecdsaVerify(senderPub, sigBytes, verifyPayload))
568
+ if (!ecdsaVerify(senderPub, sigBytes, verifyPayload)) {
569
+ logger?.warn(`decryptGroupMessage rejected: sender signature verification failed group=${groupId} mid=${messageId}`);
545
570
  return null;
571
+ }
546
572
  if (isJsonObject(result.e2ee))
547
573
  result.e2ee.sender_verified = true;
548
574
  }
549
- catch {
575
+ catch (exc) {
576
+ logger?.warn(`decryptGroupMessage rejected: sender signature verification threw group=${groupId} mid=${messageId} err=${String(exc)}`);
550
577
  return null;
551
578
  }
552
579
  }
553
580
  else if (senderCertPem) {
554
581
  // 非零信任模式但提供了证书:有证书时强制验签
555
- if (!senderSigB64)
582
+ if (!senderSigB64) {
583
+ logger?.warn(`decryptGroupMessage rejected: senderCertPem present but signature missing group=${groupId} mid=${messageId}`);
556
584
  return null;
585
+ }
557
586
  try {
558
587
  const senderPub = pemToCertPublicKey(senderCertPem);
559
588
  const sigBytes = Buffer.from(senderSigB64, 'base64');
560
589
  const verifyPayload = Buffer.concat([ciphertext, tag, aadBytes]);
561
- if (!ecdsaVerify(senderPub, sigBytes, verifyPayload))
590
+ if (!ecdsaVerify(senderPub, sigBytes, verifyPayload)) {
591
+ logger?.warn(`decryptGroupMessage rejected: optional sender signature verification failed group=${groupId} mid=${messageId}`);
562
592
  return null;
593
+ }
563
594
  if (isJsonObject(result.e2ee))
564
595
  result.e2ee.sender_verified = true;
565
596
  }
566
- catch {
597
+ catch (exc) {
598
+ logger?.warn(`decryptGroupMessage rejected: optional sender signature verification threw group=${groupId} mid=${messageId} err=${String(exc)}`);
567
599
  return null;
568
600
  }
569
601
  }
570
602
  return result;
571
603
  }
572
- catch {
604
+ catch (exc) {
605
+ logger?.warn(`decryptGroupMessage rejected: decrypt threw mid=${String(message.message_id ?? '')} err=${String(exc)}`);
573
606
  return null;
574
607
  }
575
608
  }
@@ -747,12 +780,12 @@ export function loadGroupSecret(keystore, aid, groupId, epoch) {
747
780
  }
748
781
  return loaded;
749
782
  }
750
- function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, rotatorAid, source) {
783
+ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, rotatorAid, source, logger = _noopLogger) {
751
784
  const chain = (incomingChain ?? '').trim();
752
785
  const rid = rotationId.trim();
753
786
  const rotator = rotatorAid.trim();
754
787
  if (rid && !chain) {
755
- console.warn(`[e2ee-group] 拒绝缺少 epoch_chain 的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
788
+ logger.warn(`[e2ee-group] rejecting rotation key missing epoch_chain source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
756
789
  return { ok: false };
757
790
  }
758
791
  const current = loadGroupSecret(keystore, aid, groupId);
@@ -763,7 +796,7 @@ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, inc
763
796
  return { ok: true };
764
797
  if (rid && chain && currentChain && currentChain !== chain) {
765
798
  if (!(currentPendingRotationId && currentPendingRotationId !== rid)) {
766
- console.warn(`[e2ee-group] 拒绝同 epoch 分叉 chain source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
799
+ logger.warn(`[e2ee-group] rejecting same-epoch chain fork source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
767
800
  return { ok: false };
768
801
  }
769
802
  }
@@ -776,17 +809,17 @@ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, inc
776
809
  return { ok: true, unverified: true, reason: 'missing_prev_chain' };
777
810
  if (!rotator) {
778
811
  if (rid) {
779
- console.warn(`[e2ee-group] 拒绝缺少 rotator_aid 的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
812
+ logger.warn(`[e2ee-group] rejecting rotation key missing rotator_aid source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
780
813
  return { ok: false };
781
814
  }
782
815
  return { ok: true, unverified: true, reason: 'missing_rotator_aid' };
783
816
  }
784
817
  if (!verifyEpochChain(chain, prevChain, epoch, commitment, rotator)) {
785
818
  if (rid) {
786
- console.warn(`[e2ee-group] 拒绝 epoch_chain 验证失败的新 rotation key source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
819
+ logger.warn(`[e2ee-group] rejecting rotation key with failed epoch_chain verification source=${source} group=${groupId} epoch=${epoch} rotation=${rid}`);
787
820
  return { ok: false };
788
821
  }
789
- console.warn(`[e2ee-group] epoch_chain 验证失败,按兼容档接收并标记未验证 source=${source} group=${groupId} epoch=${epoch}`);
822
+ logger.warn(`[e2ee-group] epoch_chain verification failed, accepting as unverified for compatibility source=${source} group=${groupId} epoch=${epoch}`);
790
823
  return { ok: true, unverified: true, reason: 'chain_mismatch_legacy' };
791
824
  }
792
825
  if (!rid)
@@ -914,7 +947,7 @@ export function buildKeyDistribution(groupId, epoch, groupSecret, memberAids, di
914
947
  return result;
915
948
  }
916
949
  /** 处理收到的 group key 分发消息 */
917
- export function handleKeyDistribution(message, keystore, aid, initiatorCertPem) {
950
+ export function handleKeyDistribution(message, keystore, aid, initiatorCertPem, logger) {
918
951
  const payload = 'group_id' in message
919
952
  ? message
920
953
  : (isJsonObject(message.payload) ? message.payload : message);
@@ -923,40 +956,58 @@ export function handleKeyDistribution(message, keystore, aid, initiatorCertPem)
923
956
  const groupSecretB64 = payload.group_secret;
924
957
  const commitment = payload.commitment;
925
958
  const memberAids = payload.member_aids ?? [];
926
- if (!groupId || epoch == null || !groupSecretB64 || !commitment)
959
+ logger?.debug(`handleKeyDistribution enter: group=${String(groupId ?? '')}, epoch=${String(epoch ?? '')}, aid=${aid}, members=${memberAids.length}`);
960
+ if (!groupId || epoch == null || !groupSecretB64 || !commitment) {
961
+ logger?.warn(`handleKeyDistribution rejected: missing required fields group=${String(groupId ?? '')} epoch=${String(epoch ?? '')} has_secret=${!!groupSecretB64} has_commitment=${!!commitment}`);
927
962
  return false;
963
+ }
928
964
  // 验证 Membership Manifest 签名
929
965
  const manifest = isJsonObject(payload.manifest) ? payload.manifest : undefined;
930
966
  if (initiatorCertPem) {
931
- if (!manifest)
967
+ if (!manifest) {
968
+ logger?.warn(`handleKeyDistribution rejected: manifest required when initiatorCertPem present group=${groupId} epoch=${epoch}`);
932
969
  return false;
933
- if (!verifyMembershipManifest(manifest, initiatorCertPem))
970
+ }
971
+ if (!verifyMembershipManifest(manifest, initiatorCertPem)) {
972
+ logger?.warn(`handleKeyDistribution rejected: manifest signature verification failed group=${groupId} epoch=${epoch}`);
934
973
  return false;
935
- if (manifest.group_id !== groupId || manifest.epoch !== epoch)
974
+ }
975
+ if (manifest.group_id !== groupId || manifest.epoch !== epoch) {
976
+ logger?.warn(`handleKeyDistribution rejected: manifest group_id/epoch mismatch group=${groupId} epoch=${epoch} manifest_group=${String(manifest.group_id)} manifest_epoch=${String(manifest.epoch)}`);
936
977
  return false;
978
+ }
937
979
  const manifestMembers = [...(manifest.member_aids ?? [])].sort();
938
980
  const payloadMembers = [...memberAids].sort();
939
- if (JSON.stringify(manifestMembers) !== JSON.stringify(payloadMembers))
981
+ if (JSON.stringify(manifestMembers) !== JSON.stringify(payloadMembers)) {
982
+ logger?.warn(`handleKeyDistribution rejected: manifest members mismatch payload members group=${groupId} epoch=${epoch}`);
940
983
  return false;
984
+ }
941
985
  }
942
986
  else if (manifest) {
943
- if (manifest.group_id !== groupId || manifest.epoch !== epoch)
987
+ if (manifest.group_id !== groupId || manifest.epoch !== epoch) {
988
+ logger?.warn(`handleKeyDistribution rejected: manifest group_id/epoch mismatch (no cert) group=${groupId} epoch=${epoch}`);
944
989
  return false;
990
+ }
945
991
  const manifestMembers = [...(manifest.member_aids ?? [])].sort();
946
992
  const payloadMembers = [...memberAids].sort();
947
- if (JSON.stringify(manifestMembers) !== JSON.stringify(payloadMembers))
993
+ if (JSON.stringify(manifestMembers) !== JSON.stringify(payloadMembers)) {
994
+ logger?.warn(`handleKeyDistribution rejected: manifest members mismatch payload members (no cert) group=${groupId} epoch=${epoch}`);
948
995
  return false;
996
+ }
949
997
  }
950
998
  const groupSecret = Buffer.from(groupSecretB64, 'base64');
951
999
  // 验证 commitment
952
1000
  if (!verifyMembershipCommitment(commitment, memberAids, epoch, groupId, aid, groupSecret)) {
1001
+ logger?.warn(`handleKeyDistribution rejected: commitment verification failed group=${groupId} epoch=${epoch}`);
953
1002
  return false;
954
1003
  }
955
1004
  const incomingChain = typeof payload.epoch_chain === 'string' ? payload.epoch_chain : undefined;
956
1005
  const rotationId = typeof payload.rotation_id === 'string' ? payload.rotation_id : '';
957
- const chainAssessment = assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, String(payload.distributed_by ?? payload.rotator_aid ?? ''), 'key_distribution');
958
- if (!chainAssessment.ok)
1006
+ const chainAssessment = assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, String(payload.distributed_by ?? payload.rotator_aid ?? ''), 'key_distribution', logger);
1007
+ if (!chainAssessment.ok) {
1008
+ logger?.warn(`handleKeyDistribution rejected: epoch chain assessment failed group=${groupId} epoch=${epoch} reason=${chainAssessment.reason ?? ''}`);
959
1009
  return false;
1010
+ }
960
1011
  return storeGroupSecret(keystore, aid, groupId, epoch, groupSecret, commitment, memberAids, incomingChain, rotationId, chainAssessment.unverified, chainAssessment.reason);
961
1012
  }
962
1013
  /** 构建密钥请求 payload */
@@ -971,26 +1022,34 @@ export function buildKeyRequest(groupId, epoch, requesterAid, requestId) {
971
1022
  };
972
1023
  }
973
1024
  /** 处理收到的密钥请求 */
974
- export function handleKeyRequest(request, keystore, aid, currentMembers, privateKeyPem) {
1025
+ export function handleKeyRequest(request, keystore, aid, currentMembers, privateKeyPem, logger) {
975
1026
  const payload = 'group_id' in request
976
1027
  ? request
977
1028
  : (isJsonObject(request.payload) ? request.payload : request);
978
1029
  const requesterAid = payload.requester_aid;
979
1030
  const groupId = payload.group_id;
980
1031
  const epoch = payload.epoch;
981
- if (!requesterAid || !groupId || epoch == null)
1032
+ logger?.debug(`handleKeyRequest enter: group=${String(groupId ?? '')}, epoch=${String(epoch ?? '')}, requester=${String(requesterAid ?? '')}, aid=${aid}`);
1033
+ if (!requesterAid || !groupId || epoch == null) {
1034
+ logger?.warn(`handleKeyRequest rejected: missing required fields requester=${String(requesterAid ?? '')} group=${String(groupId ?? '')} epoch=${String(epoch ?? '')}`);
982
1035
  return null;
1036
+ }
983
1037
  // 验证请求者是群成员
984
- if (!currentMembers.includes(requesterAid))
1038
+ if (!currentMembers.includes(requesterAid)) {
1039
+ logger?.warn(`handleKeyRequest rejected: requester not in current members group=${groupId} requester=${requesterAid}`);
985
1040
  return null;
1041
+ }
986
1042
  // 查本地密钥
987
1043
  const secretData = loadGroupSecret(keystore, aid, groupId, epoch);
988
- if (!secretData)
1044
+ if (!secretData) {
1045
+ logger?.warn(`handleKeyRequest rejected: no local secret for epoch group=${groupId} epoch=${epoch}`);
989
1046
  return null;
1047
+ }
990
1048
  // 历史隔离:密钥存储中记录了该 epoch 的成员列表,
991
1049
  // 若请求者不在该列表中,说明其不属于该 epoch,直接拒绝(不响应)。
992
1050
  const memberAids = (secretData.member_aids ?? []).map(String).filter(Boolean).sort();
993
1051
  if (memberAids.length > 0 && !memberAids.includes(requesterAid)) {
1052
+ logger?.warn(`handleKeyRequest rejected: requester not in epoch member list group=${groupId} epoch=${epoch} requester=${requesterAid}`);
994
1053
  return null;
995
1054
  }
996
1055
  // 确定响应使用的成员列表:优先使用密钥存储中的成员列表,
@@ -1029,55 +1088,84 @@ export function handleKeyResponse(response, keystore, aid, opts) {
1029
1088
  const groupSecretB64 = payload.group_secret;
1030
1089
  const commitment = payload.commitment;
1031
1090
  const memberAids = payload.member_aids ?? [];
1032
- if (!groupId || epoch == null || !groupSecretB64 || !commitment)
1091
+ const logger = opts?.logger;
1092
+ logger?.debug(`handleKeyResponse enter: group=${String(groupId ?? '')}, epoch=${String(epoch ?? '')}, responder=${String(payload.responder_aid ?? '')}, aid=${aid}, strict=${!!opts?.strict}`);
1093
+ if (!groupId || epoch == null || !groupSecretB64 || !commitment) {
1094
+ logger?.warn(`handleKeyResponse rejected: missing required fields group=${String(groupId ?? '')} epoch=${String(epoch ?? '')} has_secret=${!!groupSecretB64} has_commitment=${!!commitment}`);
1033
1095
  return false;
1096
+ }
1034
1097
  const expected = opts?.expectedRequest ?? null;
1035
1098
  if (expected) {
1036
- if (payload.requester_aid !== aid)
1099
+ if (payload.requester_aid !== aid) {
1100
+ logger?.warn(`handleKeyResponse rejected: requester_aid mismatch group=${groupId} epoch=${epoch} got=${String(payload.requester_aid)} expected=${aid}`);
1037
1101
  return false;
1102
+ }
1038
1103
  const expectedResponder = String(expected._expected_responder_aid ?? '');
1039
- if (expectedResponder && payload.responder_aid !== expectedResponder)
1104
+ if (expectedResponder && payload.responder_aid !== expectedResponder) {
1105
+ logger?.warn(`handleKeyResponse rejected: responder_aid mismatch group=${groupId} epoch=${epoch} got=${String(payload.responder_aid)} expected=${expectedResponder}`);
1040
1106
  return false;
1041
- if (payload.request_id !== expected.request_id)
1107
+ }
1108
+ if (payload.request_id !== expected.request_id) {
1109
+ logger?.warn(`handleKeyResponse rejected: request_id mismatch group=${groupId} epoch=${epoch}`);
1042
1110
  return false;
1043
- if (payload.group_id !== expected.group_id)
1111
+ }
1112
+ if (payload.group_id !== expected.group_id) {
1113
+ logger?.warn(`handleKeyResponse rejected: group_id mismatch with expected request`);
1044
1114
  return false;
1045
- if (Number(payload.epoch ?? 0) !== Number(expected.epoch ?? 0))
1115
+ }
1116
+ if (Number(payload.epoch ?? 0) !== Number(expected.epoch ?? 0)) {
1117
+ logger?.warn(`handleKeyResponse rejected: epoch mismatch with expected request group=${groupId}`);
1046
1118
  return false;
1119
+ }
1047
1120
  }
1048
1121
  const responderAid = String(payload.responder_aid ?? '');
1049
1122
  if (opts?.strict) {
1050
- if (!responderAid || !opts.responderCertPem)
1123
+ if (!responderAid || !opts.responderCertPem) {
1124
+ logger?.warn(`handleKeyResponse rejected (strict): missing responder aid or cert group=${groupId} epoch=${epoch}`);
1051
1125
  return false;
1052
- if ((opts.currentMembers?.length ?? 0) > 0 && !opts.currentMembers.includes(responderAid))
1126
+ }
1127
+ if ((opts.currentMembers?.length ?? 0) > 0 && !opts.currentMembers.includes(responderAid)) {
1128
+ logger?.warn(`handleKeyResponse rejected (strict): responder not in current members group=${groupId} epoch=${epoch} responder=${responderAid}`);
1053
1129
  return false;
1054
- if (!verifyGroupKeyResponseSignature(payload, opts.responderCertPem))
1130
+ }
1131
+ if (!verifyGroupKeyResponseSignature(payload, opts.responderCertPem)) {
1132
+ logger?.warn(`handleKeyResponse rejected (strict): response signature verification failed group=${groupId} epoch=${epoch} responder=${responderAid}`);
1055
1133
  return false;
1134
+ }
1056
1135
  }
1057
1136
  else if (opts?.responderCertPem && payload.response_signature) {
1058
- if (!verifyGroupKeyResponseSignature(payload, opts.responderCertPem))
1137
+ if (!verifyGroupKeyResponseSignature(payload, opts.responderCertPem)) {
1138
+ logger?.warn(`handleKeyResponse rejected: response signature verification failed group=${groupId} epoch=${epoch} responder=${responderAid}`);
1059
1139
  return false;
1140
+ }
1060
1141
  }
1061
1142
  const groupSecret = Buffer.from(groupSecretB64, 'base64');
1062
1143
  if (!verifyMembershipCommitment(commitment, memberAids, epoch, groupId, aid, groupSecret)) {
1144
+ logger?.warn(`handleKeyResponse rejected: commitment verification failed group=${groupId} epoch=${epoch}`);
1063
1145
  return false;
1064
1146
  }
1065
1147
  const manifest = isJsonObject(payload.manifest) ? payload.manifest : null;
1066
1148
  if (manifest) {
1067
- if (manifest.group_id !== groupId || manifest.epoch !== epoch)
1149
+ if (manifest.group_id !== groupId || manifest.epoch !== epoch) {
1150
+ logger?.warn(`handleKeyResponse rejected: manifest group_id/epoch mismatch group=${groupId} epoch=${epoch}`);
1068
1151
  return false;
1152
+ }
1069
1153
  const manifestMembers = Array.isArray(manifest.member_aids)
1070
1154
  ? manifest.member_aids.map((item) => String(item ?? '').trim()).filter(Boolean).sort()
1071
1155
  : [];
1072
1156
  const payloadMembers = memberAids.map((item) => String(item ?? '').trim()).filter(Boolean).sort();
1073
- if (manifestMembers.length > 0 && manifestMembers.join('\n') !== payloadMembers.join('\n'))
1157
+ if (manifestMembers.length > 0 && manifestMembers.join('\n') !== payloadMembers.join('\n')) {
1158
+ logger?.warn(`handleKeyResponse rejected: manifest members mismatch payload members group=${groupId} epoch=${epoch}`);
1074
1159
  return false;
1160
+ }
1075
1161
  }
1076
1162
  const incomingChain = typeof payload.epoch_chain === 'string' ? payload.epoch_chain : undefined;
1077
1163
  const rotationId = typeof payload.rotation_id === 'string' ? payload.rotation_id : '';
1078
- const chainAssessment = assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, String(payload.distributed_by ?? payload.rotator_aid ?? payload.responder_aid ?? ''), 'key_response');
1079
- if (!chainAssessment.ok)
1164
+ const chainAssessment = assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, String(payload.distributed_by ?? payload.rotator_aid ?? payload.responder_aid ?? ''), 'key_response', opts?.logger);
1165
+ if (!chainAssessment.ok) {
1166
+ logger?.warn(`handleKeyResponse rejected: epoch chain assessment failed group=${groupId} epoch=${epoch} reason=${chainAssessment.reason ?? ''}`);
1080
1167
  return false;
1168
+ }
1081
1169
  return storeGroupSecretEpoch(keystore, aid, groupId, epoch, groupSecret, commitment, memberAids, incomingChain, rotationId, chainAssessment.unverified, chainAssessment.reason);
1082
1170
  }
1083
1171
  // ── GroupE2EEManager 类 ───────────────────────────────────────
@@ -1090,6 +1178,7 @@ export class GroupE2EEManager {
1090
1178
  _senderCertResolver;
1091
1179
  _initiatorCertResolver;
1092
1180
  _pendingKeyRequests = new Map();
1181
+ _logger;
1093
1182
  constructor(opts) {
1094
1183
  this._identityFn = opts.identityFn;
1095
1184
  this._keystore = opts.keystore;
@@ -1098,6 +1187,7 @@ export class GroupE2EEManager {
1098
1187
  this._responseThrottle = new GroupKeyRequestThrottle(opts.responseCooldown ?? 30);
1099
1188
  this._senderCertResolver = opts.senderCertResolver ?? null;
1100
1189
  this._initiatorCertResolver = opts.initiatorCertResolver ?? null;
1190
+ this._logger = opts.logger ?? _noopLogger;
1101
1191
  }
1102
1192
  // ── 密钥管理 ──────────────────────────────────────────
1103
1193
  /** 用当前身份私钥签名 manifest */
@@ -1111,6 +1201,7 @@ export class GroupE2EEManager {
1111
1201
  /** 创建首个 epoch。返回 {epoch, commitment, distributions: [{to, payload}]} */
1112
1202
  createEpoch(groupId, memberAids) {
1113
1203
  const aid = this._currentAid();
1204
+ this._logger.debug(`create first epoch: groupId=${groupId}, members=${memberAids.length}, aid=${aid}`);
1114
1205
  const gs = generateGroupSecret();
1115
1206
  const epoch = 1;
1116
1207
  const commitment = computeMembershipCommitment(memberAids, epoch, groupId, gs);
@@ -1118,6 +1209,7 @@ export class GroupE2EEManager {
1118
1209
  storeGroupSecret(this._keystore, aid, groupId, epoch, gs, commitment, memberAids, epochChain);
1119
1210
  const manifest = this._signManifest(buildMembershipManifest(groupId, epoch, null, memberAids, { initiatorAid: aid }));
1120
1211
  const distPayload = buildKeyDistribution(groupId, epoch, gs, memberAids, aid, manifest, epochChain);
1212
+ this._logger.debug(`create first epoch success: groupId=${groupId}, epoch=${epoch}`);
1121
1213
  return {
1122
1214
  epoch,
1123
1215
  commitment,
@@ -1132,16 +1224,19 @@ export class GroupE2EEManager {
1132
1224
  const current = loadGroupSecret(this._keystore, aid, groupId);
1133
1225
  const prevEpoch = current ? Number(current.epoch) : null;
1134
1226
  const newEpoch = (prevEpoch ?? 0) + 1;
1227
+ this._logger.debug(`rotateEpoch start: groupId=${groupId}, prevEpoch=${prevEpoch}, newEpoch=${newEpoch}, members=${memberAids.length}`);
1135
1228
  const gs = generateGroupSecret();
1136
1229
  const commitment = computeMembershipCommitment(memberAids, newEpoch, groupId, gs);
1137
1230
  const prevChain = current?.epoch_chain ?? null;
1138
1231
  const epochChain = computeEpochChain(prevChain, newEpoch, commitment, aid);
1139
1232
  const stored = storeGroupSecret(this._keystore, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain);
1140
1233
  if (!stored) {
1234
+ this._logger.warn(`rotateEpoch failed (epoch already exists or newer): groupId=${groupId}, newEpoch=${newEpoch}`);
1141
1235
  throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1142
1236
  }
1143
1237
  const manifest = this._signManifest(buildMembershipManifest(groupId, newEpoch, prevEpoch, memberAids, { initiatorAid: aid }));
1144
1238
  const distPayload = buildKeyDistribution(groupId, newEpoch, gs, memberAids, aid, manifest, epochChain);
1239
+ this._logger.debug(`rotateEpoch success: groupId=${groupId}, newEpoch=${newEpoch}`);
1145
1240
  return {
1146
1241
  epoch: newEpoch,
1147
1242
  commitment,
@@ -1153,6 +1248,7 @@ export class GroupE2EEManager {
1153
1248
  /** 指定目标 epoch 号轮换(配合服务端 CAS 使用) */
1154
1249
  rotateEpochTo(groupId, newEpoch, memberAids, opts) {
1155
1250
  const aid = this._currentAid();
1251
+ this._logger.debug(`rotateEpochTo start: groupId=${groupId}, newEpoch=${newEpoch}, members=${memberAids.length}`);
1156
1252
  const current = loadGroupSecret(this._keystore, aid, groupId, newEpoch - 1)
1157
1253
  ?? loadGroupSecret(this._keystore, aid, groupId);
1158
1254
  const gs = generateGroupSecret();
@@ -1165,6 +1261,7 @@ export class GroupE2EEManager {
1165
1261
  const rotationId = opts?.rotationId ?? '';
1166
1262
  const stored = storeGroupSecret(this._keystore, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain, rotationId);
1167
1263
  if (!stored) {
1264
+ this._logger.warn(`rotateEpochTo failed (epoch already exists or newer): groupId=${groupId}, newEpoch=${newEpoch}`);
1168
1265
  throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1169
1266
  }
1170
1267
  const manifest = this._signManifest(buildMembershipManifest(groupId, newEpoch, newEpoch - 1, memberAids, { initiatorAid: aid }));
@@ -1172,6 +1269,7 @@ export class GroupE2EEManager {
1172
1269
  if (rotationId) {
1173
1270
  distPayload.rotation_id = rotationId;
1174
1271
  }
1272
+ this._logger.debug(`rotateEpochTo success: groupId=${groupId}, newEpoch=${newEpoch}, rotationId=${rotationId}`);
1175
1273
  return {
1176
1274
  epoch: newEpoch,
1177
1275
  commitment,
@@ -1186,8 +1284,10 @@ export class GroupE2EEManager {
1186
1284
  /** 加密群消息(含发送方签名)。无密钥时抛 E2EEGroupSecretMissingError。 */
1187
1285
  encrypt(groupId, payload, opts) {
1188
1286
  const aid = this._currentAid();
1287
+ this._logger.debug(`group message encrypt start: groupId=${groupId}, aid=${aid}`);
1189
1288
  const secretData = loadGroupSecret(this._keystore, aid, groupId);
1190
1289
  if (!secretData) {
1290
+ this._logger.error(`group message encrypt failed: missing group secret, groupId=${groupId}`);
1191
1291
  throw new E2EEGroupSecretMissingError(`no group secret for ${groupId}`);
1192
1292
  }
1193
1293
  const identity = this._identityFn();
@@ -1197,7 +1297,7 @@ export class GroupE2EEManager {
1197
1297
  throw new E2EEError('sender identity private key unavailable for group message signing');
1198
1298
  }
1199
1299
  const senderCertPem = identity ? identity.cert ?? null : null;
1200
- return encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1300
+ const result = encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1201
1301
  fromAid: aid,
1202
1302
  messageId: opts?.messageId ?? `gm-${crypto.randomUUID()}`,
1203
1303
  timestamp: opts?.timestamp ?? Date.now(),
@@ -1206,6 +1306,8 @@ export class GroupE2EEManager {
1206
1306
  protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1207
1307
  context: opts?.context ?? null,
1208
1308
  });
1309
+ this._logger.debug(`group message encrypt success: groupId=${groupId}, epoch=${secretData.epoch}`);
1310
+ return result;
1209
1311
  }
1210
1312
  /** 使用指定 epoch 加密群消息。 */
1211
1313
  encryptWithEpoch(groupId, epoch, payload, opts) {
@@ -1238,12 +1340,14 @@ export class GroupE2EEManager {
1238
1340
  }
1239
1341
  const groupId = message.group_id || '';
1240
1342
  const sender = message.from || message.sender_aid || '';
1343
+ this._logger.debug(`group message decrypt start: groupId=${groupId}, from=${sender}`);
1241
1344
  // 防重放预检:优先使用 AAD 内 message_id
1242
1345
  const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
1243
1346
  const aadMsgId = aad ? (aad.message_id ?? '') : '';
1244
1347
  const msgId = aadMsgId || message.message_id || '';
1245
1348
  if (!opts?.skipReplay && groupId && sender && msgId) {
1246
1349
  if (this._replayGuard.isSeen(groupId, sender, msgId)) {
1350
+ this._logger.debug(`group message replay blocked: groupId=${groupId}, from=${sender}, mid=${msgId}`);
1247
1351
  // 返回原消息(不含 e2ee 字段),调用方可通过缺失 e2ee 识别 replay
1248
1352
  return message;
1249
1353
  }
@@ -1253,18 +1357,26 @@ export class GroupE2EEManager {
1253
1357
  if (this._senderCertResolver && sender) {
1254
1358
  senderCertPem = this._senderCertResolver(sender);
1255
1359
  }
1256
- if (!senderCertPem)
1360
+ if (!senderCertPem) {
1361
+ this._logger.warn(`group message decrypt rejected: sender cert unavailable, groupId=${groupId}, from=${sender}`);
1257
1362
  return null;
1363
+ }
1258
1364
  const allSecrets = loadAllGroupSecrets(this._keystore, this._currentAid(), groupId);
1259
- if (allSecrets.size === 0)
1365
+ if (allSecrets.size === 0) {
1366
+ this._logger.warn(`group message decrypt failed: no group secret, groupId=${groupId}`);
1260
1367
  return null;
1261
- const result = decryptGroupMessage(message, allSecrets, senderCertPem);
1368
+ }
1369
+ const result = decryptGroupMessage(message, allSecrets, senderCertPem, { logger: this._logger });
1262
1370
  // 解密成功后记录防重放
1263
1371
  if (result != null) {
1264
1372
  const finalMsgId = aadMsgId || message.message_id || '';
1265
1373
  if (!opts?.skipReplay && groupId && sender && finalMsgId) {
1266
1374
  this._replayGuard.record(groupId, sender, finalMsgId);
1267
1375
  }
1376
+ this._logger.debug(`group message decrypt success: groupId=${groupId}, from=${sender}, mid=${msgId}`);
1377
+ }
1378
+ else {
1379
+ this._logger.warn(`group message decrypt failed: groupId=${groupId}, from=${sender}, mid=${msgId}`);
1268
1380
  }
1269
1381
  return result;
1270
1382
  }
@@ -1283,19 +1395,33 @@ export class GroupE2EEManager {
1283
1395
  const msgType = payload.type || '';
1284
1396
  const aid = this._currentAid();
1285
1397
  if (msgType === 'e2ee.group_key_distribution') {
1286
- let initiatorCert = null;
1398
+ const groupId = payload.group_id || '';
1399
+ const epoch = payload.epoch;
1287
1400
  const distributedBy = payload.distributed_by || '';
1401
+ this._logger.debug(`received key distribution: groupId=${groupId}, epoch=${epoch}, from=${distributedBy}`);
1402
+ let initiatorCert = null;
1288
1403
  if (this._initiatorCertResolver && distributedBy) {
1289
1404
  initiatorCert = this._initiatorCertResolver(distributedBy);
1290
1405
  }
1291
- const ok = handleKeyDistribution(payload, this._keystore, aid, initiatorCert);
1406
+ const ok = handleKeyDistribution(payload, this._keystore, aid, initiatorCert, this._logger);
1407
+ if (ok) {
1408
+ this._logger.debug(`key distribution handled successfully: groupId=${groupId}, epoch=${epoch}`);
1409
+ }
1410
+ else {
1411
+ this._logger.warn(`key distribution rejected: groupId=${groupId}, epoch=${epoch}, from=${distributedBy}`);
1412
+ }
1292
1413
  return ok ? 'distribution' : 'distribution_rejected';
1293
1414
  }
1294
1415
  if (msgType === 'e2ee.group_key_response') {
1416
+ const groupId = payload.group_id || '';
1417
+ const epoch = payload.epoch;
1418
+ this._logger.debug(`received key response: groupId=${groupId}, epoch=${epoch}`);
1295
1419
  const pendingKey = `${String(payload.group_id ?? '')}:${String(payload.epoch ?? '')}:${String(payload.request_id ?? '')}`;
1296
1420
  const expected = this._pendingKeyRequests.get(pendingKey) ?? null;
1297
- if (expected === null)
1421
+ if (expected === null) {
1422
+ this._logger.warn(`key response rejected (no matching request): groupId=${groupId}, epoch=${epoch}`);
1298
1423
  return 'response_rejected';
1424
+ }
1299
1425
  const responderAid = String(payload.responder_aid ?? '');
1300
1426
  const responderCertPem = responderAid && this._initiatorCertResolver
1301
1427
  ? this._initiatorCertResolver(responderAid)
@@ -1305,12 +1431,22 @@ export class GroupE2EEManager {
1305
1431
  responderCertPem,
1306
1432
  currentMembers: this.getMemberAids(String(payload.group_id ?? '')),
1307
1433
  strict: true,
1434
+ logger: this._logger,
1308
1435
  });
1309
1436
  if (ok && expected)
1310
1437
  this._pendingKeyRequests.delete(pendingKey);
1438
+ if (ok) {
1439
+ this._logger.debug(`key response handled successfully: groupId=${groupId}, epoch=${epoch}, responder=${responderAid}`);
1440
+ }
1441
+ else {
1442
+ this._logger.warn(`key response verification failed: groupId=${groupId}, epoch=${epoch}, responder=${responderAid}`);
1443
+ }
1311
1444
  return ok ? 'response' : 'response_rejected';
1312
1445
  }
1313
1446
  if (msgType === 'e2ee.group_key_request') {
1447
+ const groupId = payload.group_id || '';
1448
+ const requester = payload.requester_aid || '';
1449
+ this._logger.debug(`received key request: groupId=${groupId}, requester=${requester}, epoch=${payload.epoch}`);
1314
1450
  return 'request';
1315
1451
  }
1316
1452
  return null;
@@ -1350,16 +1486,28 @@ export class GroupE2EEManager {
1350
1486
  if (!requester || !groupId)
1351
1487
  return null;
1352
1488
  // 成员资格验证
1353
- if (!members.includes(requester))
1489
+ if (!members.includes(requester)) {
1490
+ this._logger.warn(`key request rejected (not a member): groupId=${groupId}, requester=${requester}`);
1354
1491
  return null;
1492
+ }
1355
1493
  if (!this._responseThrottle.allow(`response:${groupId}:${requester}`)) {
1494
+ this._logger.debug(`key request throttled: groupId=${groupId}, requester=${requester}`);
1356
1495
  return null;
1357
1496
  }
1358
1497
  const identity = this._identityFn();
1359
1498
  const privateKeyPem = identity.private_key_pem;
1360
- if (!privateKeyPem)
1499
+ if (!privateKeyPem) {
1500
+ this._logger.error(`key request handling failed: missing private key, groupId=${groupId}`);
1361
1501
  return null;
1362
- return handleKeyRequest(requestPayload, this._keystore, this._currentAid(), members, privateKeyPem);
1502
+ }
1503
+ const response = handleKeyRequest(requestPayload, this._keystore, this._currentAid(), members, privateKeyPem, this._logger);
1504
+ if (response) {
1505
+ this._logger.debug(`key request responded: groupId=${groupId}, requester=${requester}, epoch=${requestPayload.epoch}`);
1506
+ }
1507
+ else {
1508
+ this._logger.warn(`key request handling failed (no local key or requester not in epoch members): groupId=${groupId}, requester=${requester}`);
1509
+ }
1510
+ return response;
1363
1511
  }
1364
1512
  // ── 状态查询 ──────────────────────────────────────────
1365
1513
  /** 检查是否有群组密钥 */
@@ -1378,7 +1526,14 @@ export class GroupE2EEManager {
1378
1526
  }
1379
1527
  /** 加载群组密钥数据 */
1380
1528
  loadSecret(groupId, epoch) {
1381
- return loadGroupSecret(this._keystore, this._currentAid(), groupId, epoch);
1529
+ const result = loadGroupSecret(this._keystore, this._currentAid(), groupId, epoch);
1530
+ if (result) {
1531
+ this._logger.debug(`load group secret success: groupId=${groupId}, epoch=${result.epoch}`);
1532
+ }
1533
+ else {
1534
+ this._logger.debug(`load group secret not found: groupId=${groupId}, requestedEpoch=${epoch ?? 'latest'}`);
1535
+ }
1536
+ return result;
1382
1537
  }
1383
1538
  /** 清理过期的旧 epoch */
1384
1539
  cleanup(groupId, retentionSeconds = OLD_EPOCH_RETENTION_SECONDS) {