@neykoor/libsignal-node 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/lib/base-key-type.d.ts +4 -0
  4. package/lib/base-key-type.js +5 -0
  5. package/lib/chain-type.d.ts +4 -0
  6. package/lib/chain-type.js +5 -0
  7. package/lib/crypto.d.ts +6 -0
  8. package/lib/crypto.js +73 -0
  9. package/lib/curve.d.ts +9 -0
  10. package/lib/curve.js +112 -0
  11. package/lib/direction.d.ts +4 -0
  12. package/lib/direction.js +5 -0
  13. package/lib/errors.d.ts +16 -0
  14. package/lib/errors.js +28 -0
  15. package/lib/index.d.ts +20 -0
  16. package/lib/index.js +16 -0
  17. package/lib/keyhelper.d.ts +18 -0
  18. package/lib/keyhelper.js +52 -0
  19. package/lib/logger.d.ts +6 -0
  20. package/lib/logger.js +11 -0
  21. package/lib/memory-storage.d.ts +25 -0
  22. package/lib/memory-storage.js +68 -0
  23. package/lib/numeric-fingerprint.d.ts +5 -0
  24. package/lib/numeric-fingerprint.js +54 -0
  25. package/lib/prekey-bundle-validator.d.ts +2 -0
  26. package/lib/prekey-bundle-validator.js +44 -0
  27. package/lib/protobufs.d.ts +5 -0
  28. package/lib/protobufs.js +3 -0
  29. package/lib/protocol-address.d.ts +8 -0
  30. package/lib/protocol-address.js +31 -0
  31. package/lib/queue-job.d.ts +3 -0
  32. package/lib/queue-job.js +49 -0
  33. package/lib/session-builder.d.ts +12 -0
  34. package/lib/session-builder.js +153 -0
  35. package/lib/session-cipher.d.ts +29 -0
  36. package/lib/session-cipher.js +326 -0
  37. package/lib/session-record.d.ts +109 -0
  38. package/lib/session-record.js +286 -0
  39. package/lib/types.d.ts +42 -0
  40. package/lib/types.js +1 -0
  41. package/lib/util.d.ts +2 -0
  42. package/lib/util.js +10 -0
  43. package/lib/whisper-text-protocol.d.ts +3 -0
  44. package/lib/whisper-text-protocol.js +483 -0
  45. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Neykoor
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 ADDED
@@ -0,0 +1,130 @@
1
+ <div align="center">
2
+
3
+ <img src="./assets/banner.png" alt="libsignal-node-ts banner" width="100%" />
4
+
5
+ # libsignal-node-ts
6
+
7
+ **Port 100% TypeScript de `libsignal-node`, compatible con [BaileysX](https://github.com/Neykoor/BaileysX)**
8
+
9
+ [![npm](https://img.shields.io/badge/npm-libsignal--node--ts-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/libsignal-node-ts)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.6-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
11
+ [![ESM](https://img.shields.io/badge/Module-ESM-yellow)](https://nodejs.org/api/esm.html)
12
+ [![Node](https://img.shields.io/badge/Node-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
13
+ [![License](https://img.shields.io/badge/License-MIT-blue)](./LICENSE)
14
+ [![Status](https://img.shields.io/badge/Estado-Estable-success)]()
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## ✨ ¿Qué es esto?
21
+
22
+ `libsignal-node-ts` es una reescritura completa en **TypeScript** de la librería [`libsignal-node`](https://github.com/this-xys/libsignal-node) (Signal Protocol / Double Ratchet), pensada como **reemplazo directo** de la dependencia `libsignal` que usa BaileysX.
23
+
24
+ Mismo API, misma criptografía, cero `@ts-ignore`, tipado de punta a punta.
25
+
26
+ ## 🚀 Características
27
+
28
+ - 🔒 Implementación completa del **Double Ratchet** (X3DH, sesiones, ratcheting)
29
+ - 📦 Tipado estricto (`strict: true`, `noUncheckedIndexedAccess`)
30
+ - ⚡ ESM nativo
31
+ - 🔁 API idéntica a `libsignal-node` — `ProtocolAddress`, `SessionBuilder`, `SessionCipher`, `SessionRecord`
32
+ - 🧩 Drop-in: solo cambias el import, no tocas el resto de tu código
33
+
34
+ ## 📦 Instalación
35
+
36
+ ```bash
37
+ npm install libsignal-node-ts
38
+ ```
39
+
40
+ Dependencias en tiempo de ejecución: `curve25519-js` y `protobufjs` (se instalan solas junto con el paquete).
41
+
42
+ ## 📂 Estructura del código fuente
43
+
44
+ ```
45
+ libsignal-node-ts/
46
+ ├── src/
47
+ │ ├── index.ts # punto de entrada
48
+ │ ├── protocol-address.ts
49
+ │ ├── session-builder.ts
50
+ │ ├── session-cipher.ts
51
+ │ ├── session-record.ts
52
+ │ ├── curve.ts
53
+ │ ├── curve25519-js.d.ts
54
+ │ ├── crypto.ts
55
+ │ ├── keyhelper.ts
56
+ │ ├── numeric-fingerprint.ts
57
+ │ ├── prekey-bundle-validator.ts
58
+ │ ├── memory-storage.ts
59
+ │ ├── logger.ts
60
+ │ ├── errors.ts
61
+ │ ├── direction.ts
62
+ │ ├── base-key-type.ts
63
+ │ ├── chain-type.ts
64
+ │ ├── queue-job.ts
65
+ │ ├── types.ts
66
+ │ ├── protobufs.ts
67
+ │ ├── whisper-text-protocol.js # protobufs generados (ESM)
68
+ │ └── whisper-text-protocol.d.ts
69
+ ├── lib/ # salida compilada (lo que se publica en npm)
70
+ ├── package.json
71
+ └── tsconfig.json
72
+ ```
73
+
74
+ ## 🔧 Uso en BaileysX
75
+
76
+ En `src/Signal/libsignal.ts`, reemplaza:
77
+
78
+ ```ts
79
+ // @ts-ignore
80
+ import * as libsignal from 'libsignal'
81
+ // @ts-ignore
82
+ import { PreKeyWhisperMessage } from 'libsignal/lib/protobufs.js'
83
+ ```
84
+
85
+ por:
86
+
87
+ ```ts
88
+ import * as libsignal from 'libsignal-node-ts'
89
+ import { protobufs } from 'libsignal-node-ts'
90
+ const { PreKeyWhisperMessage } = protobufs
91
+ ```
92
+
93
+ Y en el `package.json` de tu proyecto, quita la dependencia `libsignal` y agrega:
94
+
95
+ ```json
96
+ "libsignal-node-ts": "^1.0.0"
97
+ ```
98
+
99
+ ## 📖 Uso básico
100
+
101
+ ```ts
102
+ import { ProtocolAddress, SessionBuilder, SessionCipher, MemorySignalStorage, keyhelper } from 'libsignal-node-ts'
103
+
104
+ const identityKeyPair = keyhelper.generateIdentityKeyPair()
105
+ const registrationId = keyhelper.generateRegistrationId()
106
+ const storage = new MemorySignalStorage(identityKeyPair, registrationId)
107
+ const addr = new ProtocolAddress('5215512345678', 1)
108
+
109
+ const builder = new SessionBuilder(storage, addr)
110
+ await builder.initOutgoing(deviceBundle)
111
+
112
+ const cipher = new SessionCipher(storage, addr)
113
+ const { type, body } = await cipher.encrypt(Buffer.from('hola mundo'))
114
+ ```
115
+
116
+ `storage` debe implementar la interfaz `SignalStorage` exportada desde el paquete (o puedes usar `MemorySignalStorage`, incluida para pruebas rápidas y almacenamiento en memoria).
117
+
118
+ ## 🧪 Compilar desde el código fuente
119
+
120
+ ```bash
121
+ npm run build
122
+ ```
123
+
124
+ Compila `src/` a `lib/` con `tsc` en modo `strict` y corrige las extensiones `.js` de los imports ESM con `tsc-esm-fix`.
125
+
126
+ ---
127
+
128
+ <div align="center">
129
+ Hecho con ☕ para el ecosistema <b>Eris-MD</b> / <b>BaileysX</b>
130
+ </div>
@@ -0,0 +1,4 @@
1
+ export declare enum BaseKeyType {
2
+ OURS = 1,
3
+ THEIRS = 2
4
+ }
@@ -0,0 +1,5 @@
1
+ export var BaseKeyType;
2
+ (function (BaseKeyType) {
3
+ BaseKeyType[BaseKeyType["OURS"] = 1] = "OURS";
4
+ BaseKeyType[BaseKeyType["THEIRS"] = 2] = "THEIRS";
5
+ })(BaseKeyType || (BaseKeyType = {}));
@@ -0,0 +1,4 @@
1
+ export declare enum ChainType {
2
+ SENDING = 1,
3
+ RECEIVING = 2
4
+ }
@@ -0,0 +1,5 @@
1
+ export var ChainType;
2
+ (function (ChainType) {
3
+ ChainType[ChainType["SENDING"] = 1] = "SENDING";
4
+ ChainType[ChainType["RECEIVING"] = 2] = "RECEIVING";
5
+ })(ChainType || (ChainType = {}));
@@ -0,0 +1,6 @@
1
+ export declare function encrypt(key: Uint8Array, data: Uint8Array, iv: Uint8Array): Buffer;
2
+ export declare function decrypt(key: Uint8Array, data: Uint8Array, iv: Uint8Array): Buffer;
3
+ export declare function calculateMAC(key: Uint8Array, data: Uint8Array): Buffer;
4
+ export declare function hash(data: Uint8Array): Buffer;
5
+ export declare function deriveSecrets(input: Uint8Array, salt: Uint8Array, info: Uint8Array, chunks?: number): Buffer[];
6
+ export declare function verifyMAC(data: Uint8Array, key: Uint8Array, mac: Uint8Array, length: number): void;
package/lib/crypto.js ADDED
@@ -0,0 +1,73 @@
1
+ import * as nodeCrypto from 'crypto';
2
+ function assertBuffer(value) {
3
+ if (!(value instanceof Uint8Array)) {
4
+ throw TypeError(`Expected Uint8Array instead of: ${value?.constructor?.name}`);
5
+ }
6
+ return value;
7
+ }
8
+ export function encrypt(key, data, iv) {
9
+ assertBuffer(key);
10
+ assertBuffer(data);
11
+ assertBuffer(iv);
12
+ const cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv);
13
+ return Buffer.concat([cipher.update(data), cipher.final()]);
14
+ }
15
+ export function decrypt(key, data, iv) {
16
+ assertBuffer(key);
17
+ assertBuffer(data);
18
+ assertBuffer(iv);
19
+ const decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv);
20
+ return Buffer.concat([decipher.update(data), decipher.final()]);
21
+ }
22
+ export function calculateMAC(key, data) {
23
+ assertBuffer(key);
24
+ assertBuffer(data);
25
+ const hmac = nodeCrypto.createHmac('sha256', key);
26
+ hmac.update(data);
27
+ return Buffer.from(hmac.digest());
28
+ }
29
+ export function hash(data) {
30
+ assertBuffer(data);
31
+ const sha512 = nodeCrypto.createHash('sha512');
32
+ sha512.update(data);
33
+ return sha512.digest();
34
+ }
35
+ export function deriveSecrets(input, salt, info, chunks) {
36
+ assertBuffer(input);
37
+ assertBuffer(salt);
38
+ assertBuffer(info);
39
+ if (salt.byteLength !== 32) {
40
+ throw new Error('Got salt of incorrect length');
41
+ }
42
+ const count = chunks || 3;
43
+ if (count < 1 || count > 3) {
44
+ throw new Error('chunks must be between 1 and 3');
45
+ }
46
+ const PRK = calculateMAC(salt, input);
47
+ const infoArray = new Uint8Array(info.byteLength + 1 + 32);
48
+ infoArray.set(info, 32);
49
+ infoArray[infoArray.length - 1] = 1;
50
+ const signed = [calculateMAC(PRK, Buffer.from(infoArray.slice(32)))];
51
+ if (count > 1) {
52
+ infoArray.set(signed[signed.length - 1]);
53
+ infoArray[infoArray.length - 1] = 2;
54
+ signed.push(calculateMAC(PRK, Buffer.from(infoArray)));
55
+ }
56
+ if (count > 2) {
57
+ infoArray.set(signed[signed.length - 1]);
58
+ infoArray[infoArray.length - 1] = 3;
59
+ signed.push(calculateMAC(PRK, Buffer.from(infoArray)));
60
+ }
61
+ PRK.fill(0);
62
+ infoArray.fill(0);
63
+ return signed;
64
+ }
65
+ export function verifyMAC(data, key, mac, length) {
66
+ const calculatedMac = calculateMAC(key, data).slice(0, length);
67
+ if (mac.length !== length || calculatedMac.length !== length) {
68
+ throw new Error('Bad MAC length');
69
+ }
70
+ if (!nodeCrypto.timingSafeEqual(mac, calculatedMac)) {
71
+ throw new Error('Bad MAC');
72
+ }
73
+ }
package/lib/curve.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export interface KeyPair {
2
+ pubKey: Buffer;
3
+ privKey: Buffer;
4
+ }
5
+ export declare function getPublicFromPrivateKey(privKey: Uint8Array): Buffer;
6
+ export declare function generateKeyPair(): KeyPair;
7
+ export declare function calculateAgreement(pubKeyInput: Uint8Array, privKey: Uint8Array): Buffer;
8
+ export declare function calculateSignature(privKey: Uint8Array, message: Uint8Array): Buffer;
9
+ export declare function verifySignature(pubKeyInput: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean;
package/lib/curve.js ADDED
@@ -0,0 +1,112 @@
1
+ import * as curveJs from 'curve25519-js';
2
+ import * as nodeCrypto from 'crypto';
3
+ import { getLogger } from './logger.js';
4
+ const PUBLIC_KEY_DER_PREFIX = Buffer.from([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0]);
5
+ const PRIVATE_KEY_DER_PREFIX = Buffer.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32]);
6
+ const KEY_BUNDLE_TYPE = Buffer.from([5]);
7
+ function prefixKeyInPublicKey(pubKey) {
8
+ return Buffer.concat([KEY_BUNDLE_TYPE, pubKey]);
9
+ }
10
+ function validatePrivKey(privKey) {
11
+ if (privKey === undefined) {
12
+ throw new Error('Undefined private key');
13
+ }
14
+ if (!(privKey instanceof Uint8Array)) {
15
+ throw new Error(`Invalid private key type: ${privKey.constructor.name}`);
16
+ }
17
+ if (privKey.byteLength !== 32) {
18
+ throw new Error(`Incorrect private key length: ${privKey.byteLength}`);
19
+ }
20
+ }
21
+ function scrubPubKeyFormat(pubKey) {
22
+ if (!(pubKey instanceof Uint8Array)) {
23
+ throw new Error(`Invalid public key type: ${pubKey.constructor.name}`);
24
+ }
25
+ if (pubKey === undefined || ((pubKey.byteLength !== 33 || pubKey[0] !== 5) && pubKey.byteLength !== 32)) {
26
+ throw new Error('Invalid public key');
27
+ }
28
+ if (pubKey.byteLength === 33) {
29
+ return Buffer.from(pubKey.slice(1));
30
+ }
31
+ getLogger().warn('Expected pubkey of length 33, please report the ST and client that generated the pubkey');
32
+ return Buffer.from(pubKey);
33
+ }
34
+ function unclampEd25519PrivateKey(clampedSk) {
35
+ const unclampedSk = new Uint8Array(clampedSk);
36
+ unclampedSk[0] = unclampedSk[0] | 6;
37
+ unclampedSk[31] = unclampedSk[31] | 128;
38
+ unclampedSk[31] = unclampedSk[31] & ~64;
39
+ return unclampedSk;
40
+ }
41
+ export function getPublicFromPrivateKey(privKey) {
42
+ const unclampedPK = unclampEd25519PrivateKey(privKey);
43
+ const keyPair = curveJs.generateKeyPair(unclampedPK);
44
+ return prefixKeyInPublicKey(Buffer.from(keyPair.public));
45
+ }
46
+ export function generateKeyPair() {
47
+ try {
48
+ const { publicKey: publicDerBytes, privateKey: privateDerBytes } = nodeCrypto.generateKeyPairSync('x25519', {
49
+ publicKeyEncoding: { format: 'der', type: 'spki' },
50
+ privateKeyEncoding: { format: 'der', type: 'pkcs8' }
51
+ });
52
+ const pubKey = publicDerBytes.slice(PUBLIC_KEY_DER_PREFIX.length, PUBLIC_KEY_DER_PREFIX.length + 32);
53
+ const privKey = privateDerBytes.slice(PRIVATE_KEY_DER_PREFIX.length, PRIVATE_KEY_DER_PREFIX.length + 32);
54
+ return {
55
+ pubKey: prefixKeyInPublicKey(pubKey),
56
+ privKey
57
+ };
58
+ }
59
+ catch (e) {
60
+ getLogger().debug(`x25519 native keygen failed, falling back to curve25519-js: ${e?.message}`);
61
+ const keyPair = curveJs.generateKeyPair(nodeCrypto.randomBytes(32));
62
+ return {
63
+ privKey: Buffer.from(keyPair.private),
64
+ pubKey: prefixKeyInPublicKey(Buffer.from(keyPair.public))
65
+ };
66
+ }
67
+ }
68
+ export function calculateAgreement(pubKeyInput, privKey) {
69
+ const pubKey = scrubPubKeyFormat(pubKeyInput);
70
+ validatePrivKey(privKey);
71
+ if (!pubKey || pubKey.byteLength !== 32) {
72
+ throw new Error('Invalid public key');
73
+ }
74
+ if (typeof nodeCrypto.diffieHellman === 'function') {
75
+ const nodePrivateKey = nodeCrypto.createPrivateKey({
76
+ key: Buffer.concat([PRIVATE_KEY_DER_PREFIX, privKey]),
77
+ format: 'der',
78
+ type: 'pkcs8'
79
+ });
80
+ const nodePublicKey = nodeCrypto.createPublicKey({
81
+ key: Buffer.concat([PUBLIC_KEY_DER_PREFIX, pubKey]),
82
+ format: 'der',
83
+ type: 'spki'
84
+ });
85
+ return nodeCrypto.diffieHellman({
86
+ privateKey: nodePrivateKey,
87
+ publicKey: nodePublicKey
88
+ });
89
+ }
90
+ const secret = curveJs.sharedKey(privKey, pubKey);
91
+ return Buffer.from(secret);
92
+ }
93
+ export function calculateSignature(privKey, message) {
94
+ validatePrivKey(privKey);
95
+ if (!message) {
96
+ throw new Error('Invalid message');
97
+ }
98
+ return Buffer.from(curveJs.sign(privKey, message));
99
+ }
100
+ export function verifySignature(pubKeyInput, msg, sig) {
101
+ const pubKey = scrubPubKeyFormat(pubKeyInput);
102
+ if (!pubKey || pubKey.byteLength !== 32) {
103
+ throw new Error('Invalid public key');
104
+ }
105
+ if (!msg) {
106
+ throw new Error('Invalid message');
107
+ }
108
+ if (!sig || sig.byteLength !== 64) {
109
+ throw new Error('Invalid signature');
110
+ }
111
+ return curveJs.verify(pubKey, msg, sig);
112
+ }
@@ -0,0 +1,4 @@
1
+ export declare enum Direction {
2
+ SENDING = 1,
3
+ RECEIVING = 2
4
+ }
@@ -0,0 +1,5 @@
1
+ export var Direction;
2
+ (function (Direction) {
3
+ Direction[Direction["SENDING"] = 1] = "SENDING";
4
+ Direction[Direction["RECEIVING"] = 2] = "RECEIVING";
5
+ })(Direction || (Direction = {}));
@@ -0,0 +1,16 @@
1
+ export declare class SignalError extends Error {
2
+ }
3
+ export declare class UntrustedIdentityKeyError extends SignalError {
4
+ addr: string;
5
+ identityKey?: Buffer;
6
+ constructor(addr: string, identityKey?: Buffer);
7
+ }
8
+ export declare class SessionError extends SignalError {
9
+ constructor(message?: string);
10
+ }
11
+ export declare class MessageCounterError extends SessionError {
12
+ constructor(message?: string);
13
+ }
14
+ export declare class PreKeyError extends SessionError {
15
+ constructor(message?: string);
16
+ }
package/lib/errors.js ADDED
@@ -0,0 +1,28 @@
1
+ export class SignalError extends Error {
2
+ }
3
+ export class UntrustedIdentityKeyError extends SignalError {
4
+ constructor(addr, identityKey) {
5
+ super();
6
+ this.name = 'UntrustedIdentityKeyError';
7
+ this.addr = addr;
8
+ this.identityKey = identityKey;
9
+ }
10
+ }
11
+ export class SessionError extends SignalError {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'SessionError';
15
+ }
16
+ }
17
+ export class MessageCounterError extends SessionError {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = 'MessageCounterError';
21
+ }
22
+ }
23
+ export class PreKeyError extends SessionError {
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = 'PreKeyError';
27
+ }
28
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ export * as crypto from './crypto.js';
2
+ export * as curve from './curve.js';
3
+ export * as keyhelper from './keyhelper.js';
4
+ export { ProtocolAddress } from './protocol-address.js';
5
+ export { SessionBuilder } from './session-builder.js';
6
+ export { assertValidDeviceKeyBundle } from './prekey-bundle-validator.js';
7
+ export { SessionCipher } from './session-cipher.js';
8
+ export { SessionRecord, SessionEntry } from './session-record.js';
9
+ export { FingerprintGenerator } from './numeric-fingerprint.js';
10
+ export { BaseKeyType } from './base-key-type.js';
11
+ export { ChainType } from './chain-type.js';
12
+ export { Direction } from './direction.js';
13
+ export { MemorySignalStorage } from './memory-storage.js';
14
+ export { setLogger, getLogger } from './logger.js';
15
+ export type { SignalLogger } from './logger.js';
16
+ export { wipeBuffer, wipeBuffers } from './util.js';
17
+ export * from './errors.js';
18
+ export type { KeyPair } from './curve.js';
19
+ export type { SignalStorage, DeviceKeyBundle, IncomingPreKeyMessage, EncryptedMessage } from './types.js';
20
+ export type { Chain, ChainKey, CurrentRatchet, IndexInfo, PendingPreKey, SerializedSessionEntry } from './session-record.js';
package/lib/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export * as crypto from './crypto.js';
2
+ export * as curve from './curve.js';
3
+ export * as keyhelper from './keyhelper.js';
4
+ export { ProtocolAddress } from './protocol-address.js';
5
+ export { SessionBuilder } from './session-builder.js';
6
+ export { assertValidDeviceKeyBundle } from './prekey-bundle-validator.js';
7
+ export { SessionCipher } from './session-cipher.js';
8
+ export { SessionRecord, SessionEntry } from './session-record.js';
9
+ export { FingerprintGenerator } from './numeric-fingerprint.js';
10
+ export { BaseKeyType } from './base-key-type.js';
11
+ export { ChainType } from './chain-type.js';
12
+ export { Direction } from './direction.js';
13
+ export { MemorySignalStorage } from './memory-storage.js';
14
+ export { setLogger, getLogger } from './logger.js';
15
+ export { wipeBuffer, wipeBuffers } from './util.js';
16
+ export * from './errors.js';
@@ -0,0 +1,18 @@
1
+ import * as curve from './curve.js';
2
+ import type { KeyPair } from './curve.js';
3
+ export interface SignedPreKey {
4
+ keyId: number;
5
+ keyPair: KeyPair;
6
+ signature: Buffer;
7
+ createdAt: number;
8
+ }
9
+ export interface PreKey {
10
+ keyId: number;
11
+ keyPair: KeyPair;
12
+ }
13
+ export declare const DEFAULT_SIGNED_PRE_KEY_MAX_AGE_MS: number;
14
+ export declare function shouldRotateSignedPreKey(createdAt: number, maxAgeMs?: number): boolean;
15
+ export declare const generateIdentityKeyPair: typeof curve.generateKeyPair;
16
+ export declare function generateRegistrationId(): number;
17
+ export declare function generateSignedPreKey(identityKeyPair: KeyPair, signedKeyId: number): SignedPreKey;
18
+ export declare function generatePreKey(keyId: number): PreKey;
@@ -0,0 +1,52 @@
1
+ import * as nodeCrypto from 'crypto';
2
+ import * as curve from './curve.js';
3
+ function isNonNegativeInteger(n) {
4
+ return typeof n === 'number' && n % 1 === 0 && n >= 0;
5
+ }
6
+ export const DEFAULT_SIGNED_PRE_KEY_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
7
+ export function shouldRotateSignedPreKey(createdAt, maxAgeMs = DEFAULT_SIGNED_PRE_KEY_MAX_AGE_MS) {
8
+ if (!isNonNegativeInteger(createdAt)) {
9
+ throw new TypeError('Invalid argument for createdAt: ' + createdAt);
10
+ }
11
+ if (!isNonNegativeInteger(maxAgeMs)) {
12
+ throw new TypeError('Invalid argument for maxAgeMs: ' + maxAgeMs);
13
+ }
14
+ return Date.now() - createdAt >= maxAgeMs;
15
+ }
16
+ export const generateIdentityKeyPair = curve.generateKeyPair;
17
+ export function generateRegistrationId() {
18
+ let registrationId;
19
+ do {
20
+ registrationId = nodeCrypto.randomBytes(2).readUInt16BE(0) & 0x3fff;
21
+ } while (registrationId === 0);
22
+ return registrationId;
23
+ }
24
+ export function generateSignedPreKey(identityKeyPair, signedKeyId) {
25
+ if (!(identityKeyPair.privKey instanceof Buffer) ||
26
+ identityKeyPair.privKey.byteLength !== 32 ||
27
+ !(identityKeyPair.pubKey instanceof Buffer) ||
28
+ identityKeyPair.pubKey.byteLength !== 33) {
29
+ throw new TypeError('Invalid argument for identityKeyPair');
30
+ }
31
+ if (!isNonNegativeInteger(signedKeyId)) {
32
+ throw new TypeError('Invalid argument for signedKeyId: ' + signedKeyId);
33
+ }
34
+ const keyPair = curve.generateKeyPair();
35
+ const sig = curve.calculateSignature(identityKeyPair.privKey, keyPair.pubKey);
36
+ return {
37
+ keyId: signedKeyId,
38
+ keyPair,
39
+ signature: sig,
40
+ createdAt: Date.now()
41
+ };
42
+ }
43
+ export function generatePreKey(keyId) {
44
+ if (!isNonNegativeInteger(keyId)) {
45
+ throw new TypeError('Invalid argument for keyId: ' + keyId);
46
+ }
47
+ const keyPair = curve.generateKeyPair();
48
+ return {
49
+ keyId,
50
+ keyPair
51
+ };
52
+ }
@@ -0,0 +1,6 @@
1
+ export interface SignalLogger {
2
+ warn(message: string, meta?: unknown): void;
3
+ debug(message: string, meta?: unknown): void;
4
+ }
5
+ export declare function setLogger(logger: SignalLogger | null): void;
6
+ export declare function getLogger(): SignalLogger;
package/lib/logger.js ADDED
@@ -0,0 +1,11 @@
1
+ const noopLogger = {
2
+ warn() { },
3
+ debug() { }
4
+ };
5
+ let currentLogger = noopLogger;
6
+ export function setLogger(logger) {
7
+ currentLogger = logger || noopLogger;
8
+ }
9
+ export function getLogger() {
10
+ return currentLogger;
11
+ }
@@ -0,0 +1,25 @@
1
+ import type { KeyPair } from './curve.js';
2
+ import type { Direction } from './direction.js';
3
+ import { SessionRecord } from './session-record.js';
4
+ import type { SignalStorage } from './types.js';
5
+ export declare class MemorySignalStorage implements SignalStorage {
6
+ private identityKeyPair;
7
+ private registrationId;
8
+ private sessions;
9
+ private preKeys;
10
+ private signedPreKeys;
11
+ private trustedIdentities;
12
+ constructor(identityKeyPair: KeyPair, registrationId: number);
13
+ loadSession(id: string): Promise<SessionRecord | undefined>;
14
+ storeSession(id: string, session: SessionRecord): Promise<void>;
15
+ isTrustedIdentity(identifier: string, identityKey: Buffer, _direction: Direction): Promise<boolean>;
16
+ saveIdentity(identifier: string, identityKey: Buffer): Promise<boolean>;
17
+ removeIdentity(identifier: string): Promise<void>;
18
+ loadPreKey(id?: number | string): Promise<KeyPair | undefined>;
19
+ removePreKey(id: number): Promise<void>;
20
+ loadSignedPreKey(id?: number | string): Promise<KeyPair | undefined>;
21
+ getOurRegistrationId(): Promise<number>;
22
+ getOurIdentity(): Promise<KeyPair>;
23
+ storePreKey(id: number, keyPair: KeyPair): void;
24
+ storeSignedPreKey(id: number, keyPair: KeyPair): void;
25
+ }
@@ -0,0 +1,68 @@
1
+ import { timingSafeEqual } from 'crypto';
2
+ import { SessionRecord } from './session-record.js';
3
+ function safeEqual(a, b) {
4
+ if (a.byteLength !== b.byteLength) {
5
+ return false;
6
+ }
7
+ return timingSafeEqual(a, b);
8
+ }
9
+ export class MemorySignalStorage {
10
+ constructor(identityKeyPair, registrationId) {
11
+ this.sessions = new Map();
12
+ this.preKeys = new Map();
13
+ this.signedPreKeys = new Map();
14
+ this.trustedIdentities = new Map();
15
+ this.identityKeyPair = identityKeyPair;
16
+ this.registrationId = registrationId;
17
+ }
18
+ async loadSession(id) {
19
+ return this.sessions.get(id);
20
+ }
21
+ async storeSession(id, session) {
22
+ this.sessions.set(id, session);
23
+ }
24
+ async isTrustedIdentity(identifier, identityKey, _direction) {
25
+ const existing = this.trustedIdentities.get(identifier);
26
+ if (!existing) {
27
+ this.trustedIdentities.set(identifier, identityKey);
28
+ return true;
29
+ }
30
+ return safeEqual(existing, identityKey);
31
+ }
32
+ async saveIdentity(identifier, identityKey) {
33
+ const existing = this.trustedIdentities.get(identifier);
34
+ this.trustedIdentities.set(identifier, identityKey);
35
+ return !existing || !safeEqual(existing, identityKey);
36
+ }
37
+ async removeIdentity(identifier) {
38
+ this.trustedIdentities.delete(identifier);
39
+ this.sessions.delete(identifier);
40
+ }
41
+ async loadPreKey(id) {
42
+ if (id === undefined) {
43
+ return undefined;
44
+ }
45
+ return this.preKeys.get(Number(id));
46
+ }
47
+ async removePreKey(id) {
48
+ this.preKeys.delete(id);
49
+ }
50
+ async loadSignedPreKey(id) {
51
+ if (id === undefined) {
52
+ return undefined;
53
+ }
54
+ return this.signedPreKeys.get(Number(id));
55
+ }
56
+ async getOurRegistrationId() {
57
+ return this.registrationId;
58
+ }
59
+ async getOurIdentity() {
60
+ return this.identityKeyPair;
61
+ }
62
+ storePreKey(id, keyPair) {
63
+ this.preKeys.set(id, keyPair);
64
+ }
65
+ storeSignedPreKey(id, keyPair) {
66
+ this.signedPreKeys.set(id, keyPair);
67
+ }
68
+ }
@@ -0,0 +1,5 @@
1
+ export declare class FingerprintGenerator {
2
+ private iterations;
3
+ constructor(iterations: number);
4
+ createFor(localIdentifier: string, localIdentityKey: ArrayBuffer, remoteIdentifier: string, remoteIdentityKey: ArrayBuffer): Promise<string>;
5
+ }