@cdteam/cdwa 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +112 -2
- package/dist/auth/index.cjs +191 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +25 -0
- package/dist/auth/index.d.ts +25 -0
- package/dist/auth/index.js +183 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/binary/index.cjs +414 -0
- package/dist/binary/index.cjs.map +1 -0
- package/dist/binary/index.d.cts +92 -0
- package/dist/binary/index.d.ts +92 -0
- package/dist/binary/index.js +407 -0
- package/dist/binary/index.js.map +1 -0
- package/dist/crypto/index.cjs +207 -0
- package/dist/crypto/index.cjs.map +1 -0
- package/dist/crypto/index.d.cts +91 -0
- package/dist/crypto/index.d.ts +91 -0
- package/dist/crypto/index.js +201 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/index-CcMoHfPz.d.cts +70 -0
- package/dist/index-CcMoHfPz.d.ts +70 -0
- package/dist/index.cjs +1254 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +97 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.js +1224 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/index.cjs +662 -0
- package/dist/messages/index.cjs.map +1 -0
- package/dist/messages/index.d.cts +122 -0
- package/dist/messages/index.d.ts +122 -0
- package/dist/messages/index.js +659 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/signal/index.cjs +38 -0
- package/dist/signal/index.cjs.map +1 -0
- package/dist/signal/index.d.cts +30 -0
- package/dist/signal/index.d.ts +30 -0
- package/dist/signal/index.js +36 -0
- package/dist/signal/index.js.map +1 -0
- package/package.json +91 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CDTeamMods
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,113 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CDWA 🚀
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Uma biblioteca moderna, modular e com foco em alta segurança e criptografia para o protocolo WhatsApp Web Multi-Device, desenhada para o ecossistema Node.js e TypeScript.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/cdwa)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](#)
|
|
8
|
+
[](https://cdteammods.github.io/CDWA/)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🌐 Site da Documentação Oficial
|
|
13
|
+
|
|
14
|
+
Acesse nosso portal interativo completo com todos os guias, exemplos e especificações da API no **GitHub Pages**:
|
|
15
|
+
|
|
16
|
+
👉 **[https://cdteammods.github.io/CDWA/](https://cdteammods.github.io/CDWA/)**
|
|
17
|
+
|
|
18
|
+
### 📑 Navegação Direta no Site:
|
|
19
|
+
* 🚀 **[Início Rápido](./docs/getting-started.html)**: Instalação, setup e primeiro bot conectado via QR Code.
|
|
20
|
+
* 🔒 **[Arquitetura & Segurança](./docs/architecture.html)**: Noise Protocol Handshake, WABinary e criptografia at-rest.
|
|
21
|
+
* 📩 **[Envio de Mensagens](./docs/sending-messages.html)**: Guia completo para textos, fotos, vídeos, notas de voz (PTT), GPS, contatos e reações.
|
|
22
|
+
* 📡 **[Eventos & Conexão](./docs/events.html)**: Monitoramento de status, reconexão automática e mensagens recebidas.
|
|
23
|
+
* 🔑 **[Autenticação & Estado](./docs/authentication.html)**: Como proteger tokens com AES-256-GCM e chave mestra.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ⚡ Por que escolher o CDWA?
|
|
28
|
+
|
|
29
|
+
Inspirada e reconstruída a partir dos conceitos pioneiros do [WhiskeySockets/Baileys](https://github.com/WhiskeySockets/Baileys), o **CDWA** soluciona as principais deficiências históricas:
|
|
30
|
+
|
|
31
|
+
| Recurso | Baileys Clássico | CDWA |
|
|
32
|
+
| :--- | :--- | :--- |
|
|
33
|
+
| **Armazenamento de Credenciais** | JSONs em texto plano no disco (vulnerável) | **Criptografia At-Rest com AES-256-GCM + KDF** |
|
|
34
|
+
| **Primitivas Criptográficas** | Libs legadas e heterogêneas | **`@noble/curves` (X25519) auditado e puro** |
|
|
35
|
+
| **Tipagem TypeScript** | Histórico com muitos tipos `any` | **Modo Estrito (Strict Mode) de ponta a ponta** |
|
|
36
|
+
| **Vazamento de Memória** | Acúmulo de listeners em conexões longas | **Ciclo de vida determinístico e buffers limpos** |
|
|
37
|
+
| **Empacotamento** | CommonJS predominante | **Dual Export moderno (ESM nativo + CJS via tsup)** |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📦 Instalação
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install cdwa
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🚀 Uso Rápido (JavaScript Puro ou TypeScript)
|
|
50
|
+
|
|
51
|
+
### Em JavaScript Puro com ES Modules (`import` - arquivo `.js`):
|
|
52
|
+
```javascript
|
|
53
|
+
import makeWASocket, { useSecureAuthState } from 'cdwa'
|
|
54
|
+
|
|
55
|
+
const auth = await useSecureAuthState('./auth_info', 'senha-secreta')
|
|
56
|
+
const sock = makeWASocket({ auth, printQRInTerminal: true })
|
|
57
|
+
|
|
58
|
+
sock.on('connection.update', ({ qr, connection }) => {
|
|
59
|
+
if (qr) console.log('Escaneie o QR Code:', qr)
|
|
60
|
+
if (connection === 'open') console.log('✅ Conectado via JavaScript Puro!')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
await sock.connect()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Em JavaScript Puro com CommonJS (`require` - arquivo `.cjs`):
|
|
67
|
+
```javascript
|
|
68
|
+
const { default: makeWASocket, useSecureAuthState } = require('cdwa')
|
|
69
|
+
|
|
70
|
+
async function run() {
|
|
71
|
+
const auth = await useSecureAuthState('./auth_info', 'senha-secreta')
|
|
72
|
+
const sock = makeWASocket({ auth, printQRInTerminal: true })
|
|
73
|
+
|
|
74
|
+
sock.on('connection.update', ({ qr, connection }) => {
|
|
75
|
+
if (qr) console.log('Escaneie o QR Code:', qr)
|
|
76
|
+
if (connection === 'open') console.log('✅ Conectado via CommonJS!')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
await sock.connect()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
run()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 📁 Estrutura do Repositório
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
├── docs/ # Site oficial de documentação (GitHub Pages)
|
|
91
|
+
│ ├── index.html # Portal inicial
|
|
92
|
+
│ ├── style.css # Design responsivo dark mode
|
|
93
|
+
│ └── *.html # Guias temáticos interligados
|
|
94
|
+
├── src/
|
|
95
|
+
│ ├── auth/ # Gestão de credenciais e criptografia at-rest (AES-256-GCM)
|
|
96
|
+
│ ├── binary/ # Protocolo binário WABinary (codificação/decodificação de stanzas)
|
|
97
|
+
│ ├── client/ # Fachada pública do cliente (makeWASocket / WhatsAppClient)
|
|
98
|
+
│ ├── crypto/ # Primitivas criptográficas (Curve25519, HKDF, AES-GCM, Noise)
|
|
99
|
+
│ ├── messages/ # Construtor de mensagens (Texto, Mídia, GPS, Contatos, Reações)
|
|
100
|
+
│ ├── signal/ # Implementação do protocolo Signal (Double Ratchet, Pre-keys)
|
|
101
|
+
│ ├── socket/ # Camada de transporte WebSocket resiliente com keep-alive
|
|
102
|
+
│ └── index.ts # Entry point principal da biblioteca
|
|
103
|
+
├── tests/ # Testes automatizados
|
|
104
|
+
├── examples/ # Exemplos de uso prático
|
|
105
|
+
├── package.json # Configuração NPM (dual ESM/CJS)
|
|
106
|
+
└── tsup.config.ts # Bundler otimizado para produção
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 📄 Licença
|
|
112
|
+
|
|
113
|
+
Distribuído sob a licença **MIT**. Veja [LICENSE](LICENSE) para mais informações.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
var ed25519 = require('@noble/curves/ed25519');
|
|
5
|
+
var hkdf = require('@noble/hashes/hkdf');
|
|
6
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
7
|
+
var aes = require('@noble/ciphers/aes');
|
|
8
|
+
var fs = require('fs');
|
|
9
|
+
var path = require('path');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
14
|
+
|
|
15
|
+
// src/auth/index.ts
|
|
16
|
+
|
|
17
|
+
// src/crypto/index.ts
|
|
18
|
+
var CryptoEngine = class {
|
|
19
|
+
/**
|
|
20
|
+
* Gera um novo par de chaves Curve25519 (Diffie-Hellman)
|
|
21
|
+
*/
|
|
22
|
+
static generateKeyPair() {
|
|
23
|
+
const priv = crypto.randomBytes(32);
|
|
24
|
+
const pub = ed25519.x25519.getPublicKey(priv);
|
|
25
|
+
return {
|
|
26
|
+
private: new Uint8Array(priv),
|
|
27
|
+
public: new Uint8Array(pub)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519
|
|
32
|
+
*/
|
|
33
|
+
static sharedKey(privateKey, publicKey) {
|
|
34
|
+
return ed25519.x25519.getSharedSecret(privateKey, publicKey);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)
|
|
38
|
+
*/
|
|
39
|
+
static deriveKey(secret, info, salt, length = 32) {
|
|
40
|
+
const infoBytes = typeof info === "string" ? new TextEncoder().encode(info) : info;
|
|
41
|
+
return hkdf.hkdf(sha256.sha256, secret, salt, infoBytes, length);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Encriptação autenticada com AES-256-GCM
|
|
45
|
+
*/
|
|
46
|
+
static aesGcmEncrypt(key, plaintext, iv, additionalData) {
|
|
47
|
+
const cipher = aes.gcm(key, iv, additionalData);
|
|
48
|
+
return cipher.encrypt(plaintext);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Decriptação autenticada com AES-256-GCM
|
|
52
|
+
*/
|
|
53
|
+
static aesGcmDecrypt(key, ciphertext, iv, additionalData) {
|
|
54
|
+
const cipher = aes.gcm(key, iv, additionalData);
|
|
55
|
+
return cipher.decrypt(ciphertext);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Encripta um payload com uma senha mestra (para proteger credenciais at-rest)
|
|
59
|
+
*/
|
|
60
|
+
static encryptAtRest(payload, masterPassphrase) {
|
|
61
|
+
const salt = crypto.randomBytes(16);
|
|
62
|
+
const iv = crypto.randomBytes(12);
|
|
63
|
+
const key = hkdf.hkdf(sha256.sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
64
|
+
const encrypted = this.aesGcmEncrypt(key, payload, iv);
|
|
65
|
+
return {
|
|
66
|
+
salt: Buffer.from(salt).toString("hex"),
|
|
67
|
+
iv: Buffer.from(iv).toString("hex"),
|
|
68
|
+
data: Buffer.from(encrypted).toString("base64")
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decripta um payload protegido com a senha mestra
|
|
73
|
+
*/
|
|
74
|
+
static decryptAtRest(encryptedBundle, masterPassphrase) {
|
|
75
|
+
const salt = Buffer.from(encryptedBundle.salt, "hex");
|
|
76
|
+
const iv = Buffer.from(encryptedBundle.iv, "hex");
|
|
77
|
+
const data = Buffer.from(encryptedBundle.data, "base64");
|
|
78
|
+
const key = hkdf.hkdf(sha256.sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
79
|
+
return this.aesGcmDecrypt(key, data, iv);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function initAuthCreds() {
|
|
83
|
+
const identityKeyPair = CryptoEngine.generateKeyPair();
|
|
84
|
+
const noiseKey = CryptoEngine.generateKeyPair();
|
|
85
|
+
const pairingEphemeral = CryptoEngine.generateKeyPair();
|
|
86
|
+
const signedPreKey = CryptoEngine.generateKeyPair();
|
|
87
|
+
const signedSignature = CryptoEngine.sharedKey(identityKeyPair.private, signedPreKey.public);
|
|
88
|
+
return {
|
|
89
|
+
noiseKey,
|
|
90
|
+
pairingEphemeralKeyPair: pairingEphemeral,
|
|
91
|
+
signedIdentityKey: identityKeyPair,
|
|
92
|
+
signedPreKey: {
|
|
93
|
+
...signedPreKey,
|
|
94
|
+
keyId: 1,
|
|
95
|
+
signature: signedSignature
|
|
96
|
+
},
|
|
97
|
+
registrationId: Math.floor(Math.random() * 16384),
|
|
98
|
+
advSecretKey: Buffer.from(crypto.randomBytes(32)).toString("base64"),
|
|
99
|
+
firstUnuploadedPreKeyId: 1,
|
|
100
|
+
nextPreKeyId: 1
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var EncryptedFileAuthState = class {
|
|
104
|
+
folderPath;
|
|
105
|
+
passphrase;
|
|
106
|
+
constructor(folderPath, passphrase) {
|
|
107
|
+
this.folderPath = folderPath;
|
|
108
|
+
this.passphrase = passphrase;
|
|
109
|
+
}
|
|
110
|
+
async ensureDir() {
|
|
111
|
+
await fs.promises.mkdir(this.folderPath, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
getFilePath(filename) {
|
|
114
|
+
return path__default.default.join(this.folderPath, `${filename}.json`);
|
|
115
|
+
}
|
|
116
|
+
async save(key, data) {
|
|
117
|
+
await this.ensureDir();
|
|
118
|
+
const filePath = this.getFilePath(key);
|
|
119
|
+
const jsonString = JSON.stringify(data, (_, value) => {
|
|
120
|
+
if (value instanceof Uint8Array) {
|
|
121
|
+
return { __type: "Buffer", data: Buffer.from(value).toString("base64") };
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
});
|
|
125
|
+
if (this.passphrase) {
|
|
126
|
+
const encrypted = CryptoEngine.encryptAtRest(new TextEncoder().encode(jsonString), this.passphrase);
|
|
127
|
+
await fs.promises.writeFile(filePath, JSON.stringify(encrypted), "utf-8");
|
|
128
|
+
} else {
|
|
129
|
+
await fs.promises.writeFile(filePath, jsonString, "utf-8");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async load(key) {
|
|
133
|
+
const filePath = this.getFilePath(key);
|
|
134
|
+
try {
|
|
135
|
+
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
136
|
+
let jsonString = content;
|
|
137
|
+
if (this.passphrase) {
|
|
138
|
+
const bundle = JSON.parse(content);
|
|
139
|
+
const decryptedBytes = CryptoEngine.decryptAtRest(bundle, this.passphrase);
|
|
140
|
+
jsonString = new TextDecoder().decode(decryptedBytes);
|
|
141
|
+
}
|
|
142
|
+
return JSON.parse(jsonString, (_, value) => {
|
|
143
|
+
if (value && value.__type === "Buffer") {
|
|
144
|
+
return new Uint8Array(Buffer.from(value.data, "base64"));
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
createKeyStore() {
|
|
153
|
+
return {
|
|
154
|
+
get: async (type, ids) => {
|
|
155
|
+
const result = {};
|
|
156
|
+
for (const id of ids) {
|
|
157
|
+
const item = await this.load(`${type}-${id}`);
|
|
158
|
+
if (item) {
|
|
159
|
+
result[id] = item;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
},
|
|
164
|
+
set: async (data) => {
|
|
165
|
+
for (const [type, entries] of Object.entries(data)) {
|
|
166
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
167
|
+
await this.save(`${type}-${id}`, value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
async function useSecureAuthState(folderPath, passphrase) {
|
|
175
|
+
const storage = new EncryptedFileAuthState(folderPath, passphrase);
|
|
176
|
+
let creds = await storage.load("creds");
|
|
177
|
+
if (!creds) {
|
|
178
|
+
creds = initAuthCreds();
|
|
179
|
+
await storage.save("creds", creds);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
creds,
|
|
183
|
+
keys: storage.createKeyStore()
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
exports.EncryptedFileAuthState = EncryptedFileAuthState;
|
|
188
|
+
exports.initAuthCreds = initAuthCreds;
|
|
189
|
+
exports.useSecureAuthState = useSecureAuthState;
|
|
190
|
+
//# sourceMappingURL=index.cjs.map
|
|
191
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/crypto/index.ts","../../src/auth/index.ts"],"names":["randomBytes","x25519","hkdf","sha256","gcm","fs","path"],"mappings":"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","file":"index.cjs","sourcesContent":["import { x25519 } from '@noble/curves/ed25519'\nimport { hkdf } from '@noble/hashes/hkdf'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { gcm } from '@noble/ciphers/aes'\nimport { randomBytes } from 'crypto'\nimport type { KeyPair } from '../types/index.js'\n\n/**\n * Utilitários criptográficos de alta performance e segurança para o protocolo WhatsApp\n */\n\nexport class CryptoEngine {\n /**\n * Gera um novo par de chaves Curve25519 (Diffie-Hellman)\n */\n static generateKeyPair(): KeyPair {\n const priv = randomBytes(32)\n const pub = x25519.getPublicKey(priv)\n return {\n private: new Uint8Array(priv),\n public: new Uint8Array(pub),\n }\n }\n\n /**\n * Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519\n */\n static sharedKey(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array {\n return x25519.getSharedSecret(privateKey, publicKey)\n }\n\n /**\n * HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)\n */\n static deriveKey(\n secret: Uint8Array,\n info: string | Uint8Array,\n salt?: Uint8Array,\n length = 32\n ): Uint8Array {\n const infoBytes = typeof info === 'string' ? new TextEncoder().encode(info) : info\n return hkdf(sha256, secret, salt, infoBytes, length)\n }\n\n /**\n * Encriptação autenticada com AES-256-GCM\n */\n static aesGcmEncrypt(\n key: Uint8Array,\n plaintext: Uint8Array,\n iv: Uint8Array,\n additionalData?: Uint8Array\n ): Uint8Array {\n const cipher = gcm(key, iv, additionalData)\n return cipher.encrypt(plaintext)\n }\n\n /**\n * Decriptação autenticada com AES-256-GCM\n */\n static aesGcmDecrypt(\n key: Uint8Array,\n ciphertext: Uint8Array,\n iv: Uint8Array,\n additionalData?: Uint8Array\n ): Uint8Array {\n const cipher = gcm(key, iv, additionalData)\n return cipher.decrypt(ciphertext)\n }\n\n /**\n * Encripta um payload com uma senha mestra (para proteger credenciais at-rest)\n */\n static encryptAtRest(payload: Uint8Array, masterPassphrase: string): { iv: string; salt: string; data: string } {\n const salt = randomBytes(16)\n const iv = randomBytes(12)\n const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode('cdwa-at-rest'), 32)\n const encrypted = this.aesGcmEncrypt(key, payload, iv)\n\n return {\n salt: Buffer.from(salt).toString('hex'),\n iv: Buffer.from(iv).toString('hex'),\n data: Buffer.from(encrypted).toString('base64'),\n }\n }\n\n /**\n * Decripta um payload protegido com a senha mestra\n */\n static decryptAtRest(\n encryptedBundle: { iv: string; salt: string; data: string },\n masterPassphrase: string\n ): Uint8Array {\n const salt = Buffer.from(encryptedBundle.salt, 'hex')\n const iv = Buffer.from(encryptedBundle.iv, 'hex')\n const data = Buffer.from(encryptedBundle.data, 'base64')\n const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode('cdwa-at-rest'), 32)\n\n return this.aesGcmDecrypt(key, data, iv)\n }\n}\n\nexport * from './noise.js'\n\n","import { randomBytes } from 'crypto'\nimport { CryptoEngine } from '../crypto/index.js'\nimport type { AuthenticationCreds, AuthenticationState, SignalKeyStore } from '../types/index.js'\nimport { promises as fs } from 'fs'\nimport path from 'path'\n\n/**\n * Cria credenciais limpas para uma nova sessão Multi-Device\n */\nexport function initAuthCreds(): AuthenticationCreds {\n const identityKeyPair = CryptoEngine.generateKeyPair()\n const noiseKey = CryptoEngine.generateKeyPair()\n const pairingEphemeral = CryptoEngine.generateKeyPair()\n const signedPreKey = CryptoEngine.generateKeyPair()\n\n // Assinatura da pré-chave com a chave de identidade\n // No WhatsApp, isso garante autenticidade contra MITM no protocolo Signal\n const signedSignature = CryptoEngine.sharedKey(identityKeyPair.private, signedPreKey.public)\n\n return {\n noiseKey,\n pairingEphemeralKeyPair: pairingEphemeral,\n signedIdentityKey: identityKeyPair,\n signedPreKey: {\n ...signedPreKey,\n keyId: 1,\n signature: signedSignature,\n },\n registrationId: Math.floor(Math.random() * 16384),\n advSecretKey: Buffer.from(randomBytes(32)).toString('base64'),\n firstUnuploadedPreKeyId: 1,\n nextPreKeyId: 1,\n }\n}\n\n/**\n * Gerenciador de estado de autenticação com suporte opcional a encriptação at-rest\n */\nexport class EncryptedFileAuthState {\n private folderPath: string\n private passphrase?: string\n\n constructor(folderPath: string, passphrase?: string) {\n this.folderPath = folderPath\n this.passphrase = passphrase\n }\n\n private async ensureDir() {\n await fs.mkdir(this.folderPath, { recursive: true })\n }\n\n private getFilePath(filename: string): string {\n return path.join(this.folderPath, `${filename}.json`)\n }\n\n async save(key: string, data: unknown): Promise<void> {\n await this.ensureDir()\n const filePath = this.getFilePath(key)\n const jsonString = JSON.stringify(data, (_, value) => {\n if (value instanceof Uint8Array) {\n return { __type: 'Buffer', data: Buffer.from(value).toString('base64') }\n }\n return value\n })\n\n if (this.passphrase) {\n const encrypted = CryptoEngine.encryptAtRest(new TextEncoder().encode(jsonString), this.passphrase)\n await fs.writeFile(filePath, JSON.stringify(encrypted), 'utf-8')\n } else {\n await fs.writeFile(filePath, jsonString, 'utf-8')\n }\n }\n\n async load<T>(key: string): Promise<T | null> {\n const filePath = this.getFilePath(key)\n try {\n const content = await fs.readFile(filePath, 'utf-8')\n let jsonString = content\n\n if (this.passphrase) {\n const bundle = JSON.parse(content)\n const decryptedBytes = CryptoEngine.decryptAtRest(bundle, this.passphrase)\n jsonString = new TextDecoder().decode(decryptedBytes)\n }\n\n return JSON.parse(jsonString, (_, value) => {\n if (value && value.__type === 'Buffer') {\n return new Uint8Array(Buffer.from(value.data, 'base64'))\n }\n return value\n }) as T\n } catch {\n return null\n }\n }\n\n createKeyStore(): SignalKeyStore {\n return {\n get: async <T>(type: string, ids: string[]): Promise<Record<string, T>> => {\n const result: Record<string, T> = {}\n for (const id of ids) {\n const item = await this.load<T>(`${type}-${id}`)\n if (item) {\n result[id] = item\n }\n }\n return result\n },\n set: async (data: Record<string, Record<string, unknown>>): Promise<void> => {\n for (const [type, entries] of Object.entries(data)) {\n for (const [id, value] of Object.entries(entries)) {\n await this.save(`${type}-${id}`, value)\n }\n }\n },\n }\n }\n}\n\n/**\n * Cria um estado de autenticação persistente com segurança reforçada\n */\nexport async function useSecureAuthState(\n folderPath: string,\n passphrase?: string\n): Promise<AuthenticationState> {\n const storage = new EncryptedFileAuthState(folderPath, passphrase)\n let creds = await storage.load<AuthenticationCreds>('creds')\n\n if (!creds) {\n creds = initAuthCreds()\n await storage.save('creds', creds)\n }\n\n return {\n creds,\n keys: storage.createKeyStore(),\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { S as SignalKeyStore, A as AuthenticationCreds, a as AuthenticationState } from '../index-CcMoHfPz.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cria credenciais limpas para uma nova sessão Multi-Device
|
|
5
|
+
*/
|
|
6
|
+
declare function initAuthCreds(): AuthenticationCreds;
|
|
7
|
+
/**
|
|
8
|
+
* Gerenciador de estado de autenticação com suporte opcional a encriptação at-rest
|
|
9
|
+
*/
|
|
10
|
+
declare class EncryptedFileAuthState {
|
|
11
|
+
private folderPath;
|
|
12
|
+
private passphrase?;
|
|
13
|
+
constructor(folderPath: string, passphrase?: string);
|
|
14
|
+
private ensureDir;
|
|
15
|
+
private getFilePath;
|
|
16
|
+
save(key: string, data: unknown): Promise<void>;
|
|
17
|
+
load<T>(key: string): Promise<T | null>;
|
|
18
|
+
createKeyStore(): SignalKeyStore;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Cria um estado de autenticação persistente com segurança reforçada
|
|
22
|
+
*/
|
|
23
|
+
declare function useSecureAuthState(folderPath: string, passphrase?: string): Promise<AuthenticationState>;
|
|
24
|
+
|
|
25
|
+
export { EncryptedFileAuthState, initAuthCreds, useSecureAuthState };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { S as SignalKeyStore, A as AuthenticationCreds, a as AuthenticationState } from '../index-CcMoHfPz.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cria credenciais limpas para uma nova sessão Multi-Device
|
|
5
|
+
*/
|
|
6
|
+
declare function initAuthCreds(): AuthenticationCreds;
|
|
7
|
+
/**
|
|
8
|
+
* Gerenciador de estado de autenticação com suporte opcional a encriptação at-rest
|
|
9
|
+
*/
|
|
10
|
+
declare class EncryptedFileAuthState {
|
|
11
|
+
private folderPath;
|
|
12
|
+
private passphrase?;
|
|
13
|
+
constructor(folderPath: string, passphrase?: string);
|
|
14
|
+
private ensureDir;
|
|
15
|
+
private getFilePath;
|
|
16
|
+
save(key: string, data: unknown): Promise<void>;
|
|
17
|
+
load<T>(key: string): Promise<T | null>;
|
|
18
|
+
createKeyStore(): SignalKeyStore;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Cria um estado de autenticação persistente com segurança reforçada
|
|
22
|
+
*/
|
|
23
|
+
declare function useSecureAuthState(folderPath: string, passphrase?: string): Promise<AuthenticationState>;
|
|
24
|
+
|
|
25
|
+
export { EncryptedFileAuthState, initAuthCreds, useSecureAuthState };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
import { x25519 } from '@noble/curves/ed25519';
|
|
3
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
4
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
5
|
+
import { gcm } from '@noble/ciphers/aes';
|
|
6
|
+
import { promises } from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
// src/auth/index.ts
|
|
10
|
+
|
|
11
|
+
// src/crypto/index.ts
|
|
12
|
+
var CryptoEngine = class {
|
|
13
|
+
/**
|
|
14
|
+
* Gera um novo par de chaves Curve25519 (Diffie-Hellman)
|
|
15
|
+
*/
|
|
16
|
+
static generateKeyPair() {
|
|
17
|
+
const priv = randomBytes(32);
|
|
18
|
+
const pub = x25519.getPublicKey(priv);
|
|
19
|
+
return {
|
|
20
|
+
private: new Uint8Array(priv),
|
|
21
|
+
public: new Uint8Array(pub)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519
|
|
26
|
+
*/
|
|
27
|
+
static sharedKey(privateKey, publicKey) {
|
|
28
|
+
return x25519.getSharedSecret(privateKey, publicKey);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)
|
|
32
|
+
*/
|
|
33
|
+
static deriveKey(secret, info, salt, length = 32) {
|
|
34
|
+
const infoBytes = typeof info === "string" ? new TextEncoder().encode(info) : info;
|
|
35
|
+
return hkdf(sha256, secret, salt, infoBytes, length);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Encriptação autenticada com AES-256-GCM
|
|
39
|
+
*/
|
|
40
|
+
static aesGcmEncrypt(key, plaintext, iv, additionalData) {
|
|
41
|
+
const cipher = gcm(key, iv, additionalData);
|
|
42
|
+
return cipher.encrypt(plaintext);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Decriptação autenticada com AES-256-GCM
|
|
46
|
+
*/
|
|
47
|
+
static aesGcmDecrypt(key, ciphertext, iv, additionalData) {
|
|
48
|
+
const cipher = gcm(key, iv, additionalData);
|
|
49
|
+
return cipher.decrypt(ciphertext);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Encripta um payload com uma senha mestra (para proteger credenciais at-rest)
|
|
53
|
+
*/
|
|
54
|
+
static encryptAtRest(payload, masterPassphrase) {
|
|
55
|
+
const salt = randomBytes(16);
|
|
56
|
+
const iv = randomBytes(12);
|
|
57
|
+
const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
58
|
+
const encrypted = this.aesGcmEncrypt(key, payload, iv);
|
|
59
|
+
return {
|
|
60
|
+
salt: Buffer.from(salt).toString("hex"),
|
|
61
|
+
iv: Buffer.from(iv).toString("hex"),
|
|
62
|
+
data: Buffer.from(encrypted).toString("base64")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decripta um payload protegido com a senha mestra
|
|
67
|
+
*/
|
|
68
|
+
static decryptAtRest(encryptedBundle, masterPassphrase) {
|
|
69
|
+
const salt = Buffer.from(encryptedBundle.salt, "hex");
|
|
70
|
+
const iv = Buffer.from(encryptedBundle.iv, "hex");
|
|
71
|
+
const data = Buffer.from(encryptedBundle.data, "base64");
|
|
72
|
+
const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
73
|
+
return this.aesGcmDecrypt(key, data, iv);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function initAuthCreds() {
|
|
77
|
+
const identityKeyPair = CryptoEngine.generateKeyPair();
|
|
78
|
+
const noiseKey = CryptoEngine.generateKeyPair();
|
|
79
|
+
const pairingEphemeral = CryptoEngine.generateKeyPair();
|
|
80
|
+
const signedPreKey = CryptoEngine.generateKeyPair();
|
|
81
|
+
const signedSignature = CryptoEngine.sharedKey(identityKeyPair.private, signedPreKey.public);
|
|
82
|
+
return {
|
|
83
|
+
noiseKey,
|
|
84
|
+
pairingEphemeralKeyPair: pairingEphemeral,
|
|
85
|
+
signedIdentityKey: identityKeyPair,
|
|
86
|
+
signedPreKey: {
|
|
87
|
+
...signedPreKey,
|
|
88
|
+
keyId: 1,
|
|
89
|
+
signature: signedSignature
|
|
90
|
+
},
|
|
91
|
+
registrationId: Math.floor(Math.random() * 16384),
|
|
92
|
+
advSecretKey: Buffer.from(randomBytes(32)).toString("base64"),
|
|
93
|
+
firstUnuploadedPreKeyId: 1,
|
|
94
|
+
nextPreKeyId: 1
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var EncryptedFileAuthState = class {
|
|
98
|
+
folderPath;
|
|
99
|
+
passphrase;
|
|
100
|
+
constructor(folderPath, passphrase) {
|
|
101
|
+
this.folderPath = folderPath;
|
|
102
|
+
this.passphrase = passphrase;
|
|
103
|
+
}
|
|
104
|
+
async ensureDir() {
|
|
105
|
+
await promises.mkdir(this.folderPath, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
getFilePath(filename) {
|
|
108
|
+
return path.join(this.folderPath, `${filename}.json`);
|
|
109
|
+
}
|
|
110
|
+
async save(key, data) {
|
|
111
|
+
await this.ensureDir();
|
|
112
|
+
const filePath = this.getFilePath(key);
|
|
113
|
+
const jsonString = JSON.stringify(data, (_, value) => {
|
|
114
|
+
if (value instanceof Uint8Array) {
|
|
115
|
+
return { __type: "Buffer", data: Buffer.from(value).toString("base64") };
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
});
|
|
119
|
+
if (this.passphrase) {
|
|
120
|
+
const encrypted = CryptoEngine.encryptAtRest(new TextEncoder().encode(jsonString), this.passphrase);
|
|
121
|
+
await promises.writeFile(filePath, JSON.stringify(encrypted), "utf-8");
|
|
122
|
+
} else {
|
|
123
|
+
await promises.writeFile(filePath, jsonString, "utf-8");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async load(key) {
|
|
127
|
+
const filePath = this.getFilePath(key);
|
|
128
|
+
try {
|
|
129
|
+
const content = await promises.readFile(filePath, "utf-8");
|
|
130
|
+
let jsonString = content;
|
|
131
|
+
if (this.passphrase) {
|
|
132
|
+
const bundle = JSON.parse(content);
|
|
133
|
+
const decryptedBytes = CryptoEngine.decryptAtRest(bundle, this.passphrase);
|
|
134
|
+
jsonString = new TextDecoder().decode(decryptedBytes);
|
|
135
|
+
}
|
|
136
|
+
return JSON.parse(jsonString, (_, value) => {
|
|
137
|
+
if (value && value.__type === "Buffer") {
|
|
138
|
+
return new Uint8Array(Buffer.from(value.data, "base64"));
|
|
139
|
+
}
|
|
140
|
+
return value;
|
|
141
|
+
});
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
createKeyStore() {
|
|
147
|
+
return {
|
|
148
|
+
get: async (type, ids) => {
|
|
149
|
+
const result = {};
|
|
150
|
+
for (const id of ids) {
|
|
151
|
+
const item = await this.load(`${type}-${id}`);
|
|
152
|
+
if (item) {
|
|
153
|
+
result[id] = item;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
},
|
|
158
|
+
set: async (data) => {
|
|
159
|
+
for (const [type, entries] of Object.entries(data)) {
|
|
160
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
161
|
+
await this.save(`${type}-${id}`, value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
async function useSecureAuthState(folderPath, passphrase) {
|
|
169
|
+
const storage = new EncryptedFileAuthState(folderPath, passphrase);
|
|
170
|
+
let creds = await storage.load("creds");
|
|
171
|
+
if (!creds) {
|
|
172
|
+
creds = initAuthCreds();
|
|
173
|
+
await storage.save("creds", creds);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
creds,
|
|
177
|
+
keys: storage.createKeyStore()
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { EncryptedFileAuthState, initAuthCreds, useSecureAuthState };
|
|
182
|
+
//# sourceMappingURL=index.js.map
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/crypto/index.ts","../../src/auth/index.ts"],"names":["sha256","randomBytes","fs"],"mappings":"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","file":"index.js","sourcesContent":["import { x25519 } from '@noble/curves/ed25519'\nimport { hkdf } from '@noble/hashes/hkdf'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { gcm } from '@noble/ciphers/aes'\nimport { randomBytes } from 'crypto'\nimport type { KeyPair } from '../types/index.js'\n\n/**\n * Utilitários criptográficos de alta performance e segurança para o protocolo WhatsApp\n */\n\nexport class CryptoEngine {\n /**\n * Gera um novo par de chaves Curve25519 (Diffie-Hellman)\n */\n static generateKeyPair(): KeyPair {\n const priv = randomBytes(32)\n const pub = x25519.getPublicKey(priv)\n return {\n private: new Uint8Array(priv),\n public: new Uint8Array(pub),\n }\n }\n\n /**\n * Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519\n */\n static sharedKey(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array {\n return x25519.getSharedSecret(privateKey, publicKey)\n }\n\n /**\n * HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)\n */\n static deriveKey(\n secret: Uint8Array,\n info: string | Uint8Array,\n salt?: Uint8Array,\n length = 32\n ): Uint8Array {\n const infoBytes = typeof info === 'string' ? new TextEncoder().encode(info) : info\n return hkdf(sha256, secret, salt, infoBytes, length)\n }\n\n /**\n * Encriptação autenticada com AES-256-GCM\n */\n static aesGcmEncrypt(\n key: Uint8Array,\n plaintext: Uint8Array,\n iv: Uint8Array,\n additionalData?: Uint8Array\n ): Uint8Array {\n const cipher = gcm(key, iv, additionalData)\n return cipher.encrypt(plaintext)\n }\n\n /**\n * Decriptação autenticada com AES-256-GCM\n */\n static aesGcmDecrypt(\n key: Uint8Array,\n ciphertext: Uint8Array,\n iv: Uint8Array,\n additionalData?: Uint8Array\n ): Uint8Array {\n const cipher = gcm(key, iv, additionalData)\n return cipher.decrypt(ciphertext)\n }\n\n /**\n * Encripta um payload com uma senha mestra (para proteger credenciais at-rest)\n */\n static encryptAtRest(payload: Uint8Array, masterPassphrase: string): { iv: string; salt: string; data: string } {\n const salt = randomBytes(16)\n const iv = randomBytes(12)\n const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode('cdwa-at-rest'), 32)\n const encrypted = this.aesGcmEncrypt(key, payload, iv)\n\n return {\n salt: Buffer.from(salt).toString('hex'),\n iv: Buffer.from(iv).toString('hex'),\n data: Buffer.from(encrypted).toString('base64'),\n }\n }\n\n /**\n * Decripta um payload protegido com a senha mestra\n */\n static decryptAtRest(\n encryptedBundle: { iv: string; salt: string; data: string },\n masterPassphrase: string\n ): Uint8Array {\n const salt = Buffer.from(encryptedBundle.salt, 'hex')\n const iv = Buffer.from(encryptedBundle.iv, 'hex')\n const data = Buffer.from(encryptedBundle.data, 'base64')\n const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode('cdwa-at-rest'), 32)\n\n return this.aesGcmDecrypt(key, data, iv)\n }\n}\n\nexport * from './noise.js'\n\n","import { randomBytes } from 'crypto'\nimport { CryptoEngine } from '../crypto/index.js'\nimport type { AuthenticationCreds, AuthenticationState, SignalKeyStore } from '../types/index.js'\nimport { promises as fs } from 'fs'\nimport path from 'path'\n\n/**\n * Cria credenciais limpas para uma nova sessão Multi-Device\n */\nexport function initAuthCreds(): AuthenticationCreds {\n const identityKeyPair = CryptoEngine.generateKeyPair()\n const noiseKey = CryptoEngine.generateKeyPair()\n const pairingEphemeral = CryptoEngine.generateKeyPair()\n const signedPreKey = CryptoEngine.generateKeyPair()\n\n // Assinatura da pré-chave com a chave de identidade\n // No WhatsApp, isso garante autenticidade contra MITM no protocolo Signal\n const signedSignature = CryptoEngine.sharedKey(identityKeyPair.private, signedPreKey.public)\n\n return {\n noiseKey,\n pairingEphemeralKeyPair: pairingEphemeral,\n signedIdentityKey: identityKeyPair,\n signedPreKey: {\n ...signedPreKey,\n keyId: 1,\n signature: signedSignature,\n },\n registrationId: Math.floor(Math.random() * 16384),\n advSecretKey: Buffer.from(randomBytes(32)).toString('base64'),\n firstUnuploadedPreKeyId: 1,\n nextPreKeyId: 1,\n }\n}\n\n/**\n * Gerenciador de estado de autenticação com suporte opcional a encriptação at-rest\n */\nexport class EncryptedFileAuthState {\n private folderPath: string\n private passphrase?: string\n\n constructor(folderPath: string, passphrase?: string) {\n this.folderPath = folderPath\n this.passphrase = passphrase\n }\n\n private async ensureDir() {\n await fs.mkdir(this.folderPath, { recursive: true })\n }\n\n private getFilePath(filename: string): string {\n return path.join(this.folderPath, `${filename}.json`)\n }\n\n async save(key: string, data: unknown): Promise<void> {\n await this.ensureDir()\n const filePath = this.getFilePath(key)\n const jsonString = JSON.stringify(data, (_, value) => {\n if (value instanceof Uint8Array) {\n return { __type: 'Buffer', data: Buffer.from(value).toString('base64') }\n }\n return value\n })\n\n if (this.passphrase) {\n const encrypted = CryptoEngine.encryptAtRest(new TextEncoder().encode(jsonString), this.passphrase)\n await fs.writeFile(filePath, JSON.stringify(encrypted), 'utf-8')\n } else {\n await fs.writeFile(filePath, jsonString, 'utf-8')\n }\n }\n\n async load<T>(key: string): Promise<T | null> {\n const filePath = this.getFilePath(key)\n try {\n const content = await fs.readFile(filePath, 'utf-8')\n let jsonString = content\n\n if (this.passphrase) {\n const bundle = JSON.parse(content)\n const decryptedBytes = CryptoEngine.decryptAtRest(bundle, this.passphrase)\n jsonString = new TextDecoder().decode(decryptedBytes)\n }\n\n return JSON.parse(jsonString, (_, value) => {\n if (value && value.__type === 'Buffer') {\n return new Uint8Array(Buffer.from(value.data, 'base64'))\n }\n return value\n }) as T\n } catch {\n return null\n }\n }\n\n createKeyStore(): SignalKeyStore {\n return {\n get: async <T>(type: string, ids: string[]): Promise<Record<string, T>> => {\n const result: Record<string, T> = {}\n for (const id of ids) {\n const item = await this.load<T>(`${type}-${id}`)\n if (item) {\n result[id] = item\n }\n }\n return result\n },\n set: async (data: Record<string, Record<string, unknown>>): Promise<void> => {\n for (const [type, entries] of Object.entries(data)) {\n for (const [id, value] of Object.entries(entries)) {\n await this.save(`${type}-${id}`, value)\n }\n }\n },\n }\n }\n}\n\n/**\n * Cria um estado de autenticação persistente com segurança reforçada\n */\nexport async function useSecureAuthState(\n folderPath: string,\n passphrase?: string\n): Promise<AuthenticationState> {\n const storage = new EncryptedFileAuthState(folderPath, passphrase)\n let creds = await storage.load<AuthenticationCreds>('creds')\n\n if (!creds) {\n creds = initAuthCreds()\n await storage.save('creds', creds)\n }\n\n return {\n creds,\n keys: storage.createKeyStore(),\n }\n}\n"]}
|