@n4tzz/n4lyx 2.7.5 → 2.7.7

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.
@@ -1,149 +1,229 @@
1
1
  "use strict";
2
-
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.useMultiFileAuthState = void 0;
5
-
6
- const { Mutex } = require("async-mutex");
7
- const { writeFile, readFile, unlink, stat, mkdir } = require("fs/promises");
8
- const { join } = require("path");
9
- const { proto } = require("../../WAProto");
10
- const { initAuthCreds } = require("./auth-utils");
11
- const { BufferJSON } = require("./generics");
3
+ exports.encodeSignedDeviceIdentity = exports.configureSuccessfulPairing = exports.generateRegistrationNode = exports.generateLoginNode = void 0;
4
+ const boom_1 = require("@hapi/boom");
5
+ const crypto_1 = require("crypto");
6
+ const WAProto_1 = require("../../WAProto");
7
+ const Defaults_1 = require("../Defaults");
8
+ const WABinary_1 = require("../WABinary");
9
+ const crypto_2 = require("./crypto");
10
+ const generics_1 = require("./generics");
11
+ const signal_1 = require("./signal");
12
+
13
+ const getUserAgent = (config) => {
14
+ return {
15
+ appVersion: {
16
+ primary: config.version[0],
17
+ secondary: config.version[1],
18
+ tertiary: config.version[2],
19
+ },
20
+ platform: WAProto_1.proto.ClientPayload.UserAgent.Platform.WEB,
21
+ releaseChannel: WAProto_1.proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
22
+ osVersion: '0.1',
23
+ device: 'Desktop',
24
+ osBuildNumber: '0.1',
25
+ localeLanguageIso6391: 'en',
26
+ mnc: '000',
27
+ mcc: '000',
28
+ localeCountryIso31661Alpha2: config.countryCode || 'US'
29
+ };
30
+ };
12
31
 
13
- const fileLocks = new Map();
32
+ const PLATFORM_MAP = {
33
+ 'Mac OS': WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
34
+ 'Windows': WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
35
+ };
14
36
 
