@badzz88/baileys 8.5.2 → 8.5.4

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 (110) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +1256 -75
  3. package/WAProto/index.d.ts +36042 -19649
  4. package/WAProto/index.js +181990 -135645
  5. package/engine-requirements.js +7 -15
  6. package/lib/Defaults/index.js +22 -17
  7. package/lib/Signal/Group/group_cipher.js +5 -5
  8. package/lib/Signal/Group/keyhelper.js +4 -4
  9. package/lib/Signal/Group/sender-chain-key.js +2 -2
  10. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/lib/Signal/Group/sender-key-message.js +3 -3
  12. package/lib/Signal/Group/sender-key-record.js +1 -1
  13. package/lib/Signal/Group/sender-message-key.js +5 -5
  14. package/lib/Signal/libsignal.js +39 -59
  15. package/lib/Signal/lid-mapping.js +28 -10
  16. package/lib/Socket/aigroups.js +5 -5
  17. package/lib/Socket/business.js +3 -11
  18. package/lib/Socket/chats.js +668 -319
  19. package/lib/Socket/communities.js +85 -89
  20. package/lib/Socket/graphql.js +435 -228
  21. package/lib/Socket/groups.js +132 -211
  22. package/lib/Socket/index.js +9 -7
  23. package/lib/Socket/interactive-handler.js +117 -60
  24. package/lib/Socket/interop.js +56 -175
  25. package/lib/Socket/managed-account.js +79 -48
  26. package/lib/Socket/messages-recv.js +898 -340
  27. package/lib/Socket/messages-send.js +462 -160
  28. package/lib/Socket/mex.js +40 -53
  29. package/lib/Socket/newsletter.js +291 -291
  30. package/lib/Socket/privacy.js +77 -265
  31. package/lib/Socket/registration.js +164 -157
  32. package/lib/Socket/socket.js +181 -199
  33. package/lib/Socket/username.js +144 -227
  34. package/lib/Store/make-cache-manager-store.js +2 -2
  35. package/lib/Store/make-in-memory-store.js +25 -141
  36. package/lib/Types/Auth.js +3 -3
  37. package/lib/Types/Label.js +1 -1
  38. package/lib/Types/LabelAssociation.js +1 -1
  39. package/lib/Types/Newsletter.js +96 -65
  40. package/lib/Types/State.js +4 -4
  41. package/lib/Types/index.js +1 -1
  42. package/lib/Utils/auth-utils.js +52 -57
  43. package/lib/Utils/browser-utils.js +1 -1
  44. package/lib/Utils/business.js +2 -6
  45. package/lib/Utils/chat-utils.js +102 -38
  46. package/lib/Utils/crypto.js +54 -84
  47. package/lib/Utils/decode-wa-message.js +316 -83
  48. package/lib/Utils/event-buffer.js +37 -61
  49. package/lib/Utils/generics.js +157 -103
  50. package/lib/Utils/group-history.js +3 -12
  51. package/lib/Utils/history.js +15 -15
  52. package/lib/Utils/index.js +1 -5
  53. package/lib/Utils/link-preview.js +3 -8
  54. package/lib/Utils/lt-hash.js +2 -35
  55. package/lib/Utils/make-mutex.js +1 -1
  56. package/lib/Utils/message-inspect.js +393 -0
  57. package/lib/Utils/message-retry-manager.js +27 -92
  58. package/lib/Utils/messages-media.js +159 -130
  59. package/lib/Utils/messages.js +144 -566
  60. package/lib/Utils/meta-ai-msmsg.js +62 -202
  61. package/lib/Utils/noise-handler.js +19 -21
  62. package/lib/Utils/offline-node-processor.js +3 -8
  63. package/lib/Utils/pre-key-manager.js +13 -23
  64. package/lib/Utils/process-message.js +197 -106
  65. package/lib/Utils/reporting-utils.js +2 -2
  66. package/lib/Utils/signal.js +28 -63
  67. package/lib/Utils/stanza-ack.js +39 -12
  68. package/lib/Utils/sync-action-utils.js +4 -7
  69. package/lib/Utils/tc-token-utils.js +20 -25
  70. package/lib/Utils/use-multi-file-auth-state.js +30 -14
  71. package/lib/Utils/validate-connection.js +59 -19
  72. package/lib/WABinary/decode.js +50 -16
  73. package/lib/WABinary/encode.js +54 -224
  74. package/lib/WABinary/generic-utils.js +7 -97
  75. package/lib/WABinary/jid-utils.js +27 -14
  76. package/lib/WAM/encode.js +7 -7
  77. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +58 -1
  78. package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -1
  79. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +13 -5
  80. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -4
  81. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +20 -7
  82. package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +865 -0
  83. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
  84. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
  85. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -2
  86. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
  87. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +2 -1
  88. package/lib/WAUSync/Protocols/index.js +1 -0
  89. package/lib/WAUSync/USyncBackoff.js +26 -0
  90. package/lib/WAUSync/USyncQuery.js +77 -6
  91. package/lib/WAUSync/USyncUser.js +16 -8
  92. package/lib/WAUSync/index.js +1 -0
  93. package/lib/antiban.js +608 -854
  94. package/lib/index.js +3 -3
  95. package/package.json +16 -19
  96. package/lib/Socket/luxu.js +0 -387
  97. package/lib/Socket/text-router.js +0 -83
  98. package/lib/Store/keyed-db.js +0 -108
  99. package/lib/Types/Mex.js +0 -112
  100. package/lib/Utils/command-loader.d.ts +0 -31
  101. package/lib/Utils/command-loader.js +0 -100
  102. package/lib/Utils/companion-reg-client-utils.js +0 -42
  103. package/lib/Utils/curve25519-js.js +0 -275
  104. package/lib/Utils/native-bridge.js +0 -82
  105. package/lib/Utils/session-pool.d.ts +0 -16
  106. package/lib/Utils/session-pool.js +0 -94
  107. package/lib/Utils/sticker.d.ts +0 -17
  108. package/lib/Utils/sticker.js +0 -145
  109. package/lib/Utils/view-once-cache.d.ts +0 -9
  110. package/lib/Utils/view-once-cache.js +0 -79
