@agentunion/fastaun 0.2.17 → 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.
@@ -460,17 +460,26 @@ export function encryptGroupMessage(groupId, epoch, groupSecret, payload, opts)
460
460
  */
461
461
  export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts) {
462
462
  const requireSignature = opts?.requireSignature ?? true;
463
+ const logger = opts?.logger;
463
464
  const payload = isJsonObject(message.payload) ? message.payload : null;
464
- if (payload === null)
465
+ if (payload === null) {
466
+ logger?.warn(`decryptGroupMessage rejected: payload is not an object mid=${String(message.message_id ?? '')}`);
465
467
  return null;
466
- 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 ?? '')}`);
467
471
  return null;
472
+ }
468
473
  const epoch = payload.epoch;
469
- if (epoch == null)
474
+ if (epoch == null) {
475
+ logger?.warn(`decryptGroupMessage rejected: missing epoch mid=${String(message.message_id ?? '')}`);
470
476
  return null;
477
+ }
471
478
  const groupSecret = groupSecrets.get(epoch);
472
- if (!groupSecret)
479
+ if (!groupSecret) {
480
+ logger?.warn(`decryptGroupMessage rejected: no group secret for epoch=${epoch} mid=${String(message.message_id ?? '')}`);
473
481
  return null;
482
+ }
474
483
  try {
475
484
  // 优先从 AAD 读取 group_id 和 message_id
476
485
  const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
@@ -483,34 +492,44 @@ export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts)
483
492
  messageId = aad.message_id || message.message_id || '';
484
493
  aadFrom = aad.from || '';
485
494
  // 外层路由字段与 AAD 绑定校验
486
- 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}`);
487
497
  return null;
498
+ }
488
499
  if (aadFrom) {
489
500
  const outerFrom = message.from || '';
490
501
  const outerSender = message.sender_aid || '';
491
- 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}`);
492
504
  return null;
493
- 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}`);
494
508
  return null;
509
+ }
495
510
  }
496
511
  }
497
512
  else {
498
513
  groupId = outerGroupId;
499
514
  messageId = message.message_id || '';
500
515
  }
501
- if (!groupId || !messageId)
516
+ if (!groupId || !messageId) {
517
+ logger?.warn(`decryptGroupMessage rejected: missing group_id or message_id group=${groupId} mid=${messageId}`);
502
518
  return null;
519
+ }
503
520
  const msgKey = deriveGroupMsgKey(groupSecret, groupId, messageId);
504
521
  const nonce = Buffer.from(payload.nonce, 'base64');
505
522
  const ciphertext = Buffer.from(payload.ciphertext, 'base64');
506
523
  const tag = Buffer.from(payload.tag, 'base64');
507
524
  if (!verifyEnvelopeMetadataAuth(payload, msgKey)) {
525
+ logger?.warn(`decryptGroupMessage rejected: envelope metadata auth failed group=${groupId} mid=${messageId}`);
508
526
  return null;
509
527
  }
510
528
  const aadBytes = aad ? aadBytesGroup(aad) : Buffer.alloc(0);
511
529
  const plaintext = aesGcmDecrypt(msgKey, ciphertext, tag, nonce, aadBytes);
512
530
  const decoded = JSON.parse(plaintext.toString('utf-8'));
513
531
  if (!validateDecryptedEnvelopeMetadata(decoded, payload, message)) {
532
+ logger?.warn(`decryptGroupMessage rejected: decrypted envelope metadata validation failed group=${groupId} mid=${messageId}`);
514
533
  return null;
515
534
  }
