@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/crypto/noise.ts","../../src/crypto/index.ts"],"names":["sha256","randomBytes","x25519","hkdf","gcm"],"mappings":"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","file":"index.cjs","sourcesContent":["import { CryptoEngine } from './index.js'\nimport { sha256 } from '@noble/hashes/sha256'\nimport type { KeyPair } from '../types/index.js'\n\nexport const NOISE_PROTOCOL_NAME = 'Noise_XX_25519_AESGCM_SHA256'\nexport const WA_NOISE_HEADER = new Uint8Array([87, 65, 6, 2]) // \"WA\\x06\\x02\"\n\n/**\n * Estado da cifra simétrica do Noise com contador sequencial (Nonce)\n */\nexport class NoiseCipherState {\n private key: Uint8Array | null = null\n private nonce = 0\n\n constructor(key?: Uint8Array) {\n if (key) {\n this.key = key\n }\n }\n\n hasKey(): boolean {\n return this.key !== null\n }\n\n setKey(key: Uint8Array) {\n this.key = key\n this.nonce = 0\n }\n\n encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Uint8Array {\n if (!this.key) {\n return plaintext\n }\n const iv = this.generateNonceBuffer(this.nonce)\n this.nonce += 1\n return CryptoEngine.aesGcmEncrypt(this.key, plaintext, iv, ad)\n }\n\n decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Uint8Array {\n if (!this.key) {\n return ciphertext\n }\n const iv = this.generateNonceBuffer(this.nonce)\n this.nonce += 1\n return CryptoEngine.aesGcmDecrypt(this.key, ciphertext, iv, ad)\n }\n\n private generateNonceBuffer(nonce: number): Uint8Array {\n const buffer = new Uint8Array(12)\n const view = new DataView(buffer.buffer)\n // Nonce no formato 64-bit integer nos últimos 8 bytes\n view.setUint32(8, nonce, false)\n return buffer\n }\n}\n\n/**\n * Máquina de estado do Noise XX Handshake para o WhatsApp Web\n */\nexport class NoiseHandshake {\n private symmetricState: {\n h: Uint8Array\n ck: Uint8Array\n cipher: NoiseCipherState\n }\n private localEphemeral: KeyPair\n private localStatic: KeyPair\n private remoteEphemeral: Uint8Array | null = null\n private remoteStatic: Uint8Array | null = null\n\n constructor(localStaticKey: KeyPair) {\n this.localStatic = localStaticKey\n this.localEphemeral = CryptoEngine.generateKeyPair()\n\n // Inicialização do estado simétrico com o nome do protocolo\n const protocolHash = sha256(new TextEncoder().encode(NOISE_PROTOCOL_NAME))\n this.symmetricState = {\n h: new Uint8Array(protocolHash),\n ck: new Uint8Array(protocolHash),\n cipher: new NoiseCipherState(),\n }\n\n // Mistura o prólogo inicial do WhatsApp (WA_NOISE_HEADER) no hash\n this.mixHash(WA_NOISE_HEADER)\n }\n\n private mixHash(data: Uint8Array) {\n const merged = new Uint8Array(this.symmetricState.h.length + data.length)\n merged.set(this.symmetricState.h, 0)\n merged.set(data, this.symmetricState.h.length)\n this.symmetricState.h = sha256(merged)\n }\n\n private mixKey(data: Uint8Array) {\n // HKDF-Extract e Expand com Chaining Key\n const derived = CryptoEngine.deriveKey(data, '', this.symmetricState.ck, 64)\n this.symmetricState.ck = derived.slice(0, 32)\n const tempK = derived.slice(32, 64)\n this.symmetricState.cipher.setKey(tempK)\n }\n\n /**\n * Passo 1: Início do Handshake -> Envia chave efêmera local (e)\n */\n startHandshake(): Uint8Array {\n this.mixHash(this.localEphemeral.public)\n // O payload inicial consiste no prólogo do WhatsApp + a chave efêmera\n const message = new Uint8Array(WA_NOISE_HEADER.length + this.localEphemeral.public.length)\n message.set(WA_NOISE_HEADER, 0)\n message.set(this.localEphemeral.public, WA_NOISE_HEADER.length)\n return message\n }\n\n /**\n * Passo 2: Recebe resposta do servidor WhatsApp <- (e, ee, s, es)\n */\n processServerHello(serverBuffer: Uint8Array): Uint8Array {\n // Extrai a chave efêmera remota (primeiros 32 bytes)\n this.remoteEphemeral = serverBuffer.slice(0, 32)\n this.mixHash(this.remoteEphemeral)\n\n // ee: Diffie-Hellman entre as duas chaves efêmeras\n const ee = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteEphemeral)\n this.mixKey(ee)\n\n // s: Chave estática encriptada do servidor (48 bytes = 32 bytes de chave + 16 bytes auth tag do AES-GCM)\n const encryptedStatic = serverBuffer.slice(32, 80)\n this.remoteStatic = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedStatic)\n this.mixHash(encryptedStatic)\n\n // es: Diffie-Hellman entre a chave efêmera local e a chave estática do servidor\n const es = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteStatic)\n this.mixKey(es)\n\n // Payload final criptografado da resposta do servidor\n const encryptedPayload = serverBuffer.slice(80)\n const decryptedPayload = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedPayload)\n this.mixHash(encryptedPayload)\n\n return decryptedPayload\n }\n\n /**\n * Passo 3: Envia resposta final de autenticação do cliente -> (s, se) + payload\n * Retorna os CipherStates de leitura e escrita para uso no WebSocket\n */\n finishHandshake(payload: Uint8Array): {\n handshakeMessage: Uint8Array\n writeCipher: NoiseCipherState\n readCipher: NoiseCipherState\n } {\n // s: Encripta nossa chave estática de identidade\n const encryptedStatic = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, this.localStatic.public)\n this.mixHash(encryptedStatic)\n\n // se: Diffie-Hellman entre nossa chave estática e a efêmera do servidor\n if (!this.remoteEphemeral) {\n throw new Error('Remote ephemeral key is missing in Noise Handshake')\n }\n const se = CryptoEngine.sharedKey(this.localStatic.private, this.remoteEphemeral)\n this.mixKey(se)\n\n // Encripta o payload final (contendo informações de registro/login)\n const encryptedPayload = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, payload)\n this.mixHash(encryptedPayload)\n\n // Mensagem de envio: [encryptedStatic (48b)] + [encryptedPayload]\n const handshakeMessage = new Uint8Array(encryptedStatic.length + encryptedPayload.length)\n handshakeMessage.set(encryptedStatic, 0)\n handshakeMessage.set(encryptedPayload, encryptedStatic.length)\n\n // Divide a chaining key em 2 chaves finais de transporte (Split)\n const transportKeys = CryptoEngine.deriveKey(new Uint8Array(0), '', this.symmetricState.ck, 64)\n const writeKey = transportKeys.slice(0, 32)\n const readKey = transportKeys.slice(32, 64)\n\n return {\n handshakeMessage,\n writeCipher: new NoiseCipherState(writeKey),\n readCipher: new NoiseCipherState(readKey),\n }\n }\n}\n","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"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { K as KeyPair } from '../index-CcMoHfPz.cjs';
|
|
2
|
+
|
|
3
|
+
declare const NOISE_PROTOCOL_NAME = "Noise_XX_25519_AESGCM_SHA256";
|
|
4
|
+
declare const WA_NOISE_HEADER: Uint8Array<ArrayBuffer>;
|
|
5
|
+
/**
|
|
6
|
+
* Estado da cifra simétrica do Noise com contador sequencial (Nonce)
|
|
7
|
+
*/
|
|
8
|
+
declare class NoiseCipherState {
|
|
9
|
+
private key;
|
|
10
|
+
private nonce;
|
|
11
|
+
constructor(key?: Uint8Array);
|
|
12
|
+
hasKey(): boolean;
|
|
13
|
+
setKey(key: Uint8Array): void;
|
|
14
|
+
encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
15
|
+
decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Uint8Array;
|
|
16
|
+
private generateNonceBuffer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Máquina de estado do Noise XX Handshake para o WhatsApp Web
|
|
20
|
+
*/
|
|
21
|
+
declare class NoiseHandshake {
|
|
22
|
+
private symmetricState;
|
|
23
|
+
private localEphemeral;
|
|
24
|
+
private localStatic;
|
|
25
|
+
private remoteEphemeral;
|
|
26
|
+
private remoteStatic;
|
|
27
|
+
constructor(localStaticKey: KeyPair);
|
|
28
|
+
private mixHash;
|
|
29
|
+
private mixKey;
|
|
30
|
+
/**
|
|
31
|
+
* Passo 1: Início do Handshake -> Envia chave efêmera local (e)
|
|
32
|
+
*/
|
|
33
|
+
startHandshake(): Uint8Array;
|
|
34
|
+
/**
|
|
35
|
+
* Passo 2: Recebe resposta do servidor WhatsApp <- (e, ee, s, es)
|
|
36
|
+
*/
|
|
37
|
+
processServerHello(serverBuffer: Uint8Array): Uint8Array;
|
|
38
|
+
/**
|
|
39
|
+
* Passo 3: Envia resposta final de autenticação do cliente -> (s, se) + payload
|
|
40
|
+
* Retorna os CipherStates de leitura e escrita para uso no WebSocket
|
|
41
|
+
*/
|
|
42
|
+
finishHandshake(payload: Uint8Array): {
|
|
43
|
+
handshakeMessage: Uint8Array;
|
|
44
|
+
writeCipher: NoiseCipherState;
|
|
45
|
+
readCipher: NoiseCipherState;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Utilitários criptográficos de alta performance e segurança para o protocolo WhatsApp
|
|
51
|
+
*/
|
|
52
|
+
declare class CryptoEngine {
|
|
53
|
+
/**
|
|
54
|
+
* Gera um novo par de chaves Curve25519 (Diffie-Hellman)
|
|
55
|
+
*/
|
|
56
|
+
static generateKeyPair(): KeyPair;
|
|
57
|
+
/**
|
|
58
|
+
* Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519
|
|
59
|
+
*/
|
|
60
|
+
static sharedKey(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|
|
61
|
+
/**
|
|
62
|
+
* HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)
|
|
63
|
+
*/
|
|
64
|
+
static deriveKey(secret: Uint8Array, info: string | Uint8Array, salt?: Uint8Array, length?: number): Uint8Array;
|
|
65
|
+
/**
|
|
66
|
+
* Encriptação autenticada com AES-256-GCM
|
|
67
|
+
*/
|
|
68
|
+
static aesGcmEncrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, additionalData?: Uint8Array): Uint8Array;
|
|
69
|
+
/**
|
|
70
|
+
* Decriptação autenticada com AES-256-GCM
|
|
71
|
+
*/
|
|
72
|
+
static aesGcmDecrypt(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, additionalData?: Uint8Array): Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* Encripta um payload com uma senha mestra (para proteger credenciais at-rest)
|
|
75
|
+
*/
|
|
76
|
+
static encryptAtRest(payload: Uint8Array, masterPassphrase: string): {
|
|
77
|
+
iv: string;
|
|
78
|
+
salt: string;
|
|
79
|
+
data: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Decripta um payload protegido com a senha mestra
|
|
83
|
+
*/
|
|
84
|
+
static decryptAtRest(encryptedBundle: {
|
|
85
|
+
iv: string;
|
|
86
|
+
salt: string;
|
|
87
|
+
data: string;
|
|
88
|
+
}, masterPassphrase: string): Uint8Array;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { CryptoEngine, NOISE_PROTOCOL_NAME, NoiseCipherState, NoiseHandshake, WA_NOISE_HEADER };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { K as KeyPair } from '../index-CcMoHfPz.js';
|
|
2
|
+
|
|
3
|
+
declare const NOISE_PROTOCOL_NAME = "Noise_XX_25519_AESGCM_SHA256";
|
|
4
|
+
declare const WA_NOISE_HEADER: Uint8Array<ArrayBuffer>;
|
|
5
|
+
/**
|
|
6
|
+
* Estado da cifra simétrica do Noise com contador sequencial (Nonce)
|
|
7
|
+
*/
|
|
8
|
+
declare class NoiseCipherState {
|
|
9
|
+
private key;
|
|
10
|
+
private nonce;
|
|
11
|
+
constructor(key?: Uint8Array);
|
|
12
|
+
hasKey(): boolean;
|
|
13
|
+
setKey(key: Uint8Array): void;
|
|
14
|
+
encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
15
|
+
decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Uint8Array;
|
|
16
|
+
private generateNonceBuffer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Máquina de estado do Noise XX Handshake para o WhatsApp Web
|
|
20
|
+
*/
|
|
21
|
+
declare class NoiseHandshake {
|
|
22
|
+
private symmetricState;
|
|
23
|
+
private localEphemeral;
|
|
24
|
+
private localStatic;
|
|
25
|
+
private remoteEphemeral;
|
|
26
|
+
private remoteStatic;
|
|
27
|
+
constructor(localStaticKey: KeyPair);
|
|
28
|
+
private mixHash;
|
|
29
|
+
private mixKey;
|
|
30
|
+
/**
|
|
31
|
+
* Passo 1: Início do Handshake -> Envia chave efêmera local (e)
|
|
32
|
+
*/
|
|
33
|
+
startHandshake(): Uint8Array;
|
|
34
|
+
/**
|
|
35
|
+
* Passo 2: Recebe resposta do servidor WhatsApp <- (e, ee, s, es)
|
|
36
|
+
*/
|
|
37
|
+
processServerHello(serverBuffer: Uint8Array): Uint8Array;
|
|
38
|
+
/**
|
|
39
|
+
* Passo 3: Envia resposta final de autenticação do cliente -> (s, se) + payload
|
|
40
|
+
* Retorna os CipherStates de leitura e escrita para uso no WebSocket
|
|
41
|
+
*/
|
|
42
|
+
finishHandshake(payload: Uint8Array): {
|
|
43
|
+
handshakeMessage: Uint8Array;
|
|
44
|
+
writeCipher: NoiseCipherState;
|
|
45
|
+
readCipher: NoiseCipherState;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Utilitários criptográficos de alta performance e segurança para o protocolo WhatsApp
|
|
51
|
+
*/
|
|
52
|
+
declare class CryptoEngine {
|
|
53
|
+
/**
|
|
54
|
+
* Gera um novo par de chaves Curve25519 (Diffie-Hellman)
|
|
55
|
+
*/
|
|
56
|
+
static generateKeyPair(): KeyPair;
|
|
57
|
+
/**
|
|
58
|
+
* Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519
|
|
59
|
+
*/
|
|
60
|
+
static sharedKey(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|
|
61
|
+
/**
|
|
62
|
+
* HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)
|
|
63
|
+
*/
|
|
64
|
+
static deriveKey(secret: Uint8Array, info: string | Uint8Array, salt?: Uint8Array, length?: number): Uint8Array;
|
|
65
|
+
/**
|
|
66
|
+
* Encriptação autenticada com AES-256-GCM
|
|
67
|
+
*/
|
|
68
|
+
static aesGcmEncrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, additionalData?: Uint8Array): Uint8Array;
|
|
69
|
+
/**
|
|
70
|
+
* Decriptação autenticada com AES-256-GCM
|
|
71
|
+
*/
|
|
72
|
+
static aesGcmDecrypt(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, additionalData?: Uint8Array): Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* Encripta um payload com uma senha mestra (para proteger credenciais at-rest)
|
|
75
|
+
*/
|
|
76
|
+
static encryptAtRest(payload: Uint8Array, masterPassphrase: string): {
|
|
77
|
+
iv: string;
|
|
78
|
+
salt: string;
|
|
79
|
+
data: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Decripta um payload protegido com a senha mestra
|
|
83
|
+
*/
|
|
84
|
+
static decryptAtRest(encryptedBundle: {
|
|
85
|
+
iv: string;
|
|
86
|
+
salt: string;
|
|
87
|
+
data: string;
|
|
88
|
+
}, masterPassphrase: string): Uint8Array;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { CryptoEngine, NOISE_PROTOCOL_NAME, NoiseCipherState, NoiseHandshake, WA_NOISE_HEADER };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { x25519 } from '@noble/curves/ed25519';
|
|
2
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
3
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
4
|
+
import { gcm } from '@noble/ciphers/aes';
|
|
5
|
+
import { randomBytes } from 'crypto';
|
|
6
|
+
|
|
7
|
+
// src/crypto/index.ts
|
|
8
|
+
var NOISE_PROTOCOL_NAME = "Noise_XX_25519_AESGCM_SHA256";
|
|
9
|
+
var WA_NOISE_HEADER = new Uint8Array([87, 65, 6, 2]);
|
|
10
|
+
var NoiseCipherState = class {
|
|
11
|
+
key = null;
|
|
12
|
+
nonce = 0;
|
|
13
|
+
constructor(key) {
|
|
14
|
+
if (key) {
|
|
15
|
+
this.key = key;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
hasKey() {
|
|
19
|
+
return this.key !== null;
|
|
20
|
+
}
|
|
21
|
+
setKey(key) {
|
|
22
|
+
this.key = key;
|
|
23
|
+
this.nonce = 0;
|
|
24
|
+
}
|
|
25
|
+
encryptWithAd(ad, plaintext) {
|
|
26
|
+
if (!this.key) {
|
|
27
|
+
return plaintext;
|
|
28
|
+
}
|
|
29
|
+
const iv = this.generateNonceBuffer(this.nonce);
|
|
30
|
+
this.nonce += 1;
|
|
31
|
+
return CryptoEngine.aesGcmEncrypt(this.key, plaintext, iv, ad);
|
|
32
|
+
}
|
|
33
|
+
decryptWithAd(ad, ciphertext) {
|
|
34
|
+
if (!this.key) {
|
|
35
|
+
return ciphertext;
|
|
36
|
+
}
|
|
37
|
+
const iv = this.generateNonceBuffer(this.nonce);
|
|
38
|
+
this.nonce += 1;
|
|
39
|
+
return CryptoEngine.aesGcmDecrypt(this.key, ciphertext, iv, ad);
|
|
40
|
+
}
|
|
41
|
+
generateNonceBuffer(nonce) {
|
|
42
|
+
const buffer = new Uint8Array(12);
|
|
43
|
+
const view = new DataView(buffer.buffer);
|
|
44
|
+
view.setUint32(8, nonce, false);
|
|
45
|
+
return buffer;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var NoiseHandshake = class {
|
|
49
|
+
symmetricState;
|
|
50
|
+
localEphemeral;
|
|
51
|
+
localStatic;
|
|
52
|
+
remoteEphemeral = null;
|
|
53
|
+
remoteStatic = null;
|
|
54
|
+
constructor(localStaticKey) {
|
|
55
|
+
this.localStatic = localStaticKey;
|
|
56
|
+
this.localEphemeral = CryptoEngine.generateKeyPair();
|
|
57
|
+
const protocolHash = sha256(new TextEncoder().encode(NOISE_PROTOCOL_NAME));
|
|
58
|
+
this.symmetricState = {
|
|
59
|
+
h: new Uint8Array(protocolHash),
|
|
60
|
+
ck: new Uint8Array(protocolHash),
|
|
61
|
+
cipher: new NoiseCipherState()
|
|
62
|
+
};
|
|
63
|
+
this.mixHash(WA_NOISE_HEADER);
|
|
64
|
+
}
|
|
65
|
+
mixHash(data) {
|
|
66
|
+
const merged = new Uint8Array(this.symmetricState.h.length + data.length);
|
|
67
|
+
merged.set(this.symmetricState.h, 0);
|
|
68
|
+
merged.set(data, this.symmetricState.h.length);
|
|
69
|
+
this.symmetricState.h = sha256(merged);
|
|
70
|
+
}
|
|
71
|
+
mixKey(data) {
|
|
72
|
+
const derived = CryptoEngine.deriveKey(data, "", this.symmetricState.ck, 64);
|
|
73
|
+
this.symmetricState.ck = derived.slice(0, 32);
|
|
74
|
+
const tempK = derived.slice(32, 64);
|
|
75
|
+
this.symmetricState.cipher.setKey(tempK);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Passo 1: Início do Handshake -> Envia chave efêmera local (e)
|
|
79
|
+
*/
|
|
80
|
+
startHandshake() {
|
|
81
|
+
this.mixHash(this.localEphemeral.public);
|
|
82
|
+
const message = new Uint8Array(WA_NOISE_HEADER.length + this.localEphemeral.public.length);
|
|
83
|
+
message.set(WA_NOISE_HEADER, 0);
|
|
84
|
+
message.set(this.localEphemeral.public, WA_NOISE_HEADER.length);
|
|
85
|
+
return message;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Passo 2: Recebe resposta do servidor WhatsApp <- (e, ee, s, es)
|
|
89
|
+
*/
|
|
90
|
+
processServerHello(serverBuffer) {
|
|
91
|
+
this.remoteEphemeral = serverBuffer.slice(0, 32);
|
|
92
|
+
this.mixHash(this.remoteEphemeral);
|
|
93
|
+
const ee = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteEphemeral);
|
|
94
|
+
this.mixKey(ee);
|
|
95
|
+
const encryptedStatic = serverBuffer.slice(32, 80);
|
|
96
|
+
this.remoteStatic = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedStatic);
|
|
97
|
+
this.mixHash(encryptedStatic);
|
|
98
|
+
const es = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteStatic);
|
|
99
|
+
this.mixKey(es);
|
|
100
|
+
const encryptedPayload = serverBuffer.slice(80);
|
|
101
|
+
const decryptedPayload = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedPayload);
|
|
102
|
+
this.mixHash(encryptedPayload);
|
|
103
|
+
return decryptedPayload;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Passo 3: Envia resposta final de autenticação do cliente -> (s, se) + payload
|
|
107
|
+
* Retorna os CipherStates de leitura e escrita para uso no WebSocket
|
|
108
|
+
*/
|
|
109
|
+
finishHandshake(payload) {
|
|
110
|
+
const encryptedStatic = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, this.localStatic.public);
|
|
111
|
+
this.mixHash(encryptedStatic);
|
|
112
|
+
if (!this.remoteEphemeral) {
|
|
113
|
+
throw new Error("Remote ephemeral key is missing in Noise Handshake");
|
|
114
|
+
}
|
|
115
|
+
const se = CryptoEngine.sharedKey(this.localStatic.private, this.remoteEphemeral);
|
|
116
|
+
this.mixKey(se);
|
|
117
|
+
const encryptedPayload = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, payload);
|
|
118
|
+
this.mixHash(encryptedPayload);
|
|
119
|
+
const handshakeMessage = new Uint8Array(encryptedStatic.length + encryptedPayload.length);
|
|
120
|
+
handshakeMessage.set(encryptedStatic, 0);
|
|
121
|
+
handshakeMessage.set(encryptedPayload, encryptedStatic.length);
|
|
122
|
+
const transportKeys = CryptoEngine.deriveKey(new Uint8Array(0), "", this.symmetricState.ck, 64);
|
|
123
|
+
const writeKey = transportKeys.slice(0, 32);
|
|
124
|
+
const readKey = transportKeys.slice(32, 64);
|
|
125
|
+
return {
|
|
126
|
+
handshakeMessage,
|
|
127
|
+
writeCipher: new NoiseCipherState(writeKey),
|
|
128
|
+
readCipher: new NoiseCipherState(readKey)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/crypto/index.ts
|
|
134
|
+
var CryptoEngine = class {
|
|
135
|
+
/**
|
|
136
|
+
* Gera um novo par de chaves Curve25519 (Diffie-Hellman)
|
|
137
|
+
*/
|
|
138
|
+
static generateKeyPair() {
|
|
139
|
+
const priv = randomBytes(32);
|
|
140
|
+
const pub = x25519.getPublicKey(priv);
|
|
141
|
+
return {
|
|
142
|
+
private: new Uint8Array(priv),
|
|
143
|
+
public: new Uint8Array(pub)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Executa Diffie-Hellman compartilhado (Shared Secret) usando Curve25519
|
|
148
|
+
*/
|
|
149
|
+
static sharedKey(privateKey, publicKey) {
|
|
150
|
+
return x25519.getSharedSecret(privateKey, publicKey);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* HKDF baseado em SHA-256 para derivação segura de chaves (RFC 5869)
|
|
154
|
+
*/
|
|
155
|
+
static deriveKey(secret, info, salt, length = 32) {
|
|
156
|
+
const infoBytes = typeof info === "string" ? new TextEncoder().encode(info) : info;
|
|
157
|
+
return hkdf(sha256, secret, salt, infoBytes, length);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Encriptação autenticada com AES-256-GCM
|
|
161
|
+
*/
|
|
162
|
+
static aesGcmEncrypt(key, plaintext, iv, additionalData) {
|
|
163
|
+
const cipher = gcm(key, iv, additionalData);
|
|
164
|
+
return cipher.encrypt(plaintext);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Decriptação autenticada com AES-256-GCM
|
|
168
|
+
*/
|
|
169
|
+
static aesGcmDecrypt(key, ciphertext, iv, additionalData) {
|
|
170
|
+
const cipher = gcm(key, iv, additionalData);
|
|
171
|
+
return cipher.decrypt(ciphertext);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Encripta um payload com uma senha mestra (para proteger credenciais at-rest)
|
|
175
|
+
*/
|
|
176
|
+
static encryptAtRest(payload, masterPassphrase) {
|
|
177
|
+
const salt = randomBytes(16);
|
|
178
|
+
const iv = randomBytes(12);
|
|
179
|
+
const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
180
|
+
const encrypted = this.aesGcmEncrypt(key, payload, iv);
|
|
181
|
+
return {
|
|
182
|
+
salt: Buffer.from(salt).toString("hex"),
|
|
183
|
+
iv: Buffer.from(iv).toString("hex"),
|
|
184
|
+
data: Buffer.from(encrypted).toString("base64")
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Decripta um payload protegido com a senha mestra
|
|
189
|
+
*/
|
|
190
|
+
static decryptAtRest(encryptedBundle, masterPassphrase) {
|
|
191
|
+
const salt = Buffer.from(encryptedBundle.salt, "hex");
|
|
192
|
+
const iv = Buffer.from(encryptedBundle.iv, "hex");
|
|
193
|
+
const data = Buffer.from(encryptedBundle.data, "base64");
|
|
194
|
+
const key = hkdf(sha256, new TextEncoder().encode(masterPassphrase), salt, new TextEncoder().encode("cdwa-at-rest"), 32);
|
|
195
|
+
return this.aesGcmDecrypt(key, data, iv);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export { CryptoEngine, NOISE_PROTOCOL_NAME, NoiseCipherState, NoiseHandshake, WA_NOISE_HEADER };
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/crypto/noise.ts","../../src/crypto/index.ts"],"names":["sha256"],"mappings":"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","file":"index.js","sourcesContent":["import { CryptoEngine } from './index.js'\nimport { sha256 } from '@noble/hashes/sha256'\nimport type { KeyPair } from '../types/index.js'\n\nexport const NOISE_PROTOCOL_NAME = 'Noise_XX_25519_AESGCM_SHA256'\nexport const WA_NOISE_HEADER = new Uint8Array([87, 65, 6, 2]) // \"WA\\x06\\x02\"\n\n/**\n * Estado da cifra simétrica do Noise com contador sequencial (Nonce)\n */\nexport class NoiseCipherState {\n private key: Uint8Array | null = null\n private nonce = 0\n\n constructor(key?: Uint8Array) {\n if (key) {\n this.key = key\n }\n }\n\n hasKey(): boolean {\n return this.key !== null\n }\n\n setKey(key: Uint8Array) {\n this.key = key\n this.nonce = 0\n }\n\n encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Uint8Array {\n if (!this.key) {\n return plaintext\n }\n const iv = this.generateNonceBuffer(this.nonce)\n this.nonce += 1\n return CryptoEngine.aesGcmEncrypt(this.key, plaintext, iv, ad)\n }\n\n decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Uint8Array {\n if (!this.key) {\n return ciphertext\n }\n const iv = this.generateNonceBuffer(this.nonce)\n this.nonce += 1\n return CryptoEngine.aesGcmDecrypt(this.key, ciphertext, iv, ad)\n }\n\n private generateNonceBuffer(nonce: number): Uint8Array {\n const buffer = new Uint8Array(12)\n const view = new DataView(buffer.buffer)\n // Nonce no formato 64-bit integer nos últimos 8 bytes\n view.setUint32(8, nonce, false)\n return buffer\n }\n}\n\n/**\n * Máquina de estado do Noise XX Handshake para o WhatsApp Web\n */\nexport class NoiseHandshake {\n private symmetricState: {\n h: Uint8Array\n ck: Uint8Array\n cipher: NoiseCipherState\n }\n private localEphemeral: KeyPair\n private localStatic: KeyPair\n private remoteEphemeral: Uint8Array | null = null\n private remoteStatic: Uint8Array | null = null\n\n constructor(localStaticKey: KeyPair) {\n this.localStatic = localStaticKey\n this.localEphemeral = CryptoEngine.generateKeyPair()\n\n // Inicialização do estado simétrico com o nome do protocolo\n const protocolHash = sha256(new TextEncoder().encode(NOISE_PROTOCOL_NAME))\n this.symmetricState = {\n h: new Uint8Array(protocolHash),\n ck: new Uint8Array(protocolHash),\n cipher: new NoiseCipherState(),\n }\n\n // Mistura o prólogo inicial do WhatsApp (WA_NOISE_HEADER) no hash\n this.mixHash(WA_NOISE_HEADER)\n }\n\n private mixHash(data: Uint8Array) {\n const merged = new Uint8Array(this.symmetricState.h.length + data.length)\n merged.set(this.symmetricState.h, 0)\n merged.set(data, this.symmetricState.h.length)\n this.symmetricState.h = sha256(merged)\n }\n\n private mixKey(data: Uint8Array) {\n // HKDF-Extract e Expand com Chaining Key\n const derived = CryptoEngine.deriveKey(data, '', this.symmetricState.ck, 64)\n this.symmetricState.ck = derived.slice(0, 32)\n const tempK = derived.slice(32, 64)\n this.symmetricState.cipher.setKey(tempK)\n }\n\n /**\n * Passo 1: Início do Handshake -> Envia chave efêmera local (e)\n */\n startHandshake(): Uint8Array {\n this.mixHash(this.localEphemeral.public)\n // O payload inicial consiste no prólogo do WhatsApp + a chave efêmera\n const message = new Uint8Array(WA_NOISE_HEADER.length + this.localEphemeral.public.length)\n message.set(WA_NOISE_HEADER, 0)\n message.set(this.localEphemeral.public, WA_NOISE_HEADER.length)\n return message\n }\n\n /**\n * Passo 2: Recebe resposta do servidor WhatsApp <- (e, ee, s, es)\n */\n processServerHello(serverBuffer: Uint8Array): Uint8Array {\n // Extrai a chave efêmera remota (primeiros 32 bytes)\n this.remoteEphemeral = serverBuffer.slice(0, 32)\n this.mixHash(this.remoteEphemeral)\n\n // ee: Diffie-Hellman entre as duas chaves efêmeras\n const ee = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteEphemeral)\n this.mixKey(ee)\n\n // s: Chave estática encriptada do servidor (48 bytes = 32 bytes de chave + 16 bytes auth tag do AES-GCM)\n const encryptedStatic = serverBuffer.slice(32, 80)\n this.remoteStatic = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedStatic)\n this.mixHash(encryptedStatic)\n\n // es: Diffie-Hellman entre a chave efêmera local e a chave estática do servidor\n const es = CryptoEngine.sharedKey(this.localEphemeral.private, this.remoteStatic)\n this.mixKey(es)\n\n // Payload final criptografado da resposta do servidor\n const encryptedPayload = serverBuffer.slice(80)\n const decryptedPayload = this.symmetricState.cipher.decryptWithAd(this.symmetricState.h, encryptedPayload)\n this.mixHash(encryptedPayload)\n\n return decryptedPayload\n }\n\n /**\n * Passo 3: Envia resposta final de autenticação do cliente -> (s, se) + payload\n * Retorna os CipherStates de leitura e escrita para uso no WebSocket\n */\n finishHandshake(payload: Uint8Array): {\n handshakeMessage: Uint8Array\n writeCipher: NoiseCipherState\n readCipher: NoiseCipherState\n } {\n // s: Encripta nossa chave estática de identidade\n const encryptedStatic = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, this.localStatic.public)\n this.mixHash(encryptedStatic)\n\n // se: Diffie-Hellman entre nossa chave estática e a efêmera do servidor\n if (!this.remoteEphemeral) {\n throw new Error('Remote ephemeral key is missing in Noise Handshake')\n }\n const se = CryptoEngine.sharedKey(this.localStatic.private, this.remoteEphemeral)\n this.mixKey(se)\n\n // Encripta o payload final (contendo informações de registro/login)\n const encryptedPayload = this.symmetricState.cipher.encryptWithAd(this.symmetricState.h, payload)\n this.mixHash(encryptedPayload)\n\n // Mensagem de envio: [encryptedStatic (48b)] + [encryptedPayload]\n const handshakeMessage = new Uint8Array(encryptedStatic.length + encryptedPayload.length)\n handshakeMessage.set(encryptedStatic, 0)\n handshakeMessage.set(encryptedPayload, encryptedStatic.length)\n\n // Divide a chaining key em 2 chaves finais de transporte (Split)\n const transportKeys = CryptoEngine.deriveKey(new Uint8Array(0), '', this.symmetricState.ck, 64)\n const writeKey = transportKeys.slice(0, 32)\n const readKey = transportKeys.slice(32, 64)\n\n return {\n handshakeMessage,\n writeCipher: new NoiseCipherState(writeKey),\n readCipher: new NoiseCipherState(readKey),\n }\n }\n}\n","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"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos fundamentais do protocolo e do cliente
|
|
3
|
+
*/
|
|
4
|
+
interface KeyPair {
|
|
5
|
+
public: Uint8Array;
|
|
6
|
+
private: Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
interface SignedKeyPair extends KeyPair {
|
|
9
|
+
keyId: number;
|
|
10
|
+
signature: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
interface AuthenticationCreds {
|
|
13
|
+
noiseKey: KeyPair;
|
|
14
|
+
pairingEphemeralKeyPair: KeyPair;
|
|
15
|
+
signedIdentityKey: KeyPair;
|
|
16
|
+
signedPreKey: SignedKeyPair;
|
|
17
|
+
registrationId: number;
|
|
18
|
+
advSecretKey: string;
|
|
19
|
+
me?: {
|
|
20
|
+
id: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
};
|
|
23
|
+
account?: Record<string, unknown>;
|
|
24
|
+
signalIdentities?: Array<unknown>;
|
|
25
|
+
myAppStateKeyId?: string;
|
|
26
|
+
firstUnuploadedPreKeyId: number;
|
|
27
|
+
nextPreKeyId: number;
|
|
28
|
+
lastAccountSyncTimestamp?: number;
|
|
29
|
+
platform?: string;
|
|
30
|
+
}
|
|
31
|
+
interface SignalKeyStore {
|
|
32
|
+
get<T>(type: string, ids: string[]): Promise<Record<string, T>>;
|
|
33
|
+
set(data: Record<string, Record<string, unknown>>): Promise<void>;
|
|
34
|
+
clear?(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
interface AuthenticationState {
|
|
37
|
+
creds: AuthenticationCreds;
|
|
38
|
+
keys: SignalKeyStore;
|
|
39
|
+
}
|
|
40
|
+
interface SocketConfig {
|
|
41
|
+
waWebSocketUrl?: string;
|
|
42
|
+
connectTimeoutMs?: number;
|
|
43
|
+
keepAliveIntervalMs?: number;
|
|
44
|
+
printQRInTerminal?: boolean;
|
|
45
|
+
auth?: AuthenticationState;
|
|
46
|
+
security?: {
|
|
47
|
+
storagePassphrase?: string;
|
|
48
|
+
enableEncryptedStorage?: boolean;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
interface BinaryNodeAttributes {
|
|
52
|
+
[key: string]: string | number | undefined;
|
|
53
|
+
}
|
|
54
|
+
interface BinaryNode {
|
|
55
|
+
tag: string;
|
|
56
|
+
attrs: BinaryNodeAttributes;
|
|
57
|
+
content?: BinaryNode[] | string | Uint8Array;
|
|
58
|
+
}
|
|
59
|
+
type ConnectionState = 'close' | 'connecting' | 'open';
|
|
60
|
+
interface ConnectionUpdate {
|
|
61
|
+
connection?: ConnectionState;
|
|
62
|
+
lastDisconnect?: {
|
|
63
|
+
error?: Error;
|
|
64
|
+
date?: Date;
|
|
65
|
+
};
|
|
66
|
+
qr?: string;
|
|
67
|
+
isNewLogin?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type { AuthenticationCreds as A, BinaryNode as B, ConnectionState as C, KeyPair as K, SignalKeyStore as S, AuthenticationState as a, SocketConfig as b, ConnectionUpdate as c, BinaryNodeAttributes as d, SignedKeyPair as e };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos fundamentais do protocolo e do cliente
|
|
3
|
+
*/
|
|
4
|
+
interface KeyPair {
|
|
5
|
+
public: Uint8Array;
|
|
6
|
+
private: Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
interface SignedKeyPair extends KeyPair {
|
|
9
|
+
keyId: number;
|
|
10
|
+
signature: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
interface AuthenticationCreds {
|
|
13
|
+
noiseKey: KeyPair;
|
|
14
|
+
pairingEphemeralKeyPair: KeyPair;
|
|
15
|
+
signedIdentityKey: KeyPair;
|
|
16
|
+
signedPreKey: SignedKeyPair;
|
|
17
|
+
registrationId: number;
|
|
18
|
+
advSecretKey: string;
|
|
19
|
+
me?: {
|
|
20
|
+
id: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
};
|
|
23
|
+
account?: Record<string, unknown>;
|
|
24
|
+
signalIdentities?: Array<unknown>;
|
|
25
|
+
myAppStateKeyId?: string;
|
|
26
|
+
firstUnuploadedPreKeyId: number;
|
|
27
|
+
nextPreKeyId: number;
|
|
28
|
+
lastAccountSyncTimestamp?: number;
|
|
29
|
+
platform?: string;
|
|
30
|
+
}
|
|
31
|
+
interface SignalKeyStore {
|
|
32
|
+
get<T>(type: string, ids: string[]): Promise<Record<string, T>>;
|
|
33
|
+
set(data: Record<string, Record<string, unknown>>): Promise<void>;
|
|
34
|
+
clear?(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
interface AuthenticationState {
|
|
37
|
+
creds: AuthenticationCreds;
|
|
38
|
+
keys: SignalKeyStore;
|
|
39
|
+
}
|
|
40
|
+
interface SocketConfig {
|
|
41
|
+
waWebSocketUrl?: string;
|
|
42
|
+
connectTimeoutMs?: number;
|
|
43
|
+
keepAliveIntervalMs?: number;
|
|
44
|
+
printQRInTerminal?: boolean;
|
|
45
|
+
auth?: AuthenticationState;
|
|
46
|
+
security?: {
|
|
47
|
+
storagePassphrase?: string;
|
|
48
|
+
enableEncryptedStorage?: boolean;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
interface BinaryNodeAttributes {
|
|
52
|
+
[key: string]: string | number | undefined;
|
|
53
|
+
}
|
|
54
|
+
interface BinaryNode {
|
|
55
|
+
tag: string;
|
|
56
|
+
attrs: BinaryNodeAttributes;
|
|
57
|
+
content?: BinaryNode[] | string | Uint8Array;
|
|
58
|
+
}
|
|
59
|
+
type ConnectionState = 'close' | 'connecting' | 'open';
|
|
60
|
+
interface ConnectionUpdate {
|
|
61
|
+
connection?: ConnectionState;
|
|
62
|
+
lastDisconnect?: {
|
|
63
|
+
error?: Error;
|
|
64
|
+
date?: Date;
|
|
65
|
+
};
|
|
66
|
+
qr?: string;
|
|
67
|
+
isNewLogin?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type { AuthenticationCreds as A, BinaryNode as B, ConnectionState as C, KeyPair as K, SignalKeyStore as S, AuthenticationState as a, SocketConfig as b, ConnectionUpdate as c, BinaryNodeAttributes as d, SignedKeyPair as e };
|