@@ -1,262 +1,122 @@
1
1
  'use strict'
2
2
 
3
- const Crypto_1 = require('./crypto')
3
+ const rb = require('whatsapp-rust-bridge')
4
4
  const { proto } = require('../../WAProto')
5
5
 
6
6
  const BOT_MESSAGE_INFO = 'Bot Message'
7
7
  const KEY_LENGTH = 32
8
- const AUTH_TAG_LENGTH = 16
9
-
10
- const MSG_ID_HEX_RE = /^[0-9A-Fa-f]{32}$/
11
8
 
12
9
  const unpadRandomMax16 = value => {
13
10
  const bytes = new Uint8Array(value)
14
11
  if (!bytes.length) {
15
12
  throw new Error('unpadPkcs7 given empty bytes')
16
13
  }
17
-
18
14
  const padLength = bytes[bytes.length - 1]
19
15
  if (padLength > bytes.length) {
20
16
  throw new Error(`unpad given ${bytes.length} bytes, but pad is ${padLength}`)
21
17
  }
22
-
23
18
  return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.length - padLength)
24
19
  }
25
20
 
26
21
  const toBuffer = value => {
27
- if (Buffer.isBuffer(value)) {
28
- return value
29
- }
30
-
31
- if (value instanceof Uint8Array) {
32
- return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
33
- }
34
-
22
+ if (Buffer.isBuffer(value)) return value
23
+ if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
35
24
  return Buffer.from(value)
36
25
  }
37
26
 