15
- const getFileLock = (path) => {
16
- if (!fileLocks.has(path)) {
17
- fileLocks.set(path, new Mutex());
37
+ const getWebInfo = (config) => {
38
+ let webSubPlatform = WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER;
39
+ if (config.syncFullHistory && PLATFORM_MAP[config.browser[0]] && config.browser[1] === 'Desktop') {
40
+ webSubPlatform = PLATFORM_MAP[config.browser[0]];
18
41
  }
19
- return fileLocks.get(path);
42
+ return { webSubPlatform };
20
43
  };
21
44
 
22
- const fixFileName = (file) => {
23
- return file?.replace(/\//g, '__')?.replace(/:/g, '-');
45
+ const getClientPayload = (config) => {
46
+ const payload = {
47
+ connectType: WAProto_1.proto.ClientPayload.ConnectType.WIFI_UNKNOWN,
48
+ connectReason: WAProto_1.proto.ClientPayload.ConnectReason.USER_ACTIVATED,
49
+ userAgent: getUserAgent(config),
50
+ };
51
+ payload.webInfo = getWebInfo(config);
52
+ return payload;
24
53
  };
25
54
 
26
- const delay = (ms) => new Promise(res => setTimeout(res, ms));
27
-
28
- const safeWrite = async (filePath, data) => {
29
- const tmp = filePath + ".tmp";
30
- await writeFile(tmp, JSON.stringify(data, BufferJSON.replacer));
31
- await writeFile(filePath, await readFile(tmp));
32
- await unlink(tmp).catch(() => { });
55
+ const generateLoginNode = (userJid, config) => {
56
+ const { user, device } = (0, WABinary_1.jidDecode)(userJid);
57
+ const payload = {
58
+ ...getClientPayload(config),
59
+ passive: true,
60
+ pull: true,
61
+ username: +user,
62
+ device: device,
63
+ lidDbMigrated: false
64
+ };
65
+ return WAProto_1.proto.ClientPayload.fromObject(payload);
33
66
  };
67
+ exports.generateLoginNode = generateLoginNode;
34
68
 
35
- const useMultiFileAuthState = async (folder) => {
69
+ const getPlatformType = (platform) => {
70
+ const platformType = platform.toUpperCase();
71
+ return WAProto_1.proto.DeviceProps.PlatformType[platformType] || WAProto_1.proto.DeviceProps.PlatformType.CHROME;
72
+ };
36
73
 
37
- const folderInfo = await stat(folder).catch(() => null);
74
+ const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
75
+ const appVersionBuf = (0, crypto_1.createHash)('md5')
76
+ .update(config.version.join('.'))
77
+ .digest();
78
+
79
+ const companion = {
80
+ os: config.browser[0],
81
+ platformType: getPlatformType(config.browser[1]),
82
+ requireFullSync: config.syncFullHistory,
83
+ historySyncConfig: {
84
+ storageQuotaMb: 10240,
85
+ inlineInitialPayloadInE2EeMsg: true,
86
+ recentSyncDaysLimit: undefined,
87
+ supportCallLogHistory: false,
88
+ supportBotUserAgentChatHistory: true,
89
+ supportCagReactionsAndPolls: true,
90
+ supportBizHostedMsg: true,
91
+ supportRecentSyncChunkMessageCountTuning: true,
92
+ supportHostedGroupMsg: true,
93
+ supportFbidBotChatHistory: true,
94
+ supportAddOnHistorySyncMigration: undefined,
95
+ supportMessageAssociation: true,
96
+ supportGroupHistory: false,
97
+ onDemandReady: undefined,
98
+ supportGuestChat: undefined
99
+ },
100
+ version: {
101
+ primary: 10,
102
+ secondary: 15,
103
+ tertiary: 7
104
+ }
105
+ };
38
106
 
39
- if (folderInfo && !folderInfo.isDirectory()) {
40
- throw new Error(`Path ${folder} is not a directory`);
107
+ const companionProto = WAProto_1.proto.DeviceProps.encode(companion).finish();
108
+
109
+ const registerPayload = {
110
+ ...getClientPayload(config),
111
+ passive: false,
112
+ pull: false,
113
+ devicePairingData: {
114
+ buildHash: appVersionBuf,
115
+ deviceProps: companionProto,
116
+ eRegid: (0, generics_1.encodeBigEndian)(registrationId),
117
+ eKeytype: Defaults_1.KEY_BUNDLE_TYPE,
118
+ eIdent: signedIdentityKey.public,
119
+ eSkeyId: (0, generics_1.encodeBigEndian)(signedPreKey.keyId, 3),
120
+ eSkeyVal: signedPreKey.keyPair.public,
121
+ eSkeySig: signedPreKey.signature,
122
+ },
123
+ };
124
+ return WAProto_1.proto.ClientPayload.fromObject(registerPayload);
125
+ };
126
+ exports.generateRegistrationNode = generateRegistrationNode;
127
+
128
+ const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, signalIdentities }) => {
129
+ const msgId = stanza.attrs.id;
130
+ const pairSuccessNode = (0, WABinary_1.getBinaryNodeChild)(stanza, 'pair-success');
131
+ const deviceIdentityNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'device-identity');
132
+ const platformNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'platform');
133
+ const deviceNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'device');
134
+ const businessNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'biz');
135
+
136
+ if (!deviceIdentityNode || !deviceNode) {
137
+ throw new boom_1.Boom('Missing device-identity or device in pair success node', { data: stanza });
41
138
  }
42
139
 
43
- if (!folderInfo) {
44
- await mkdir(folder, { recursive: true });
45
- }
140
+ const bizName = businessNode?.attrs.name;
141
+ const jid = deviceNode.attrs.jid;
142
+ const lid = deviceNode.attrs.lid;
46
143
 
47
- const writeData = async (data, file) => {
48
- const filePath = join(folder, fixFileName(file));
49
- const mutex = getFileLock(filePath);
50
-
51
- return mutex.acquire().then(async (release) => {
52
- try {
53
- await safeWrite(filePath, data);
54
- } catch (e) {
55
- await delay(100);
56
- try {
57
- await safeWrite(filePath, data);
58
- } catch (_) { }
59
- } finally {
60
- release();
61
- }
62
- });
63
- };
144
+ const { details, hmac, accountType } = WAProto_1.proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityNode.content);
64
145
 
65
- const readData = async (file) => {
66
- const filePath = join(folder, fixFileName(file));
67
- const mutex = getFileLock(filePath);
68
-
69
- return mutex.acquire().then(async (release) => {
70
- try {
71
- const data = await readFile(filePath, { encoding: 'utf-8' });
72
- return JSON.parse(data, BufferJSON.reviver);
73
- } catch (_) {
74
- return null;
75
- } finally {
76
- release();
77
- }
78
- });
79
- };
146
+ let hmacPrefix = Buffer.from([]);
147
+ if (accountType !== undefined && accountType === WAProto_1.proto.ADVEncryptionType.HOSTED) {
148
+ hmacPrefix = Buffer.from([0x06, 0x05]);
149
+ }
80
150
 
81
- const removeData = async (file) => {
82
- const filePath = join(folder, fixFileName(file));
83
- const mutex = getFileLock(filePath);
151
+ const advSign = (0, crypto_2.hmacSign)(Buffer.concat([hmacPrefix, details]), Buffer.from(advSecretKey, 'base64'));
152
+ if (Buffer.compare(hmac, advSign) !== 0) {
153
+ throw new boom_1.Boom('Invalid account signature');
154
+ }
84
155
 
85
- return mutex.acquire().then(async (release) => {
86
- try {
87
- await unlink(filePath);
88
- } catch (_) { }
89
- finally {
90
- release();
91
- }
92
- });
93
- };
156
+ const account = WAProto_1.proto.ADVSignedDeviceIdentity.decode(details);
157
+ const { accountSignatureKey, accountSignature, details: deviceDetails } = account;
94
158
 
