wowok 2.1.16 → 2.1.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 (97) hide show
  1. package/dist/cjs/w/call/base.js +1 -0
  2. package/dist/cjs/w/call/base.js.map +1 -1
  3. package/dist/cjs/w/call/entity.js +1 -0
  4. package/dist/cjs/w/call/entity.js.map +1 -1
  5. package/dist/cjs/w/call/passport.js +1 -0
  6. package/dist/cjs/w/call/passport.js.map +1 -1
  7. package/dist/cjs/w/call/permission.js.map +1 -1
  8. package/dist/cjs/w/call/proof.js +1 -0
  9. package/dist/cjs/w/call/proof.js.map +1 -1
  10. package/dist/cjs/w/call/util.js +1 -0
  11. package/dist/cjs/w/call/util.js.map +1 -1
  12. package/dist/cjs/w/local/account.d.ts +2 -2
  13. package/dist/cjs/w/local/account.js +101 -28
  14. package/dist/cjs/w/local/account.js.map +1 -1
  15. package/dist/cjs/w/local/index.d.ts +1 -1
  16. package/dist/cjs/w/local/index.js +17 -8
  17. package/dist/cjs/w/local/index.js.map +1 -1
  18. package/dist/cjs/w/local/local.js +10 -5
  19. package/dist/cjs/w/local/local.js.map +1 -1
  20. package/dist/cjs/w/local/wip.js +1 -0
  21. package/dist/cjs/w/local/wip.js.map +1 -1
  22. package/dist/cjs/w/messenger/crypto.js +4 -4
  23. package/dist/cjs/w/messenger/crypto.js.map +1 -1
  24. package/dist/cjs/w/messenger/messenger-api.d.ts +1 -3
  25. package/dist/cjs/w/messenger/messenger-api.js +75 -56
  26. package/dist/cjs/w/messenger/messenger-api.js.map +1 -1
  27. package/dist/cjs/w/messenger/messenger-manager.d.ts +1 -6
  28. package/dist/cjs/w/messenger/messenger-manager.js +99 -150
  29. package/dist/cjs/w/messenger/messenger-manager.js.map +1 -1
  30. package/dist/cjs/w/messenger/messenger.d.ts +88 -31
  31. package/dist/cjs/w/messenger/messenger.js +448 -441
  32. package/dist/cjs/w/messenger/messenger.js.map +1 -1
  33. package/dist/cjs/w/messenger/server.d.ts +9 -1
  34. package/dist/cjs/w/messenger/server.js +56 -5
  35. package/dist/cjs/w/messenger/server.js.map +1 -1
  36. package/dist/cjs/w/messenger/session.d.ts +13 -10
  37. package/dist/cjs/w/messenger/session.js +290 -176
  38. package/dist/cjs/w/messenger/session.js.map +1 -1
  39. package/dist/cjs/w/messenger/storage.d.ts +30 -0
  40. package/dist/cjs/w/messenger/storage.js +138 -5
  41. package/dist/cjs/w/messenger/storage.js.map +1 -1
  42. package/dist/cjs/w/messenger/types.d.ts +37 -2
  43. package/dist/cjs/w/messenger/types.js +14 -3
  44. package/dist/cjs/w/messenger/types.js.map +1 -1
  45. package/dist/cjs/w/query/object.js +1 -0
  46. package/dist/cjs/w/query/object.js.map +1 -1
  47. package/dist/cjs/w/util.js +1 -0
  48. package/dist/cjs/w/util.js.map +1 -1
  49. package/dist/esm/w/call/base.js +1 -0
  50. package/dist/esm/w/call/base.js.map +1 -1
  51. package/dist/esm/w/call/entity.js +1 -0
  52. package/dist/esm/w/call/entity.js.map +1 -1
  53. package/dist/esm/w/call/passport.js +1 -0
  54. package/dist/esm/w/call/passport.js.map +1 -1
  55. package/dist/esm/w/call/permission.js.map +1 -1
  56. package/dist/esm/w/call/proof.js +1 -0
  57. package/dist/esm/w/call/proof.js.map +1 -1
  58. package/dist/esm/w/call/util.js +1 -0
  59. package/dist/esm/w/call/util.js.map +1 -1
  60. package/dist/esm/w/local/account.d.ts +2 -2
  61. package/dist/esm/w/local/account.js +101 -28
  62. package/dist/esm/w/local/account.js.map +1 -1
  63. package/dist/esm/w/local/index.d.ts +1 -1
  64. package/dist/esm/w/local/index.js +17 -8
  65. package/dist/esm/w/local/index.js.map +1 -1
  66. package/dist/esm/w/local/local.js +10 -5
  67. package/dist/esm/w/local/local.js.map +1 -1
  68. package/dist/esm/w/local/wip.js +1 -0
  69. package/dist/esm/w/local/wip.js.map +1 -1
  70. package/dist/esm/w/messenger/crypto.js +4 -4
  71. package/dist/esm/w/messenger/crypto.js.map +1 -1
  72. package/dist/esm/w/messenger/messenger-api.d.ts +1 -3
  73. package/dist/esm/w/messenger/messenger-api.js +75 -56
  74. package/dist/esm/w/messenger/messenger-api.js.map +1 -1
  75. package/dist/esm/w/messenger/messenger-manager.d.ts +1 -6
  76. package/dist/esm/w/messenger/messenger-manager.js +99 -150
  77. package/dist/esm/w/messenger/messenger-manager.js.map +1 -1
  78. package/dist/esm/w/messenger/messenger.d.ts +88 -31
  79. package/dist/esm/w/messenger/messenger.js +448 -441
  80. package/dist/esm/w/messenger/messenger.js.map +1 -1
  81. package/dist/esm/w/messenger/server.d.ts +9 -1
  82. package/dist/esm/w/messenger/server.js +56 -5
  83. package/dist/esm/w/messenger/server.js.map +1 -1
  84. package/dist/esm/w/messenger/session.d.ts +13 -10
  85. package/dist/esm/w/messenger/session.js +290 -176
  86. package/dist/esm/w/messenger/session.js.map +1 -1
  87. package/dist/esm/w/messenger/storage.d.ts +30 -0
  88. package/dist/esm/w/messenger/storage.js +138 -5
  89. package/dist/esm/w/messenger/storage.js.map +1 -1
  90. package/dist/esm/w/messenger/types.d.ts +37 -2
  91. package/dist/esm/w/messenger/types.js +14 -3
  92. package/dist/esm/w/messenger/types.js.map +1 -1
  93. package/dist/esm/w/query/object.js +1 -0
  94. package/dist/esm/w/query/object.js.map +1 -1
  95. package/dist/esm/w/util.js +1 -0
  96. package/dist/esm/w/util.js.map +1 -1
  97. package/package.json +3 -3
