whalibmob 5.5.57 → 5.5.60
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.
- package/README.md +110 -2
- package/cli.js +1 -1
- package/lib/Attestation.js +212 -0
- package/lib/Client.js +28 -26
- package/lib/DeviceManager.js +234 -203
- package/lib/MediaService.js +63 -29
- package/lib/Registration.js +164 -61
- package/lib/Store.js +14 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +55 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +76 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +29 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +34 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +40 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
- package/lib/WAUSync/Protocols/index.js +17 -0
- package/lib/WAUSync/USyncQuery.js +113 -0
- package/lib/WAUSync/USyncUser.js +40 -0
- package/lib/WAUSync/index.js +11 -0
- package/lib/messages/MessageSender.js +40 -17
- package/lib/signal/SenderKey.js +12 -0
- package/lib/signal/SignalProtocol.js +68 -44
- package/lib/signal/WaSignalGroup/ciphertext-message.js +15 -0
- package/lib/signal/WaSignalGroup/group-session-builder.js +45 -0
- package/lib/signal/WaSignalGroup/group_cipher.js +92 -0
- package/lib/signal/WaSignalGroup/index.js +27 -0
- package/lib/signal/WaSignalGroup/keyhelper.js +24 -0
- package/lib/signal/WaSignalGroup/sender-chain-key.js +35 -0
- package/lib/signal/WaSignalGroup/sender-key-distribution-message.js +136 -0
- package/lib/signal/WaSignalGroup/sender-key-message.js +139 -0
- package/lib/signal/WaSignalGroup/sender-key-name.js +59 -0
- package/lib/signal/WaSignalGroup/sender-key-record.js +63 -0
- package/lib/signal/WaSignalGroup/sender-key-state.js +101 -0
- package/lib/signal/WaSignalGroup/sender-message-key.js +34 -0
- package/package.json +8 -9
- package/.env.example +0 -49
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
##
|
|
5
5
|
|
|
6
6
|
> [!IMPORTANT]
|
|
7
|
-
> new repository whalibmob It will be maintained at the https://github.com/Kunboruto50/whalibmob.git Since I lost the Kunboruto20 account, whalibmob will be maintained and rewritten by me soon because I know that many functions in whalibmob no longer work after the WhatsApp mobile protocol change, these days I will take care of whalibmob, all good friends Fun enjoyable for all c
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
|
|
10
|
+
CONTACT ME ON TELEGRAM IF YOU WANT TO WORK WITH ME AND IF YOU HAVE PROBLEM WITH WHALIBMOB : @brtyu545
|
|
11
11
|
|
|
12
12
|
TELEGRAM NEW WHALIBMOB CHANNEL JOIN HERE https://t.me/+jWzq-I9o0Xc1Mzc8
|
|
13
13
|
|
|
@@ -126,6 +126,7 @@ npm install -g whalibmob
|
|
|
126
126
|
- [Library API](#library-api)
|
|
127
127
|
- [Connecting Account](#connecting-account)
|
|
128
128
|
- [Register a New Number](#register-a-new-number)
|
|
129
|
+
- [Device Attestation with Frida (optional)](#device-attestation-with-frida-optional)
|
|
129
130
|
- [Connect](#connect)
|
|
130
131
|
- [Saving & Restoring Sessions](#saving--restoring-sessions)
|
|
131
132
|
- [Signal Store Utilities](#signal-store-utilities)
|
|
@@ -272,6 +273,113 @@ if (result.status === 'ok') {
|
|
|
272
273
|
}
|
|
273
274
|
```
|
|
274
275
|
|
|
276
|
+
### Device Attestation with Frida (optional)
|
|
277
|
+
|
|
278
|
+
WhatsApp's registration servers score every `/code` and `/register` request on how
|
|
279
|
+
much it looks like a genuine phone. A real device proves itself with a hardware
|
|
280
|
+
attestation token — **Play Integrity** plus a **Keystore**-signed request on
|
|
281
|
+
Android, **App Attest** on iOS. whalibmob sends these fields on every registration
|
|
282
|
+
request, but it can only fill them with real values if it can talk to an actual
|
|
283
|
+
device.
|
|
284
|
+
|
|
285
|
+
This repository ships a **`frida/` folder** containing the on-device scripts that
|
|
286
|
+
produce those tokens. It is entirely **optional**: with no device attached,
|
|
287
|
+
whalibmob sends the same empty attestation fields a real phone sends when its
|
|
288
|
+
integrity check fails, and registration still works. Attaching a device raises the
|
|
289
|
+
trust score, which helps when you keep hitting `no_routes` or block screens.
|
|
290
|
+
|
|
291
|
+
> Requires a **rooted Android phone** or a **jailbroken iPhone** with the official
|
|
292
|
+
> WhatsApp app installed from the Play Store / App Store. Sideloaded APKs do not
|
|
293
|
+
> work — the attestation is bound to the store-signed build.
|
|
294
|
+
|
|
295
|
+
#### What's in the folder
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
frida/
|
|
299
|
+
android/ Play Integrity + Keystore attestation server (/integrity, /cert, /info)
|
|
300
|
+
ios/ App Attest attestation server (/integrity)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Each platform folder has its own `README.md` with the full device prerequisites.
|
|
304
|
+
|
|
305
|
+
#### 1. Build the script
|
|
306
|
+
|
|
307
|
+
Install [Frida](https://frida.re) on your computer, then build the bundle for your
|
|
308
|
+
platform:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
cd frida/android # or: cd frida/ios
|
|
312
|
+
npm install
|
|
313
|
+
npm run build # produces server_with_dependencies.js
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### 2. Run it on the device
|
|
317
|
+
|
|
318
|
+
Make sure the Frida server is running on the phone, then attach to WhatsApp:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
# Android — open WhatsApp and reach the "register a number" screen FIRST,
|
|
322
|
+
# otherwise the integrity components are not loaded yet
|
|
323
|
+
frida -U "WhatsApp" -l server_with_dependencies.js
|
|
324
|
+
|
|
325
|
+
# iOS
|
|
326
|
+
frida -U -l server_with_dependencies.js -f "net.whatsapp.WhatsApp"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The script starts a small HTTP server **on the phone**, listening on port `1119`
|
|
330
|
+
(or `1120` if you attached to WhatsApp Business). Wait for:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
[*] Server ready on port 1119
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
#### 3. Make the port reachable
|
|
337
|
+
|
|
338
|
+
whalibmob talks to that server over plain HTTP, so the port has to be reachable
|
|
339
|
+
from the machine running your bot. Over USB, forward it with adb:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
adb forward tcp:1119 tcp:1119 # Android
|
|
343
|
+
iproxy 1119 1119 # iOS (libimobiledevice)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Alternatively, if the phone is on the same Wi-Fi, use its LAN IP directly.
|
|
347
|
+
|
|
348
|
+
#### 4. Point whalibmob at it
|
|
349
|
+
|
|
350
|
+
Set the host — and the port, if it isn't the default `1119`:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
WA_FRIDA_HOST=127.0.0.1
|
|
354
|
+
WA_FRIDA_PORT=1119 # optional; use 1120 for WhatsApp Business
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Or in `.env`:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
WA_FRIDA_HOST=127.0.0.1
|
|
361
|
+
WA_FRIDA_PORT=1119
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
That is the whole integration. On the next `requestSmsCode()` / `verifyCode()`
|
|
365
|
+
call, whalibmob queries the device automatically and attaches the attestation to
|
|
366
|
+
the request:
|
|
367
|
+
|
|
368
|
+
| Platform | Fields it fills |
|
|
369
|
+
|----------|--------------------------------------------------------------------|
|
|
370
|
+
| Android | `gpia` (Play Integrity) in the body, plus a Keystore signature and certificate chain on the request |
|
|
371
|
+
| iOS | An App Attest assertion and attestation on the request |
|
|
372
|
+
|
|
373
|
+
Keep the Frida session open while you register. When you unset `WA_FRIDA_HOST`,
|
|
374
|
+
whalibmob silently goes back to the empty-attestation path — no code changes, and
|
|
375
|
+
nothing else in your bot is affected.
|
|
376
|
+
|
|
377
|
+
> **Troubleshooting.** If the fields come back empty, check in this order: the
|
|
378
|
+
> Frida session is still attached, the port is forwarded, and — on Android — that
|
|
379
|
+
> you opened WhatsApp's registration screen at least once before attaching, since
|
|
380
|
+
> the integrity components are lazy-loaded. whalibmob never fails a registration
|
|
381
|
+
> because attestation is unavailable; it just falls back to empty values.
|
|
382
|
+
|
|
275
383
|
### Connect
|
|
276
384
|
|
|
277
385
|
```js
|
package/cli.js
CHANGED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ─── Device attestation client ────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// Talks to the on-device Frida attestation server shipped in ./frida (Android
|
|
6
|
+
// Play Integrity / Keystore attestation, iOS App Attest) and folds its output
|
|
7
|
+
// into the registration request, mirroring how Auties00/cobalt consumes the
|
|
8
|
+
// same scripts.
|
|
9
|
+
//
|
|
10
|
+
// Design (matches Cobalt's LinkedWhatsAppClientDeviceAttestor):
|
|
11
|
+
// • Two data shapes are produced —
|
|
12
|
+
// Android: { gpia, gg, gi, gp, ge, ga } (Play Integrity legs)
|
|
13
|
+
// iOS: { attestation, assertion, keyId } (App Attest)
|
|
14
|
+
// • A NONE fallback returns empty strings for every field. The WhatsApp
|
|
15
|
+
// registration server tolerates empty attestation as a low-trust signal,
|
|
16
|
+
// so registration still works with no rooted/jailbroken device attached.
|
|
17
|
+
//
|
|
18
|
+
// Activation: set WA_FRIDA_HOST (e.g. 127.0.0.1) — and optionally WA_FRIDA_PORT
|
|
19
|
+
// (default 1119 for WhatsApp consumer, 1120 for Business) — to make the client
|
|
20
|
+
// query the local Frida server. When WA_FRIDA_HOST is unset every method
|
|
21
|
+
// returns the empty NONE fallback and performs no network I/O.
|
|
22
|
+
|
|
23
|
+
const crypto = require('crypto');
|
|
24
|
+
const http = require('http');
|
|
25
|
+
|
|
26
|
+
const DEFAULT_PORT = 1119; // WhatsApp consumer; Business build listens on 1120
|
|
27
|
+
const REQUEST_TIMEMS = 15000;
|
|
28
|
+
|
|
29
|
+
// ─── Empty (NONE) fallbacks ───────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const EMPTY_ANDROID = { gpia: '', gg: '', gi: '', gp: '', ge: '', ga: '' };
|
|
32
|
+
const EMPTY_IOS = { attestation: '', assertion: '', keyId: '' };
|
|
33
|
+
const EMPTY_BODY = { bodyAttestation: null, authorizationHeader: null };
|
|
34
|
+
|
|
35
|
+
function fridaHost() {
|
|
36
|
+
return process.env.WA_FRIDA_HOST || null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function fridaPort() {
|
|
40
|
+
return parseInt(process.env.WA_FRIDA_PORT, 10) || DEFAULT_PORT;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isEnabled() {
|
|
44
|
+
return !!fridaHost();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── base64 / hex helpers ─────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
function toBase64Url(buf) {
|
|
50
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sha256(buf) {
|
|
54
|
+
return crypto.createHash('sha256').update(buf).digest();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function toHex(buf) {
|
|
58
|
+
return Buffer.from(buf).toString('hex');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Local Frida HTTP GET (best-effort; never throws) ─────────────────────────
|
|
62
|
+
// Returns the parsed JSON object on success, or null on any failure/timeout so
|
|
63
|
+
// the caller can fall back to the empty NONE attestation.
|
|
64
|
+
|
|
65
|
+
function fridaGet(pathname, query) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const host = fridaHost();
|
|
68
|
+
if (!host) return resolve(null);
|
|
69
|
+
|
|
70
|
+
const qs = query
|
|
71
|
+
? '?' + Object.keys(query)
|
|
72
|
+
.filter(k => query[k] != null)
|
|
73
|
+
.map(k => k + '=' + encodeURIComponent(query[k]))
|
|
74
|
+
.join('&')
|
|
75
|
+
: '';
|
|
76
|
+
|
|
77
|
+
const req = http.request(
|
|
78
|
+
{ host, port: fridaPort(), path: pathname + qs, method: 'GET', timeout: REQUEST_TIMEMS },
|
|
79
|
+
(res) => {
|
|
80
|
+
const chunks = [];
|
|
81
|
+
res.on('data', d => chunks.push(d));
|
|
82
|
+
res.on('end', () => {
|
|
83
|
+
try { resolve(JSON.parse(Buffer.concat(chunks).toString('utf8'))); }
|
|
84
|
+
catch (_) { resolve(null); }
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
req.on('error', () => resolve(null));
|
|
89
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
90
|
+
req.end();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Android: Play Integrity legs (gpia + _gg _gi _gp _ge _ga) ────────────────
|
|
95
|
+
//
|
|
96
|
+
// Queries /integrity?authKey=<nonce>. The Frida server returns { token }, which
|
|
97
|
+
// becomes the Play Integrity verdict token (gpia). The _gg/_gi/_gp/_ge/_ga GMS
|
|
98
|
+
// legs are only produced by the full native attestor, so a Frida-only setup
|
|
99
|
+
// leaves them empty — the server accepts a gpia-only attestation.
|
|
100
|
+
//
|
|
101
|
+
// nonceB64 is the URL-safe base64 request nonce (see attestationFieldsAndroid).
|
|
102
|
+
async function androidIntegrity(nonceB64) {
|
|
103
|
+
if (!isEnabled()) return { ...EMPTY_ANDROID };
|
|
104
|
+
const res = await fridaGet('/integrity', { authKey: nonceB64 });
|
|
105
|
+
if (!res || !res.token) return { ...EMPTY_ANDROID };
|
|
106
|
+
return { gpia: res.token, gg: '', gi: '', gp: '', ge: '', ga: '' };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── Android: Keystore attestation over the ENC body (&H= + Authorization) ────
|
|
110
|
+
//
|
|
111
|
+
// Queries /cert?authKey=<authKeyB64>&enc=<encB64>. Both parameters are decoded
|
|
112
|
+
// with the STANDARD base64 decoder on the device, so they must not be URL-safe:
|
|
113
|
+
// authKey — the 32-byte raw auth key (the Noise public key, no 0x05 prefix);
|
|
114
|
+
// the server embeds it in the Keystore attestation challenge and
|
|
115
|
+
// locates it in the certificate by its 64-hex-char representation.
|
|
116
|
+
// enc — the UTF-8 bytes of the ENC payload string, base64-encoded; the
|
|
117
|
+
// server signs exactly those bytes.
|
|
118
|
+
// The server returns { signature, certificate }: signature → the &H= body
|
|
119
|
+
// suffix, certificate → the Authorization cert-chain header.
|
|
120
|
+
async function androidAttestBody(encB64, authKeyB64) {
|
|
121
|
+
if (!isEnabled()) return { ...EMPTY_BODY };
|
|
122
|
+
const encParam = Buffer.from(encB64, 'utf8').toString('base64');
|
|
123
|
+
const res = await fridaGet('/cert', { authKey: authKeyB64, enc: encParam });
|
|
124
|
+
if (!res || !res.signature || !res.certificate) return { ...EMPTY_BODY };
|
|
125
|
+
return { bodyAttestation: res.signature, authorizationHeader: res.certificate };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Android: device /info (apk hashes / signature / secret key) ──────────────
|
|
129
|
+
async function androidInfo() {
|
|
130
|
+
if (!isEnabled()) return null;
|
|
131
|
+
return fridaGet('/info');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── iOS: App Attest (attestation + assertion + keyId) ────────────────────────
|
|
135
|
+
//
|
|
136
|
+
// Queries /integrity?authKey=<clientDataHashB64>. The Frida server returns
|
|
137
|
+
// { attestation, assertion }; the keyId is generated per call on-device and
|
|
138
|
+
// echoed by the server when available. clientDataHash is the base64 of the
|
|
139
|
+
// Noise public key (the server SHA-256-hashes it internally).
|
|
140
|
+
async function iosAppAttest(noisePubB64) {
|
|
141
|
+
if (!isEnabled()) return { ...EMPTY_IOS };
|
|
142
|
+
const res = await fridaGet('/integrity', { authKey: noisePubB64 });
|
|
143
|
+
if (!res || !res.attestation || !res.assertion) return { ...EMPTY_IOS };
|
|
144
|
+
return {
|
|
145
|
+
attestation: res.attestation,
|
|
146
|
+
assertion: res.assertion,
|
|
147
|
+
keyId: res.keyId || ''
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─── High-level: attestation fields shipped on every attested endpoint ────────
|
|
152
|
+
//
|
|
153
|
+
// Returns the alternating name/value pairs Cobalt ships via attestationFields():
|
|
154
|
+
// Android → gpia,_gg,_gi,_gp,_ge,_ga,push_token
|
|
155
|
+
// iOS → push_token
|
|
156
|
+
//
|
|
157
|
+
// These fields are part of the plaintext that gets AES-GCM-sealed into the
|
|
158
|
+
// ENC envelope, so the Play Integrity nonce is derived from the store (a stable
|
|
159
|
+
// per-session value), NOT from the ENC body. pushToken comes from the optional
|
|
160
|
+
// push client — empty by default.
|
|
161
|
+
async function attestationFields(device, nonceB64, opts) {
|
|
162
|
+
opts = opts || {};
|
|
163
|
+
const pushToken = opts.pushToken || '';
|
|
164
|
+
|
|
165
|
+
if (device && device.os === 'android') {
|
|
166
|
+
const a = await androidIntegrity(nonceB64 || '');
|
|
167
|
+
return [
|
|
168
|
+
'gpia', a.gpia,
|
|
169
|
+
'_gg', a.gg,
|
|
170
|
+
'_gi', a.gi,
|
|
171
|
+
'_gp', a.gp,
|
|
172
|
+
'_ge', a.ge,
|
|
173
|
+
'_ga', a.ga,
|
|
174
|
+
'push_token', pushToken
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// iOS ships only push_token in the form body; App Attest rides in &H= + header.
|
|
179
|
+
return ['push_token', pushToken];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ─── High-level: body attestation (&H= suffix + Authorization header) ─────────
|
|
183
|
+
//
|
|
184
|
+
// encB64 — the base64url ENC payload (without the "ENC=" prefix)
|
|
185
|
+
// device — { os }
|
|
186
|
+
// keys.noisePubB64 — standard base64 of the 32-byte Noise public key. Android
|
|
187
|
+
// uses it as the Keystore attestation challenge auth key;
|
|
188
|
+
// iOS uses it as the App Attest clientDataHash source.
|
|
189
|
+
async function attestBody(encB64, device, keys) {
|
|
190
|
+
keys = keys || {};
|
|
191
|
+
if (device && device.os === 'android') {
|
|
192
|
+
return androidAttestBody(encB64, keys.noisePubB64 || '');
|
|
193
|
+
}
|
|
194
|
+
const data = await iosAppAttest(keys.noisePubB64 || '');
|
|
195
|
+
if (!data.attestation || !data.assertion || !data.keyId) return { ...EMPTY_BODY };
|
|
196
|
+
return {
|
|
197
|
+
bodyAttestation: '{"assertion":"' + data.assertion + '"}',
|
|
198
|
+
authorizationHeader: data.attestation + '|' + data.keyId
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
isEnabled,
|
|
204
|
+
attestationFields,
|
|
205
|
+
attestBody,
|
|
206
|
+
androidInfo,
|
|
207
|
+
toHex,
|
|
208
|
+
toBase64Url,
|
|
209
|
+
EMPTY_ANDROID,
|
|
210
|
+
EMPTY_IOS,
|
|
211
|
+
EMPTY_BODY
|
|
212
|
+
};
|
package/lib/Client.js
CHANGED
|
@@ -676,7 +676,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
676
676
|
_whaDbg('[DBG] DM_DECRYPT id=' + id + ' type=' + encType + ' sigJid=' + sigJid + ' cipherLen=' + cipherBuf.length);
|
|
677
677
|
|
|
678
678
|
this._signal.decrypt(sigJid, encType, cipherBuf)
|
|
679
|
-
.then(plaintext => {
|
|
679
|
+
.then(async plaintext => {
|
|
680
680
|
const decoded = this._decodeMsg(plaintext);
|
|
681
681
|
_whaDbg('[DBG] DM_DECODED id=' + id + ' type=' + decoded.type + ' ptLen=' + (plaintext ? plaintext.length : 0) + ' hasSKDM=' + !!(decoded.skdm || decoded.type === 'senderKeyDistribution'));
|
|
682
682
|
|
|
@@ -685,7 +685,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
685
685
|
// axolotlBytes may be absent in newer WA proto versions — that's OK for DM pkmsg flow.
|
|
686
686
|
if (decoded.skdm && decoded.skdm.axolotlBytes && decoded.skdm.axolotlBytes[0] === 0x33) {
|
|
687
687
|
try {
|
|
688
|
-
this._signal.processSKDM(decoded.skdm.groupId, sigJid, decoded.skdm.axolotlBytes);
|
|
688
|
+
await this._signal.processSKDM(decoded.skdm.groupId, sigJid, decoded.skdm.axolotlBytes);
|
|
689
689
|
_whaDbg('[DBG] SKDM_PROCESSED groupId=' + decoded.skdm.groupId + ' sender=' + sigJid);
|
|
690
690
|
} catch (e) {
|
|
691
691
|
_whaDbg('[DBG] SKDM_ERR ' + e.message);
|
|
@@ -696,7 +696,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
696
696
|
if (decoded.type === 'senderKeyDistribution') {
|
|
697
697
|
if (decoded.axolotlBytes && decoded.axolotlBytes[0] === 0x33) {
|
|
698
698
|
try {
|
|
699
|
-
this._signal.processSKDM(decoded.groupId, sigJid, decoded.axolotlBytes);
|
|
699
|
+
await this._signal.processSKDM(decoded.groupId, sigJid, decoded.axolotlBytes);
|
|
700
700
|
_whaDbg('[DBG] SKDM_ONLY_PROCESSED groupId=' + decoded.groupId + ' sender=' + sigJid);
|
|
701
701
|
} catch (e) {
|
|
702
702
|
_whaDbg('[DBG] SKDM_ONLY_ERR ' + e.message);
|
|
@@ -745,24 +745,25 @@ class WhalibmobClient extends EventEmitter {
|
|
|
745
745
|
? skmsgNode.content
|
|
746
746
|
: Buffer.from(skmsgNode.content || '');
|
|
747
747
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
748
|
+
this._signal.senderKeyDecrypt(from, participant, cipherBuf)
|
|
749
|
+
.then(plaintext => {
|
|
750
|
+
const decoded = this._decodeMsg(plaintext);
|
|
751
|
+
if (decoded.type === 'senderKeyDistribution') return;
|
|
752
|
+
if (decoded.type === 'protocol') {
|
|
753
|
+
if (decoded.subtype === 'history_sync' && decoded.historySyncNotification) {
|
|
754
|
+
this._handleHistorySync(decoded.historySyncNotification, id);
|
|
755
|
+
}
|
|
756
|
+
if (decoded.subtype === 'app_state_sync_key_share' && decoded.appStateSyncKeyShare) {
|
|
757
|
+
this._handleAppStateSyncKeyShare(decoded.appStateSyncKeyShare);
|
|
758
|
+
}
|
|
759
|
+
return;
|
|
758
760
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
}
|
|
761
|
+
this.emit('message', { id, from, participant, ts, decoded, isGroup: true, node });
|
|
762
|
+
this._sendReadReceipt(id, fromRaw, partRaw);
|
|
763
|
+
})
|
|
764
|
+
.catch(err => {
|
|
765
|
+
this.emit('decrypt_error', { id, from, participant, err });
|
|
766
|
+
});
|
|
766
767
|
}
|
|
767
768
|
|
|
768
769
|
_decodeMsg(buf) {
|
|
@@ -801,14 +802,14 @@ class WhalibmobClient extends EventEmitter {
|
|
|
801
802
|
|
|
802
803
|
tasks.push(
|
|
803
804
|
this._signal.decrypt(senderJid, encType, cipherBuf)
|
|
804
|
-
.then(decrypted => {
|
|
805
|
+
.then(async decrypted => {
|
|
805
806
|
const decoded = this._decodeMsg(decrypted);
|
|
806
807
|
if (decoded && decoded.type === 'senderKeyDistribution' &&
|
|
807
808
|
decoded.groupId && decoded.axolotlBytes) {
|
|
808
|
-
this._signal.processSKDM(decoded.groupId, senderJid, decoded.axolotlBytes);
|
|
809
|
+
await this._signal.processSKDM(decoded.groupId, senderJid, decoded.axolotlBytes);
|
|
809
810
|
} else if (decoded && decoded.skdm &&
|
|
810
811
|
decoded.skdm.groupId && decoded.skdm.axolotlBytes) {
|
|
811
|
-
this._signal.processSKDM(decoded.skdm.groupId, senderJid, decoded.skdm.axolotlBytes);
|
|
812
|
+
await this._signal.processSKDM(decoded.skdm.groupId, senderJid, decoded.skdm.axolotlBytes);
|
|
812
813
|
}
|
|
813
814
|
})
|
|
814
815
|
.catch(() => {})
|
|
@@ -1255,6 +1256,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
1255
1256
|
const connNode = findChild(node, 'media_conn');
|
|
1256
1257
|
if (!connNode) return;
|
|
1257
1258
|
const auth = (connNode.attrs && connNode.attrs.auth) || '';
|
|
1259
|
+
const ttl = connNode.attrs && connNode.attrs.ttl ? parseInt(connNode.attrs.ttl, 10) : 300;
|
|
1258
1260
|
const hosts = [];
|
|
1259
1261
|
if (Array.isArray(connNode.content)) {
|
|
1260
1262
|
for (const child of connNode.content) {
|
|
@@ -1264,9 +1266,9 @@ class WhalibmobClient extends EventEmitter {
|
|
|
1264
1266
|
}
|
|
1265
1267
|
}
|
|
1266
1268
|
}
|
|
1267
|
-
this._mediaConn = { hosts, auth };
|
|
1268
|
-
if (this._sender) this._sender.setMediaConnection(hosts, auth);
|
|
1269
|
-
this.emit('media_conn', { hosts, auth });
|
|
1269
|
+
this._mediaConn = { hosts, auth, ttl, fetchDate: Date.now() };
|
|
1270
|
+
if (this._sender) this._sender.setMediaConnection(hosts, auth, ttl, Date.now());
|
|
1271
|
+
this.emit('media_conn', { hosts, auth, ttl });
|
|
1270
1272
|
}
|
|
1271
1273
|
|
|
1272
1274
|
// ─── Pre-key upload ───────────────────────────────────────────────────────
|