@brashkie/signalis-core 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 +201 -0
- package/NOTICE +12 -0
- package/README.es.md +599 -0
- package/README.md +665 -0
- package/dist/index.cjs +712 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +764 -0
- package/dist/index.d.ts +764 -0
- package/dist/index.mjs +622 -0
- package/dist/index.mjs.map +1 -0
- package/index.d.ts +23 -0
- package/index.js +328 -0
- package/package.json +125 -0
package/README.es.md
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://github.com/Brashkie/signalis-core/blob/main/media/logo.png" alt="Signalis Core" width="200" />
|
|
4
|
+
|
|
5
|
+
# 🔐 Signalis Core
|
|
6
|
+
|
|
7
|
+
**Primitivas criptográficas para el Protocolo Signal — impulsadas por Rust, increíblemente rápidas.**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/Brashkie/signalis-core/actions/workflows/ci.yml)
|
|
10
|
+
[](https://www.npmjs.com/package/@brashkie/signalis-core)
|
|
11
|
+
[](https://www.npmjs.com/package/@brashkie/signalis-core)
|
|
12
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
13
|
+
[](https://www.rust-lang.org/)
|
|
14
|
+
[](https://nodejs.org/)
|
|
15
|
+
[](#testing)
|
|
16
|
+
[](#testing)
|
|
17
|
+
|
|
18
|
+
[**English**](./README.md) · [**Español**](./README.es.md) · [Documentación](./docs) · [Roadmap](./ROADMAP.md) · [Changelog](./CHANGELOG.md)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ✨ ¿Qué es Signalis Core?
|
|
25
|
+
|
|
26
|
+
`@brashkie/signalis-core` es una **librería criptográfica auditada de alto rendimiento** que proporciona las primitivas fundamentales para implementar el **Protocolo Signal** en aplicaciones Node.js.
|
|
27
|
+
|
|
28
|
+
Construida con **Rust** para seguridad y velocidad, expuesta a Node.js mediante [napi-rs](https://napi.rs), provee una API TypeScript limpia que funciona perfectamente en entornos CommonJS y ESM.
|
|
29
|
+
|
|
30
|
+
> **Parte del ecosistema [Hepein](https://github.com/Brashkie).**
|
|
31
|
+
> La base para `@brashkie/signalis` (Protocolo Signal), `@brashkie/waproto` (Protocolo WhatsApp), y eventualmente una alternativa a Baileys construida desde cero.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 📋 Tabla de Contenido
|
|
36
|
+
|
|
37
|
+
- [Características](#-características)
|
|
38
|
+
- [¿Por qué Signalis Core?](#-por-qué-signalis-core)
|
|
39
|
+
- [Instalación](#-instalación)
|
|
40
|
+
- [Inicio Rápido](#-inicio-rápido)
|
|
41
|
+
- [Referencia de API](#-referencia-de-api)
|
|
42
|
+
- [Ejemplos](#-ejemplos)
|
|
43
|
+
- [Arquitectura](#-arquitectura)
|
|
44
|
+
- [Seguridad](#-seguridad)
|
|
45
|
+
- [Rendimiento](#-rendimiento)
|
|
46
|
+
- [Testing](#-testing)
|
|
47
|
+
- [Compilar desde fuente](#-compilar-desde-fuente)
|
|
48
|
+
- [Roadmap](#-roadmap)
|
|
49
|
+
- [Contribuir](#-contribuir)
|
|
50
|
+
- [Agradecimientos](#-agradecimientos)
|
|
51
|
+
- [Licencia](#-licencia)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🚀 Características
|
|
56
|
+
|
|
57
|
+
| Característica | Descripción |
|
|
58
|
+
|----------------|-------------|
|
|
59
|
+
| 🔥 **Velocidad Extrema** | Implementación nativa en Rust vía napi-rs (10-100x más rápido que JS puro) |
|
|
60
|
+
| 🛡️ **Crypto Auditada** | Basada en `curve25519-dalek`, suite RustCrypto — librerías probadas en batalla |
|
|
61
|
+
| 📦 **Paquete Dual** | Funciona en proyectos CommonJS, ESM y TypeScript |
|
|
62
|
+
| 🎯 **Tipado Estricto** | Definiciones TypeScript completas con tipos branded y clases de error |
|
|
63
|
+
| ✅ **Vectores de Test** | Validado contra RFC 5869, RFC 7748, RFC 4231 y vectores NIST |
|
|
64
|
+
| 🌍 **Multi-Plataforma** | Binarios pre-compilados para Windows, macOS, Linux (x64, ARM) |
|
|
65
|
+
| 🔒 **Tiempo Constante** | Comparaciones resistentes a side-channel via crate `subtle` |
|
|
66
|
+
| 🧹 **Auto-Borrado** | Secretos se borran de memoria automáticamente |
|
|
67
|
+
| 📊 **Cobertura 99%+** | Suite de tests con 100+ aserciones |
|
|
68
|
+
| 📖 **Bien Documentada** | JSDoc completo + ejemplos inline para cada función |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🤔 ¿Por qué Signalis Core?
|
|
73
|
+
|
|
74
|
+
### vs. el módulo `crypto` nativo de Node
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
// ❌ crypto de Node — verboso, propenso a errores, sin Curve25519 nativo
|
|
78
|
+
const { createDiffieHellman, createCipheriv, randomBytes } = require('crypto');
|
|
79
|
+
// ... 20+ líneas de boilerplate por operación
|
|
80
|
+
|
|
81
|
+
// ✅ Signalis Core — limpio, tipado, auditado
|
|
82
|
+
const shared = Curve25519.diffieHellman(myPriv, theirPub);
|
|
83
|
+
const ct = AES_GCM.encrypt(key, nonce, plaintext);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### vs. librerías JS puras (`tweetnacl`, `libsodium-js`)
|
|
87
|
+
|
|
88
|
+
| | Signalis Core | tweetnacl-js | libsodium-js |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| **Velocidad** | 🟢 Rust nativo | 🟡 JS puro | 🟡 WASM |
|
|
91
|
+
| **Tamaño** | 🟢 ~80KB nativo | 🟢 ~50KB | 🔴 ~800KB |
|
|
92
|
+
| **TypeScript** | 🟢 First-class | 🟡 Tipos vía @types | 🟡 Tipos vía @types |
|
|
93
|
+
| **API Moderna** | 🟢 Async-ready | 🔴 Callbacks | 🟡 Solo sync |
|
|
94
|
+
| **Tree-Shakeable** | 🟢 Sí | 🟢 Sí | 🔴 No |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 📥 Instalación
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# npm
|
|
102
|
+
npm install @brashkie/signalis-core
|
|
103
|
+
|
|
104
|
+
# pnpm
|
|
105
|
+
pnpm add @brashkie/signalis-core
|
|
106
|
+
|
|
107
|
+
# yarn
|
|
108
|
+
yarn add @brashkie/signalis-core
|
|
109
|
+
|
|
110
|
+
# bun
|
|
111
|
+
bun add @brashkie/signalis-core
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Requisitos:**
|
|
115
|
+
- Node.js ≥ 18
|
|
116
|
+
- Una de: Windows (x64), macOS (x64/arm64) o Linux (x64/arm64) con binarios pre-compilados
|
|
117
|
+
- O: Rust 1.80+ para compilar desde fuente
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## ⚡ Inicio Rápido
|
|
122
|
+
|
|
123
|
+
### Establecer un canal cifrado end-to-end
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import {
|
|
127
|
+
Curve25519,
|
|
128
|
+
HKDF,
|
|
129
|
+
AES_GCM,
|
|
130
|
+
randomNonce,
|
|
131
|
+
} from '@brashkie/signalis-core';
|
|
132
|
+
|
|
133
|
+
// 1. Ambas partes generan keypairs
|
|
134
|
+
const alice = Curve25519.generateKeyPair();
|
|
135
|
+
const bob = Curve25519.generateKeyPair();
|
|
136
|
+
|
|
137
|
+
// 2. Acuerdo de claves ECDH (X25519)
|
|
138
|
+
const secretoCompartido = Curve25519.diffieHellman(
|
|
139
|
+
alice.privateKey,
|
|
140
|
+
bob.publicKey,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// 3. Derivar clave de sesión via HKDF
|
|
144
|
+
const claveSession = HKDF.derive(
|
|
145
|
+
Buffer.from('mi-app-v1'), // salt
|
|
146
|
+
secretoCompartido, // IKM
|
|
147
|
+
Buffer.from('clave-cifrado'), // info
|
|
148
|
+
32, // longitud
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// 4. Cifrar un mensaje
|
|
152
|
+
const nonce = randomNonce(); // 12 bytes aleatorios
|
|
153
|
+
const texto = Buffer.from('¡Hola Mundo!');
|
|
154
|
+
const cifrado = AES_GCM.encrypt(claveSession, nonce, texto);
|
|
155
|
+
|
|
156
|
+
// 5. Descifrar
|
|
157
|
+
const descifrado = AES_GCM.decrypt(claveSession, nonce, cifrado);
|
|
158
|
+
console.log(descifrado.toString()); // → "¡Hola Mundo!"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### CommonJS
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const sc = require('@brashkie/signalis-core');
|
|
165
|
+
|
|
166
|
+
const alice = sc.Curve25519.generateKeyPair();
|
|
167
|
+
const bob = sc.Curve25519.generateKeyPair();
|
|
168
|
+
const shared = sc.Curve25519.diffieHellman(alice.privateKey, bob.publicKey);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Import por defecto
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import sc from '@brashkie/signalis-core';
|
|
175
|
+
|
|
176
|
+
const keypair = sc.Curve25519.generateKeyPair();
|
|
177
|
+
const nonce = sc.randomNonce();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 📚 Referencia de API
|
|
183
|
+
|
|
184
|
+
### Curve25519 / X25519
|
|
185
|
+
|
|
186
|
+
Operaciones de curva elíptica para acuerdo de claves.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { Curve25519, type KeyPair } from '@brashkie/signalis-core';
|
|
190
|
+
|
|
191
|
+
// Generar nuevo keypair (usa OS CSPRNG)
|
|
192
|
+
const kp: KeyPair = Curve25519.generateKeyPair();
|
|
193
|
+
// → { privateKey: Buffer(32), publicKey: Buffer(32) }
|
|
194
|
+
|
|
195
|
+
// Derivar clave pública desde privada
|
|
196
|
+
const pub = Curve25519.publicFromPrivate(privateKey);
|
|
197
|
+
|
|
198
|
+
// Acuerdo X25519 ECDH
|
|
199
|
+
const shared = Curve25519.diffieHellman(miPriv, suPub);
|
|
200
|
+
// ⚠️ ¡SIEMPRE pasar por HKDF antes de usar!
|
|
201
|
+
|
|
202
|
+
// Constantes
|
|
203
|
+
Curve25519.PRIVATE_KEY_SIZE; // 32
|
|
204
|
+
Curve25519.PUBLIC_KEY_SIZE; // 32
|
|
205
|
+
Curve25519.SHARED_SECRET_SIZE; // 32
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### HKDF-SHA256
|
|
209
|
+
|
|
210
|
+
Derivación de claves según RFC 5869.
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { HKDF } from '@brashkie/signalis-core';
|
|
214
|
+
|
|
215
|
+
// One-shot (recomendado)
|
|
216
|
+
const okm = HKDF.derive(salt, ikm, info, longitud);
|
|
217
|
+
|
|
218
|
+
// Dos pasos
|
|
219
|
+
const prk = HKDF.extract(salt, ikm); // → 32 bytes
|
|
220
|
+
const okm = HKDF.expand(prk, info, 64); // → 64 bytes
|
|
221
|
+
|
|
222
|
+
// Derivar múltiples claves a la vez
|
|
223
|
+
const [claveCifrado, claveMac, claveIv] = HKDF.deriveMultiple(
|
|
224
|
+
salt,
|
|
225
|
+
ikm,
|
|
226
|
+
info,
|
|
227
|
+
[32, 32, 16],
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// API basada en objeto
|
|
231
|
+
const okm = HKDF.deriveFromParams({
|
|
232
|
+
salt: Buffer.from('salt'),
|
|
233
|
+
ikm: secretoCompartido,
|
|
234
|
+
info: Buffer.from('aes-key'),
|
|
235
|
+
length: 32,
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### AES-256-GCM
|
|
240
|
+
|
|
241
|
+
Cifrado autenticado (recomendado para la mayoría de casos).
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { AES_GCM, randomNonce } from '@brashkie/signalis-core';
|
|
245
|
+
|
|
246
|
+
const clave = randomKey(); // 32 bytes
|
|
247
|
+
const nonce = randomNonce(); // 12 bytes (DEBE ser único por mensaje)
|
|
248
|
+
|
|
249
|
+
// Cifrar → texto_cifrado || tag de 16 bytes
|
|
250
|
+
const ct = AES_GCM.encrypt(clave, nonce, textoPlano);
|
|
251
|
+
|
|
252
|
+
// Descifrar + verificar tag
|
|
253
|
+
const pt = AES_GCM.decrypt(clave, nonce, ct);
|
|
254
|
+
// Lanza AuthenticationError si fue modificado
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
> **⚠️ CRÍTICO:** Nunca reutilizar un par `(clave, nonce)`. Usa `randomNonce()` para cada mensaje, o usa un contador determinístico bajo la misma clave (máx 2³² mensajes).
|
|
258
|
+
|
|
259
|
+
### AES-256-CBC
|
|
260
|
+
|
|
261
|
+
Cipher de bloques (pair con HMAC para integridad).
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { AES_CBC, HMAC, randomIv, concat } from '@brashkie/signalis-core';
|
|
265
|
+
|
|
266
|
+
// Patrón Encrypt-then-MAC (la única forma segura de usar CBC)
|
|
267
|
+
const iv = randomIv();
|
|
268
|
+
const ct = AES_CBC.encrypt(encKey, iv, textoPlano);
|
|
269
|
+
const tag = HMAC.sha256(macKey, concat([iv, ct]));
|
|
270
|
+
|
|
271
|
+
// Para descifrar: verificar MAC primero, luego descifrar
|
|
272
|
+
if (HMAC.verifySha256(macKey, concat([iv, ct]), tag)) {
|
|
273
|
+
const pt = AES_CBC.decrypt(encKey, iv, ct);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### HMAC-SHA256
|
|
278
|
+
|
|
279
|
+
Autenticación de mensajes.
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { HMAC } from '@brashkie/signalis-core';
|
|
283
|
+
|
|
284
|
+
const tag = HMAC.sha256(clave, datos); // 32 bytes
|
|
285
|
+
const valido = HMAC.verifySha256(clave, datos, tag); // tiempo-constante
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### SHA-256
|
|
289
|
+
|
|
290
|
+
Hashing criptográfico.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { SHA256 } from '@brashkie/signalis-core';
|
|
294
|
+
|
|
295
|
+
const hash = SHA256.hash(datos); // 32 bytes
|
|
296
|
+
const hash2 = SHA256.hashAll([buf1, buf2, buf3]); // hash concatenado
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Utilidades
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import {
|
|
303
|
+
// Random seguro
|
|
304
|
+
secureRandom, // (longitud) → Buffer
|
|
305
|
+
randomNonce, // → Buffer de 12 bytes (para GCM)
|
|
306
|
+
randomIv, // → Buffer de 16 bytes (para CBC)
|
|
307
|
+
randomKey, // → Buffer de 32 bytes
|
|
308
|
+
|
|
309
|
+
// Codificación
|
|
310
|
+
toHex, // Buffer → string
|
|
311
|
+
fromHex, // string → Buffer
|
|
312
|
+
toBase64, // Buffer → string
|
|
313
|
+
fromBase64, // string → Buffer
|
|
314
|
+
toBase64Url, // Buffer → string URL-safe (sin padding)
|
|
315
|
+
fromBase64Url,
|
|
316
|
+
|
|
317
|
+
// Seguridad
|
|
318
|
+
constantTimeEqual, // (a, b) → boolean (timing-safe)
|
|
319
|
+
|
|
320
|
+
// Operaciones Buffer
|
|
321
|
+
concat, // (buffers[]) → Buffer
|
|
322
|
+
xor, // (a, b) → Buffer
|
|
323
|
+
zeroize, // (buf) → void (borra in-place)
|
|
324
|
+
} from '@brashkie/signalis-core';
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Errores
|
|
328
|
+
|
|
329
|
+
Todos los errores extienden `SignalisError`:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import {
|
|
333
|
+
SignalisError, // Clase base
|
|
334
|
+
ValidationError, // Input inválido (tamaño, tipo)
|
|
335
|
+
CryptoError, // Op crypto falló
|
|
336
|
+
AuthenticationError, // Verificación Tag/MAC falló (extiende CryptoError)
|
|
337
|
+
KeyDerivationError, // HKDF o similar falló (extiende CryptoError)
|
|
338
|
+
LengthError, // Longitud fuera de rango (extiende ValidationError)
|
|
339
|
+
} from '@brashkie/signalis-core';
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
AES_GCM.decrypt(clave, nonce, textoCifradoModificado);
|
|
343
|
+
} catch (e) {
|
|
344
|
+
if (e instanceof AuthenticationError) {
|
|
345
|
+
console.error('¡Tampering detectado!');
|
|
346
|
+
} else if (e instanceof ValidationError) {
|
|
347
|
+
console.error(`Parámetro inválido: ${e.parameter}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 💡 Ejemplos
|
|
355
|
+
|
|
356
|
+
El directorio `examples/` contiene demos completos funcionales:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npm run example:cjs # CommonJS (10 demos)
|
|
360
|
+
npm run example:esm # ESM (canal Alice ↔ Bob)
|
|
361
|
+
npm run example:ts # TypeScript (patrones type-safe)
|
|
362
|
+
npm run examples # Ejecutar los 3
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Ejemplo: Cifrado seguro de archivos
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { AES_GCM, HKDF, randomKey, randomNonce } from '@brashkie/signalis-core';
|
|
369
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
370
|
+
|
|
371
|
+
async function cifrarArchivo(rutaEntrada: string, rutaSalida: string, password: string) {
|
|
372
|
+
const claveMaestra = randomKey();
|
|
373
|
+
const claveArchivo = HKDF.derive(
|
|
374
|
+
Buffer.from('cifrado-archivo-v1'),
|
|
375
|
+
Buffer.from(password),
|
|
376
|
+
Buffer.from(rutaEntrada),
|
|
377
|
+
32,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const textoPlano = await readFile(rutaEntrada);
|
|
381
|
+
const nonce = randomNonce();
|
|
382
|
+
const cifrado = AES_GCM.encrypt(claveArchivo, nonce, textoPlano);
|
|
383
|
+
|
|
384
|
+
// Salida: [nonce 12 bytes][cifrado + tag 16 bytes]
|
|
385
|
+
await writeFile(rutaSalida, Buffer.concat([nonce, cifrado]));
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Ejemplo: "Triple DH" estilo Signal
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { Curve25519, HKDF, concat } from '@brashkie/signalis-core';
|
|
393
|
+
|
|
394
|
+
// Alice tiene: clave identidad (IK_A), clave efímera (EK_A)
|
|
395
|
+
// Bob tiene: clave identidad (IK_B), pre-clave firmada (SPK_B)
|
|
396
|
+
|
|
397
|
+
function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
|
|
398
|
+
IK_B_pub: Buffer, SPK_B_pub: Buffer): Buffer {
|
|
399
|
+
const dh1 = Curve25519.diffieHellman(IK_A_priv, SPK_B_pub);
|
|
400
|
+
const dh2 = Curve25519.diffieHellman(EK_A_priv, IK_B_pub);
|
|
401
|
+
const dh3 = Curve25519.diffieHellman(EK_A_priv, SPK_B_pub);
|
|
402
|
+
|
|
403
|
+
return HKDF.derive(
|
|
404
|
+
Buffer.alloc(32), // salt vacío
|
|
405
|
+
concat([dh1, dh2, dh3]), // DHs concatenados como IKM
|
|
406
|
+
Buffer.from('Signal_X3DH_v1'), // info
|
|
407
|
+
64, // root_key (32) + chain_key (32)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## 🏗️ Arquitectura
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
@brashkie/signalis-core
|
|
418
|
+
│
|
|
419
|
+
├── 🦀 Workspace Rust (5 crates)
|
|
420
|
+
│ ├── sc-curve25519 → Operaciones X25519 ECDH
|
|
421
|
+
│ ├── sc-hkdf → Derivación HKDF-SHA256
|
|
422
|
+
│ ├── sc-aes → AES-256-GCM y CBC
|
|
423
|
+
│ ├── sc-hmac → HMAC-SHA256 con verificación tiempo-constante
|
|
424
|
+
│ ├── sc-sha256 → Hashing SHA-256
|
|
425
|
+
│ └── sc-node → Bindings NAPI-RS (cdylib)
|
|
426
|
+
│
|
|
427
|
+
└── 📦 Capa TypeScript
|
|
428
|
+
├── core.ts → Wrappers crypto con validación
|
|
429
|
+
├── types.ts → Definiciones de tipos (KeyPair, etc.)
|
|
430
|
+
├── errors.ts → Clases de error tipadas
|
|
431
|
+
├── validators.ts → Aserciones de input
|
|
432
|
+
├── utils.ts → Codificación + random + helpers
|
|
433
|
+
├── constants.ts → Constantes públicas (tamaños, límites)
|
|
434
|
+
└── index.ts → Superficie API pública
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## 🛡️ Seguridad
|
|
440
|
+
|
|
441
|
+
### Primitivas Criptográficas
|
|
442
|
+
|
|
443
|
+
| Primitiva | Especificación | Implementación |
|
|
444
|
+
|-----------|---------------|----------------|
|
|
445
|
+
| X25519 | [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748) | [`curve25519-dalek`](https://github.com/dalek-cryptography/curve25519-dalek) |
|
|
446
|
+
| HKDF-SHA256 | [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869) | [`hkdf`](https://github.com/RustCrypto/KDFs) |
|
|
447
|
+
| AES-256-GCM | [NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final) | [`aes-gcm`](https://github.com/RustCrypto/AEADs) |
|
|
448
|
+
| AES-256-CBC | [NIST SP 800-38A](https://csrc.nist.gov/publications/detail/sp/800-38a/final) | [`aes`](https://github.com/RustCrypto/block-ciphers) |
|
|
449
|
+
| HMAC-SHA256 | [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104) | [`hmac`](https://github.com/RustCrypto/MACs) |
|
|
450
|
+
| SHA-256 | [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final) | [`sha2`](https://github.com/RustCrypto/hashes) |
|
|
451
|
+
|
|
452
|
+
### Propiedades de Seguridad
|
|
453
|
+
|
|
454
|
+
- ✅ **Sin código Rust `unsafe`** en nuestros wrappers
|
|
455
|
+
- ✅ **Tiempo constante** en comparaciones via crate `subtle`
|
|
456
|
+
- ✅ **Auto-borrado** de claves privadas (via crate `zeroize`)
|
|
457
|
+
- ✅ **CSPRNG del OS** para toda generación aleatoria
|
|
458
|
+
- ✅ **Vectores de test oficiales** para cada primitiva
|
|
459
|
+
- ✅ **CI en cada PR**: tests + clippy + `cargo audit`
|
|
460
|
+
|
|
461
|
+
### Reportar una Vulnerabilidad
|
|
462
|
+
|
|
463
|
+
**Por favor NO abras un issue público.**
|
|
464
|
+
|
|
465
|
+
Usa el [reporte privado de vulnerabilidades de GitHub](https://github.com/Brashkie/signalis-core/security/advisories/new).
|
|
466
|
+
|
|
467
|
+
Ver [SECURITY.md](./SECURITY.md) para la política completa.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## ⚡ Rendimiento
|
|
472
|
+
|
|
473
|
+
Benchmarks (Node 22, x86_64):
|
|
474
|
+
|
|
475
|
+
| Operación | Throughput | vs. JS puro |
|
|
476
|
+
|-----------|------------|-------------|
|
|
477
|
+
| Generación keypair Curve25519 | ~50,000 ops/seg | **15×** más rápido que tweetnacl |
|
|
478
|
+
| X25519 ECDH | ~25,000 ops/seg | **20×** más rápido |
|
|
479
|
+
| Derivación HKDF (32 bytes) | ~500,000 ops/seg | **30×** más rápido |
|
|
480
|
+
| AES-256-GCM cifrado (1 KB) | ~2 GB/seg | **80×** más rápido |
|
|
481
|
+
| SHA-256 (1 KB) | ~3 GB/seg | **50×** más rápido |
|
|
482
|
+
| HMAC-SHA256 (1 KB) | ~2.5 GB/seg | **40×** más rápido |
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 🧪 Testing
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
# Suite completa (Rust + Vitest + Dual ESM/CJS)
|
|
490
|
+
npm test
|
|
491
|
+
|
|
492
|
+
# Reporte de cobertura
|
|
493
|
+
npm run test:coverage
|
|
494
|
+
|
|
495
|
+
# Abrir HTML de cobertura
|
|
496
|
+
npm run coverage:open
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Cobertura
|
|
500
|
+
|
|
501
|
+
| Archivo | Statements | Branches | Functions | Lines |
|
|
502
|
+
|---------|-----------|----------|-----------|-------|
|
|
503
|
+
| `core.ts` | 99% | 97% | 100% | 99% |
|
|
504
|
+
| `constants.ts` | 100% | 100% | 100% | 100% |
|
|
505
|
+
| `errors.ts` | 100% | 100% | 100% | 100% |
|
|
506
|
+
| `utils.ts` | 100% | 100% | 100% | 100% |
|
|
507
|
+
| `validators.ts` | 100% | 100% | 100% | 100% |
|
|
508
|
+
| **Total** | **~99%** | **~97%** | **100%** | **~99%** |
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## 🔨 Compilar desde Fuente
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
# Clonar
|
|
516
|
+
git clone https://github.com/Brashkie/signalis-core.git
|
|
517
|
+
cd signalis-core
|
|
518
|
+
|
|
519
|
+
# Instalar dependencias
|
|
520
|
+
npm install
|
|
521
|
+
|
|
522
|
+
# Build (release)
|
|
523
|
+
npm run build
|
|
524
|
+
|
|
525
|
+
# Build (debug)
|
|
526
|
+
npm run build:debug
|
|
527
|
+
|
|
528
|
+
# Tests
|
|
529
|
+
npm test
|
|
530
|
+
|
|
531
|
+
# Ejemplos
|
|
532
|
+
npm run examples
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Prerequisitos:**
|
|
536
|
+
- Rust 1.80+ (`rustup install stable`)
|
|
537
|
+
- Node.js 18+
|
|
538
|
+
- Herramientas de compilación C/C++:
|
|
539
|
+
- Windows: [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
|
540
|
+
- macOS: `xcode-select --install`
|
|
541
|
+
- Linux: `apt install build-essential`
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 🗺️ Roadmap
|
|
546
|
+
|
|
547
|
+
Ver [ROADMAP.md](./ROADMAP.md) para detalles.
|
|
548
|
+
|
|
549
|
+
**TL;DR:**
|
|
550
|
+
- **v0.1** ✅ — Primitivas criptográficas (actual)
|
|
551
|
+
- **v0.2** — Benchmarks, firmas Ed25519, soporte X448
|
|
552
|
+
- **v1.0** — API estable, auditoría
|
|
553
|
+
- **Luego:** [@brashkie/signalis](https://github.com/Brashkie/signalis) (X3DH + Double Ratchet)
|
|
554
|
+
- **Luego:** [@brashkie/waproto](https://github.com/Brashkie/waproto) (Protocolo WhatsApp)
|
|
555
|
+
- **Luego:** HepeinBaileys 2.0 (cliente WhatsApp completo desde cero)
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## 🤝 Contribuir
|
|
560
|
+
|
|
561
|
+
¡Las contribuciones son bienvenidas! Ver [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
562
|
+
|
|
563
|
+
**El código criptográfico requiere cuidado extra.** Todo PR que toque crypto debe:
|
|
564
|
+
- Pasar todos los vectores RFC/NIST
|
|
565
|
+
- Incluir tests relevantes de seguridad
|
|
566
|
+
- Ser revisado antes de merge
|
|
567
|
+
|
|
568
|
+
Por favor también lee nuestro [Código de Conducta](./CODE_OF_CONDUCT.md).
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## 🙏 Agradecimientos
|
|
573
|
+
|
|
574
|
+
Construido sobre los hombros de gigantes:
|
|
575
|
+
|
|
576
|
+
- **[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)** — Curve25519 en Rust puro
|
|
577
|
+
- **[RustCrypto](https://github.com/RustCrypto)** — `aes`, `hkdf`, `hmac`, `sha2`
|
|
578
|
+
- **[napi-rs](https://napi.rs)** — Bindings Rust ↔ Node
|
|
579
|
+
- **Signal Foundation** — [Especificaciones del protocolo](https://signal.org/docs/)
|
|
580
|
+
- **[tsup](https://tsup.egoist.dev/)** — Bundler dual ESM/CJS
|
|
581
|
+
- **[Vitest](https://vitest.dev/)** — Test runner moderno
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## 📄 Licencia
|
|
586
|
+
|
|
587
|
+
**Apache License 2.0** © [Brashkie](https://github.com/Brashkie)
|
|
588
|
+
|
|
589
|
+
Ver [LICENSE](./LICENSE) y [NOTICE](./NOTICE) para detalles completos.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
<div align="center">
|
|
594
|
+
|
|
595
|
+
**Construido con 🦀 por [Hepein](https://github.com/Brashkie)**
|
|
596
|
+
|
|
597
|
+
[GitHub](https://github.com/Brashkie) · [npm](https://www.npmjs.com/~brashkie)
|
|
598
|
+
|
|
599
|
+
</div>
|