38
- const normalizeLidJid = jid => {
39
- if (!jid || !jid.endsWith('@lid') || !jid.includes(':')) {
40
- return jid
41
- }
42
-
43
- return `${jid.split(':')[0]}@lid`
44
- }
45
-
46
- const buildMessageIdRepresentations = messageId => {
47
- const ascii = Buffer.from(messageId)
48
- const binary = MSG_ID_HEX_RE.test(messageId) ? Buffer.from(messageId, 'hex') : ascii
49
- return [
50
- { label: 'msgIdAscii', value: ascii },
51
- ...(binary.equals(ascii) ? [] : [{ label: 'msgIdBinary', value: binary }])
52
- ]
53
- }
54
27
 
55
- const pushUnique = (items, seen, item) => {
56
- const key = JSON.stringify([
57
- item.messageId,
58
- item.idSource,
59
- item.idSources,
60
- item.infoSource,
61
- item.aadSource,
62
- item.info.toString('hex'),
63
- item.aad.toString('hex')
64
- ])
65
28
 
66
- if (!seen.has(key)) {
67
- seen.add(key)
68
- items.push(item)
69
- }
29
+ const normalizeLidJid = jid => {
30
+ if (!jid || !jid.endsWith('@lid') || !jid.includes(':')) return jid
31
+ return `${jid.split(':')[0]}@lid`
70
32
  }
71
33
 
72
- const getCandidateIds = messageKey => {
73
- const orderedCandidates = [
74
- messageKey?.botType === 'full'
75
- ? { source: 'stanzaId', messageId: messageKey?.stanzaId }
76
- : { source: 'botEditTargetId', messageId: messageKey?.botEditTargetId },
77
- { source: 'targetId', messageId: messageKey?.targetId },
78
- { source: 'metaTargetId', messageId: messageKey?.metaTargetId },
79
- { source: 'stanzaId', messageId: messageKey?.stanzaId }
80
- ]
81
34
 
82
- const targetIdCandidates = Array.isArray(messageKey?.targetIdCandidates) ? messageKey.targetIdCandidates : []
83
- for (let index = 0; index < targetIdCandidates.length; index += 1) {
84
- orderedCandidates.push({
85
- source: `targetIdCandidates[${index}]`,
86
- messageId: targetIdCandidates[index]
87
- })
88
- }
89
-
90
- const grouped = new Map()
91
- for (const candidate of orderedCandidates) {
92
- if (!candidate.messageId) {
93
- continue
94
- }
95
-
96
- const messageId = String(candidate.messageId)
97
- const existing = grouped.get(messageId)
98
- if (existing) {
99
- if (!existing.idSources.includes(candidate.source)) {
100
- existing.idSources.push(candidate.source)
101
- }
102
- } else {
103
- grouped.set(messageId, {
104
- messageId,
105
- idSource: candidate.source,
106
- idSources: [candidate.source]
107
- })
108
- }
109
- }
110
-
111
- return Array.from(grouped.values())
112
- }
113
-
114
- const getJidCandidates = messageKey => {
115
- const ordered = [
116
- { source: 'meId', jid: messageKey?.meId },
117
- { source: 'conversationJid', jid: messageKey?.conversationJid },
118
- { source: 'senderJid', jid: messageKey?.senderJid },
119
- { source: 'meLidNormalized', jid: normalizeLidJid(messageKey?.meLid) }
120
- ]
121
35
 
36
+ const selectMsgIdCandidates = messageKey => {
122
37
  const seen = new Set()
123
- const candidates = []
124
- for (const candidate of ordered) {
125
- if (!candidate.jid) {
126
- continue
127
- }
128
-
129
- const jid = String(candidate.jid)
130
- if (!seen.has(jid)) {
131
- seen.add(jid)
132
- candidates.push({ source: candidate.source, jid, value: Buffer.from(jid) })
38
+ const result = []
39
+ for (const id of [messageKey?.botEditTargetId, messageKey?.stanzaId, messageKey?.metaTargetId]) {
40
+ const s = id ? String(id) : ''
41
+ if (s && !seen.has(s)) {
42
+ seen.add(s)
43
+ result.push(s)
133
44
  }
134
45
  }
135
-
136
- return candidates
46
+ return result
137
47
  }
138
48
 
139
- const buildMsmsgDecryptionStrategies = messageKey => {
140
- const botJid = String(messageKey?.participant || '')
141
- const botJidBuffer = Buffer.from(botJid)
142
- const targetIds = getCandidateIds(messageKey)
143
- const jidCandidates = getJidCandidates(messageKey)
144
- const primaryJid = jidCandidates[0]
145
- const alternateJid = jidCandidates.find(
146
- candidate => candidate.source !== primaryJid?.source && candidate.jid !== botJid
147
- )
148
- const strategies = []
149
- const seen = new Set()
150
49
 
151
- for (const idCandidate of targetIds) {
152
- const idForms = buildMessageIdRepresentations(idCandidate.messageId)
153
- for (const idForm of idForms) {
154
- pushUnique(strategies, seen, {
155
- mode: '2step',
156
- idSource: idCandidate.idSource,
157
- idSources: idCandidate.idSources,
158
- infoSource: `${idForm.label}+meId+botJid`,
159
- aadSource: `${idForm.label}+0+botJid`,
160
- authTagLayout: 'trailing',
161
- messageId: idCandidate.messageId,
162
- info: Buffer.concat([idForm.value, primaryJid.value, botJidBuffer, Buffer.alloc(0)]),
163
- aad: Buffer.concat([idForm.value, Buffer.from([0]), botJidBuffer]),
164
- attemptLabel: `${idCandidate.idSource}:${idForm.label}:primary`
165
- })
166
50
 
167
- if (alternateJid) {
168
- pushUnique(strategies, seen, {
169
- mode: '2step',
170
- idSource: idCandidate.idSource,
171
- idSources: idCandidate.idSources,
172
- infoSource: `${idForm.label}+${alternateJid.source}+botJid`,
173
- aadSource: `${idForm.label}+0+${alternateJid.source}`,
174
- authTagLayout: 'trailing',
175
- messageId: idCandidate.messageId,
176
- info: Buffer.concat([idForm.value, alternateJid.value, botJidBuffer, Buffer.alloc(0)]),
177
- aad: Buffer.concat([idForm.value, Buffer.from([0]), alternateJid.value]),
178
- attemptLabel: `${idCandidate.idSource}:${idForm.label}:${alternateJid.source}`
179
- })
180
- }
51
+ const selectTargetJidCandidates = messageKey => {
52
+ const seen = new Set()
53
+ const result = []
54
+ for (const jid of [normalizeLidJid(messageKey?.meId), normalizeLidJid(messageKey?.meLid)]) {
55
+ const s = jid ? String(jid) : ''
56
+ if (s && !seen.has(s)) {
57
+ seen.add(s)
58
+ result.push(s)
181
59
  }
182
60
  }
183
-
184
- return strategies.slice(0, 12)
185
- }
186
-
187
- const assertRequired = (value, label) => {
188
- if (
189
- !value ||
190
- (Buffer.isBuffer(value) && value.length === 0) ||
191
- (value instanceof Uint8Array && value.byteLength === 0)
192
- ) {
193
- throw new Error(`Missing required ${label} for msmsg decryption`)
194
- }
195
- }
196
-
197
- const decryptWithStrategy = (messageSecret, msMsg, strategy) => {
198
- const baseSecret = Buffer.from(Crypto_1.hkdf(toBuffer(messageSecret), KEY_LENGTH, { info: BOT_MESSAGE_INFO }))
199
- const key = Buffer.from(Crypto_1.hkdf(baseSecret, KEY_LENGTH, { info: strategy.info }))
200
- const payload = toBuffer(msMsg.encPayload)
201
- // ciphertext = payload without last 16 bytes (auth tag) + auth tag appended → standard GCM layout
202
- const ciphertextWithTag = Buffer.concat([payload.slice(0, -AUTH_TAG_LENGTH), payload.slice(-AUTH_TAG_LENGTH)])
203
- return Buffer.from(Crypto_1.aesDecryptGCM(ciphertextWithTag, key, toBuffer(msMsg.encIv), strategy.aad))
61
+ return result
204
62
  }
205
63
 
206
64
  const decodeDecryptedMsmsgMessage = decrypted => {
207
- const messageBuffer = toBuffer(decrypted)
208
-
65
+ const buf = toBuffer(decrypted)
209
66
  try {
210
- const unpadded = Buffer.from(unpadRandomMax16(messageBuffer))
67
+ const unpadded = Buffer.from(unpadRandomMax16(buf))
211
68
  const decoded = proto.Message.decode(unpadded)
212
- const hasContent = Object.keys(decoded).some(key => key !== 'messageContextInfo' && decoded[key] != null)
213
- if (hasContent) {
214
- return decoded
215
- }
69
+ const hasContent = Object.keys(decoded).some(k => k !== 'messageContextInfo' && decoded[k] != null)
70
+ if (hasContent) return decoded
216
71
  } catch {}
217
-
218
- return proto.Message.decode(messageBuffer)
72
+ return proto.Message.decode(buf)
219
73
  }
220
74
 
75
+
221
76
  const decryptMsmsgBotMessage = async (messageSecret, messageKey, msMsg) => {
222
- assertRequired(messageSecret, 'messageSecret')
223
- assertRequired(messageKey?.participant, 'participant')
224
- assertRequired(messageKey?.meId, 'meId')
225
- assertRequired(msMsg?.encIv, 'encIv')
226
- assertRequired(msMsg?.encPayload, 'encPayload')
227
- if (getCandidateIds(messageKey).length === 0) {
228
- throw new Error('Missing required target message id for msmsg decryption')
77
+ if (!messageSecret || (messageSecret instanceof Uint8Array && !messageSecret.byteLength)) {
78
+ throw new Error('Missing required messageSecret for msmsg decryption')
229
79
  }
80
+ if (!messageKey?.participant) throw new Error('Missing required participant for msmsg decryption')
81
+ if (!messageKey?.meId) throw new Error('Missing required meId for msmsg decryption')
82
+ if (!msMsg?.encIv) throw new Error('Missing required encIv for msmsg decryption')
83
+ if (!msMsg?.encPayload) throw new Error('Missing required encPayload for msmsg decryption')
230
84
 
231
- const strategies = buildMsmsgDecryptionStrategies(messageKey)
232
- const attemptedStrategies = []
233
- let lastError
85
+ const msgIdCandidates = selectMsgIdCandidates(messageKey)
86
+ if (!msgIdCandidates.length) throw new Error('Missing required target message id for msmsg decryption')
234
87
 
235
- for (const strategy of strategies) {
236
- const attempt = {
237
- idSource: strategy.idSource,
238
- idSources: strategy.idSources,
239
- infoSource: strategy.infoSource,
240
- aadSource: strategy.aadSource,
241
- messageId: strategy.messageId
242
- }
88
+ const targetJidCandidates = selectTargetJidCandidates(messageKey)
89
+ if (!targetJidCandidates.length) throw new Error('Missing required target JID for msmsg decryption')
90
+
91
+ const botJidBuf = Buffer.from(String(messageKey.participant))
92
+ const payload = toBuffer(msMsg.encPayload)
93
+ const iv = toBuffer(msMsg.encIv)
243
94
 
244
- try {
245
- return await decryptWithStrategy(messageSecret, msMsg, strategy)
246
- } catch (error) {
247
- attemptedStrategies.push(attempt)
248
- lastError = error
95
+
96
+ const baseKey = Buffer.from(rb.hkdf(toBuffer(messageSecret), KEY_LENGTH, { info: BOT_MESSAGE_INFO }))
97
+
98
+ let lastError
99
+ for (const msgId of msgIdCandidates) {
100
+
101
+ const idBuf = Buffer.from(msgId)
102
+ for (const targetJid of targetJidCandidates) {
103
+ const info = Buffer.concat([idBuf, Buffer.from(targetJid), botJidBuf])
104
+ const key = Buffer.from(rb.hkdf(baseKey, KEY_LENGTH, { info }))
105
+ const aad = Buffer.concat([idBuf, Buffer.from([0x00]), botJidBuf])
106
+ try {
107
+ return Buffer.from(rb.aesDecryptGCM(payload, key, iv, aad))
108
+ } catch (e) {
109
+ lastError = e
110
+ }
249
111
  }
250
112
  }
251
113
 
252
- const error = new Error('Failed to decrypt msmsg with bounded deterministic strategies')
253
- error.attemptedStrategies = attemptedStrategies
254
- error.cause = lastError
255
- throw error
114
+ const err = new Error('msmsg decryption failed: all key derivation candidates exhausted')
115
+ err.cause = lastError
116
+ throw err
256
117
  }
257
118
 
258
119
  module.exports = {
259
- buildMsmsgDecryptionStrategies,
260
120
  decodeDecryptedMsmsgMessage,
261
121
  decryptMsmsgBotMessage
262
122
  }
@@ -4,15 +4,15 @@ exports.makeNoiseHandler = void 0
4
4
  const boom_1 = require('@hapi/boom')
5
5
  const index_js_1 = require('../../WAProto/index.js')
6
6
  const Defaults_1 = require('../Defaults')
7
- const rb = require('./native-bridge').loadRustBridge()
7
+ const rb = require('whatsapp-rust-bridge')
8
+
8
9
 
9
- // rustNodeToJs mirrors the one in decode.js — converts InternalBinaryNode to plain JS object
10
10
  const rustNodeToJs = node => {
11
11
  if (!node || typeof node !== 'object') return node
12
12
  const result = { tag: node.tag, attrs: node.attrs || {} }
13
13
  const content = node.content
14
14
  if (content === undefined || content === null) {
15
- // no content
15
+
16
16
  } else if (content instanceof Uint8Array) {
17
17
  result.content = Buffer.from(content)
18
18
  } else if (typeof content === 'string') {
@@ -33,14 +33,6 @@ const makeNoiseHandler = ({
33
33
  }) => {
34
34
  logger = logger.child({ class: 'ns' })
35
35
 
36
- if (!rb) {
37
- throw new Error(
38
- 'Establishing a WhatsApp connection requires the "whatsapp-rust-bridge" native module ' +
39
- '(Noise protocol handshake + session), which failed to load. Make sure it is installed: ' +
40
- 'npm install whatsapp-rust-bridge@0.5.4'
41
- )
42
- }
43
-
44
36
  const session = new rb.NoiseSession(
45
37
  publicKey instanceof Uint8Array ? publicKey : new Uint8Array(publicKey),
46
38
  NOISE_HEADER instanceof Uint8Array ? NOISE_HEADER : new Uint8Array(NOISE_HEADER),
@@ -52,7 +44,7 @@ const makeNoiseHandler = ({
52
44
  let pendingBytes = null
53
45
 
54
46
  const processFrames = async onFrame => {
55
- // decodeFrame returns an Array of InternalBinaryNode (post-handshake) or Uint8Array (handshake)
47
+
56
48
  const frames = session.decodeFrame(new Uint8Array(0))
57
49
  for (let i = 0; i < frames.length; i++) {
58
50
  const item = frames[i]
@@ -93,7 +85,7 @@ const makeNoiseHandler = ({
93
85
  } else {
94
86
  try {
95
87
  frame = rustNodeToJs(item.toJSON())
96
- } catch (e) {
88
+ } catch {
97
89
  continue
98
90
  }
99
91
  }
@@ -119,7 +111,7 @@ const makeNoiseHandler = ({
119
111
  },
120
112
  finishInit,
121
113
  processHandshake: ({ serverHello }, noiseKey) => {
122
- // Rust handles the crypto: authenticate ephemeral, ECDH, decrypt static+payload
114
+
123
115
  const certPayload = session.processHandshakeInit(
124
116
  serverHello.ephemeral,
125
117
  serverHello.static,
@@ -127,7 +119,7 @@ const makeNoiseHandler = ({
127
119
  privateKey instanceof Uint8Array ? privateKey : new Uint8Array(privateKey)
128
120
  )
129
121
 
130
- // Certificate validation stays in JS (uses protobuf + WA-specific constants)
122
+
131
123
  const { intermediate: certIntermediate, leaf } = index_js_1.proto.CertChain.decode(certPayload)
132
124
  if (!leaf?.details || !leaf?.signature) {
133
125
  throw new boom_1.Boom('invalid noise leaf certificate', { statusCode: 400 })
@@ -148,13 +140,20 @@ const makeNoiseHandler = ({
148
140
  throw new boom_1.Boom('certification match failed', { statusCode: 400 })
149
141
  }
150
142
 
151
- // Encrypt our static key and do final ECDH
143
+
144
+ let serverStaticPub
145
+ try {
146
+ const leafDetails = index_js_1.proto.CertChain.NoiseCertificate.Details.decode(leaf.details)
147
+ if (leafDetails.key?.length === 32) serverStaticPub = Buffer.from(leafDetails.key)
148
+ } catch {}
149
+
150
+
152
151
  const keyEnc = session.processHandshakeFinish(
153
152
  noiseKey.public instanceof Uint8Array ? noiseKey.public : new Uint8Array(noiseKey.public),
154
153
  noiseKey.private instanceof Uint8Array ? noiseKey.private : new Uint8Array(noiseKey.private),
155
154
  serverHello.ephemeral
156
155
  )
157
- return Buffer.from(keyEnc)
156
+ return { keyEnc: Buffer.from(keyEnc), serverStaticPub }
158
157
  },
159
158
  encodeFrame: data => {
160
159
  const u8 = data instanceof Uint8Array ? data : new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
@@ -162,14 +161,13 @@ const makeNoiseHandler = ({
162
161
  },
163
162
  decodeFrame: async (newData, onFrame) => {
164
163
  if (isWaitingForTransport) {
165
- pendingBytes = pendingBytes
166
- ? Buffer.concat([pendingBytes, Buffer.from(newData)])
167
- : Buffer.from(newData)
164
+ pendingBytes = pendingBytes ? Buffer.concat([pendingBytes, Buffer.from(newData)]) : Buffer.from(newData)
168
165
  pendingOnFrame = onFrame
169
166
  return
170
167
  }
171
168
 
172
- const u8 = newData instanceof Uint8Array ? newData : new Uint8Array(newData.buffer, newData.byteOffset, newData.byteLength)
169
+ const u8 =
170
+ newData instanceof Uint8Array ? newData : new Uint8Array(newData.buffer, newData.byteOffset, newData.byteLength)
173
171
  const frames = session.decodeFrame(u8)
174
172
 
175
173
  for (let i = 0; i < frames.length; i++) {
@@ -1,12 +1,7 @@
1
1
  'use strict'
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
3
  exports.makeOfflineNodeProcessor = makeOfflineNodeProcessor
4
- /**
5
- * Creates a processor for offline stanza nodes that:
6
- * - Queues nodes for sequential processing
7
- * - Yields to the event loop periodically to avoid blocking
8
- * - Catches handler errors to prevent the processing loop from crashing
9
- */
4
+
10
5
  function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
11
6
  const nodes = []
12
7
  let isProcessing = false
@@ -27,8 +22,8 @@ function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
27
22
  }
28
23
  await nodeProcessor(node).catch(err => deps.onUnexpectedError(err, `processing offline ${type}`))
29
24
  processedInBatch++
30
- // Yield to event loop after processing a batch
31
- // This prevents blocking the event loop for too long when there are many offline nodes
25
+
26
+
32
27
  if (processedInBatch >= batchSize) {
33
28
  processedInBatch = 0
34
29
  await deps.yieldToEventLoop()
@@ -7,35 +7,29 @@ var __importDefault =
7
7
  Object.defineProperty(exports, '__esModule', { value: true })
8
8
  exports.PreKeyManager = void 0
9
9
  const p_queue_1 = __importDefault(require('p-queue'))
10
- /**
11
- * Manages pre-key operations with proper concurrency control
12
- */
10
+
13
11
  class PreKeyManager {
14
12
  constructor(store, logger) {
15
13
  this.store = store
16
14
  this.logger = logger
17
15
  this.queues = new Map()
18
16
  }
19
- /**
20
- * Get or create a queue for a specific key type
21
- */
17
+
22
18
  getQueue(keyType) {
23
19
  if (!this.queues.has(keyType)) {
24
20
  this.queues.set(keyType, new p_queue_1.default({ concurrency: 1 }))
25
21
  }
26
22
  return this.queues.get(keyType)
27
23
  }
28
- /**
29
- * Process pre-key operations (updates and deletions)
30
- */
24
+
31
25
  async processOperations(data, keyType, transactionCache, mutations, isInTransaction) {
32
26
  const keyData = data[keyType]
33
27
  if (!keyData) return
34
28
  return this.getQueue(keyType).add(async () => {
35
- // Ensure structures exist
29
+
36
30
  transactionCache[keyType] = transactionCache[keyType] || {}
37
31
  mutations[keyType] = mutations[keyType] || {}
38
- // Separate deletions from updates
32
+
39
33
  const deletions = []
40
34
  const updates = {}
41
35
  for (const keyId in keyData) {
@@ -45,23 +39,21 @@ class PreKeyManager {
45
39
  updates[keyId] = keyData[keyId]
46
40
  }
47
41
  }
48
- // Process updates (no validation needed)
42
+
49
43
  if (Object.keys(updates).length > 0) {
50
44
  Object.assign(transactionCache[keyType], updates)
51
45
  Object.assign(mutations[keyType], updates)
52
46
  }
53
- // Process deletions with validation
47
+
54
48
  if (deletions.length > 0) {
55
49
  await this.processDeletions(keyType, deletions, transactionCache, mutations, isInTransaction)
56
50
  }
57
51
  })
58
52
  }
59
- /**
60
- * Process deletions with validation
61
- */
53
+
62
54
  async processDeletions(keyType, ids, transactionCache, mutations, isInTransaction) {
63
55
  if (isInTransaction) {
64
- // In transaction, only allow deletion if key exists in cache
56
+
65
57
  for (const keyId of ids) {
66
58
  if (transactionCache[keyType]?.[keyId]) {
67
59
  transactionCache[keyType][keyId] = null
@@ -71,7 +63,7 @@ class PreKeyManager {
71
63
  }
72
64
  }
73
65
  } else {
74
- // Outside transaction, validate against store
66
+
75
67
  const existingKeys = await this.store.get(keyType, ids)
76
68
  for (const keyId of ids) {
77
69
  if (existingKeys[keyId]) {
@@ -83,17 +75,15 @@ class PreKeyManager {
83
75
  }
84
76
  }
85
77
  }
86
- /**
87
- * Validate and process pre-key deletions outside transactions
88
- */
78
+
89
79
  async validateDeletions(data, keyType) {
90
80
  const keyData = data[keyType]
91
81
  if (!keyData) return
92
82
  return this.getQueue(keyType).add(async () => {
93
- // Find all deletion requests
83
+
94
84
  const deletionIds = Object.keys(keyData).filter(id => keyData[id] === null)
95
85
  if (deletionIds.length === 0) return
96
- // Validate deletions
86
+
97
87
  const existingKeys = await this.store.get(keyType, deletionIds)
98
88
  for (const keyId of deletionIds) {
99
89
  if (!existingKeys[keyId]) {