516
535
  const e2ee = {
@@ -534,43 +553,56 @@ export function decryptGroupMessage(message, groupSecrets, senderCertPem, opts)
534
553
  // 发送方签名验证
535
554
  const senderSigB64 = payload.sender_signature;
536
555
  if (requireSignature) {
537
- if (!senderSigB64)
556
+ if (!senderSigB64) {
557
+ logger?.warn(`decryptGroupMessage rejected: sender signature required but missing group=${groupId} mid=${messageId}`);
538
558
  return null;
539
- if (!senderCertPem)
559
+ }
560
+ if (!senderCertPem) {
561
+ logger?.warn(`decryptGroupMessage rejected: sender signature required but senderCertPem missing group=${groupId} mid=${messageId}`);
540
562
  return null;
563
+ }
541
564
  try {
542
565
  const senderPub = pemToCertPublicKey(senderCertPem);
543
566
  const sigBytes = Buffer.from(senderSigB64, 'base64');
544
567
  const verifyPayload = Buffer.concat([ciphertext, tag, aadBytes]);
545
- if (!ecdsaVerify(senderPub, sigBytes, verifyPayload))
568
+ if (!ecdsaVerify(senderPub, sigBytes, verifyPayload)) {
569
+ logger?.warn(`decryptGroupMessage rejected: sender signature verification failed group=${groupId} mid=${messageId}`);
546
570
  return null;
571
+ }
547
572
  if (isJsonObject(result.e2ee))
548
573
  result.e2ee.sender_verified = true;
549
574
  }
550
- catch {
575
+ catch (exc) {
576
+ logger?.warn(`decryptGroupMessage rejected: sender signature verification threw group=${groupId} mid=${messageId} err=${String(exc)}`);
551
577
  return null;
552
578
  }
553
579
  }
554
580
  else if (senderCertPem) {
555
581
  // 非零信任模式但提供了证书:有证书时强制验签
556
- if (!senderSigB64)
582
+ if (!senderSigB64) {
583
+ logger?.warn(`decryptGroupMessage rejected: senderCertPem present but signature missing group=${groupId} mid=${messageId}`);
557
584
  return null;
585
+ }
558
586
  try {
559
587
  const senderPub = pemToCertPublicKey(senderCertPem);
560
588
  const sigBytes = Buffer.from(senderSigB64, 'base64');
561
589
  const verifyPayload = Buffer.concat([ciphertext, tag, aadBytes]);
562
- 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}`);
563
592
  return null;
593
+ }
564
594
  if (isJsonObject(result.e2ee))
565
595
  result.e2ee.sender_verified = true;
566
596
  }
567
- catch {
597
+ catch (exc) {
598
+ logger?.warn(`decryptGroupMessage rejected: optional sender signature verification threw group=${groupId} mid=${messageId} err=${String(exc)}`);
568
599
  return null;
569
600
  }
570
601
  }
571
602
  return result;
572
603
  }
573
- catch {
604
+ catch (exc) {
605
+ logger?.warn(`decryptGroupMessage rejected: decrypt threw mid=${String(message.message_id ?? '')} err=${String(exc)}`);
574
606
  return null;
575
607
  }
576
608
  }
@@ -753,7 +785,7 @@ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, inc
753
785
  const rid = rotationId.trim();
754
786
  const rotator = rotatorAid.trim();
755
787
  if (rid && !chain) {
756
- logger.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}`);
757
789
  return { ok: false };
758
790
  }
759
791
  const current = loadGroupSecret(keystore, aid, groupId);
@@ -764,7 +796,7 @@ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, inc
764
796
  return { ok: true };
765
797
  if (rid && chain && currentChain && currentChain !== chain) {
766
798
  if (!(currentPendingRotationId && currentPendingRotationId !== rid)) {
767
- logger.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}`);
768
800
  return { ok: false };
769
801
  }
770
802
  }
@@ -777,17 +809,17 @@ function assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, inc
777
809
  return { ok: true, unverified: true, reason: 'missing_prev_chain' };
778
810
  if (!rotator) {
779
811
  if (rid) {
780
- logger.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}`);
781
813
  return { ok: false };
782
814
  }
783
815
  return { ok: true, unverified: true, reason: 'missing_rotator_aid' };
784
816
  }
785
817
  if (!verifyEpochChain(chain, prevChain, epoch, commitment, rotator)) {
786
818
  if (rid) {
787
- logger.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}`);
788
820
  return { ok: false };
789
821
  }
790
- logger.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}`);
791
823
  return { ok: true, unverified: true, reason: 'chain_mismatch_legacy' };
792
824
  }
793
825
  if (!rid)
