@brashkie/signalis-core 0.1.0 → 0.3.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/README.es.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- <img src="https://github.com/Brashkie/signalis-core/blob/main/media/logo.png" alt="Signalis Core" width="200" />
3
+ <img src="media/logo.png" alt="Signalis Core" width="200" />
4
4
 
5
5
  # 🔐 Signalis Core
6
6
 
@@ -13,7 +13,7 @@
13
13
  [![Rust](https://img.shields.io/badge/rust-1.80%2B-orange.svg)](https://www.rust-lang.org/)
14
14
  [![Node](https://img.shields.io/badge/node-%E2%89%A518-339933.svg)](https://nodejs.org/)
15
15
  [![Cobertura](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](#testing)
16
- [![Tests](https://img.shields.io/badge/tests-100%2B%20pasando-success.svg)](#testing)
16
+ [![Tests](https://img.shields.io/badge/tests-269%2B%20pasando-success.svg)](#testing)
17
17
 
18
18
  [**English**](./README.md) · [**Español**](./README.es.md) · [Documentación](./docs) · [Roadmap](./ROADMAP.md) · [Changelog](./CHANGELOG.md)
19
19
 
@@ -32,9 +32,94 @@ Construida con **Rust** para seguridad y velocidad, expuesta a Node.js mediante
32
32
 
33
33
  ---
34
34
 
35
+ ## 🎉 Novedades en v0.3.0
36
+
37
+ **v0.3.0 trae soporte Android + ChaCha20-Poly1305 — totalmente retrocompatible con v0.2.0.**
38
+
39
+ | Nuevo | Descripción |
40
+ |-------|-------------|
41
+ | 🆕 **Android arm64-v8a** | Binario nativo para celulares Android modernos (React Native, Termux) |
42
+ | 🆕 **Android armv7** | Binario nativo para dispositivos Android más viejos (Android 4.4+) |
43
+ | 🆕 **ChaCha20-Poly1305** | AEAD según RFC 8439 — 2-3× más rápido que AES-GCM en ARM sin AES-NI |
44
+ | 🆕 **`constantTimeEq()`** | Comparación de Buffer en tiempo constante (para verificar MAC/firmas) |
45
+ | 🆕 **`nativeSecureRandom()`** | CSPRNG basado en OS vía Rust (alternativa a `secureRandom` JS-side) |
46
+ | 🆕 **Crate `sc-utils`** | Helpers utilitarios públicos (random, constant_time_eq, secure_zeroize) |
47
+ | 🆕 **CI: gate cargo-audit** | Dependencias transitivas vulnerables ahora bloquean PRs automáticamente |
48
+
49
+ ### Ejemplo rápido: ChaCha20-Poly1305
50
+
51
+ ```ts
52
+ import { ChaCha20Poly1305, secureRandom } from '@brashkie/signalis-core';
53
+
54
+ const key = secureRandom(32);
55
+ const nonce = secureRandom(12);
56
+
57
+ const ct = ChaCha20Poly1305.encrypt(key, nonce, Buffer.from('¡Hola!'));
58
+ const pt = ChaCha20Poly1305.decrypt(key, nonce, ct); // → "¡Hola!"
59
+
60
+ // Con Datos Autenticados Adicionales
61
+ const aad = Buffer.from('metadata del header');
62
+ const ct2 = ChaCha20Poly1305.encryptWithAad(key, nonce, plaintext, aad);
63
+ const pt2 = ChaCha20Poly1305.decryptWithAad(key, nonce, ct2, aad);
64
+ ```
65
+
66
+ ### ¿Por qué usar ChaCha20-Poly1305 en lugar de AES-GCM?
67
+
68
+ Usá **AES-GCM** cuando corras en:
69
+ - Servidores x86_64 (Intel/AMD con hardware AES-NI)
70
+ - Desktops modernos
71
+ - CPUs ARM nuevos con Extensiones Cripto ARMv8
72
+
73
+ Usá **ChaCha20-Poly1305** cuando corras en:
74
+ - Celulares Android (la mayoría no tiene AES-NI)
75
+ - IoT / dispositivos embebidos
76
+ - Cualquier cosa sin hardware AES acelerado
77
+
78
+ **Ambos tienen garantías de seguridad idénticas.** La diferencia es puramente de rendimiento, dependiendo de si tu target tiene hardware AES.
79
+
80
+ Ver [MIGRATION.md](./MIGRATION.md) para detalles de upgrade (es un drop-in replacement).
81
+
82
+ ---
83
+
84
+ ## 🌍 Plataformas Soportadas
85
+
86
+ `@brashkie/signalis-core` envía binarios nativos pre-compilados para **9 plataformas** vía `optionalDependencies` de npm. El binario correcto se descarga automáticamente según tu sistema operativo + arquitectura.
87
+
88
+ | SO | Arquitectura | Sub-paquete | Estado |
89
+ |----|--------------|-------------|--------|
90
+ | 🐧 Linux | x64 (glibc) | `signalis-core-linux-x64-gnu` | ✅ |
91
+ | 🐧 Linux | x64 (musl) | `signalis-core-linux-x64-musl` | ✅ |
92
+ | 🐧 Linux | arm64 (glibc) | `signalis-core-linux-arm64-gnu` | ✅ |
93
+ | 🍎 macOS | x64 (Intel) | `signalis-core-darwin-x64` | ✅ |
94
+ | 🍎 macOS | arm64 (Apple Silicon) | `signalis-core-darwin-arm64` | ✅ |
95
+ | 🪟 Windows | x64 | `signalis-core-win32-x64-msvc` | ✅ |
96
+ | 🪟 Windows | arm64 | `signalis-core-win32-arm64-msvc` | ✅ |
97
+ | 🤖 Android | arm64-v8a | `signalis-core-android-arm64` | 🆕 v0.3.0 |
98
+ | 🤖 Android | armv7 | `signalis-core-android-arm-eabi` | 🆕 v0.3.0 |
99
+
100
+ Próximas plataformas en v0.4.0: **iOS arm64**, **WASM (navegadores)**, **FreeBSD x64**.
101
+
102
+ ### Instalación en Android
103
+
104
+ El mismo `npm install` funciona en cualquier entorno Node.js corriendo en Android, incluyendo:
105
+ - **Termux** en celulares Android (`pkg install nodejs`)
106
+ - **React Native** con target Android
107
+ - Apps con **NodeJS-Mobile**
108
+ - Builds personalizados de Node embebido para IoT
109
+
110
+ ```bash
111
+ # En Android (Termux por ejemplo):
112
+ pkg install nodejs
113
+ npm install @brashkie/signalis-core
114
+ # → npm descarga automáticamente signalis-core-android-arm64
115
+ ```
116
+
117
+ ---
118
+
35
119
  ## 📋 Tabla de Contenido
36
120
 
37
121
  - [Características](#-características)
122
+ - [Plataformas Soportadas](#-plataformas-soportadas)
38
123
  - [¿Por qué Signalis Core?](#-por-qué-signalis-core)
39
124
  - [Instalación](#-instalación)
40
125
  - [Inicio Rápido](#-inicio-rápido)
@@ -57,14 +142,16 @@ Construida con **Rust** para seguridad y velocidad, expuesta a Node.js mediante
57
142
  | Característica | Descripción |
58
143
  |----------------|-------------|
59
144
  | 🔥 **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 |
145
+ | 🛡️ **Crypto Auditada** | Basada en `curve25519-dalek`, `ed25519-dalek`, suite RustCrypto — librerías probadas en batalla |
146
+ | ✍️ **Firmas Digitales** | Ed25519 (RFC 8032) y XEd25519 (estilo Signal) — **NUEVO v0.2.0** |
147
+ | 🔐 **AEAD con AAD** | AES-256-GCM con Datos Autenticados Adicionales — **NUEVO v0.2.0** |
61
148
  | 📦 **Paquete Dual** | Funciona en proyectos CommonJS, ESM y TypeScript |
62
149
  | 🎯 **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) |
150
+ | ✅ **Vectores de Test** | Validado contra RFC 5869, RFC 7748, RFC 8032, RFC 4231 y vectores NIST |
151
+ | 🌍 **Multi-Plataforma** | Binarios pre-compilados para Windows, macOS, Linux (x64, ARM) + **Android arm64/armv7** |
65
152
  | 🔒 **Tiempo Constante** | Comparaciones resistentes a side-channel via crate `subtle` |
66
153
  | 🧹 **Auto-Borrado** | Secretos se borran de memoria automáticamente |
67
- | 📊 **Cobertura 99%+** | Suite de tests con 100+ aserciones |
154
+ | 📊 **Cobertura 99%+** | Suite de tests con 269+ aserciones |
68
155
  | 📖 **Bien Documentada** | JSDoc completo + ejemplos inline para cada función |
69
156
 
70
157
  ---
@@ -205,6 +292,86 @@ Curve25519.PUBLIC_KEY_SIZE; // 32
205
292
  Curve25519.SHARED_SECRET_SIZE; // 32
206
293
  ```
207
294
 
295
+ ### Ed25519
296
+
297
+ **NUEVO en v0.2.0.** Firmas digitales Ed25519 estándar (RFC 8032). Determinístico — la misma entrada siempre produce la misma firma.
298
+
299
+ ```typescript
300
+ import { Ed25519, type KeyPair, type Signature } from '@brashkie/signalis-core';
301
+
302
+ // Generar nuevo keypair para firmar
303
+ const keys: KeyPair = Ed25519.generateKeyPair();
304
+ // → { privateKey: Buffer(32), publicKey: Buffer(32) }
305
+
306
+ // Determinístico desde una semilla de 32 bytes
307
+ const fromSeed = Ed25519.keyPairFromSeed(seed);
308
+
309
+ // Derivar pública desde privada
310
+ const pub = Ed25519.publicFromPrivate(privateKey);
311
+
312
+ // Firmar un mensaje → firma de 64 bytes
313
+ const sig: Signature = Ed25519.sign(privateKey, message);
314
+
315
+ // Verificar (lanza SignatureError si falla)
316
+ Ed25519.verify(publicKey, message, sig);
317
+
318
+ // Verificar (retorna boolean, no lanza)
319
+ const ok = Ed25519.verifyBool(publicKey, message, sig);
320
+
321
+ // Constantes
322
+ Ed25519.PRIVATE_KEY_SIZE; // 32
323
+ Ed25519.PUBLIC_KEY_SIZE; // 32
324
+ Ed25519.SIGNATURE_SIZE; // 64
325
+ Ed25519.SEED_SIZE; // 32
326
+ ```
327
+
328
+ ### XEd25519
329
+
330
+ **NUEVO en v0.2.0.** Firmar con el **MISMO** keypair Curve25519 que se usa para ECDH. Esto es lo que el Protocolo Signal usa para las llaves de identidad.
331
+
332
+ ```typescript
333
+ import { Curve25519, XEd25519 } from '@brashkie/signalis-core';
334
+
335
+ // UN solo keypair para ECDH y firmar
336
+ const identidad = Curve25519.generateKeyPair();
337
+
338
+ // Para ECDH:
339
+ const shared = Curve25519.diffieHellman(identidad.privateKey, peerPublic);
340
+
341
+ // La MISMA llave para firmar:
342
+ const sig = XEd25519.sign(identidad.privateKey, message);
343
+
344
+ // Verificar con la MISMA llave pública Curve25519
345
+ XEd25519.verify(identidad.publicKey, message, sig);
346
+
347
+ // Las firmas XEd25519 NO son determinísticas (usan OS RNG)
348
+ const sig1 = XEd25519.sign(identidad.privateKey, message);
349
+ const sig2 = XEd25519.sign(identidad.privateKey, message);
350
+ // sig1.equals(sig2) → false (intencionalmente probabilístico)
351
+
352
+ // Para firmas determinísticas (tests), pasa el random de 64 bytes:
353
+ const random = secureRandom(64);
354
+ const detSig = XEd25519.signWithRandom(identidad.privateKey, message, random);
355
+
356
+ // Verificar (boolean, no lanza)
357
+ const ok = XEd25519.verifyBool(identidad.publicKey, message, sig);
358
+
359
+ // Constantes
360
+ XEd25519.PRIVATE_KEY_SIZE; // 32 (igual que Curve25519)
361
+ XEd25519.PUBLIC_KEY_SIZE; // 32 (igual que Curve25519)
362
+ XEd25519.SIGNATURE_SIZE; // 64
363
+ XEd25519.RANDOM_SIZE; // 64
364
+ ```
365
+
366
+ **¿Cuándo usar Ed25519 vs XEd25519?**
367
+
368
+ | Necesitas | Usa |
369
+ |-----------|-----|
370
+ | Ed25519 estándar, determinístico, compatible con RFC 8032 | **Ed25519** |
371
+ | Una sola llave de identidad para ECDH + firmar (estilo Signal) | **XEd25519** |
372
+ | Firmas reproducibles desde semilla | **Ed25519** |
373
+ | Compatibilidad con semántica del Protocolo Signal | **XEd25519** |
374
+
208
375
  ### HKDF-SHA256
209
376
 
210
377
  Derivación de claves según RFC 5869.
@@ -254,6 +421,26 @@ const pt = AES_GCM.decrypt(clave, nonce, ct);
254
421
  // Lanza AuthenticationError si fue modificado
255
422
  ```
256
423
 
424
+ **Con Datos Autenticados Adicionales (AAD) — NUEVO en v0.2.0:**
425
+
426
+ ```typescript
427
+ import { AES_GCM } from '@brashkie/signalis-core';
428
+
429
+ // AAD se autentica pero NO se cifra — útil para cabeceras/metadatos
430
+ const cabecera = Buffer.from('msg_id=42|emisor=alice');
431
+ const cuerpo = Buffer.from('contenido cifrado del cuerpo');
432
+
433
+ const ct = AES_GCM.encryptWithAad(clave, nonce, cuerpo, cabecera);
434
+
435
+ // Descifrar — DEBE pasar el mismo AAD, o AuthenticationError
436
+ const pt = AES_GCM.decryptWithAad(clave, nonce, ct, cabecera);
437
+
438
+ // Modificar la cabecera (AAD) → falla
439
+ const tampered = Buffer.from(cabecera);
440
+ tampered[0] ^= 0xff;
441
+ AES_GCM.decryptWithAad(clave, nonce, ct, tampered); // lanza AuthenticationError
442
+ ```
443
+
257
444
  > **⚠️ 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
445
 
259
446
  ### AES-256-CBC
@@ -274,6 +461,43 @@ if (HMAC.verifySha256(macKey, concat([iv, ct]), tag)) {
274
461
  }
275
462
  ```
276
463
 
464
+ ### ChaCha20-Poly1305 🆕
465
+
466
+ Cifrado autenticado según RFC 8439. Alternativa a AES-GCM, más rápido en ARM sin AES-NI.
467
+
468
+ ```typescript
469
+ import { ChaCha20Poly1305, secureRandom } from '@brashkie/signalis-core';
470
+
471
+ const clave = secureRandom(32); // ChaCha20Poly1305.KEY_SIZE
472
+ const nonce = secureRandom(12); // ChaCha20Poly1305.NONCE_SIZE
473
+
474
+ // Cifrado/descifrado básico (sin AAD)
475
+ const ct = ChaCha20Poly1305.encrypt(clave, nonce, textoplano);
476
+ const pt = ChaCha20Poly1305.decrypt(clave, nonce, ct);
477
+
478
+ // Con Datos Autenticados Adicionales
479
+ const aad = Buffer.from('header público');
480
+ const ct2 = ChaCha20Poly1305.encryptWithAad(clave, nonce, textoplano, aad);
481
+ const pt2 = ChaCha20Poly1305.decryptWithAad(clave, nonce, ct2, aad);
482
+
483
+ // Constantes
484
+ ChaCha20Poly1305.KEY_SIZE; // 32
485
+ ChaCha20Poly1305.NONCE_SIZE; // 12
486
+ ChaCha20Poly1305.TAG_SIZE; // 16 (anexado al ciphertext)
487
+ ```
488
+
489
+ **Rendimiento vs AES-GCM:**
490
+
491
+ | Target | Ganador | Por qué |
492
+ |--------|---------|---------|
493
+ | Server x86_64 (AES-NI) | AES-GCM | Aceleración por hardware |
494
+ | Desktop moderno (AES-NI) | AES-GCM | Aceleración por hardware |
495
+ | Android arm64 (sin ARMv8 crypto) | **ChaCha20-Poly1305** | CPU puro, ChaCha es más rápido |
496
+ | IoT / embebido | **ChaCha20-Poly1305** | No requiere hardware AES |
497
+ | Apple Silicon (serie M) | Cualquiera (parejos) | Ambos bien optimizados |
498
+
499
+ **Seguridad:** ChaCha20-Poly1305 y AES-GCM tienen propiedades de seguridad equivalentes. La decisión es puramente sobre rendimiento en tu target.
500
+
277
501
  ### HMAC-SHA256
278
502
 
279
503
  Autenticación de mensajes.
@@ -315,7 +539,9 @@ import {
315
539
  fromBase64Url,
316
540
 
317
541
  // Seguridad
318
- constantTimeEqual, // (a, b) → boolean (timing-safe)
542
+ constantTimeEqual, // (a, b) → boolean (timing-safe, JS-side)
543
+ constantTimeEq, // 🆕 v0.3.0 — igual pero vía Rust side
544
+ nativeSecureRandom, // 🆕 v0.3.0 — CSPRNG del OS vía Rust (vs node:crypto)
319
545
 
320
546
  // Operaciones Buffer
321
547
  concat, // (buffers[]) → Buffer
@@ -324,6 +550,14 @@ import {
324
550
  } from '@brashkie/signalis-core';
325
551
  ```
326
552
 
553
+ #### ¿Cuándo usar `constantTimeEq` vs `constantTimeEqual`?
554
+
555
+ Ambos comparan en tiempo constante. `constantTimeEqual` usa la semántica de `Buffer.compare` de JavaScript (implementación JS-side). `constantTimeEq` (v0.3.0) pasa por la crate auditada `subtle::ConstantTimeEq` de Rust. Las dos son seguras — usá `constantTimeEq` si querés asegurarte de que tu camino timing-safe sea el mismo que usa el resto de la librería internamente.
556
+
557
+ #### ¿Cuándo usar `nativeSecureRandom` vs `secureRandom`?
558
+
559
+ `secureRandom(n)` usa `crypto.randomBytes()` de Node. `nativeSecureRandom(n)` (v0.3.0) pasa por `OsRng` de Rust (que llama directamente a `getrandom`/`BCryptGenRandom`/`SecRandomCopyBytes`). Ambos son criptográficamente seguros. Usá `nativeSecureRandom` si querés una sola superficie de auditoría para entropía en todo tu stack.
560
+
327
561
  ### Errores
328
562
 
329
563
  Todos los errores extienden `SignalisError`:
@@ -335,6 +569,7 @@ import {
335
569
  CryptoError, // Op crypto falló
336
570
  AuthenticationError, // Verificación Tag/MAC falló (extiende CryptoError)
337
571
  KeyDerivationError, // HKDF o similar falló (extiende CryptoError)
572
+ SignatureError, // Verificación Ed25519/XEd25519 falló (extiende CryptoError) — NUEVO v0.2.0
338
573
  LengthError, // Longitud fuera de rango (extiende ValidationError)
339
574
  } from '@brashkie/signalis-core';
340
575
 
@@ -345,6 +580,8 @@ try {
345
580
  console.error('¡Tampering detectado!');
346
581
  } else if (e instanceof ValidationError) {
347
582
  console.error(`Parámetro inválido: ${e.parameter}`);
583
+ } else if (e instanceof SignatureError) {
584
+ console.error('¡Firma inválida!');
348
585
  }
349
586
  }
350
587
  ```
@@ -356,10 +593,13 @@ try {
356
593
  El directorio `examples/` contiene demos completos funcionales:
357
594
 
358
595
  ```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
596
+ npm run example:cjs # CommonJS (10 demos)
597
+ npm run example:esm # ESM (canal Alice ↔ Bob)
598
+ npm run example:ts # TypeScript (patrones type-safe)
599
+ npm run example:signing # Ed25519 + XEd25519 — NUEVO v0.2.0
600
+ npm run example:aad # AES-GCM con AAD — NUEVO v0.2.0
601
+ npm run example:e2e # Canal E2E completo — NUEVO v0.2.0
602
+ npm run examples # Ejecutar todos
363
603
  ```
364
604
 
365
605
  ### Ejemplo: Cifrado seguro de archivos
@@ -409,6 +649,66 @@ function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
409
649
  }
410
650
  ```
411
651
 
652
+ ### Ejemplo: Firmar aserciones de identidad (NUEVO v0.2.0)
653
+
654
+ ```typescript
655
+ import { Curve25519, XEd25519 } from '@brashkie/signalis-core';
656
+
657
+ // Clave de identidad de largo plazo de Alice (Curve25519/XEd25519)
658
+ const aliceIdentidad = Curve25519.generateKeyPair();
659
+
660
+ // Alice genera una clave efímera para la sesión
661
+ const aliceEfimera = Curve25519.generateKeyPair();
662
+
663
+ // Alice firma su efímera con su identidad — prueba "esta efímera es mía"
664
+ const pruebaAuth = XEd25519.sign(
665
+ aliceIdentidad.privateKey,
666
+ aliceEfimera.publicKey,
667
+ );
668
+
669
+ // Bob verifica la autorización:
670
+ // 1. Tiene la clave pública de identidad de Alice de fuente confiable
671
+ // 2. Recibe la efímera + firma
672
+ try {
673
+ XEd25519.verify(aliceIdentidad.publicKey, aliceEfimera.publicKey, pruebaAuth);
674
+ // ✅ Bob confía que la efímera pertenece a Alice
675
+ } catch {
676
+ // ❌ Mallory intentó MITM con su propia efímera
677
+ }
678
+ ```
679
+
680
+ ### Ejemplo: Mensajes cifrados con cabeceras autenticadas (NUEVO v0.2.0)
681
+
682
+ ```typescript
683
+ import { AES_GCM, secureRandom } from '@brashkie/signalis-core';
684
+
685
+ const claveSesion = derivadaDesdeECDHyHKDF;
686
+
687
+ function enviarMensaje(cuerpo: Buffer, msgId: number) {
688
+ const nonce = secureRandom(12);
689
+ const cabecera = Buffer.from(JSON.stringify({
690
+ msg_id: msgId,
691
+ timestamp: Date.now(),
692
+ emisor: 'alice',
693
+ }));
694
+
695
+ // Cabecera autenticada pero NO cifrada (receptor la necesita en claro)
696
+ const cifrado = AES_GCM.encryptWithAad(claveSesion, nonce, cuerpo, cabecera);
697
+
698
+ return { cabecera, nonce, cifrado };
699
+ }
700
+
701
+ function recibirMensaje(paquete: { cabecera: Buffer; nonce: Buffer; cifrado: Buffer }) {
702
+ // Falla la descifrado si SE MODIFICÓ ciphertext O cabecera
703
+ return AES_GCM.decryptWithAad(
704
+ claveSesion,
705
+ paquete.nonce,
706
+ paquete.cifrado,
707
+ paquete.cabecera,
708
+ );
709
+ }
710
+ ```
711
+
412
712
  ---
413
713
 
414
714
  ## 🏗️ Arquitectura
@@ -416,18 +716,20 @@ function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
416
716
  ```
417
717
  @brashkie/signalis-core
418
718
 
419
- ├── 🦀 Workspace Rust (5 crates)
719
+ ├── 🦀 Workspace Rust (8 crates)
420
720
  │ ├── sc-curve25519 → Operaciones X25519 ECDH
721
+ │ ├── sc-ed25519 → Firmas Ed25519 (RFC 8032) — NUEVO v0.2.0
722
+ │ ├── sc-xed25519 → Firmas XEd25519 estilo Signal — NUEVO v0.2.0
421
723
  │ ├── sc-hkdf → Derivación HKDF-SHA256
422
- │ ├── sc-aes → AES-256-GCM y CBC
724
+ │ ├── sc-aes → AES-256-GCM (con AAD) y CBC
423
725
  │ ├── sc-hmac → HMAC-SHA256 con verificación tiempo-constante
424
726
  │ ├── sc-sha256 → Hashing SHA-256
425
727
  │ └── sc-node → Bindings NAPI-RS (cdylib)
426
728
 
427
729
  └── 📦 Capa TypeScript
428
730
  ├── core.ts → Wrappers crypto con validación
429
- ├── types.ts → Definiciones de tipos (KeyPair, etc.)
430
- ├── errors.ts → Clases de error tipadas
731
+ ├── types.ts → Definiciones de tipos (KeyPair, Signature, etc.)
732
+ ├── errors.ts → Clases de error tipadas (incl. SignatureError)
431
733
  ├── validators.ts → Aserciones de input
432
734
  ├── utils.ts → Codificación + random + helpers
433
735
  ├── constants.ts → Constantes públicas (tamaños, límites)
@@ -443,8 +745,10 @@ function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
443
745
  | Primitiva | Especificación | Implementación |
444
746
  |-----------|---------------|----------------|
445
747
  | X25519 | [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748) | [`curve25519-dalek`](https://github.com/dalek-cryptography/curve25519-dalek) |
748
+ | Ed25519 | [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032) | [`ed25519-dalek`](https://github.com/dalek-cryptography/ed25519-dalek) — **NUEVO v0.2.0** |
749
+ | XEd25519 | [Signal Spec](https://signal.org/docs/specifications/xeddsa/) | Impl custom sobre `curve25519-dalek` — **NUEVO v0.2.0** |
446
750
  | 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) |
751
+ | AES-256-GCM | [NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final) | [`aes-gcm`](https://github.com/RustCrypto/AEADs) (con soporte AAD) |
448
752
  | 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
753
  | HMAC-SHA256 | [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104) | [`hmac`](https://github.com/RustCrypto/MACs) |
450
754
  | SHA-256 | [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final) | [`sha2`](https://github.com/RustCrypto/hashes) |
@@ -476,8 +780,13 @@ Benchmarks (Node 22, x86_64):
476
780
  |-----------|------------|-------------|
477
781
  | Generación keypair Curve25519 | ~50,000 ops/seg | **15×** más rápido que tweetnacl |
478
782
  | X25519 ECDH | ~25,000 ops/seg | **20×** más rápido |
783
+ | Ed25519 firmar | ~25,000 ops/seg | **20×** más rápido — NUEVO v0.2.0 |
784
+ | Ed25519 verificar | ~10,000 ops/seg | **15×** más rápido — NUEVO v0.2.0 |
785
+ | XEd25519 firmar | ~20,000 ops/seg | — — NUEVO v0.2.0 |
786
+ | XEd25519 verificar | ~10,000 ops/seg | — — NUEVO v0.2.0 |
479
787
  | Derivación HKDF (32 bytes) | ~500,000 ops/seg | **30×** más rápido |
480
788
  | AES-256-GCM cifrado (1 KB) | ~2 GB/seg | **80×** más rápido |
789
+ | AES-GCM con AAD | <5% overhead vs sin AAD | — NUEVO v0.2.0 |
481
790
  | SHA-256 (1 KB) | ~3 GB/seg | **50×** más rápido |
482
791
  | HMAC-SHA256 (1 KB) | ~2.5 GB/seg | **40×** más rápido |
483
792
 
@@ -547,9 +856,10 @@ npm run examples
547
856
  Ver [ROADMAP.md](./ROADMAP.md) para detalles.
548
857
 
549
858
  **TL;DR:**
550
- - **v0.1** ✅ — Primitivas criptográficas (actual)
551
- - **v0.2** — Benchmarks, firmas Ed25519, soporte X448
552
- - **v1.0** — API estable, auditoría
859
+ - **v0.1** ✅ — Primitivas criptográficas (Curve25519, HKDF, AES, HMAC, SHA-256)
860
+ - **v0.2** Ed25519, XEd25519, AES-GCM con AAD (release actual)
861
+ - **v0.3** — Benchmarks, soporte X448, más vectores de test
862
+ - **v1.0** — API estable, auditoría externa
553
863
  - **Luego:** [@brashkie/signalis](https://github.com/Brashkie/signalis) (X3DH + Double Ratchet)
554
864
  - **Luego:** [@brashkie/waproto](https://github.com/Brashkie/waproto) (Protocolo WhatsApp)
555
865
  - **Luego:** HepeinBaileys 2.0 (cliente WhatsApp completo desde cero)
@@ -574,9 +884,10 @@ Por favor también lee nuestro [Código de Conducta](./CODE_OF_CONDUCT.md).
574
884
  Construido sobre los hombros de gigantes:
575
885
 
576
886
  - **[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)** — Curve25519 en Rust puro
887
+ - **[ed25519-dalek](https://github.com/dalek-cryptography/ed25519-dalek)** — Ed25519 en Rust puro (agregado en v0.2.0)
577
888
  - **[RustCrypto](https://github.com/RustCrypto)** — `aes`, `hkdf`, `hmac`, `sha2`
578
889
  - **[napi-rs](https://napi.rs)** — Bindings Rust ↔ Node
579
- - **Signal Foundation** — [Especificaciones del protocolo](https://signal.org/docs/)
890
+ - **Signal Foundation** — [Especificaciones del protocolo](https://signal.org/docs/) (incluyendo [XEd25519](https://signal.org/docs/specifications/xeddsa/))
580
891
  - **[tsup](https://tsup.egoist.dev/)** — Bundler dual ESM/CJS
581
892
  - **[Vitest](https://vitest.dev/)** — Test runner moderno
582
893
 
@@ -596,4 +907,4 @@ Ver [LICENSE](./LICENSE) y [NOTICE](./NOTICE) para detalles completos.
596
907
 
597
908
  [GitHub](https://github.com/Brashkie) · [npm](https://www.npmjs.com/~brashkie)
598
909
 
599
- </div>
910
+ </div>