95
- let creds = await readData('creds.json');
159
+ const deviceIdentity = WAProto_1.proto.ADVDeviceIdentity.decode(deviceDetails);
96
160
 
97
- if (!creds) {
98
- creds = initAuthCreds();
99
- await writeData(creds, 'creds.json');
161
+ const accountSignaturePrefix = deviceIdentity.deviceType === WAProto_1.proto.ADVEncryptionType.HOSTED
162
+ ? Buffer.from([0x06, 0x05])
163
+ : Buffer.from([0x06, 0x00]);
164
+ const accountMsg = Buffer.concat([accountSignaturePrefix, deviceDetails, signedIdentityKey.public]);
165
+
166
+ if (!crypto_2.Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
167
+ throw new boom_1.Boom('Failed to verify account signature');
100
168
  }
101
169
 
102
- return {
103
- state: {
104
- creds,
105
- keys: {
106
- get: async (type, ids) => {
107
- const data = {};
108
-
109
- await Promise.all(ids.map(async (id) => {
110
- let value = await readData(`${type}-${id}.json`);
111
-
112
- if (type === 'app-state-sync-key' && value) {
113
- value = proto.Message.AppStateSyncKeyData.fromObject(value);
114
- }
115
-
116
- data[id] = value;
117
- }));
118
-
119
- return data;
120
- },
121
-
122
- set: async (data) => {
123
- const tasks = [];
124
-
125
- for (const category in data) {
126
- for (const id in data[category]) {
127
- const value = data[category][id];
128
- const file = `${category}-${id}.json`;
129
-
130
- if (value) {
131
- tasks.push(writeData(value, file));
132
- } else {
133
- tasks.push(removeData(file));
134
- }
135
- }
170
+ const deviceMsg = Buffer.concat([
171
+ Buffer.from([0x06, 0x01]),
172
+ deviceDetails,
173
+ signedIdentityKey.public,
174
+ accountSignatureKey
175
+ ]);
176
+ account.deviceSignature = crypto_2.Curve.sign(signedIdentityKey.private, deviceMsg);
177
+
178
+ const identity = (0, signal_1.createSignalIdentity)(jid, accountSignatureKey);
179
+ const accountEnc = (0, exports.encodeSignedDeviceIdentity)(account, false);
180
+
181
+ const reply = {
182
+ tag: 'iq',
183
+ attrs: {
184
+ to: WABinary_1.S_WHATSAPP_NET,
185
+ type: 'result',
186
+ id: msgId,
187
+ },
188
+ content: [
189
+ {
190
+ tag: 'pair-device-sign',
191
+ attrs: {},
192
+ content: [
193
+ {
194
+ tag: 'device-identity',
195
+ attrs: { 'key-index': deviceIdentity.keyIndex.toString() },
196
+ content: accountEnc
136
197
  }
137
-
138
- await Promise.all(tasks);
139
- }
198
+ ]
140
199
  }
141
- },
200
+ ]
201
+ };
142
202
 
143
- saveCreds: async () => {
144
- await writeData(creds, 'creds.json');
145
- }
203
+ const authUpdate = {
204
+ account,
205
+ me: { id: jid, name: bizName, lid },
206
+ signalIdentities: [
207
+ ...(signalIdentities || []),
208
+ identity
209
+ ],
210
+ platform: platformNode?.attrs.name
211
+ };
212
+
213
+ return {
214
+ creds: authUpdate,
215
+ reply
146
216
  };
147
217
  };
218
+ exports.configureSuccessfulPairing = configureSuccessfulPairing;
148
219
 
149
- exports.useMultiFileAuthState = useMultiFileAuthState;
220
+ const encodeSignedDeviceIdentity = (account, includeSignatureKey) => {
221
+ account = { ...account };
222
+ if (!includeSignatureKey || !account.accountSignatureKey?.length) {
223
+ account.accountSignatureKey = null;
224
+ }
225
+ return WAProto_1.proto.ADVSignedDeviceIdentity
226
+ .encode(account)
227
+ .finish();
228
+ };
229
+ exports.encodeSignedDeviceIdentity = encodeSignedDeviceIdentity;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n4tzz/n4lyx",
3
- "version": "2.7.5",
3
+ "version": "2.7.7",
4
4
  "description": "N4lyx - WhatsApp Web API Library powered by N4tzzOfficial",
5
5
  "keywords": [
6
6
  "n4lyx",