@@ -924,40 +956,58 @@ export function handleKeyDistribution(message, keystore, aid, initiatorCertPem,
924
956
  const groupSecretB64 = payload.group_secret;
925
957
  const commitment = payload.commitment;
926
958
  const memberAids = payload.member_aids ?? [];
927
- 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}`);
928
962
  return false;
963
+ }
929
964
  // 验证 Membership Manifest 签名
930
965
  const manifest = isJsonObject(payload.manifest) ? payload.manifest : undefined;
931
966
  if (initiatorCertPem) {
932
- if (!manifest)
967
+ if (!manifest) {
968
+ logger?.warn(`handleKeyDistribution rejected: manifest required when initiatorCertPem present group=${groupId} epoch=${epoch}`);
933
969
  return false;
934
- if (!verifyMembershipManifest(manifest, initiatorCertPem))
970
+ }
971
+ if (!verifyMembershipManifest(manifest, initiatorCertPem)) {
972
+ logger?.warn(`handleKeyDistribution rejected: manifest signature verification failed group=${groupId} epoch=${epoch}`);
935
973
  return false;
936
- 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)}`);
937
977
  return false;
978
+ }
938
979
  const manifestMembers = [...(manifest.member_aids ?? [])].sort();
939
980
  const payloadMembers = [...memberAids].sort();
940
- 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}`);
941
983
  return false;
984
+ }
942
985
  }
943
986
  else if (manifest) {
944
- 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}`);
945
989
  return false;
990
+ }
946
991
  const manifestMembers = [...(manifest.member_aids ?? [])].sort();
947
992
  const payloadMembers = [...memberAids].sort();
948
- 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}`);
949
995
  return false;
996
+ }
950
997
  }
951
998
  const groupSecret = Buffer.from(groupSecretB64, 'base64');
952
999
  // 验证 commitment
953
1000
  if (!verifyMembershipCommitment(commitment, memberAids, epoch, groupId, aid, groupSecret)) {
1001
+ logger?.warn(`handleKeyDistribution rejected: commitment verification failed group=${groupId} epoch=${epoch}`);
954
1002
  return false;
955
1003
  }
956
1004
  const incomingChain = typeof payload.epoch_chain === 'string' ? payload.epoch_chain : undefined;
957
1005
  const rotationId = typeof payload.rotation_id === 'string' ? payload.rotation_id : '';
958
1006
  const chainAssessment = assessIncomingEpochChain(keystore, aid, groupId, epoch, commitment, incomingChain, rotationId, String(payload.distributed_by ?? payload.rotator_aid ?? ''), 'key_distribution', logger);
959
- if (!chainAssessment.ok)
1007
+ if (!chainAssessment.ok) {
1008
+ logger?.warn(`handleKeyDistribution rejected: epoch chain assessment failed group=${groupId} epoch=${epoch} reason=${chainAssessment.reason ?? ''}`);
960
1009
  return false;
1010
+ }
961
1011
  return storeGroupSecret(keystore, aid, groupId, epoch, groupSecret, commitment, memberAids, incomingChain, rotationId, chainAssessment.unverified, chainAssessment.reason);
962
1012
  }
963
1013
  /** 构建密钥请求 payload */
@@ -972,26 +1022,34 @@ export function buildKeyRequest(groupId, epoch, requesterAid, requestId) {
972
1022
  };
973
1023
  }
974
1024
  /** 处理收到的密钥请求 */
975
- export function handleKeyRequest(request, keystore, aid, currentMembers, privateKeyPem) {
1025
+ export function handleKeyRequest(request, keystore, aid, currentMembers, privateKeyPem, logger) {
976
1026
  const payload = 'group_id' in request
977
1027
  ? request
978
1028
  : (isJsonObject(request.payload) ? request.payload : request);
979
1029
  const requesterAid = payload.requester_aid;
980
1030
  const groupId = payload.group_id;
981
1031
  const epoch = payload.epoch;
982
- 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 ?? '')}`);
983
1035
  return null;
1036
+ }
984
1037
  // 验证请求者是群成员
985
- if (!currentMembers.includes(requesterAid))
1038
+ if (!currentMembers.includes(requesterAid)) {
1039
+ logger?.warn(`handleKeyRequest rejected: requester not in current members group=${groupId} requester=${requesterAid}`);
986
1040
  return null;
1041
+ }
987
1042
  // 查本地密钥
988
1043
  const secretData = loadGroupSecret(keystore, aid, groupId, epoch);
989
- if (!secretData)
1044
+ if (!secretData) {
1045
+ logger?.warn(`handleKeyRequest rejected: no local secret for epoch group=${groupId} epoch=${epoch}`);
990
1046
  return null;
1047
+ }
991
1048
  // 历史隔离:密钥存储中记录了该 epoch 的成员列表,
