@brashkie/signalis-core 0.1.0 β†’ 0.2.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.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
  [![Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](#testing)
16
- [![Tests](https://img.shields.io/badge/tests-100%2B%20passing-success.svg)](#testing)
16
+ [![Tests](https://img.shields.io/badge/tests-269%2B%20passing-success.svg)](#testing)
17
17
 
18
18
  [**English**](./README.md) Β· [**EspaΓ±ol**](./README.es.md) Β· [Docs](./docs) Β· [Roadmap](./ROADMAP.md) Β· [Changelog](./CHANGELOG.md)
19
19
 
@@ -32,10 +32,27 @@ Built with **Rust** for safety and speed, exposed to Node.js via [napi-rs](https
32
32
 
33
33
  ---
34
34
 
35
+ ## πŸŽ‰ What's New in v0.2.0
36
+
37
+ **v0.2.0 introduces digital signatures and AAD authenticated encryption β€” fully backwards compatible with v0.1.0.**
38
+
39
+ | New | Description |
40
+ |-----|-------------|
41
+ | πŸ†• **Ed25519** | Standard digital signatures (RFC 8032) β€” deterministic |
42
+ | πŸ†• **XEd25519** | Signal-style signing with Curve25519 keys β€” one identity key for ECDH + signing |
43
+ | πŸ†• **AES-GCM with AAD** | Additional Authenticated Data for binding metadata to ciphertext |
44
+ | πŸ†• **SignatureError** | New typed error class for signature failures |
45
+ | πŸ†• **Signature type** | Branded type with `asSignature()` helper |
46
+
47
+ See [MIGRATION.md](./MIGRATION.md) for upgrade details (it's a drop-in replacement).
48
+
49
+ ---
50
+
35
51
  ## πŸ“‹ Table of Contents
36
52
 
37
53
  - [πŸ” Signalis Core](#-signalis-core)
38
54
  - [✨ What is Signalis Core?](#-what-is-signalis-core)
55
+ - [πŸŽ‰ What's New in v0.2.0](#-whats-new-in-v020)
39
56
  - [πŸ“‹ Table of Contents](#-table-of-contents)
40
57
  - [πŸš€ Features](#-features)
41
58
  - [πŸ€” Why Signalis Core?](#-why-signalis-core)
@@ -49,6 +66,8 @@ Built with **Rust** for safety and speed, exposed to Node.js via [napi-rs](https
49
66
  - [Default Import](#default-import)
50
67
  - [πŸ“š API Reference](#-api-reference)
51
68
  - [Curve25519 / X25519](#curve25519--x25519)
69
+ - [Ed25519](#ed25519)
70
+ - [XEd25519](#xed25519)
52
71
  - [HKDF-SHA256](#hkdf-sha256)
53
72
  - [AES-256-GCM](#aes-256-gcm)
54
73
  - [AES-256-CBC](#aes-256-cbc)
@@ -59,6 +78,8 @@ Built with **Rust** for safety and speed, exposed to Node.js via [napi-rs](https
59
78
  - [πŸ’‘ Examples](#-examples)
60
79
  - [Example: Secure file encryption](#example-secure-file-encryption)
61
80
  - [Example: Signal-style "Triple DH"](#example-signal-style-triple-dh)
81
+ - [Example: Signing identity assertions (NEW v0.2.0)](#example-signing-identity-assertions-new-v020)
82
+ - [Example: Encrypted messages with authenticated headers (NEW v0.2.0)](#example-encrypted-messages-with-authenticated-headers-new-v020)
62
83
  - [πŸ—οΈ Architecture](#️-architecture)
63
84
  - [Build Output](#build-output)
64
85
  - [πŸ›‘οΈ Security](#️-security)
@@ -82,14 +103,16 @@ Built with **Rust** for safety and speed, exposed to Node.js via [napi-rs](https
82
103
  | Feature | Description |
83
104
  |---------|-------------|
84
105
  | πŸ”₯ **Blazing Fast** | Native Rust implementation via napi-rs (10-100x faster than pure JS) |
85
- | πŸ›‘οΈ **Audited Crypto** | Built on `curve25519-dalek`, RustCrypto suite β€” battle-tested libraries |
106
+ | πŸ›‘οΈ **Audited Crypto** | Built on `curve25519-dalek`, `ed25519-dalek`, RustCrypto suite β€” battle-tested libraries |
107
+ | ✍️ **Digital Signatures** | Ed25519 (RFC 8032) and XEd25519 (Signal-style) β€” **NEW v0.2.0** |
108
+ | πŸ” **AEAD with AAD** | AES-256-GCM with Additional Authenticated Data β€” **NEW v0.2.0** |
86
109
  | πŸ“¦ **Dual Package** | Works in CommonJS, ESM, and TypeScript projects |
87
110
  | 🎯 **Type-Safe** | Full TypeScript definitions with branded types and rich error classes |
88
- | βœ… **Test Vectors** | Validated against RFC 5869, RFC 7748, RFC 4231, and NIST vectors |
111
+ | βœ… **Test Vectors** | Validated against RFC 5869, RFC 7748, RFC 8032, RFC 4231, and NIST vectors |
89
112
  | 🌍 **Cross-Platform** | Prebuilt binaries for Windows, macOS, Linux (x64, ARM) |
90
113
  | πŸ”’ **Constant-Time** | Side-channel resistant comparisons via `subtle` crate |
91
114
  | 🧹 **Auto-Zeroization** | Secrets are wiped from memory automatically |
92
- | πŸ“Š **99%+ Coverage** | Comprehensive test suite with 100+ assertions |
115
+ | πŸ“Š **99%+ Coverage** | Comprehensive test suite with 269+ assertions |
93
116
  | πŸ“– **Well Documented** | Complete JSDoc + inline examples for every function |
94
117
 
95
118
  ---
@@ -234,6 +257,86 @@ Curve25519.PUBLIC_KEY_SIZE; // 32
234
257
  Curve25519.SHARED_SECRET_SIZE; // 32
235
258
  ```
236
259
 
260
+ ### Ed25519
261
+
262
+ **NEW in v0.2.0.** Standard Ed25519 digital signatures (RFC 8032). Deterministic β€” same input always produces the same signature.
263
+
264
+ ```typescript
265
+ import { Ed25519, type KeyPair, type Signature } from '@brashkie/signalis-core';
266
+
267
+ // Generate a new signing keypair
268
+ const keys: KeyPair = Ed25519.generateKeyPair();
269
+ // β†’ { privateKey: Buffer(32), publicKey: Buffer(32) }
270
+
271
+ // Deterministic from a 32-byte seed
272
+ const fromSeed = Ed25519.keyPairFromSeed(seed);
273
+
274
+ // Derive public from private
275
+ const pub = Ed25519.publicFromPrivate(privateKey);
276
+
277
+ // Sign a message β†’ 64-byte signature
278
+ const sig: Signature = Ed25519.sign(privateKey, message);
279
+
280
+ // Verify (throws SignatureError on failure)
281
+ Ed25519.verify(publicKey, message, sig);
282
+
283
+ // Verify (returns boolean, no throw)
284
+ const ok = Ed25519.verifyBool(publicKey, message, sig);
285
+
286
+ // Constants
287
+ Ed25519.PRIVATE_KEY_SIZE; // 32
288
+ Ed25519.PUBLIC_KEY_SIZE; // 32
289
+ Ed25519.SIGNATURE_SIZE; // 64
290
+ Ed25519.SEED_SIZE; // 32
291
+ ```
292
+
293
+ ### XEd25519
294
+
295
+ **NEW in v0.2.0.** Sign with the **SAME** Curve25519 keypair used for ECDH. This is what the Signal Protocol uses for identity keys.
296
+
297
+ ```typescript
298
+ import { Curve25519, XEd25519 } from '@brashkie/signalis-core';
299
+
300
+ // ONE keypair for both ECDH and signing
301
+ const identity = Curve25519.generateKeyPair();
302
+
303
+ // Use for ECDH:
304
+ const shared = Curve25519.diffieHellman(identity.privateKey, peerPublic);
305
+
306
+ // Use the SAME key to sign:
307
+ const sig = XEd25519.sign(identity.privateKey, message);
308
+
309
+ // Verify with the SAME Curve25519 public key
310
+ XEd25519.verify(identity.publicKey, message, sig);
311
+
312
+ // XEd25519 signatures are NOT deterministic (use OS RNG)
313
+ const sig1 = XEd25519.sign(identity.privateKey, message);
314
+ const sig2 = XEd25519.sign(identity.privateKey, message);
315
+ // sig1.equals(sig2) β†’ false (intentionally probabilistic)
316
+
317
+ // For deterministic signing (testing), provide explicit 64-byte random:
318
+ const random = secureRandom(64);
319
+ const detSig = XEd25519.signWithRandom(identity.privateKey, message, random);
320
+
321
+ // Verify (boolean, no throw)
322
+ const ok = XEd25519.verifyBool(identity.publicKey, message, sig);
323
+
324
+ // Constants
325
+ XEd25519.PRIVATE_KEY_SIZE; // 32 (same as Curve25519)
326
+ XEd25519.PUBLIC_KEY_SIZE; // 32 (same as Curve25519)
327
+ XEd25519.SIGNATURE_SIZE; // 64
328
+ XEd25519.RANDOM_SIZE; // 64
329
+ ```
330
+
331
+ **When to use Ed25519 vs XEd25519:**
332
+
333
+ | Need | Use |
334
+ |------|-----|
335
+ | Standard Ed25519, deterministic, RFC 8032 compliant | **Ed25519** |
336
+ | Single identity key for ECDH + signing (Signal style) | **XEd25519** |
337
+ | Reproducible signatures from seed | **Ed25519** |
338
+ | Compatibility with Signal Protocol semantics | **XEd25519** |
339
+
237
340
  ### HKDF-SHA256
238
341
 
239
342
  Key derivation per RFC 5869.
@@ -283,6 +386,26 @@ const pt = AES_GCM.decrypt(key, nonce, ct);
283
386
  // Throws AuthenticationError if tampered
284
387
  ```
285
388
 
389
+ **With Additional Authenticated Data (AAD) β€” NEW in v0.2.0:**
390
+
391
+ ```typescript
392
+ import { AES_GCM } from '@brashkie/signalis-core';
393
+
394
+ // AAD is authenticated but NOT encrypted β€” useful for headers/metadata
395
+ const header = Buffer.from('msg_id=42|sender=alice');
396
+ const body = Buffer.from('encrypted body content');
397
+
398
+ const ct = AES_GCM.encryptWithAad(key, nonce, body, header);
399
+
400
+ // Decrypt β€” MUST pass same AAD, or AuthenticationError
401
+ const pt = AES_GCM.decryptWithAad(key, nonce, ct, header);
402
+
403
+ // Tampering with header (AAD) β†’ fails
404
+ const tampered = Buffer.from(header);
405
+ tampered[0] ^= 0xff;
406
+ AES_GCM.decryptWithAad(key, nonce, ct, tampered); // throws AuthenticationError
407
+ ```
408
+
286
409
  > **⚠️ CRITICAL:** Never reuse a `(key, nonce)` pair. Use `randomNonce()` for every message, or use a deterministic counter under the same key (max 2Β³Β² messages).
287
410
 
288
411
  ### AES-256-CBC
@@ -364,6 +487,7 @@ import {
364
487
  CryptoError, // Crypto op failed
365
488
  AuthenticationError, // Tag/MAC verification failed (extends CryptoError)
366
489
  KeyDerivationError, // HKDF or similar failed (extends CryptoError)
490
+ SignatureError, // Ed25519/XEd25519 verify failed (extends CryptoError) β€” NEW v0.2.0
367
491
  LengthError, // Output length out of bounds (extends ValidationError)
368
492
  } from '@brashkie/signalis-core';
369
493
 
@@ -374,6 +498,8 @@ try {
374
498
  console.error('Tampering detected!');
375
499
  } else if (e instanceof ValidationError) {
376
500
  console.error(`Invalid parameter: ${e.parameter}`);
501
+ } else if (e instanceof SignatureError) {
502
+ console.error('Invalid signature!');
377
503
  }
378
504
  }
379
505
  ```
@@ -385,10 +511,13 @@ try {
385
511
  The `examples/` directory contains complete working demos:
386
512
 
387
513
  ```bash
388
- npm run example:cjs # CommonJS (10 demos)
389
- npm run example:esm # ESM (Alice ↔ Bob channel)
390
- npm run example:ts # TypeScript (type-safe patterns)
391
- npm run examples # Run all three
514
+ npm run example:cjs # CommonJS (10 demos)
515
+ npm run example:esm # ESM (Alice ↔ Bob channel)
516
+ npm run example:ts # TypeScript (type-safe patterns)
517
+ npm run example:signing # Ed25519 + XEd25519 β€” NEW v0.2.0
518
+ npm run example:aad # AES-GCM with AAD β€” NEW v0.2.0
519
+ npm run example:e2e # Complete E2E channel β€” NEW v0.2.0
520
+ npm run examples # Run all
392
521
  ```
393
522
 
394
523
  ### Example: Secure file encryption
@@ -438,6 +567,66 @@ function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
438
567
  }
439
568
  ```
440
569
 
570
+ ### Example: Signing identity assertions (NEW v0.2.0)
571
+
572
+ ```typescript
573
+ import { Curve25519, XEd25519 } from '@brashkie/signalis-core';
574
+
575
+ // Alice's long-term identity key (Curve25519/XEd25519)
576
+ const aliceIdentity = Curve25519.generateKeyPair();
577
+
578
+ // Alice generates an ephemeral session key
579
+ const aliceEphemeral = Curve25519.generateKeyPair();
580
+
581
+ // Alice signs her ephemeral with her identity β€” proves "this ephemeral is mine"
582
+ const authProof = XEd25519.sign(
583
+ aliceIdentity.privateKey,
584
+ aliceEphemeral.publicKey,
585
+ );
586
+
587
+ // Bob verifies the authorization:
588
+ // 1. Got Alice's identity public key from a trusted source
589
+ // 2. Receives ephemeral + signature
590
+ try {
591
+ XEd25519.verify(aliceIdentity.publicKey, aliceEphemeral.publicKey, authProof);
592
+ // βœ… Bob now trusts the ephemeral key belongs to Alice
593
+ } catch {
594
+ // ❌ Mallory tried to MITM with her own ephemeral
595
+ }
596
+ ```
597
+
598
+ ### Example: Encrypted messages with authenticated headers (NEW v0.2.0)
599
+
600
+ ```typescript
601
+ import { AES_GCM, secureRandom } from '@brashkie/signalis-core';
602
+
603
+ const sessionKey = derivedFromECDHandHKDF;
604
+
605
+ function sendMessage(body: Buffer, msgId: number) {
606
+ const nonce = secureRandom(12);
607
+ const header = Buffer.from(JSON.stringify({
608
+ msg_id: msgId,
609
+ timestamp: Date.now(),
610
+ sender: 'alice',
611
+ }));
612
+
613
+ // Header is authenticated but NOT encrypted (receiver needs it as plaintext)
614
+ const ciphertext = AES_GCM.encryptWithAad(sessionKey, nonce, body, header);
615
+
616
+ return { header, nonce, ciphertext };
617
+ }
618
+
619
+ function receiveMessage(packet: { header: Buffer; nonce: Buffer; ciphertext: Buffer }) {
620
+ // Decryption fails if EITHER ciphertext OR header was tampered
621
+ return AES_GCM.decryptWithAad(
622
+ sessionKey,
623
+ packet.nonce,
624
+ packet.ciphertext,
625
+ packet.header,
626
+ );
627
+ }
628
+ ```
629
+
441
630
  ---
442
631
 
443
632
  ## πŸ—οΈ Architecture
@@ -445,18 +634,20 @@ function tripleDH(IK_A_priv: Buffer, EK_A_priv: Buffer,
445
634
  ```
446
635
  @brashkie/signalis-core
447
636
  β”‚
448
- β”œβ”€β”€ πŸ¦€ Rust Workspace (5 crates)
637
+ β”œβ”€β”€ πŸ¦€ Rust Workspace (8 crates)
449
638
  β”‚ β”œβ”€β”€ sc-curve25519 β†’ X25519 ECDH operations
639
+ β”‚ β”œβ”€β”€ sc-ed25519 β†’ Ed25519 signatures (RFC 8032) β€” NEW v0.2.0
640
+ β”‚ β”œβ”€β”€ sc-xed25519 β†’ XEd25519 Signal-style signatures β€” NEW v0.2.0
450
641
  β”‚ β”œβ”€β”€ sc-hkdf β†’ HKDF-SHA256 derivation
451
- β”‚ β”œβ”€β”€ sc-aes β†’ AES-256-GCM & CBC
642
+ β”‚ β”œβ”€β”€ sc-aes β†’ AES-256-GCM (with AAD) & CBC
452
643
  β”‚ β”œβ”€β”€ sc-hmac β†’ HMAC-SHA256 with constant-time verify
453
644
  β”‚ β”œβ”€β”€ sc-sha256 β†’ SHA-256 hashing
454
645
  β”‚ └── sc-node β†’ NAPI-RS bindings (cdylib)
455
646
  β”‚
456
647
  └── πŸ“¦ TypeScript Layer
457
648
  β”œβ”€β”€ core.ts β†’ Crypto wrappers with validation
458
- β”œβ”€β”€ types.ts β†’ Type definitions (KeyPair, etc.)
459
- β”œβ”€β”€ errors.ts β†’ Typed error classes
649
+ β”œβ”€β”€ types.ts β†’ Type definitions (KeyPair, Signature, etc.)
650
+ β”œβ”€β”€ errors.ts β†’ Typed error classes (incl. SignatureError)
460
651
  β”œβ”€β”€ validators.ts β†’ Input assertions
461
652
  β”œβ”€β”€ utils.ts β†’ Encoding + random + buffer helpers
462
653
  β”œβ”€β”€ constants.ts β†’ Public constants (sizes, limits)
@@ -487,8 +678,10 @@ dist/
487
678
  | Primitive | Spec | Implementation |
488
679
  |-----------|------|----------------|
489
680
  | X25519 | [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748) | [`curve25519-dalek`](https://github.com/dalek-cryptography/curve25519-dalek) (audited by NCC Group) |
681
+ | Ed25519 | [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032) | [`ed25519-dalek`](https://github.com/dalek-cryptography/ed25519-dalek) (audited) β€” **NEW v0.2.0** |
682
+ | XEd25519 | [Signal Spec](https://signal.org/docs/specifications/xeddsa/) | Custom impl over `curve25519-dalek` β€” **NEW v0.2.0** |
490
683
  | HKDF-SHA256 | [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869) | [`hkdf`](https://github.com/RustCrypto/KDFs) (RustCrypto) |
491
- | AES-256-GCM | [NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final) | [`aes-gcm`](https://github.com/RustCrypto/AEADs) (RustCrypto) |
684
+ | AES-256-GCM | [NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final) | [`aes-gcm`](https://github.com/RustCrypto/AEADs) (RustCrypto, AAD support) |
492
685
  | AES-256-CBC | [NIST SP 800-38A](https://csrc.nist.gov/publications/detail/sp/800-38a/final) | [`aes`](https://github.com/RustCrypto/block-ciphers) (RustCrypto) |
493
686
  | HMAC-SHA256 | [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104) | [`hmac`](https://github.com/RustCrypto/MACs) (RustCrypto) |
494
687
  | SHA-256 | [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final) | [`sha2`](https://github.com/RustCrypto/hashes) (RustCrypto) |
@@ -521,12 +714,17 @@ Benchmarks (Node 22, x86_64):
521
714
  |-----------|------------|-------------|
522
715
  | Curve25519 keygen | ~50,000 ops/sec | **15Γ—** faster than tweetnacl |
523
716
  | X25519 ECDH | ~25,000 ops/sec | **20Γ—** faster |
717
+ | Ed25519 sign | ~25,000 ops/sec | **20Γ—** faster β€” NEW v0.2.0 |
718
+ | Ed25519 verify | ~10,000 ops/sec | **15Γ—** faster β€” NEW v0.2.0 |
719
+ | XEd25519 sign | ~20,000 ops/sec | β€” β€” NEW v0.2.0 |
720
+ | XEd25519 verify | ~10,000 ops/sec | β€” β€” NEW v0.2.0 |
524
721
  | HKDF derive (32 bytes) | ~500,000 ops/sec | **30Γ—** faster |
525
722
  | AES-256-GCM encrypt (1 KB) | ~2 GB/sec | **80Γ—** faster |
723
+ | AES-GCM with AAD | <5% overhead vs no AAD | β€” NEW v0.2.0 |
526
724
  | SHA-256 (1 KB) | ~3 GB/sec | **50Γ—** faster |
527
725
  | HMAC-SHA256 (1 KB) | ~2.5 GB/sec | **40Γ—** faster |
528
726
 
529
- *Run `npm run bench` to benchmark on your machine (coming in v0.2).*
727
+ *Run `npm run bench` to benchmark on your machine (coming in v0.3).*
530
728
 
531
729
  ---
532
730
 
@@ -562,15 +760,20 @@ npm run test:coverage:ui
562
760
 
563
761
  ### What's Tested
564
762
 
565
- - βœ… All RFC test vectors (RFC 5869, 7748, 4231)
763
+ - βœ… All RFC test vectors (RFC 5869, 7748, 8032, 4231)
566
764
  - βœ… All NIST test vectors (AES, SHA-256)
765
+ - βœ… Ed25519 RFC 8032 vector 1 (empty message) β€” NEW v0.2.0
766
+ - βœ… XEd25519 round-trips with deterministic + probabilistic signing β€” NEW v0.2.0
767
+ - βœ… AES-GCM AAD authentication (tampered AAD fails decryption) β€” NEW v0.2.0
567
768
  - βœ… Input validation for every public function
568
769
  - βœ… Error handling for every code path
569
770
  - βœ… Round-trip encryption / decryption
570
- - βœ… Tampering detection (AES-GCM tag failures)
771
+ - βœ… Tampering detection (AES-GCM tag failures, signature failures)
571
772
  - βœ… Both CommonJS and ESM consumption paths
572
773
  - βœ… Default export and named exports
573
774
 
775
+ **Test count: 269+ assertions across Rust (48) + Vitest (172) + CJS (12) + ESM (15) + new v0.2.0 tests (49).**
776
+
574
777
  ---
575
778
 
576
779
  ## πŸ”¨ Building from Source
@@ -611,9 +814,10 @@ npm run examples
611
814
  See [ROADMAP.md](./ROADMAP.md) for detailed plans.
612
815
 
613
816
  **TL;DR:**
614
- - **v0.1** βœ… β€” Cryptographic primitives (current)
615
- - **v0.2** β€” Benchmarks, Ed25519 signatures, X448 support
616
- - **v1.0** β€” Stable API, audit
817
+ - **v0.1** βœ… β€” Cryptographic primitives (Curve25519, HKDF, AES, HMAC, SHA-256)
818
+ - **v0.2** βœ… β€” Ed25519, XEd25519, AES-GCM with AAD (current release)
819
+ - **v0.3** β€” Benchmarks, X448 support, more test vectors
820
+ - **v1.0** β€” Stable API, external audit
617
821
  - **Then:** [@brashkie/signalis](https://github.com/Brashkie/signalis) (X3DH + Double Ratchet)
618
822
  - **Then:** [@brashkie/waproto](https://github.com/Brashkie/waproto) (WhatsApp Protocol)
619
823
  - **Then:** HepeinBaileys 2.0 (full WhatsApp client from scratch)
@@ -638,9 +842,10 @@ Please also read our [Code of Conduct](./CODE_OF_CONDUCT.md).
638
842
  Built on the shoulders of giants:
639
843
 
640
844
  - **[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)** β€” Curve25519 in pure Rust
845
+ - **[ed25519-dalek](https://github.com/dalek-cryptography/ed25519-dalek)** β€” Ed25519 in pure Rust (added in v0.2.0)
641
846
  - **[RustCrypto](https://github.com/RustCrypto)** β€” `aes`, `hkdf`, `hmac`, `sha2`
642
847
  - **[napi-rs](https://napi.rs)** β€” Rust ↔ Node bindings
643
- - **Signal Foundation** β€” [Protocol specifications](https://signal.org/docs/)
848
+ - **Signal Foundation** β€” [Protocol specifications](https://signal.org/docs/) (including [XEd25519](https://signal.org/docs/specifications/xeddsa/))
644
849
  - **[tsup](https://tsup.egoist.dev/)** β€” Dual ESM/CJS bundler
645
850
  - **[Vitest](https://vitest.dev/)** β€” Modern test runner
646
851
 
@@ -662,4 +867,4 @@ See [LICENSE](./LICENSE) and [NOTICE](./NOTICE) for full details.
662
867
 
663
868
  [GitHub](https://github.com/Brashkie) Β· [npm](https://www.npmjs.com/~brashkie)
664
869
 
665
- </div>
870
+ </div>
package/SECURITY.md ADDED
@@ -0,0 +1,191 @@
1
+ # πŸ›‘οΈ Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported | Status |
6
+ |---------|-----------|--------|
7
+ | 0.2.x | βœ… | Active development |
8
+ | 0.1.x | ⚠️ | Security fixes only (until 2026-08-01) |
9
+ | < 0.1 | ❌ | Not supported |
10
+
11
+ When a new minor version is released, the previous minor enters **3 months of security-only support**.
12
+
13
+ ---
14
+
15
+ ## 🚨 Reporting a Vulnerability
16
+
17
+ **Please do NOT open public GitHub issues for security bugs.**
18
+
19
+ ### Reporting Channels
20
+
21
+ Use one of these private channels:
22
+
23
+ 1. **GitHub Security Advisories** (preferred):
24
+ - https://github.com/Brashkie/signalis-core/security/advisories/new
25
+
26
+ 2. **Email**: brashkie@hepein.com
27
+ - PGP key: [download from GitHub profile]
28
+ - Subject: `[SECURITY] signalis-core: <brief description>`
29
+
30
+ ### What to Include
31
+
32
+ To help us triage quickly, please provide:
33
+
34
+ - **Description** of the vulnerability
35
+ - **Affected versions** (if known)
36
+ - **Reproduction steps** (proof of concept welcome)
37
+ - **Impact assessment** (your view on severity)
38
+ - **Suggested fix** (if any)
39
+ - **Your contact info** (for follow-up)
40
+
41
+ ### Response Timeline
42
+
43
+ | Severity | First response | Fix target |
44
+ |----------|---------------|------------|
45
+ | **Critical** (remote exploit, key recovery) | 24 hours | 7 days |
46
+ | **High** (timing leaks, DoS) | 3 days | 30 days |
47
+ | **Medium** (specific edge cases) | 7 days | Next minor release |
48
+ | **Low** (theoretical, hard to exploit) | 14 days | Best effort |
49
+
50
+ ### Disclosure Process
51
+
52
+ 1. We acknowledge receipt within the timeframes above
53
+ 2. We investigate and confirm the issue
54
+ 3. We develop a fix in a private branch
55
+ 4. We prepare a security advisory
56
+ 5. We coordinate a release (typically 7-30 days from confirmation)
57
+ 6. **You are credited** in the advisory (unless you prefer anonymity)
58
+
59
+ ### Bounty Program
60
+
61
+ We do **not** currently offer monetary bounties, but we:
62
+ - Publicly credit researchers (with permission)
63
+ - Provide signed acknowledgment letters
64
+ - Add you to the contributors hall of fame
65
+
66
+ If you're interested in funded research, please reach out.
67
+
68
+ ---
69
+
70
+ ## πŸ”’ Security Considerations
71
+
72
+ ### Cryptographic Implementation
73
+
74
+ All primitives use **audited, widely-deployed Rust crates**:
75
+
76
+ | Primitive | Crate | Last audited |
77
+ |-----------|-------|--------------|
78
+ | Curve25519 | `curve25519-dalek` 4.x | 2023 (Cure53) |
79
+ | Ed25519 | `ed25519-dalek` 2.x | 2023 (Cure53) |
80
+ | AES-GCM | `aes-gcm` (RustCrypto) | 2022 |
81
+ | SHA-256/HMAC | `sha2`, `hmac` (RustCrypto) | 2022 |
82
+ | HKDF | `hkdf` (RustCrypto) | 2022 |
83
+
84
+ We do **not** implement primitives ourselves. Our work is:
85
+ - Glue code (NAPI bindings)
86
+ - Validation logic
87
+ - Type safety wrappers
88
+ - Test infrastructure
89
+
90
+ ### Side-Channel Resistance
91
+
92
+ The underlying Rust crates provide:
93
+ - βœ… **Constant-time** scalar multiplication (Curve25519, Ed25519)
94
+ - βœ… **Constant-time** equality checks (HMAC verify, point equality)
95
+ - βœ… **AES-NI** hardware acceleration (when available)
96
+ - βœ… **Zeroization** of private key buffers on drop
97
+
98
+ ### Known Limitations
99
+
100
+ #### XEd25519 (v0.2.0)
101
+
102
+ XEd25519 implementation follows the [Signal specification](https://signal.org/docs/specifications/xeddsa/). While Signal has deployed this in production for years, our implementation is **new** and has not been independently audited.
103
+
104
+ **Mitigation:** We use `curve25519-dalek`'s low-level primitives and `ed25519-dalek`'s `hazmat` API, both of which ARE audited. Only the high-level XEd25519 logic is ours.
105
+
106
+ If you require audited XEd25519, consider waiting for v1.0.0 (audited release).
107
+
108
+ #### Random Number Generation
109
+
110
+ We use `OsRng` from the `rand` crate, which sources entropy from:
111
+ - **Linux**: `getrandom()` syscall
112
+ - **macOS**: `SecRandomCopyBytes`
113
+ - **Windows**: `BCryptGenRandom`
114
+ - **iOS**: same as macOS
115
+
116
+ These are the standard, recommended sources. However:
117
+ - 🚨 **VMs with poor entropy** can produce weak keys
118
+ - 🚨 **Hibernation/snapshots** can reuse RNG state
119
+
120
+ Mitigation: Generate keys after the system has accumulated entropy, especially in VM contexts.
121
+
122
+ ### Memory Safety
123
+
124
+ - **Rust side**: Compile-time memory safety, zeroization on drop
125
+ - **JS/Node side**: Buffers can be GC'd at any time
126
+
127
+ If you need guaranteed key wiping in JS:
128
+ ```typescript
129
+ // Explicit overwrite (best effort)
130
+ function wipeKey(buf: Buffer) {
131
+ buf.fill(0);
132
+ }
133
+ ```
134
+
135
+ **Note:** Due to JS GC behavior, this is best-effort. For highest assurance, use HSM/TPM-backed keys.
136
+
137
+ ### Network Security
138
+
139
+ This library provides **cryptographic primitives only**. It does NOT:
140
+ - ❌ Establish network connections
141
+ - ❌ Implement TLS/HTTPS
142
+ - ❌ Validate certificates
143
+ - ❌ Manage sessions
144
+
145
+ Your application is responsible for transport-layer security.
146
+
147
+ ---
148
+
149
+ ## 🚫 Out of Scope
150
+
151
+ The following are **not considered vulnerabilities**:
152
+
153
+ - πŸ”΅ **Performance issues** (open as feature request)
154
+ - πŸ”΅ **API ergonomics** (open as feature request)
155
+ - πŸ”΅ **Documentation typos** (open as PR)
156
+ - πŸ”΅ **Behavior on invalid inputs** that throws clear errors (working as intended)
157
+ - πŸ”΅ **Side-channels in user code** (your code's responsibility)
158
+ - πŸ”΅ **Issues in dependencies** (report upstream, but tell us too)
159
+ - πŸ”΅ **Hypothetical attacks** without practical demonstration
160
+
161
+ ---
162
+
163
+ ## πŸ† Hall of Fame
164
+
165
+ We thank the following security researchers for responsible disclosure:
166
+
167
+ _(Empty for now β€” be the first!)_
168
+
169
+ ---
170
+
171
+ ## πŸ“š Security Resources
172
+
173
+ For learning about cryptographic security:
174
+
175
+ - πŸ“˜ [Cryptography Engineering](https://www.schneier.com/books/cryptography-engineering/) β€” Schneier, Ferguson, Kohno
176
+ - πŸ“˜ [Real-World Cryptography](https://www.manning.com/books/real-world-cryptography) β€” David Wong
177
+ - πŸ“˜ [Serious Cryptography](https://nostarch.com/seriouscrypto) β€” Jean-Philippe Aumasson
178
+ - 🌐 [Signal Protocol Documentation](https://signal.org/docs/)
179
+ - 🌐 [Cryptography Stack Exchange](https://crypto.stackexchange.com/)
180
+
181
+ ---
182
+
183
+ ## πŸ”„ Policy Updates
184
+
185
+ This policy may be updated. Significant changes will be:
186
+ 1. Committed to the repo with a clear commit message
187
+ 2. Announced in the next release notes
188
+ 3. Posted in GitHub Discussions
189
+
190
+ **Last updated:** May 2026
191
+ **Next scheduled review:** November 2026