@@ -10,6 +10,7 @@
10
10
  * 3. 消息加密和解密
11
11
  */
12
12
  import { KeyHelper, SessionBuilder, SessionCipher, SignalProtocolAddress, } from "libsignal-protocol-typescript";
13
+ // eslint-disable-next-line import/no-cycle
13
14
  import { Account } from "../local/account.js";
14
15
  import { SignalProtocolStorage } from "./storage.js";
15
16
  import { MessengerServerClient } from "./server.js";
@@ -62,8 +63,8 @@ export class MessengerSession {
62
63
  prefixedPublicKey[0] = 0x05; // Signal Protocol 标准前缀
63
64
  prefixedPublicKey.set(publicKey, 1);
64
65
  const signalIdentity = {
65
- privKey: privateKey.buffer.slice(privateKey.byteOffset, privateKey.byteOffset + privateKey.byteLength),
66
- pubKey: prefixedPublicKey.buffer.slice(prefixedPublicKey.byteOffset, prefixedPublicKey.byteOffset + prefixedPublicKey.byteLength),
66
+ privKey: privateKey.slice().buffer,
67
+ pubKey: prefixedPublicKey.slice().buffer,
67
68
  };
68
69
  await this.store.setIdentity(signalIdentity, registrationId);
69
70
  const xed25519 = recoverXed25519FromX25519PrivateKey(privateKey);
@@ -89,17 +90,38 @@ export class MessengerSession {
89
90
  */
90
91
  async registerDevice(userAddress) {
91
92
  const identity = await this.ensureIdentity(userAddress);
92
- // 首次注册时上传最大数量的预密钥
93
- const prekeys = await this.generatePreKeyBatch(this.config.prekey_count);
94
- // 注意:KeyHelper.generateIdentityKeyPair 返回的 identityKey 已经带有 0x05 前缀了
95
- // 不要再次 encodeX25519PublicKey,否则会有双重前缀!
93
+ const signedPreKeyId = 1;
94
+ let signedPreKey;
95
+ let signedPreKeyPublicKey;
96
+ let signedPreKeySignature;
97
+ let isNewKey = false;
98
+ // 【关键修复】使用带签名的存储方法
99
+ const existingSignedPreKey = await this.store.loadSignedPreKeyWithSignature(signedPreKeyId);
100
+ if (existingSignedPreKey) {
101
+ signedPreKeyPublicKey = arrayBufferToUint8Array(existingSignedPreKey.pubKey);
102
+ signedPreKeySignature = arrayBufferToUint8Array(existingSignedPreKey.signature);
103
+ signedPreKey = {
104
+ keyId: signedPreKeyId,
105
+ keyPair: {
106
+ pubKey: existingSignedPreKey.pubKey,
107
+ privKey: existingSignedPreKey.privKey,
108
+ },
109
+ signature: existingSignedPreKey.signature,
110
+ };
111
+ }
112
+ else {
113
+ signedPreKey = await KeyHelper.generateSignedPreKey(identity.signalIdentity, signedPreKeyId);
114
+ signedPreKeyPublicKey = arrayBufferToUint8Array(signedPreKey.keyPair.pubKey);
115
+ signedPreKeySignature = arrayBufferToUint8Array(signedPreKey.signature);
116
+ isNewKey = true;
117
+ }
118
+ // 生成 One-Time PreKeys(暂不保存)
119
+ const oneTimePrekeys = await this.generatePreKeyBatch(this.config.prekey_count, userAddress);
96
120
  const prefixedIdentityKey = arrayBufferToUint8Array(identity.signalIdentity.pubKey);
97
- // 获取账户信息用于签名
98
121
  const account = await Account.Instance().get(userAddress, false);
99
122
  if (!account?.pubkey) {
100
123
  throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found for ${userAddress}`);
101
124
  }
102
- // 构造签名
103
125
  const timestamp = Date.now();
104
126
  const nonce = this.generateNonce();
105
127
  const message = `register:${account.pubkey}:${timestamp}:${nonce}`;
@@ -110,40 +132,111 @@ export class MessengerSession {
110
132
  deviceId: DEFAULT_DEVICE_ID,
111
133
  registrationId: identity.registrationId,
112
134
  identityKey: bytesToBase64(prefixedIdentityKey),
113
- prekeys,
135
+ signedPrekey: {
136
+ keyId: signedPreKeyId,
137
+ publicKey: bytesToBase64(signedPreKeyPublicKey),
138
+ signature: bytesToBase64(signedPreKeySignature),
139
+ },
140
+ prekeys: oneTimePrekeys,
114
141
  publicKey: account.pubkey,
115
142
  signatureScheme: "ED25519",
116
143
  signature: bytesToBase64(signature),
117
144
  timestamp,
118
145
  nonce,
119
146
  };
147
+ console.log(`[Session Debug] Sending register request to server...`);
120
148
  await this.serverClient.registerDevice(registerRequest);
149
+ console.log(`[Session Debug] Server register request successful!`);
150
+ console.log(`[Session Debug] Verifying registration complete...`);
151
+ await this.verifyRegistrationComplete(userAddress);
152
+ console.log(`[Session Debug] Registration verification complete!`);
153
+ // 【关键修复】只有是新 key 时才保存到本地数据库(包含签名)
154
+ if (isNewKey) {
155
+ await this.store.storeSignedPreKeyWithSignature(signedPreKeyId, signedPreKey.keyPair, signedPreKeySignature.buffer.slice(0));
156
+ }
157
+ }
158
+ /**
159
+ * 验证注册是否完成
160
+ * 轮询检查服务器是否已存在该用户
161
+ */
162
+ async verifyRegistrationComplete(userAddress, maxRetries = 10, retryDelay = 500) {
163
+ for (let i = 0; i < maxRetries; i++) {
164
+ try {
165
+ const status = await this.serverClient.getPrekeyStatus(userAddress);
166
+ // 如果成功获取状态,说明注册已完成
167
+ console.log(`Registration verified for ${userAddress}, prekeys: ${status.currentCount}/${status.maxAllowed}`);
168
+ return;
169
+ }
170
+ catch (error) {
171
+ // 如果是 404,说明注册还没完成,继续等待
172
+ if (error?.message?.includes("404") || error?.status === 404) {
173
+ if (i < maxRetries - 1) {
174
+ console.log(`Waiting for registration to complete... (${i + 1}/${maxRetries})`);
175
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
176
+ continue;
177
+ }
178
+ }
179
+ // 其他错误,抛出
180
+ throw error;
181
+ }
182
+ }
183
+ throw new MessengerError(MessengerErrorCode.REGISTRATION_FAILED, `Registration verification failed after ${maxRetries} attempts`);
184
+ }
185
+ /**
186
+ * 检查设备是否已在服务器上注册
187
+ * 通过查询服务器上的预密钥状态来判断
188
+ */
189
+ async isDeviceRegistered(userAddress) {
190
+ try {
191
+ // 检查本地是否有 signedPreKey(使用带签名的方法)
192
+ const signedPreKey = await this.store.loadSignedPreKeyWithSignature(1);
193
+ if (!signedPreKey) {
194
+ return false;
195
+ }
196
+ // 尝试从服务器获取预密钥状态
197
+ const _status = await this.serverClient.getPrekeyStatus(userAddress);
198
+ return true;
199
+ }
200
+ catch (error) {
201
+ // 如果是 404,说明设备未注册
202
+ if (error?.message?.includes("404") || error?.status === 404) {
203
+ return false;
204
+ }
205
+ return false;
206
+ }
121
207
  }
122
208
  /**
123
- * 生成预密钥批次
209
+ * 生成预密钥批次(One-Time PreKeys)
124
210
  */
125
- async generatePreKeyBatch(count) {
211
+ async generatePreKeyBatch(count, userAddress) {
126
212
  if (count <= 0)
127
213
  return [];
128
- const identity = await this.ensureIdentity();
129
214
  const meta = await this.store.getMeta();
130
215
  let nextPreKeyId = meta.nextPreKeyId || 1;
131
216
  const prekeys = [];
132
217
  for (let i = 0; i < count; i++) {
133
218
  const keyId = nextPreKeyId + i;
134
- // 使用双棘轮库的 KeyHelper.generateSignedPreKey 生成带 X25519 签名的 signed prekey
135
- const signedPreKey = await KeyHelper.generateSignedPreKey(identity.signalIdentity, keyId);
136
- const prekeyPublicKey = arrayBufferToUint8Array(signedPreKey.keyPair.pubKey);
137
- // 注意:KeyHelper.generateSignedPreKey 返回的 prekey 已经带有 0x05 前缀了
138
- // 不要再次 encodeX25519PublicKey,否则会有双重前缀!
139
- const prefixedPublicKey = prekeyPublicKey;
140
- // 双棘轮库已经生成了 X25519 签名,直接使用
141
- const signature = arrayBufferToUint8Array(signedPreKey.signature);
142
- await this.store.storePreKey(keyId, signedPreKey.keyPair);
143
- await this.store.storeSignedPreKey(keyId, signedPreKey.keyPair);
219
+ const existingPreKey = await this.store.loadPreKey(keyId);
220
+ if (existingPreKey) {
221
+ const pubKeyBytes = new Uint8Array(existingPreKey.pubKey);
222
+ const publicKeyBase64 = bytesToBase64(pubKeyBytes);
223
+ const signResult = await Account.Instance().signData(userAddress, pubKeyBytes);
224
+ const signature = Buffer.from(signResult.signature.slice(2), "hex");
225
+ prekeys.push({
226
+ keyId,
227
+ publicKey: publicKeyBase64,
228
+ signature: bytesToBase64(signature),
229
+ });
230
+ continue;
231
+ }
232
+ const preKey = await KeyHelper.generatePreKey(keyId);
233
+ const pubKeyBytes = new Uint8Array(preKey.keyPair.pubKey);
234
+ const signResult = await Account.Instance().signData(userAddress, pubKeyBytes);
235
+ const signature = Buffer.from(signResult.signature.slice(2), "hex");
236
+ await this.store.storePreKey(keyId, preKey.keyPair);
144
237
  prekeys.push({
145
238
  keyId,
146
- publicKey: bytesToBase64(prefixedPublicKey),
239
+ publicKey: bytesToBase64(pubKeyBytes),
147
240
  signature: bytesToBase64(signature),
148
241
  });
149
242
  }
@@ -153,44 +246,30 @@ export class MessengerSession {
153
246
  }
154
247
  /**
155
248
  * 确保服务器上有足够可用的预密钥
156
- *
157
- * 逻辑:查询服务器当前预密钥数量,按需生成并上传,补充到服务器最大容量
158
249
  */
159
250
  async ensurePreKeys(userAddress, force = false) {
160
- // 1. 查询服务器当前预密钥状态
161
251
  const status = await this.serverClient.getPrekeyStatus(userAddress);
162
- // 2. 如果服务器已满且非强制模式,直接返回
163
252
  if (!force && status.currentCount >= status.maxAllowed) {
164
- throw new MessengerError(MessengerErrorCode.PREKEYS_FULL, `Prekeys already full on server: ${status.currentCount}/${status.maxAllowed}`);
253
+ return;
165
254
  }
166
- // 3. 计算需要补充的数量
167
255
  const needCount = status.maxAllowed - status.currentCount;
168
256
  if (needCount <= 0) {
169
- /*console.log(
170
- `No need to refill prekeys, server has ${status.currentCount}/${status.maxAllowed}`,
171
- );*/
172
257
  return;
173
258
  }
174
- /*console.log(
175
- `Refilling prekeys: server has ${status.currentCount}/${status.maxAllowed}, need ${needCount}`,
176
- );*/
177
- // 4. 确保身份已初始化
178
259
  await this.ensureIdentity(userAddress);
179
- // 5. 生成所需数量的预密钥
180
- const prekeys = await this.generatePreKeyBatch(needCount);
181
- // 6. 获取账户信息用于签名
260
+ const prekeys = await this.generatePreKeyBatch(needCount, userAddress);
261
+ if (prekeys.length === 0) {
262
+ return;
263
+ }
182
264
  const account = await Account.Instance().get(userAddress, false);
183
265
  if (!account?.pubkey) {
184
266
  throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found for ${userAddress}`);
185
267
  }
186
- // 7. 构造上传请求(包含签名)
187
- // 8. 生成签名
188
268
  const timestamp = Date.now();
189
269
  const nonce = this.generateNonce();
190
270
  const message = `upload_prekeys:${account.pubkey}:${timestamp}:${nonce}`;
191
271
  const signResult = await Account.Instance().signData(userAddress, message);
192
272
  const signature = Buffer.from(signResult.signature.slice(2), "hex");
193
- // 9. 上传预密钥(服务器会自动丢弃超过最大数量的部分)
194
273
  await this.serverClient.uploadPreKeys({
195
274
  userAddress,
196
275
  prekeys,
@@ -212,185 +291,220 @@ export class MessengerSession {
212
291
  /**
213
292
  * 建立与对方的会话
214
293
  */
215
- async establishSession(myAddress, peerAddress, peerDeviceId = DEFAULT_DEVICE_ID) {
294
+ async establishSession(myAddress, peerAddress, peerDeviceId = DEFAULT_DEVICE_ID, remoteBundle) {
216
295
  const protocolAddress = new SignalProtocolAddress(peerAddress, peerDeviceId);
217
- // 检查是否已有会话
218
296
  const existingSession = await this.store.loadSession(protocolAddress.toString());
219
297
  if (existingSession) {
220
298
  return;
221
299
  }
222
- // 获取账户信息以生成签名
223
- const account = await Account.Instance().get(myAddress, false);
224
- if (!account?.pubkey) {
225
- throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found for ${myAddress}`);
300
+ if (!remoteBundle) {
301
+ const account = await Account.Instance().get(myAddress, false);
302
+ if (!account?.pubkey) {
303
+ throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found for ${myAddress}`);
304
+ }
305
+ const timestamp = Date.now();
306
+ const nonce = this.generateNonce();
307
+ const publicKeyWithFlag = account.pubkey;
308
+ const message = `get_bundle:${myAddress}:${timestamp}:${nonce}`;
309
+ const signResult = await Account.Instance().signData(myAddress, message);
310
+ const signature = Buffer.from(signResult.signature.slice(2), "hex");
311
+ remoteBundle = await this.serverClient.fetchRemoteBundle(peerAddress, myAddress, publicKeyWithFlag, {
312
+ signatureScheme: "ED25519",
313
+ signature: bytesToBase64(new Uint8Array(signature)),
314
+ timestamp,
315
+ nonce,
316
+ }, peerDeviceId);
226
317
  }
227
- // 构造签名
228
- const timestamp = Date.now();
229
- const nonce = this.generateNonce();
230
- const publicKeyWithFlag = account.pubkey;
231
- // 签名格式: get_bundle:{address}:{timestamp}:{nonce}
232
- const message = `get_bundle:${myAddress}:${timestamp}:${nonce}`;
233
- const signResult = await Account.Instance().signData(myAddress, message);
234
- const signature = Buffer.from(signResult.signature.slice(2), "hex");
235
- // 获取对方密钥包(带签名验证)
236
- const remoteBundle = await this.serverClient.fetchRemoteBundle(peerAddress, myAddress, publicKeyWithFlag, {
237
- signatureScheme: "ED25519",
238
- signature: bytesToBase64(new Uint8Array(signature)),
239
- timestamp,
240
- nonce,
241
- }, peerDeviceId);
242
- const preferredPreKey = remoteBundle.signedPrekey;
243
- if (!preferredPreKey) {
318
+ const oneTimePreKey = remoteBundle.oneTimePrekey;
319
+ const signedPreKey = remoteBundle.signedPrekey;
320
+ if (!signedPreKey) {
244
321
  throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `No signed prekey available for ${peerAddress}`);
245
322
  }
246
- // 转换为 Signal Protocol 设备格式
247
323
  const identityKeyBytes = base64ToBytes(remoteBundle.identityKey);
248
- const preKeyPublicBytes = base64ToBytes(preferredPreKey.publicKey);
249
- const signatureBytes = base64ToBytes(preferredPreKey.signature);
250
- // 注意:identityKey 和 signedPreKey.publicKey 都不要 decode 去掉前缀!
251
- // 因为当初签名时用的就是带 0x05 前缀的原始公钥!
252
324
  const originalIdentityKeyWithPrefix = new Uint8Array(identityKeyBytes);
253
- const originalPreKeyWithPrefix = new Uint8Array(preKeyPublicBytes);
325
+ const signedPreKeyPublicBytes = base64ToBytes(signedPreKey.publicKey);
326
+ const signedPreKeySignatureBytes = base64ToBytes(signedPreKey.signature);
327
+ const originalSignedPreKeyWithPrefix = new Uint8Array(signedPreKeyPublicBytes);
328
+ let oneTimePreKeyData = undefined;
329
+ if (oneTimePreKey) {
330
+ const oneTimePreKeyPublicBytes = base64ToBytes(oneTimePreKey.publicKey);
331
+ const originalOneTimePreKeyWithPrefix = new Uint8Array(oneTimePreKeyPublicBytes);
332
+ oneTimePreKeyData = {
333
+ keyId: oneTimePreKey.keyId,
334
+ publicKey: originalOneTimePreKeyWithPrefix.buffer.slice(originalOneTimePreKeyWithPrefix.byteOffset, originalOneTimePreKeyWithPrefix.byteOffset +
335
+ originalOneTimePreKeyWithPrefix.byteLength),
336
+ };
337
+ }
254
338
  const device = {
255
339
  registrationId: remoteBundle.registrationId,
256
340
  identityKey: originalIdentityKeyWithPrefix.buffer.slice(originalIdentityKeyWithPrefix.byteOffset, originalIdentityKeyWithPrefix.byteOffset +
257
341
  originalIdentityKeyWithPrefix.byteLength),
258
342
  signedPreKey: {
259
- keyId: preferredPreKey.keyId,
260
- publicKey: originalPreKeyWithPrefix.buffer.slice(originalPreKeyWithPrefix.byteOffset, originalPreKeyWithPrefix.byteOffset +
261
- originalPreKeyWithPrefix.byteLength),
262
- signature: new Uint8Array(signatureBytes).buffer.slice(signatureBytes.byteOffset, signatureBytes.byteOffset + signatureBytes.byteLength),
343
+ keyId: signedPreKey.keyId,
344
+ publicKey: originalSignedPreKeyWithPrefix.buffer.slice(originalSignedPreKeyWithPrefix.byteOffset, originalSignedPreKeyWithPrefix.byteOffset +
345
+ originalSignedPreKeyWithPrefix.byteLength),
346
+ signature: new Uint8Array(signedPreKeySignatureBytes).buffer.slice(signedPreKeySignatureBytes.byteOffset, signedPreKeySignatureBytes.byteOffset +
347
+ signedPreKeySignatureBytes.byteLength),
263
348
  },
264
- // 服务器只提供 signedPreKey,没有单独的 one-time preKey
265
- // 这是可以的,Signal Protocol 支持仅使用 signedPreKey 建立会话
266
- preKey: undefined,
349
+ preKey: oneTimePreKeyData,
267
350
  };
268
- // 建立会话
269
351
  const builder = new SessionBuilder(this.store, protocolAddress);
270
352
  await builder.processPreKey(device);
271
- // 显式存储对方的身份密钥,以便后续解密对方的回复
272
- // 使用 protocolAddress.toString() 作为键(包含 deviceId)
273
353
  await this.store.saveIdentity(protocolAddress.toString(), device.identityKey);
274
354
  }
275
355
  /**
276
356
  * 加密消息
277
- * @param myAddress 发送方地址
278
- * @param peerAddress 接收方地址
279
- * @param plaintext 明文
280
- * @param peerDeviceId 设备ID
281
- * @returns 加密后的消息(type 表示底层 Signal Protocol 消息类型:3=PreKeyMessage, 1=WhisperMessage)
282
357
  */
283
358
  async encryptMessage(myAddress, peerAddress, plaintext, peerDeviceId = DEFAULT_DEVICE_ID) {
284
359
  const protocolAddress = new SignalProtocolAddress(peerAddress, peerDeviceId);
285
- // 检查是否已有会话(用于确定底层消息类型)
286
360
  const sessionKey = protocolAddress.toString();
287
- const existingSession = await this.store.loadSession(sessionKey);
288
- const isNewSession = !existingSession;
289
- // 只有在新会话时才建立会话,避免重置已存在的会话状态
290
- if (isNewSession) {
291
- await this.establishSession(myAddress, peerAddress, peerDeviceId);
292
- }
293
- const cipher = new SessionCipher(this.store, protocolAddress);
294
- const encoder = new TextEncoder();
295
- const cipherMessage = await cipher.encrypt(encoder.encode(plaintext).buffer);
296
- // Convert body to ArrayBuffer if it's a string
297
- let bodyBuffer;
298
- if (typeof cipherMessage.body === "string") {
299
- const bodyBytes = Buffer.from(cipherMessage.body, "binary");
300
- bodyBuffer = new Uint8Array(bodyBytes).buffer;
301
- }
302
- else if (cipherMessage.body) {
303
- bodyBuffer = cipherMessage.body;
304
- }
305
- else {
306
- throw new MessengerError(MessengerErrorCode.ENCRYPTION_FAILED, "Cipher message body is empty");
361
+ let existingSession = await this.store.loadSession(sessionKey);
362
+ let isNewSession = !existingSession;
363
+ let retryCount = 0;
364
+ const maxRetries = 2;
365
+ while (retryCount <= maxRetries) {
366
+ try {
367
+ if (isNewSession) {
368
+ console.log(`[Session] 建立新会话 (尝试 ${retryCount + 1}/${maxRetries + 1})`);
369
+ const account = await Account.Instance().get(myAddress, false);
370
+ if (!account?.pubkey) {
371
+ throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found for ${myAddress}`);
372
+ }
373
+ const timestamp = Date.now();
374
+ const nonce = this.generateNonce();
375
+ const publicKeyWithFlag = account.pubkey;
376
+ const message = `get_bundle:${myAddress}:${timestamp}:${nonce}`;
377
+ const signResult = await Account.Instance().signData(myAddress, message);
378
+ const signature = Buffer.from(signResult.signature.slice(2), "hex");
379
+ const remoteBundle = await this.serverClient.fetchRemoteBundle(peerAddress, myAddress, publicKeyWithFlag, {
380
+ signatureScheme: "ED25519",
381
+ signature: bytesToBase64(new Uint8Array(signature)),
382
+ timestamp,
383
+ nonce,
384
+ }, peerDeviceId);
385
+ await this.establishSession(myAddress, peerAddress, peerDeviceId, remoteBundle);
386
+ }
387
+ const cipher = new SessionCipher(this.store, protocolAddress);
388
+ const encoder = new TextEncoder();
389
+ const cipherMessage = await cipher.encrypt(encoder.encode(plaintext).buffer);
390
+ let bodyBuffer;
391
+ if (typeof cipherMessage.body === "string") {
392
+ const bodyBytes = Buffer.from(cipherMessage.body, "binary");
393
+ bodyBuffer = new Uint8Array(bodyBytes).buffer;
394
+ }
395
+ else if (cipherMessage.body) {
396
+ bodyBuffer = cipherMessage.body;
397
+ }
398
+ else {
399
+ throw new MessengerError(MessengerErrorCode.ENCRYPTION_FAILED, "Cipher message body is empty");
400
+ }
401
+ return {
402
+ type: cipherMessage.type,
403
+ body: bodyBuffer,
404
+ registrationId: cipherMessage.registrationId,
405
+ };
406
+ }
407
+ catch (error) {
408
+ console.error(`[Session] 加密失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, error);
409
+ if (retryCount >= maxRetries) {
410
+ throw error;
411
+ }
412
+ console.log(`[Session] 尝试删除旧会话并重新建立...`);
413
+ try {
414
+ if (existingSession) {
415
+ await this.store.removeSession(sessionKey);
416
+ existingSession = undefined;
417
+ }
418
+ isNewSession = true;
419
+ retryCount++;
420
+ await new Promise((resolve) => setTimeout(resolve, 100));
421
+ }
422
+ catch (rebuildError) {
423
+ console.error(`[Session] 重新建立会话失败:`, rebuildError);
424
+ throw error;
425
+ }
426
+ }
307
427
  }
308
- // 使用 SessionCipher 返回的消息类型(1 3)
309
- // SessionCipher 根据 session.pendingPreKey 是否存在来决定消息类型
310
- /*const msgType = cipherMessage.type === 3 ? "PREKEY" : "WHISPER";
311
- console.log(
312
- `[Session Debug] 加密完成: msgType=${msgType}(${cipherMessage.type}), bodyLength=${bodyBuffer.byteLength}, isNewSession=${isNewSession}`,
313
- );*/
314
- return {
315
- type: cipherMessage.type,
316
- body: bodyBuffer,
317
- registrationId: cipherMessage.registrationId,
318
- };
428
+ throw new MessengerError(MessengerErrorCode.ENCRYPTION_FAILED, "Encryption failed after multiple attempts");
319
429
  }
320
430
  /**
321
431
  * 解密消息
322
432
  */
323
433
  async decryptMessage(myAddress, peerAddress, ciphertext, msgType, peerDeviceId = DEFAULT_DEVICE_ID) {
324
434
  const protocolAddress = new SignalProtocolAddress(peerAddress, peerDeviceId);
325
- // Signal 协议中,接收方直接使用自己的私钥解密消息
326
- // decryptPreKeyWhisperMessage 会自动处理会话建立,不需要预先获取发送方的密钥包
327
- // 发送方使用接收方的预密钥公钥加密,接收方使用自己的预密钥私钥解密
328
- const cipher = new SessionCipher(this.store, protocolAddress);
329
- const decoder = new TextDecoder();
330
- // 检查会话状态
331
435
  const sessionKey = protocolAddress.toString();
332
- const existingSession = await this.store.loadSession(sessionKey);
333
- let plaintextBuffer;
334
- const ciphertextArray = new Uint8Array(ciphertext);
436
+ let existingSession = await this.store.loadSession(sessionKey);
437
+ const decoder = new TextDecoder();
335
438
  let ciphertextBinary = "";
439
+ const ciphertextArray = new Uint8Array(ciphertext);
336
440
  for (let i = 0; i < ciphertextArray.length; i++) {
337
441
  ciphertextBinary += String.fromCharCode(ciphertextArray[i]);
338
442
  }
339
- try {
340
- if (msgType === MessageType.PREKEY_MESSAGE) {
341
- // 【新增】如果已有会话,说明对方重置了会话,需要删除旧会话
342
- if (existingSession) {
343
- await this.store.removeSession(sessionKey);
344
- // 重新创建 cipher(使用更新后的 store)
345
- const newCipher = new SessionCipher(this.store, protocolAddress);
346
- plaintextBuffer =
347
- await newCipher.decryptPreKeyWhisperMessage(ciphertextBinary, "binary");
443
+ let retryCount = 0;
444
+ const maxRetries = 2;
445
+ while (retryCount <= maxRetries) {
446
+ try {
447
+ const cipher = new SessionCipher(this.store, protocolAddress);
448
+ let plaintextBuffer;
449
+ if (msgType === MessageType.PREKEY_MESSAGE) {
450
+ console.log(`[Session] 收到 PREKEY_MESSAGE,发送方想用新的 PreKey Bundle 建立会话`);
451
+ if (existingSession && retryCount === 0) {
452
+ console.log(`[Session] 检测到已有会话,但收到 PREKEY_MESSAGE,先删除旧会话以建立新会话`);
453
+ await this.store.removeSession(sessionKey);
454
+ existingSession = undefined;
455
+ }
456
+ plaintextBuffer = await cipher.decryptPreKeyWhisperMessage(ciphertextBinary, "binary");
457
+ console.log(`[Session] PREKEY_MESSAGE 解密成功,新会话已建立`);
348
458
  }
349
459
  else {
350
- plaintextBuffer = await cipher.decryptPreKeyWhisperMessage(ciphertextBinary, "binary");
460
+ plaintextBuffer = await cipher.decryptWhisperMessage(ciphertextBinary, "binary");
351
461
  }
462
+ return decoder.decode(plaintextBuffer);
352
463
  }
353
- else {
354
- // 解密前检查会话状态
355
- /*const sessionBefore = await this.store.loadSession(sessionKey);
356
- console.log(
357
- `[Session Debug] 解密前会话状态: ${sessionBefore ? "存在" : "不存在"}`,
358
- );*/
359
- plaintextBuffer = await cipher.decryptWhisperMessage(ciphertextBinary, "binary");
360
- // 解密后检查会话状态
361
- /*const sessionAfter = await this.store.loadSession(sessionKey);
362
- console.log(
363
- `[Session Debug] 解密后会话状态: ${sessionAfter ? "存在" : "不存在"}`,
364
- );*/
365
- }
366
- }
367
- catch (error) {
368
- /*console.error(`[Session Debug] 解密失败:`);
369
- console.error(
370
- ` 错误类型: ${error instanceof Error ? error.constructor.name : typeof error}`,
371
- );
372
- console.error(
373
- ` 错误消息: ${error instanceof Error ? error.message : String(error)}`,
374
- );
375
- if (error instanceof Error && error.stack) {
376
- console.error(` 堆栈: ${error.stack.split("\n")[0]}`);
464
+ catch (error) {
465
+ console.error(`[Session Debug] 解密失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`);
466
+ console.error(` 错误类型: ${error instanceof Error ? error.constructor.name : typeof error}`);
467
+ console.error(` 错误消息: ${error instanceof Error ? error.message : String(error)}`);
468
+ if (error instanceof Error && error.stack) {
469
+ console.error(` 堆栈: ${error.stack.split("\n")[0]}`);
470
+ }
471
+ console.error(` 上下文:`);
472
+ console.error(` - myAddress: ${myAddress}`);
473
+ console.error(` - peerAddress: ${peerAddress}`);
474
+ console.error(` - msgType: ${msgType} (${msgType === MessageType.PREKEY_MESSAGE ? "PREKEY" : "WHISPER"})`);
475
+ console.error(` - sessionKey: ${sessionKey}`);
476
+ console.error(` - existingSession: ${existingSession ? "存在" : "不存在"}`);
477
+ console.error(` - ciphertext.length: ${ciphertext.byteLength}`);
478
+ const identityKey = await this.store.getIdentityKeyPair();
479
+ const regId = await this.store.getLocalRegistrationId();
480
+ console.error(` - identityKey: ${identityKey ? "存在" : "缺失"}`);
481
+ console.error(` - registrationId: ${regId}`);
482
+ if (retryCount >= maxRetries) {
483
+ throw error;
484
+ }
485
+ console.log(`[Session] 尝试重新建立会话...`);
486
+ try {
487
+ if (existingSession) {
488
+ console.log(`[Session] 删除旧会话`);
489
+ await this.store.removeSession(sessionKey);
490
+ existingSession = undefined;
491
+ }
492
+ if (msgType === MessageType.PREKEY_MESSAGE) {
493
+ console.log(`[Session] 收到的是 PREKEY_MESSAGE,等待发送方的 PreKey 即可重建会话`);
494
+ }
495
+ else {
496
+ console.log(`[Session] 收到的是 WHISPER_MESSAGE,需要让对方重新发送 PREKEY_MESSAGE`);
497
+ }
498
+ retryCount++;
499
+ await new Promise((resolve) => setTimeout(resolve, 100));
500
+ }
501
+ catch (rebuildError) {
502
+ console.error(`[Session] 重新建立会话失败:`, rebuildError);
503
+ throw error;
504
+ }
377
505
  }
378
- console.error(` 上下文:`);
379
- console.error(` - myAddress: ${myAddress}`);
380
- console.error(` - peerAddress: ${peerAddress}`);
381
- console.error(
382
- ` - msgType: ${msgType} (${msgType === MessageType.PREKEY_MESSAGE ? "PREKEY" : "WHISPER"})`,
383
- );
384
- console.error(` - sessionKey: ${sessionKey}`);
385
- console.error(
386
- ` - existingSession: ${existingSession ? "存在" : "不存在"}`,
387
- );
388
- console.error(` - ciphertext.length: ${ciphertext.byteLength}`);
389
- */
390
- console.log("error:", error);
391
- throw error;
392
506
  }
393
- return decoder.decode(plaintextBuffer);
507
+ throw new MessengerError(MessengerErrorCode.DECRYPTION_FAILED, "Decryption failed after multiple attempts");
394
508
  }
395
509
  /**
396
510
  * 获取本地身份公钥