992
1049
  // 若请求者不在该列表中,说明其不属于该 epoch,直接拒绝(不响应)。
993
1050
  const memberAids = (secretData.member_aids ?? []).map(String).filter(Boolean).sort();
994
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}`);
995
1053
  return null;
996
1054
  }
997
1055
  // 确定响应使用的成员列表:优先使用密钥存储中的成员列表,
@@ -1030,55 +1088,84 @@ export function handleKeyResponse(response, keystore, aid, opts) {
1030
1088
  const groupSecretB64 = payload.group_secret;
1031
1089
  const commitment = payload.commitment;
1032
1090
  const memberAids = payload.member_aids ?? [];
1033
- 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}`);
1034
1095
  return false;
1096
+ }
1035
1097
  const expected = opts?.expectedRequest ?? null;
1036
1098
  if (expected) {
1037
- 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}`);
1038
1101
  return false;
1102
+ }
1039
1103
  const expectedResponder = String(expected._expected_responder_aid ?? '');
1040
- 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}`);
1041
1106
  return false;
1042
- 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}`);
1043
1110
  return false;
1044
- 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`);
1045
1114
  return false;
1046
- 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}`);
1047
1118
  return false;
1119
+ }
1048
1120
  }
1049
1121
  const responderAid = String(payload.responder_aid ?? '');
1050
1122
  if (opts?.strict) {
1051
- if (!responderAid || !opts.responderCertPem)
1123
+ if (!responderAid || !opts.responderCertPem) {
1124
+ logger?.warn(`handleKeyResponse rejected (strict): missing responder aid or cert group=${groupId} epoch=${epoch}`);
1052
1125
  return false;
1053
- 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}`);
1054
1129
  return false;
1055
- 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}`);
1056
1133
  return false;
1134
+ }
1057
1135
  }
1058
1136
  else if (opts?.responderCertPem && payload.response_signature) {
1059
- 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}`);
1060
1139
  return false;
1140
+ }
1061
1141
  }
1062
1142
  const groupSecret = Buffer.from(groupSecretB64, 'base64');
1063
1143
  if (!verifyMembershipCommitment(commitment, memberAids, epoch, groupId, aid, groupSecret)) {
1144
+ logger?.warn(`handleKeyResponse rejected: commitment verification failed group=${groupId} epoch=${epoch}`);
1064
1145
  return false;
1065
1146
  }
1066
1147
  const manifest = isJsonObject(payload.manifest) ? payload.manifest : null;
1067
1148
  if (manifest) {
1068
- 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}`);
1069
1151
  return false;
1152
+ }
1070
1153
  const manifestMembers = Array.isArray(manifest.member_aids)
1071
1154
  ? manifest.member_aids.map((item) => String(item ?? '').trim()).filter(Boolean).sort()
1072
1155
  : [];
1073
1156
  const payloadMembers = memberAids.map((item) => String(item ?? '').trim()).filter(Boolean).sort();
1074
- 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}`);
1075
1159
  return false;
1160
+ }
1076
1161
  }
1077
1162
  const incomingChain = typeof payload.epoch_chain === 'string' ? payload.epoch_chain : undefined;
1078
1163
  const rotationId = typeof payload.rotation_id === 'string' ? payload.rotation_id : '';
1079
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);
1080
- if (!chainAssessment.ok)
1165
+ if (!chainAssessment.ok) {
1166
+ logger?.warn(`handleKeyResponse rejected: epoch chain assessment failed group=${groupId} epoch=${epoch} reason=${chainAssessment.reason ?? ''}`);
1081
1167
  return false;
1168
+ }
1082
1169
  return storeGroupSecretEpoch(keystore, aid, groupId, epoch, groupSecret, commitment, memberAids, incomingChain, rotationId, chainAssessment.unverified, chainAssessment.reason);
1083
1170
  }
1084
1171
  // ── GroupE2EEManager 类 ───────────────────────────────────────
@@ -1114,6 +1201,7 @@ export class GroupE2EEManager {
1114
1201
  /** 创建首个 epoch。返回 {epoch, commitment, distributions: [{to, payload}]} */
1115
1202
  createEpoch(groupId, memberAids) {
1116
1203
  const aid = this._currentAid();
1204
+ this._logger.debug(`create first epoch: groupId=${groupId}, members=${memberAids.length}, aid=${aid}`);
1117
1205
  const gs = generateGroupSecret();
1118
1206
  const epoch = 1;
1119
1207
  const commitment = computeMembershipCommitment(memberAids, epoch, groupId, gs);
@@ -1121,6 +1209,7 @@ export class GroupE2EEManager {
1121
1209
  storeGroupSecret(this._keystore, aid, groupId, epoch, gs, commitment, memberAids, epochChain);
1122
1210
  const manifest = this._signManifest(buildMembershipManifest(groupId, epoch, null, memberAids, { initiatorAid: aid }));
1123
1211
  const distPayload = buildKeyDistribution(groupId, epoch, gs, memberAids, aid, manifest, epochChain);
1212
+ this._logger.debug(`create first epoch success: groupId=${groupId}, epoch=${epoch}`);
1124
1213
  return {
1125
1214
  epoch,
1126
1215
  commitment,
@@ -1135,16 +1224,19 @@ export class GroupE2EEManager {
1135
1224
  const current = loadGroupSecret(this._keystore, aid, groupId);
1136
1225
  const prevEpoch = current ? Number(current.epoch) : null;
1137
1226
  const newEpoch = (prevEpoch ?? 0) + 1;
1227
+ this._logger.debug(`rotateEpoch start: groupId=${groupId}, prevEpoch=${prevEpoch}, newEpoch=${newEpoch}, members=${memberAids.length}`);
1138
1228
  const gs = generateGroupSecret();
1139
1229
  const commitment = computeMembershipCommitment(memberAids, newEpoch, groupId, gs);
1140
1230
  const prevChain = current?.epoch_chain ?? null;
1141
1231
  const epochChain = computeEpochChain(prevChain, newEpoch, commitment, aid);
1142
1232
  const stored = storeGroupSecret(this._keystore, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain);
1143
1233
  if (!stored) {
1234
+ this._logger.warn(`rotateEpoch failed (epoch already exists or newer): groupId=${groupId}, newEpoch=${newEpoch}`);
1144
1235
  throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1145
1236
  }
1146
1237
  const manifest = this._signManifest(buildMembershipManifest(groupId, newEpoch, prevEpoch, memberAids, { initiatorAid: aid }));
1147
1238
  const distPayload = buildKeyDistribution(groupId, newEpoch, gs, memberAids, aid, manifest, epochChain);
1239
+ this._logger.debug(`rotateEpoch success: groupId=${groupId}, newEpoch=${newEpoch}`);
1148
1240
  return {
1149
1241
  epoch: newEpoch,
1150
1242
  commitment,
@@ -1156,6 +1248,7 @@ export class GroupE2EEManager {
1156
1248
  /** 指定目标 epoch 号轮换(配合服务端 CAS 使用) */
1157
1249
  rotateEpochTo(groupId, newEpoch, memberAids, opts) {
1158
1250
  const aid = this._currentAid();
1251
+ this._logger.debug(`rotateEpochTo start: groupId=${groupId}, newEpoch=${newEpoch}, members=${memberAids.length}`);
1159
1252
  const current = loadGroupSecret(this._keystore, aid, groupId, newEpoch - 1)
1160
1253
  ?? loadGroupSecret(this._keystore, aid, groupId);
1161
1254
  const gs = generateGroupSecret();
@@ -1168,6 +1261,7 @@ export class GroupE2EEManager {
1168
1261
  const rotationId = opts?.rotationId ?? '';
1169
1262
  const stored = storeGroupSecret(this._keystore, aid, groupId, newEpoch, gs, commitment, memberAids, epochChain, rotationId);
1170
1263
  if (!stored) {
1264
+ this._logger.warn(`rotateEpochTo failed (epoch already exists or newer): groupId=${groupId}, newEpoch=${newEpoch}`);
1171
1265
  throw new Error(`group ${groupId} epoch ${newEpoch} secret already exists or is newer; abort distribution`);
1172
1266
  }
1173
1267
  const manifest = this._signManifest(buildMembershipManifest(groupId, newEpoch, newEpoch - 1, memberAids, { initiatorAid: aid }));
@@ -1175,6 +1269,7 @@ export class GroupE2EEManager {
1175
1269
  if (rotationId) {
1176
1270
  distPayload.rotation_id = rotationId;
1177
1271
  }
1272
+ this._logger.debug(`rotateEpochTo success: groupId=${groupId}, newEpoch=${newEpoch}, rotationId=${rotationId}`);
1178
1273
  return {
1179
1274
  epoch: newEpoch,
1180
1275
  commitment,
@@ -1189,8 +1284,10 @@ export class GroupE2EEManager {
1189
1284
  /** 加密群消息(含发送方签名)。无密钥时抛 E2EEGroupSecretMissingError。 */
1190
1285
  encrypt(groupId, payload, opts) {
1191
1286
  const aid = this._currentAid();
1287
+ this._logger.debug(`group message encrypt start: groupId=${groupId}, aid=${aid}`);
1192
1288
  const secretData = loadGroupSecret(this._keystore, aid, groupId);
1193
1289
  if (!secretData) {
1290
+ this._logger.error(`group message encrypt failed: missing group secret, groupId=${groupId}`);
1194
1291
  throw new E2EEGroupSecretMissingError(`no group secret for ${groupId}`);
1195
1292
  }
1196
1293
  const identity = this._identityFn();
@@ -1200,7 +1297,7 @@ export class GroupE2EEManager {
1200
1297
  throw new E2EEError('sender identity private key unavailable for group message signing');
1201
1298
  }
1202
1299
  const senderCertPem = identity ? identity.cert ?? null : null;
1203
- return encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1300
+ const result = encryptGroupMessage(groupId, secretData.epoch, secretData.secret, payload, {
1204
1301
  fromAid: aid,
1205
1302
  messageId: opts?.messageId ?? `gm-${crypto.randomUUID()}`,
1206
1303
  timestamp: opts?.timestamp ?? Date.now(),
@@ -1209,6 +1306,8 @@ export class GroupE2EEManager {
1209
1306
  protectedHeaders: opts?.protectedHeaders ?? opts?.protected_headers ?? opts?.headers,
1210
1307
  context: opts?.context ?? null,
1211
1308
  });
1309
+ this._logger.debug(`group message encrypt success: groupId=${groupId}, epoch=${secretData.epoch}`);
1310
+ return result;
1212
1311
  }
1213
1312
  /** 使用指定 epoch 加密群消息。 */
1214
1313
  encryptWithEpoch(groupId, epoch, payload, opts) {
@@ -1241,12 +1340,14 @@ export class GroupE2EEManager {
1241
1340
  }
1242
1341
  const groupId = message.group_id || '';
1243
1342
  const sender = message.from || message.sender_aid || '';
1343
+ this._logger.debug(`group message decrypt start: groupId=${groupId}, from=${sender}`);
1244
1344
  // 防重放预检:优先使用 AAD 内 message_id
1245
1345
  const aad = isJsonObject(payload.aad) ? payload.aad : undefined;
1246
1346
  const aadMsgId = aad ? (aad.message_id ?? '') : '';
1247
1347
  const msgId = aadMsgId || message.message_id || '';
1248
1348
  if (!opts?.skipReplay && groupId && sender && msgId) {
1249
1349
  if (this._replayGuard.isSeen(groupId, sender, msgId)) {
1350
+ this._logger.debug(`group message replay blocked: groupId=${groupId}, from=${sender}, mid=${msgId}`);
1250
1351
  // 返回原消息(不含 e2ee 字段),调用方可通过缺失 e2ee 识别 replay
1251
1352
  return message;
1252
1353
  }
@@ -1256,18 +1357,26 @@ export class GroupE2EEManager {
1256
1357
  if (this._senderCertResolver && sender) {
1257
1358
  senderCertPem = this._senderCertResolver(sender);
1258
1359
  }
1259
- if (!senderCertPem)
1360
+ if (!senderCertPem) {
1361
+ this._logger.warn(`group message decrypt rejected: sender cert unavailable, groupId=${groupId}, from=${sender}`);
1260
1362
  return null;
1363
+ }
1261
1364
  const allSecrets = loadAllGroupSecrets(this._keystore, this._currentAid(), groupId);
1262
- if (allSecrets.size === 0)
1365
+ if (allSecrets.size === 0) {
1366
+ this._logger.warn(`group message decrypt failed: no group secret, groupId=${groupId}`);
1263
1367
  return null;
1264
- const result = decryptGroupMessage(message, allSecrets, senderCertPem);
1368
+ }
1369
+ const result = decryptGroupMessage(message, allSecrets, senderCertPem, { logger: this._logger });
1265
1370
  // 解密成功后记录防重放
1266
1371
  if (result != null) {
1267
1372
  const finalMsgId = aadMsgId || message.message_id || '';
1268
1373
  if (!opts?.skipReplay && groupId && sender && finalMsgId) {
1269
1374
  this._replayGuard.record(groupId, sender, finalMsgId);
1270
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}`);
1271
1380
  }
1272
1381
  return result;
1273
1382
  }
@@ -1286,19 +1395,33 @@ export class GroupE2EEManager {
1286
1395
  const msgType = payload.type || '';
1287
1396
  const aid = this._currentAid();
1288
1397
  if (msgType === 'e2ee.group_key_distribution') {
1289
- let initiatorCert = null;
1398
+ const groupId = payload.group_id || '';
1399
+ const epoch = payload.epoch;
1290
1400
  const distributedBy = payload.distributed_by || '';
1401
+ this._logger.debug(`received key distribution: groupId=${groupId}, epoch=${epoch}, from=${distributedBy}`);
1402
+ let initiatorCert = null;
1291
1403
  if (this._initiatorCertResolver && distributedBy) {
1292
1404
  initiatorCert = this._initiatorCertResolver(distributedBy);
1293
1405
  }
1294
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
+ }
1295
1413
  return ok ? 'distribution' : 'distribution_rejected';
1296
1414
  }
1297
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}`);
1298
1419
  const pendingKey = `${String(payload.group_id ?? '')}:${String(payload.epoch ?? '')}:${String(payload.request_id ?? '')}`;
1299
1420
  const expected = this._pendingKeyRequests.get(pendingKey) ?? null;
1300
- if (expected === null)
1421
+ if (expected === null) {
1422
+ this._logger.warn(`key response rejected (no matching request): groupId=${groupId}, epoch=${epoch}`);
1301
1423
  return 'response_rejected';
1424
+ }
1302
1425
  const responderAid = String(payload.responder_aid ?? '');
1303
1426
  const responderCertPem = responderAid && this._initiatorCertResolver
1304
1427
  ? this._initiatorCertResolver(responderAid)
@@ -1312,9 +1435,18 @@ export class GroupE2EEManager {
1312
1435
  });
1313
1436
  if (ok && expected)
1314
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
+ }
1315
1444
  return ok ? 'response' : 'response_rejected';
1316
1445
  }
1317
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}`);
1318
1450
  return 'request';
1319
1451
  }
1320
1452
  return null;
@@ -1354,16 +1486,28 @@ export class GroupE2EEManager {
1354
1486
  if (!requester || !groupId)
1355
1487
  return null;
1356
1488
  // 成员资格验证
1357
- if (!members.includes(requester))
1489
+ if (!members.includes(requester)) {
1490
+ this._logger.warn(`key request rejected (not a member): groupId=${groupId}, requester=${requester}`);
1358
1491
  return null;
1492
+ }
1359
1493
  if (!this._responseThrottle.allow(`response:${groupId}:${requester}`)) {
1494
+ this._logger.debug(`key request throttled: groupId=${groupId}, requester=${requester}`);
1360
1495
  return null;
1361
1496
  }
1362
1497
  const identity = this._identityFn();
1363
1498
  const privateKeyPem = identity.private_key_pem;
1364
- if (!privateKeyPem)
1499
+ if (!privateKeyPem) {
1500
+ this._logger.error(`key request handling failed: missing private key, groupId=${groupId}`);
1365
1501
  return null;
1366
- 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;
1367
1511
  }
1368
1512
  // ── 状态查询 ──────────────────────────────────────────
1369
1513
  /** 检查是否有群组密钥 */
@@ -1382,7 +1526,14 @@ export class GroupE2EEManager {
1382
1526
  }
1383
1527
  /** 加载群组密钥数据 */
1384
1528
  loadSecret(groupId, epoch) {
1385
- 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;
1386
1537
  }
1387
1538
  /** 清理过期的旧 epoch */
1388
1539
  cleanup(groupId, retentionSeconds = OLD_EPOCH_RETENTION_SECONDS) {