@vollcrypt/messages-wasm 0.9.0 → 0.9.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.
package/README.md CHANGED
@@ -1,60 +1,705 @@
1
- # @vollcrypt/messages-wasm
1
+ <div align="center">
2
+ <h1>Vollcrypt Messages</h1>
3
+ <p><strong>E2EE Message Encryption and Session Management module for Node.js, WebAssembly, and Rust</strong></p>
4
+
5
+ <p>
6
+ <a href="https://www.npmjs.com/package/@vollcrypt/messages-node">
7
+ <img src="https://img.shields.io/npm/v/@vollcrypt/messages-node?label=%40vollcrypt%2Fmessages-node&color=cb3837" alt="npm (node)">
8
+ </a>
9
+ <a href="https://www.npmjs.com/package/@vollcrypt/messages-wasm">
10
+ <img src="https://img.shields.io/npm/v/@vollcrypt/messages-wasm?label=%40vollcrypt%2Fmessages-wasm&color=cb3837" alt="npm (wasm)">
11
+ </a>
12
+ </p>
13
+ </div>
2
14
 
3
- **Cross-platform, quantum-resistant cryptography engine - WebAssembly Binding**
15
+ ---
4
16
 
5
- [![npm](https://img.shields.io/npm/v/@vollcrypt/messages-wasm.svg)](https://www.npmjs.com/package/@vollcrypt/messages-wasm)
6
- [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/BeratVural/vollcrypt/blob/main/LICENSE-GPL)
7
- [![License: Commercial](https://img.shields.io/badge/License-Commercial-goldenrod.svg)](https://github.com/BeratVural/vollcrypt/blob/main/LICENSE-COMMERCIAL.md)
17
+ This module contains the cryptographic primitives and session managers needed to build secure end-to-end encrypted (E2EE) messaging systems. It is compiled from a single Rust core to three targets: Node.js (native bindings), WebAssembly, and native Rust.
8
18
 
9
- This package provides the **WebAssembly (WASM) bindings** for the Vollcrypt cryptography engine. It is compiled directly from Rust using `wasm-pack`, bringing robust, post-quantum cryptography straight to the user's browser or frontend application (React, Next.js, Vue, etc.) without relying on slow JavaScript cryptography implementations.
19
+ ## Table of Contents
10
20
 
11
- ## Features
21
+ - [Installation](#installation)
22
+ - [Quick Start](#quick-start)
23
+ - [API Reference](#api-reference)
24
+ - [Identity and Key Exchange](#identity-and-key-exchange)
25
+ - [Post-Quantum Cryptography](#post-quantum-cryptography)
26
+ - [Symmetric Encryption](#symmetric-encryption)
27
+ - [Key Derivation](#key-derivation)
28
+ - [Session Security (PCS Ratchet & Hashing)](#session-security)
29
+ - [Sealed Sender](#sealed-sender)
30
+ - [Key Verification Codes](#key-verification-codes)
31
+ - [Key Transparency Log](#key-transparency-log)
32
+ - [Device Registry](#device-registry)
33
+ - [Full E2EE Flow Example](#full-e2ee-flow-example)
12
34
 
13
- - **Universal:** Runs in any modern browser without native binary dependencies.
14
- - **Quantum-Resistant:** Implements the NIST FIPS 203 (ML-KEM-768) standard combined with X25519 for hybrid key exchange.
15
- - **Client-Side E2EE:** Perfect for End-to-End Encryption where keys never leave the user's device.
16
- - **Secure Defaults:** Provides AES-256-GCM, Ed25519, HKDF-SHA256, and post-compromise security ratchets out of the box.
35
+ ---
17
36
 
18
37
  ## Installation
19
38
 
39
+ ### Node.js (Server-side & Native Addon)
40
+
41
+ ```bash
42
+ npm install @vollcrypt/messages-node
43
+ ```
44
+
45
+ Prebuilt native binaries are provided for:
46
+ - Linux x64 (`linux-x64-gnu`)
47
+ - macOS x64 (`darwin-x64`)
48
+ - Windows x64 (`win32-x64-msvc`)
49
+
50
+ ### WebAssembly (Browser / React / Next.js)
51
+
20
52
  ```bash
21
53
  npm install @vollcrypt/messages-wasm
22
54
  ```
23
55
 
24
- *Note: Since this is a WebAssembly module, you may need to configure your bundler (Webpack, Vite, Rollup) to handle `.wasm` files depending on your frontend setup.*
56
+ ### Rust (Cargo)
57
+
58
+ In a Cargo workspace:
59
+ ```toml
60
+ vollcrypt-core = { path = "../vollcrypt/src/core" }
61
+ ```
62
+
63
+ ---
25
64
 
26
65
  ## Quick Start
27
66
 
28
- ```javascript
29
- import * as vollcrypt from '@vollcrypt/messages-wasm';
67
+ ### Node.js — Generate Keys and Encrypt a Message
68
+
69
+ ```ts
70
+ import {
71
+ generateEd25519Keypair,
72
+ encryptAesGcm,
73
+ decryptAesGcm,
74
+ } from '@vollcrypt/messages-node';
75
+ import crypto from 'crypto';
76
+
77
+ // Identity keypair
78
+ const [identitySecret, identityPublic] = generateEd25519Keypair();
79
+
80
+ // Session key (in practice, derived via KEM handshake)
81
+ const sessionKey = crypto.randomBytes(32);
82
+ const plaintext = Buffer.from('Hello, Vollcrypt');
83
+
84
+ // Encrypt
85
+ const ciphertext = encryptAesGcm(sessionKey, plaintext, null);
86
+
87
+ // Decrypt
88
+ const decrypted = decryptAesGcm(sessionKey, ciphertext, null);
89
+ console.log(decrypted.toString()); // Hello, Vollcrypt
90
+ ```
91
+
92
+ ### WebAssembly — Browser
93
+
94
+ ```ts
95
+ import init, {
96
+ generateEd25519Keypair,
97
+ encryptAesGcm,
98
+ decryptAesGcm,
99
+ } from '@vollcrypt/messages-wasm';
100
+
101
+ await init();
102
+
103
+ const [identitySecret, identityPublic] = generateEd25519Keypair();
104
+ const sessionKey = crypto.getRandomValues(new Uint8Array(32));
105
+ const plaintext = new TextEncoder().encode('Hello, Vollcrypt');
106
+
107
+ const ciphertext = encryptAesGcm(sessionKey, plaintext, null);
108
+ const decrypted = decryptAesGcm(sessionKey, ciphertext, null);
109
+ console.log(new TextDecoder().decode(decrypted));
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Architecture and Protocol Design
115
+
116
+ The Vollcrypt messages module provides the core cryptographic mechanisms to build end-to-end encrypted sessions between endpoints. The state machine enforces Perfect Forward Secrecy (PFS) through time-windowed ratchets, and Post-Compromise Security (PCS) through X25519 Diffie-Hellman ratcheting.
117
+
118
+ ### E2EE Lifecycle Sequence Diagram
119
+
120
+ ```mermaid
121
+ sequenceDiagram
122
+ autonumber
123
+ actor Alice as Alice (Client)
124
+ actor Bob as Bob (Client)
125
+ participant Server as Directory Server / KTransparency
126
+
127
+ Note over Bob: Generates Identity Keypair (Ed25519)<br/>Ratchet Keypair (X25519)<br/>PQ KEM Keypair (ML-KEM-768)
128
+ Bob->>Server: Register and upload Bob's public keys
129
+ Server->>Server: Append Bob's keys to signed Key Transparency Log
130
+
131
+ Note over Alice: Generates Identity Keypair (Ed25519)
132
+ Alice->>Server: Query Bob's public keys
133
+ Server-->>Alice: Bob's (X25519_pub, MLKEM_pub) + Signature verification
134
+
135
+ Note over Alice: authenticated_kem_encapsulate(<br/>Alice_X25519_sk, Bob_X25519_pub, Bob_MLKEM_pub, Alice_ID_sk<br/>) -> (ciphertext, shared_secret)
136
+ Note over Alice: Derive SRK & WindowKey_n<br/>encrypt_aes_gcm(payload, window_key) -> ciphertext
137
+ Note over Alice: pack_envelope() -> sealed_packet
138
+ Alice->>Server: Deliver sealed_packet (recipient: Bob)
139
+ Server->>Bob: Forward sealed_packet
140
+
141
+ Note over Bob: unpack_envelope(sealed_packet)
142
+ Note over Bob: authenticated_kem_decapsulate(<br/>Bob_X25519_sk, Alice_X25519_pub, Bob_MLKEM_dk, ciphertext, Alice_ID_pk<br/>) -> shared_secret
143
+ Note over Bob: Verify Alice's signature on ciphertext
144
+ Note over Bob: Derive Bob's WindowKey_n & Decrypt payload
145
+ ```
146
+
147
+ ---
148
+
149
+ ## API Reference
150
+
151
+ The APIs are exposed in both the Node.js native binding (`@vollcrypt/messages-node`) and the WebAssembly binding (`@vollcrypt/messages-wasm`).
152
+
153
+ * **JavaScript/TypeScript target types:** Both bindings expose the identical **camelCase** naming convention. In Node.js, functions accept/return `Buffer` (or `Uint8Array`), whereas in WASM, functions accept/return standard browser `Uint8Array` views.
154
+ * **Rust target:** Native Rust developers should consume the `vollcrypt-core` crate directly, which exposes identical logic via **snake_case** functions.
155
+
156
+ ---
157
+
158
+ ### 1. Identity and Key Exchange
159
+
160
+ #### `generateEd25519Keypair() → [secretKey: Buffer, publicKey: Buffer]`
161
+ Generates a new Ed25519 keypair for user or device signing identity.
162
+ * **Returns:** Array containing the `secretKey` (64 bytes) and `publicKey` (32 bytes).
163
+
164
+ #### `signMessage(secretKey: Uint8Array, message: Uint8Array) → Buffer`
165
+ Signs a message with an Ed25519 private key.
166
+ * **Parameters:**
167
+ * `secretKey`: 64-byte Ed25519 private key buffer.
168
+ * `message`: Arbitrary binary buffer to sign.
169
+ * **Returns:** 64-byte Ed25519 signature buffer.
170
+ * **Errors:** Throws an exception if the secret key length is invalid.
171
+
172
+ #### `verifySignature(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array) → boolean`
173
+ Verifies an Ed25519 digital signature.
174
+ * **Parameters:**
175
+ * `publicKey`: 32-byte Ed25519 public key.
176
+ * `message`: Signed payload.
177
+ * `signature`: 64-byte signature to verify.
178
+ * **Returns:** `true` if the signature is valid; `false` otherwise.
179
+
180
+ #### `generateX25519Keypair() → [secretKey: Buffer, publicKey: Buffer]`
181
+ Generates a new X25519 Diffie-Hellman keypair for classical key exchange.
182
+ * **Returns:** Array containing the X25519 `secretKey` (32 bytes) and `publicKey` (32 bytes).
183
+
184
+ #### `ecdhSharedSecret(ourSecret: Uint8Array, theirPublic: Uint8Array) → Buffer`
185
+ Computes an X25519 ECDH shared secret.
186
+ * **Parameters:**
187
+ * `ourSecret`: 32-byte X25519 secret key.
188
+ * `theirPublic`: 32-byte X25519 public key.
189
+ * **Returns:** 32-byte computed shared secret.
190
+ * **Errors:** Throws if input lengths do not equal 32 bytes.
191
+
192
+ #### `generateMnemonic() → string`
193
+ Generates a new random English BIP-39 mnemonic phrase containing 24 words (256-bit entropy).
194
+ * **Returns:** A 24-word string separated by spaces.
195
+
196
+ #### `mnemonicToSeed(phrase: string, password?: string | null) → Buffer`
197
+ Converts a BIP-39 mnemonic phrase to a 64-byte binary seed.
198
+ * **Parameters:**
199
+ * `phrase`: The 24-word mnemonic string.
200
+ * `password`: Optional passphrase/password protection (salt) string.
201
+ * **Returns:** 64-byte seed.
202
+ * **Errors:** Throws if the mnemonic phrase is invalid or has a wrong checksum.
203
+
204
+ ---
205
+
206
+ ### 2. Post-Quantum Cryptography (PQC)
207
+
208
+ #### `mlKemKeygen() → [decapsKey: Buffer, encapsKey: Buffer]`
209
+ Generates a new ML-KEM-768 keypair (NIST FIPS 203).
210
+ * **Returns:** Array containing the `decapsulationKey` (2400 bytes) and `encapsulationKey` (1184 bytes).
211
+
212
+ #### `mlKemEncapsulate(encapsulationKey: Uint8Array) → MlKemEncapsulationResult`
213
+ Performs ML-KEM-768 key encapsulation.
214
+ * **Parameters:**
215
+ * `encapsulationKey`: 1184-byte ML-KEM public key.
216
+ * **Returns:** Object with:
217
+ * `ciphertext`: Buffer (1088 bytes) to transmit.
218
+ * `sharedSecret`: Buffer (32 bytes) derived key.
219
+ * **Errors:** Throws if the key size is incorrect.
220
+
221
+ #### `mlKemDecapsulate(decapsulationKey: Uint8Array, ciphertext: Uint8Array) → Buffer`
222
+ Decapsulates a KEM ciphertext to extract the shared secret.
223
+ * **Parameters:**
224
+ * `decapsulationKey`: 2400-byte ML-KEM private key.
225
+ * `ciphertext`: 1088-byte encapsulated ciphertext.
226
+ * **Returns:** 32-byte shared secret.
227
+ * **Errors:** Throws if inputs are malformed.
228
+
229
+ #### `hybridKemEncapsulate(x25519OurSecret: Uint8Array, x25519TheirPublic: Uint8Array, mlKemEk: Uint8Array) → HybridKemResult`
230
+ Performs a hybrid KEM encapsulation combining X25519 and ML-KEM-768.
231
+ * **Parameters:**
232
+ * `x25519OurSecret`: Alice's ephemeral 32-byte X25519 secret key.
233
+ * `x25519TheirPublic`: Bob's static 32-byte X25519 public key.
234
+ * `mlKemEk`: Bob's static 1184-byte ML-KEM encapsulation key.
235
+ * **Returns:** Object with:
236
+ * `sharedKey`: Buffer (32 bytes) derived via HKDF-SHA256.
237
+ * `mlKemCiphertext`: Buffer (1088 bytes) KEM output.
238
+
239
+ #### `hybridKemDecapsulate(x25519OurSecret: Uint8Array, x25519TheirPublic: Uint8Array, mlKemDk: Uint8Array, mlKemCt: Uint8Array) → Buffer`
240
+ Decapsulates a hybrid KEM ciphertext.
241
+ * **Parameters:**
242
+ * `x25519OurSecret`: Bob's static X25519 secret key.
243
+ * `x25519TheirPublic`: Alice's ephemeral X25519 public key.
244
+ * `mlKemDk`: Bob's static ML-KEM private key.
245
+ * `mlKemCt`: 1088-byte ML-KEM ciphertext.
246
+ * **Returns:** 32-byte derived shared key.
247
+
248
+ #### `authenticatedKemEncapsulate(ourX25519Sk: Uint8Array, recipientX25519Pub: Uint8Array, recipientMlkemEk: Uint8Array, senderIdentitySk: Uint8Array) → [ciphertext: Buffer, sharedSecret: Buffer]`
249
+ Performs a hybrid KEM encapsulation and signs the ciphertext with the sender's Ed25519 identity key.
250
+ * **Parameters:**
251
+ * `ourX25519Sk`: Sender's ephemeral X25519 secret key.
252
+ * `recipientX25519Pub`: Recipient's static X25519 public key.
253
+ * `recipientMlkemEk`: Recipient's static ML-KEM public key.
254
+ * `senderIdentitySk`: Sender's static Ed25519 signing secret key.
255
+ * **Returns:** Array containing the signed KEM `ciphertext` (1154 bytes) and the `sharedSecret` (32 bytes).
256
+
257
+ #### `authenticatedKemDecapsulate(ourX25519Sk: Uint8Array, senderX25519Pub: Uint8Array, ourMlkemDk: Uint8Array, authenticatedCiphertext: Uint8Array, senderIdentityPk: Uint8Array) → Buffer`
258
+ Verifies the sender's signature before decapsulating the hybrid shared secret.
259
+ * **Parameters:**
260
+ * `ourX25519Sk`: Bob's static X25519 secret key.
261
+ * `senderX25519Pub`: Alice's ephemeral X25519 public key.
262
+ * `ourMlkemDk`: Bob's static ML-KEM decapsulation key.
263
+ * `authenticatedCiphertext`: The signed KEM ciphertext.
264
+ * `senderIdentityPk`: Alice's static Ed25519 identity public key.
265
+ * **Returns:** 32-byte shared secret.
266
+ * **Errors:** Throws if the Ed25519 signature is invalid, refusing KEM negotiation.
267
+
268
+ ---
269
+
270
+ ### 3. Symmetric Encryption (AES-GCM)
271
+
272
+ All symmetric algorithms use AES-256-GCM. Nonces/IVs are 12 bytes and are generated internally using a cryptographically secure random number generator (`OsRng`).
273
+
274
+ #### `encryptAesGcm(key: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array | null) → Buffer`
275
+ Encrypts payload. The internally generated 12-byte IV is prepended to the returned ciphertext.
276
+ * **Parameters:**
277
+ * `key`: 32-byte encryption key.
278
+ * `plaintext`: Raw data to encrypt.
279
+ * `aad`: Optional Additional Authenticated Data.
280
+ * **Returns:** Ciphertext buffer with a length of `12 + plaintext.length + 16` bytes.
281
+
282
+ #### `decryptAesGcm(key: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array | null) → Buffer`
283
+ Decrypts and validates an AES-GCM ciphertext. Assumes a prepended 12-byte IV.
284
+ * **Parameters:**
285
+ * `key`: 32-byte encryption key.
286
+ * `ciphertext`: Ciphertext buffer (containing the 12-byte IV prepended).
287
+ * `aad`: Optional Additional Authenticated Data.
288
+ * **Returns:** Decrypted plaintext.
289
+ * **Errors:** Throws on key length errors or authentication tag mismatch (tampering).
290
+
291
+ #### `encryptAesGcmPadded(key: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array | null) → Buffer`
292
+ Encrypts plaintext after padding it to standard boundaries (using PKCS#7 padding) to hide message size.
293
+
294
+ #### `decryptAesGcmPadded(key: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array | null) → Buffer`
295
+ Decrypts and removes PKCS#7 padding.
296
+
297
+ #### `encryptAesGcmChunked(key: Uint8Array, plaintext: Uint8Array, aad: Uint8Array | null, chunkSize: number) → Buffer`
298
+ Splits payload into multiple chunks of size `chunkSize` and encrypts each independently.
299
+
300
+ #### `decryptAesGcmChunked(key: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array | null) → Buffer`
301
+ Decrypts a chunked ciphertext stream.
302
+
303
+ #### `padMessage(content: Uint8Array) → Buffer`
304
+ Pads message data according to PKCS#7 standard block padding.
305
+
306
+ ---
307
+
308
+ ### 4. Key Derivation and Wrapping
309
+
310
+ #### `derivePbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, keyLen: number) → Buffer`
311
+ Derives a key from a password using PBKDF2-SHA256.
312
+ * **Security Policy:** Always use at least **600,000 iterations** for password-based key wrapping.
313
+
314
+ #### `deriveHkdf(ikm: Uint8Array, salt: Uint8Array | null, info: Uint8Array | null, keyLen: number) → Buffer`
315
+ Derives a cryptographically strong key from input keying material (IKM) using HKDF-SHA256.
316
+
317
+ #### `deriveSrk(dek: Uint8Array, chatId: Uint8Array) → Buffer`
318
+ Derives a 32-byte Session Root Key (SRK) from a Data Encryption Key (DEK) and a unique chat identifier.
319
+
320
+ #### `deriveWindowKey(srk: Uint8Array, windowIndex: number) → Buffer`
321
+ Derives a time-window-specific encryption key from the Session Root Key.
322
+ * `windowIndex`: `floor(UNIX_timestamp / window_size_seconds)`.
323
+
324
+ #### `wrapKey(kek: Uint8Array, keyToWrap: Uint8Array) → Buffer`
325
+ Wraps a key using AES-256 Key Wrap (AES-KW, RFC 3394) under a Key Encryption Key (KEK).
326
+ * **Errors:** Throws if input lengths are invalid.
327
+
328
+ #### `unwrapKey(kek: Uint8Array, wrappedKey: Uint8Array) → Buffer`
329
+ Unwraps an AES-KW wrapped key.
330
+
331
+ ---
332
+
333
+ ### 5. Session Security & Ratcheting
334
+
335
+ #### `generateRatchetKeypair() → [secretKey: Buffer, publicKey: Buffer]`
336
+ Generates a new ephemeral X25519 keypair for Post-Compromise Security (PCS) ratcheting steps.
337
+
338
+ #### `ratchetSrk(currentSrk: Uint8Array, ourRatchetSecret: Uint8Array, theirRatchetPub: Uint8Array, chatId: Uint8Array, ratchetStep: number, isSender: boolean) → Buffer`
339
+ Advances the Session Root Key (SRK) with an ephemeral ECDH exchange value.
340
+ * **Parameters:**
341
+ * `currentSrk`: Current 32-byte Session Root Key.
342
+ * `ourRatchetSecret`: Our ephemeral 32-byte X25519 secret key.
343
+ * `theirRatchetPub`: Their ephemeral X25519 public key.
344
+ * `chatId`: Unique conversation identifier.
345
+ * `ratchetStep`: Monotonic counter representing the ratchet step.
346
+ * `isSender`: True if computing as the sender of the ratcheted message.
347
+ * **Returns:** 32-byte newly derived SRK. Old SRK must be zeroed immediately.
348
+
349
+ #### `shouldRatchet(messageCount: number, windowChanged: boolean, messagesPerRatchet: number, ratchetOnNewWindow: boolean) → boolean`
350
+ Utility logic to check if the client is due to rotate keys.
351
+
352
+ ---
353
+
354
+ ### 6. Transcript Hashing
30
355
 
31
- // Generate an Ed25519 Identity Keypair
32
- const identity = vollcrypt.generate_ed25519_keypair();
33
- console.log("Public Key:", Buffer.from(identity.public_key).toString('hex'));
356
+ Transcript hashing provides replay, deletion, and out-of-order message insertion detection by linking each message to the historical chain hash state.
34
357
 
35
- // Sign and Verify
36
- const message = new TextEncoder().encode("Hello from Vollcrypt WASM!");
37
- const signature = vollcrypt.sign_message(identity.private_key, message);
38
- const isValid = vollcrypt.verify_signature(identity.public_key, message, signature);
358
+ #### `transcriptNew(sessionId: Uint8Array) → Buffer`
359
+ Initializes a new transcript chain state.
360
+ * **Returns:** 32-byte initial chain hash (`SHA-256(sessionId)`).
39
361
 
40
- console.log("Signature Valid:", isValid); // true
362
+ #### `transcriptComputeMessageHash(messageId: Uint8Array, senderId: Uint8Array, timestamp: number, ciphertext: Uint8Array) Buffer`
363
+ Computes the SHA-256 hash of a message envelope.
364
+ * **Returns:** 32-byte message hash.
41
365
 
42
- // Hybrid Key Exchange (X25519)
43
- const alice = vollcrypt.generate_x25519_keypair();
44
- const bob = vollcrypt.generate_x25519_keypair();
45
- const sharedSecret = vollcrypt.ecdh_shared_secret(alice.private_key, bob.public_key);
366
+ #### `transcriptUpdate(chainState: Uint8Array, messageHash: Uint8Array) → Buffer`
367
+ Updates the running chain state.
368
+ * `new_chain = SHA-256(old_chain || messageHash)`.
46
369
 
47
- console.log("Shared Secret Derived successfully.");
370
+ #### `transcriptVerifySync(hashA: Uint8Array, hashB: Uint8Array) → boolean`
371
+ Verifies whether two transcript hashes match using a constant-time comparison.
372
+
373
+ ---
374
+
375
+ ### 7. Sealed Sender
376
+
377
+ #### `sealMessage(recipientX25519Pub: Uint8Array, senderId: Uint8Array, content: Uint8Array) → Buffer`
378
+ Encrypts the sender's identity together with the content under an ephemeral ECDH key, hiding sender metadata from routing servers.
379
+
380
+ #### `unsealMessage(sealedPacket: Uint8Array, ourX25519Sk: Uint8Array) → [senderId: Buffer, content: Buffer]`
381
+ Decrypts a sealed sender packet.
382
+
383
+ ---
384
+
385
+ ### 8. Key Verification Codes
386
+
387
+ #### `generateVerificationCode(keyA: Uint8Array, keyB: Uint8Array, conversationId: Uint8Array) → string`
388
+ Generates a JSON string containing out-of-band verification codes (both formatted decimal groups and emoji sequences) for human comparison.
389
+
390
+ ---
391
+
392
+ ### 9. Key Transparency Log
393
+
394
+ #### `keyLogCreateEntry(userId: Uint8Array, publicKey: Uint8Array, timestamp: number, prevEntryHash: Uint8Array, action: number, signingKey: Uint8Array) → string (json)`
395
+ Generates and signs a Key Transparency append-only log entry.
396
+
397
+ #### `keyLogVerifyChain(entriesJson: string) → boolean`
398
+ Verifies the integrity, signature sequence, and linkage of a JSON array of Key Transparency log entries.
399
+
400
+ ---
401
+
402
+ ### 10. Device Registry
403
+
404
+ #### `registryEmpty() → string (json)`
405
+ Returns an empty device registry state object.
406
+
407
+ #### `registryAddDevice(registryJson: string, deviceId: string, name: string, addedAt: number, publicKey: string) → string (json)`
408
+ Adds a device public key to the registry JSON string.
409
+
410
+ #### `registryRevokeDevice(registryJson: string, deviceId: string) → string (json)`
411
+ Revokes a device by its ID.
412
+
413
+ ---
414
+
415
+ ## Full E2EE Flow Example
416
+
417
+ This script illustrates the complete cryptographic pipeline for generating keys, performing the Hybrid KEM handshake, deriving keys, encrypting a message via Sealed Sender, updating transcripts, and decrypting the result.
418
+
419
+ ```ts
420
+ import {
421
+ generateEd25519Keypair,
422
+ generateX25519Keypair,
423
+ generateMlKem768Keypair,
424
+ authenticatedKemEncapsulate,
425
+ authenticatedKemDecapsulate,
426
+ deriveSrk,
427
+ deriveWindowKey,
428
+ transcriptNew,
429
+ transcriptComputeMessageHash,
430
+ transcriptUpdate,
431
+ transcriptVerifySync,
432
+ encryptAesGcm,
433
+ decryptAesGcm,
434
+ sealMessage,
435
+ unsealMessage
436
+ } from '@vollcrypt/messages-node';
437
+
438
+ // 1. Participant Identity Keypair Generation
439
+ const [aliceIdSk, aliceIdPk] = generateEd25519Keypair();
440
+ const [aliceX25519Sk, aliceX25519Pk] = generateX25519Keypair();
441
+ const [aliceMlkemDecaps, aliceMlkemEncaps] = generateMlKem768Keypair(); // [decaps, encaps]
442
+
443
+ const [bobIdSk, bobIdPk] = generateEd25519Keypair();
444
+ const [bobX25519Sk, bobX25519Pk] = generateX25519Keypair();
445
+ const [bobMlkemDecaps, bobMlkemEncaps] = generateMlKem768Keypair();
446
+
447
+ // 2. Hybrid Handshake Execution
448
+ const conversationId = Buffer.from('conv-alice-bob-001');
449
+
450
+ // Alice generates an ephemeral X25519 keypair for the hybrid KEM
451
+ const [aliceEphSk, aliceEphPk] = generateX25519Keypair();
452
+
453
+ // Alice encapsulates key material for Bob
454
+ const [authCiphertext, aliceSharedSecret] = authenticatedKemEncapsulate(
455
+ aliceEphSk, // Alice's ephemeral X25519 secret
456
+ bobX25519Pk, // Bob's static X25519 public key
457
+ bobMlkemEncaps, // Bob's static ML-KEM encapsulation key
458
+ aliceIdSk // Alice's signing key (for authentication)
459
+ );
460
+
461
+ // Bob receives Alice's ephemeral public key (extracted from handshake metadata or sent alongside)
462
+ // and decapsulates the shared secret
463
+ const bobSharedSecret = authenticatedKemDecapsulate(
464
+ bobX25519Sk, // Bob's static X25519 secret key
465
+ aliceEphPk, // Alice's ephemeral X25519 public key
466
+ bobMlkemDecaps, // Bob's static ML-KEM decapsulation key
467
+ authCiphertext, // The signed KEM ciphertext envelope
468
+ aliceIdPk // Alice's static Ed25519 public key
469
+ );
470
+
471
+ // 3. Key Derivation (SRK & Window Keys)
472
+ const aliceSrk = deriveSrk(aliceSharedSecret, conversationId);
473
+ const bobSrk = deriveSrk(bobSharedSecret, conversationId);
474
+
475
+ const windowIndex = Math.floor(Date.now() / 1000 / 3600);
476
+ const aliceWindowKey = deriveWindowKey(aliceSrk, windowIndex);
477
+ const bobWindowKey = deriveWindowKey(bobSrk, windowIndex);
478
+
479
+ // 4. Transcript Initialization
480
+ let aliceChain = transcriptNew(conversationId);
481
+ let bobChain = transcriptNew(conversationId);
482
+
483
+ // 5. Encrypting & Sealing the Message (Alice)
484
+ const messageId = Buffer.from('msg-001');
485
+ const senderId = Buffer.from('alice@example.com');
486
+ const timestamp = Math.floor(Date.now() / 1000);
487
+ const aad = Buffer.concat([messageId, senderId, Buffer.from(timestamp.toString())]);
488
+ const plaintext = Buffer.from('Hello Bob');
489
+
490
+ const ciphertext = encryptAesGcm(aliceWindowKey, plaintext, aad);
491
+ const sealed = sealMessage(bobX25519Pk, senderId, ciphertext);
492
+
493
+ // Update Alice's Transcript chain hash state
494
+ const msgHash = transcriptComputeMessageHash(messageId, senderId, timestamp, ciphertext);
495
+ aliceChain = transcriptUpdate(aliceChain, msgHash);
496
+
497
+ // 6. Unsealing & Decrypting the Message (Bob)
498
+ const [revealedSender, revealedCiphertext] = unsealMessage(sealed, bobX25519Sk);
499
+ const decrypted = decryptAesGcm(bobWindowKey, revealedCiphertext, aad);
500
+
501
+ // Update Bob's Transcript chain hash state
502
+ bobChain = transcriptUpdate(bobChain, msgHash);
503
+
504
+ // 7. Verify Integrity and Equivalence
505
+ console.log(decrypted.toString()); // "Hello Bob"
506
+ console.log(transcriptVerifySync(aliceChain, bobChain)); // true
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Secure Client Integration Guidelines
512
+
513
+ When integrating `@vollcrypt/messages-node` or `@vollcrypt/messages-wasm` inside client applications, you must obey the following cryptographic safety mandates:
514
+
515
+ ### 1. Active Memory Zeroization
516
+ JavaScript runtimes do not automatically clear memory and are vulnerable to garbage-collection delays, which leaves raw keys exposed in memory.
517
+ * **Clear buffers immediately after use:** Overwrite raw sensitive private keys or derived keys using `TypedArray.prototype.fill(0)`.
518
+ ```ts
519
+ // Example: Zeroizing KEK or salt buffers in JS
520
+ const rawKey = new Uint8Array([/* secret bytes */]);
521
+ try {
522
+ // perform cryptographic operation...
523
+ } finally {
524
+ rawKey.fill(0); // Safely scrubs key from memory
525
+ }
526
+ ```
527
+
528
+ ### 2. Leverage Non-Extractable CryptoKeys (Web Crypto API)
529
+ Never store raw public/private keys in plaintext inside Javascript context variables or state trees.
530
+ * When receiving raw key buffers from the WebAssembly module, immediately import them into browser-native `SubtleCrypto` with `extractable: false`.
531
+ * Scrub the original WASM-returned buffer immediately afterwards.
532
+ ```ts
533
+ const rawWasmKey = ...; // Uint8Array
534
+ const cryptoKey = await window.crypto.subtle.importKey(
535
+ "raw",
536
+ rawWasmKey.buffer,
537
+ { name: "AES-GCM" },
538
+ false, // extractable = false prevents programmatic reading via XSS
539
+ ["encrypt", "decrypt"]
540
+ );
541
+ rawWasmKey.fill(0); // Zeroize the raw buffer
542
+ ```
543
+
544
+ ### 3. Avoid Persistent Insecure Storage
545
+ * **Never** write raw secrets, seeds, or unencrypted keys to `localStorage`, `sessionStorage`, or cookies.
546
+ * Always wrap keys using `wrapKey()` (AES-256-KW) with a key encryption key (KEK) derived via `derivePbkdf2()` (with at least 600,000 iterations) from a user master password before writing to `IndexedDB` or local disk.
547
+
548
+ ---
549
+
550
+ ## Test Coverage and Verification
551
+
552
+ Vollcrypt Messages is covered by a highly comprehensive suite of **219 unit, integration, and adversarial tests** that are run in the Rust core module.
553
+
554
+ To execute the tests:
555
+ ```bash
556
+ cargo test --manifest-path vollcrypt-messages/core/Cargo.toml
557
+ ```
558
+
559
+ ### Verification Status
560
+
561
+ All tests compile warning-free and pass successfully:
562
+
563
+ | Test Category | Total Tests | Passed | Ignored | Status |
564
+ | :--- | :---: | :---: | :---: | :---: |
565
+ | **Identity and Key Exchange** | 18 | 18 | 0 | ✅ Passed |
566
+ | **Post-Quantum Cryptography (PQC)** | 14 | 14 | 0 | ✅ Passed |
567
+ | **Symmetric Encryption (AES-GCM)** | 35 | 35 | 0 | ✅ Passed |
568
+ | **Key Derivation and Wrapping** | 12 | 12 | 0 | ✅ Passed |
569
+ | **OOB Verification Codes** | 10 | 10 | 0 | ✅ Passed |
570
+ | **Key Transparency Log** | 8 | 8 | 0 | ✅ Passed |
571
+ | **Device Registry** | 5 | 5 | 0 | ✅ Passed |
572
+ | **Adversarial & Stress Integration Suites** | 8 | 8 | 0 | ✅ Passed |
573
+ | **Others** | 109 | 109 | 0 | ✅ Passed |
574
+ | **Total** | **219** | **219** | **0** | **✅ All Tests Passed** |
575
+
576
+ This includes advanced stress tests covering network chaos and out-of-order recovery, concurrent PCS ratchet updates and transactional state rollbacks, sealed sender byte malleability (verifying immunity to bit-flips across all packet bytes), and unwinding panic memory zeroization checks.
577
+
578
+ ---
579
+
580
+ ## Performance Benchmarks
581
+
582
+ Vollcrypt Messages includes a dedicated high-performance benchmarking suite in Rust to measure the raw execution speed and throughput of the cryptographic core.
583
+
584
+ ### Running the Benchmarks
585
+ To run the performance benchmarks:
586
+ ```bash
587
+ cargo run --manifest-path vollcrypt-messages/core/Cargo.toml --release --bin perf
588
+ ```
589
+
590
+ *Note: For maximum performance on x86_64 CPUs, ensure you build with hardware AES-NI acceleration enabled:*
591
+ ```bash
592
+ RUSTFLAGS="-C target-cpu=native" cargo run --manifest-path vollcrypt-messages/core/Cargo.toml --release --bin perf
48
593
  ```
49
594
 
50
- ## Documentation
595
+ #### Automated Hardware & Resource Monitoring
596
+ We provide an automated PowerShell runner script ([benchmark_runner.ps1](file:///c:/Users/iTopya/Desktop/Project/vollcrypt/vollcrypt-messages/core/benchmark_runner.ps1)) that compiles the benchmark binary under release mode with AES-NI acceleration, collects your hardware specifications, tracks real-time CPU/RAM/GPU/Disk utilization, and formats everything into a markdown report.
597
+
598
+ To run this automated script on Windows:
599
+ ```powershell
600
+ powershell -ExecutionPolicy Bypass -File vollcrypt-messages/core/benchmark_runner.ps1
601
+ ```
602
+ The script will run the benchmarks, track resource usage, and generate a new [performance_report_optimized.md](file:///c:/Users/iTopya/Desktop/Project/vollcrypt/vollcrypt-messages/core/performance_report_optimized.md) inside `vollcrypt-messages/core/`.
603
+
604
+ ### Reference Benchmark Figures
605
+
606
+ *The following values represent typical execution speeds measured under a Zero-Allocation architecture utilizing native hardware AES-NI acceleration on standard modern hardware (e.g. Intel Core i7 / AMD Ryzen 5 class CPU):*
607
+
608
+ #### Symmetric Throughput (AES-256-GCM)
609
+ | Operation | Data Payload Size | Throughput (Pure Software) | Throughput (Hardware AES-NI) |
610
+ | :--- | :---: | :---: | :---: |
611
+ | **AES-GCM Encryption** | 64 KB | ~300 MB/s | ~9,200 MB/s |
612
+ | **AES-GCM Decryption** | 64 KB | ~295 MB/s | ~10,200 MB/s |
613
+ | **AES-GCM Encryption** | 1 MB | ~290 MB/s | ~9,300 MB/s |
614
+ | **AES-GCM Decryption** | 1 MB | ~300 MB/s | ~10,100 MB/s |
615
+ | **AES-GCM Chunked (1MB Chunks)** | 16 MB | ~240 MB/s | ~5,200 MB/s |
616
+
617
+ #### Cryptographic Primitives & Handshakes
618
+ | Primitive | Operation / Scenario | Execution Speed (ops/second) |
619
+ | :--- | :---: | :---: |
620
+ | **HKDF-SHA256** | Key Expansion (32-byte key) | ~850,000 ops/s |
621
+ | **PBKDF2-HMAC-SHA256** | Password Hashing (600,000 iterations) | ~11.6 ops/s (Single thread) |
622
+ | **ML-KEM-768** | Encapsulation (Client Handshake) | ~8,300 ops/s |
623
+ | **ML-KEM-768** | Decapsulation (Recipient Handshake) | ~6,500 ops/s |
624
+ | **Sealed Sender** | Ephemeral DH Seal (Alice) | ~16,600 ops/s |
625
+ | **Sealed Sender** | Decryption & Parse Unseal (Bob) | ~22,400 ops/s |
626
+ | **PCS Ratchet** | Ephemeral X25519 Key Pair Gen | ~64,600 ops/s |
627
+ | **PCS Ratchet** | Session KDF Update (Step Computation) | ~21,800 ops/s |
628
+ | **Key Verification** | Out-of-band Numeric/Emoji Code Gen | ~473,000 ops/s |
629
+ | **Key Transparency** | 100-entry Chain Verification | ~300 ops/s |
630
+
631
+ #### Asymmetric Identity Primitives (Ed25519 & BIP-39)
632
+ | Primitive | Operation / Scenario | Execution Speed (ops/second) |
633
+ | :--- | :---: | :---: |
634
+ | **Ed25519 Signing** | Sign 1KB Payload | ~28,400 ops/s |
635
+ | **Ed25519 Verification** | Verify 1KB Signature | ~30,600 ops/s |
636
+ | **Ed25519 Signing** | Sign 1MB Payload | ~410 ops/s |
637
+ | **Ed25519 Verification** | Verify 1MB Signature | ~820 ops/s |
638
+ | **BIP-39 Mnemonic** | 24-word Mnemonic to 64-byte Seed | ~1,090 ops/s |
639
+
640
+ #### Multi-threaded Concurrency Throughput
641
+ | Scenario | Execution Context | Aggregate Throughput |
642
+ | :--- | :--- | :---: |
643
+ | **Multi-threaded Handshake Scaling** | Concurrent ML-KEM Encaps + Unseal (12 threads) | ~88,200 ops/s |
644
+
645
+ #### Memory State Lookup Latencies (Replay Prevention Store)
646
+ | Operation | Target Hash State | Average Latency |
647
+ | :--- | :---: | :---: |
648
+ | **Hash Lookup (Hit)** | Populated with 100,000 hashes | ~91.64 ns |
649
+ | **Hash Lookup (Miss)** | Populated with 100,000 hashes | ~36.40 ns |
650
+ | **Hash Insertion** | Populated with 100,000 hashes | ~109.78 ns |
651
+
652
+ ### Measured Performance on Test Device
653
+
654
+ The actual performance benchmarks measured on the test device, along with the device's hardware specs and real-time resource utilization, are recorded in the [Performance Report](file:///c:/Users/iTopya/Desktop/Project/vollcrypt/vollcrypt-messages/core/performance_report_optimized.md).
655
+
656
+ #### System Specifications
657
+ * **Operating System (OS):** Microsoft Windows 11 Home
658
+ * **Processor (CPU):** AMD Ryzen 5 7500F 6-Core Processor
659
+ * **System Memory (RAM):** 15.62 GB
660
+ * **Graphics Controller (GPU):** NVIDIA GeForce GTX 1660 SUPER
661
+ * **Disk Volumes:**
662
+ * C: 465.1 GB total, 40.1 GB free
663
+ * D: 931.5 GB total, 733.8 GB free
664
+
665
+ #### Executed Benchmark Metrics (with Hardware AES-NI & Zero-Allocation)
666
+ * **AES-GCM-RAW (64 KB)**: Encrypt ~9238.73 MB/s | Decrypt ~10219.26 MB/s
667
+ * **AES-GCM-RAW (1 MB)**: Encrypt ~9324.03 MB/s | Decrypt ~10175.09 MB/s
668
+ * **AES-GCM-RAW (16 MB)**: Encrypt ~6814.46 MB/s | Decrypt ~7829.51 MB/s
669
+ * **AES-GCM (1 KB)**: Encrypt ~4629.74 MB/s | Decrypt ~6033.75 MB/s
670
+ * **AES-GCM (64 KB)**: Encrypt ~8170.76 MB/s | Decrypt ~6152.79 MB/s
671
+ * **AES-GCM (1 MB)**: Encrypt ~2940.28 MB/s | Decrypt ~10270.89 MB/s
672
+ * **AES-GCM (16 MB)**: Encrypt ~4413.80 MB/s | Decrypt ~7205.84 MB/s
673
+ * **AES-GCM-CHUNKED (16 MB payload, 1 MB chunks)**: Encrypt ~5242.40 MB/s | Decrypt ~5857.67 MB/s
674
+ * **AES-GCM-CHUNKED (64 MB payload, 1 MB chunks)**: Encrypt ~4667.03 MB/s | Decrypt ~4630.15 MB/s
675
+ * **HKDF-SHA256**: ~851,788.76 ops/s
676
+ * **PBKDF2-HMAC-SHA256 (600k iterations)**: ~11.66 ops/s
677
+ * **ML-KEM-768 Encapsulation**: ~8,357.29 ops/s
678
+ * **ML-KEM-768 Decapsulation**: ~6,582.30 ops/s
679
+ * **Sealed Sender Seal**: ~16,660.06 ops/s
680
+ * **Sealed Sender Unseal**: ~22,491.93 ops/s
681
+ * **PCS Ratchet Keypair Gen**: ~64,686.40 ops/s
682
+ * **PCS Ratchet Step Computation**: ~21,814.25 ops/s
683
+ * **Key Verification Code Gen**: ~473,126.42 ops/s
684
+ * **Key Transparency Log (100 entries chain verification)**: ~304.31 ops/s
685
+ * **Ed25519 Signing (1KB)**: ~28,494.74 ops/s
686
+ * **Ed25519 Verification (1KB)**: ~30,631.25 ops/s
687
+ * **Ed25519 Signing (1MB)**: ~414.84 ops/s
688
+ * **Ed25519 Verification (1MB)**: ~829.89 ops/s
689
+ * **BIP-39 Mnemonic to Seed**: ~1098.39 ops/s
690
+ * **Multi-threaded Handshake Scaling (12 threads)**: ~88,248.92 aggregate ops/s
691
+ * **Replay Store Lookup (Hit)**: ~91.64 ns
692
+ * **Replay Store Lookup (Miss)**: ~36.40 ns
693
+ * **Replay Store Insertion**: ~109.78 ns
694
+
695
+ #### Resource Utilization during run
696
+ * **Total Test Duration:** 20.67 seconds
697
+ * **System CPU Utilization:** Min 9% | Max 100% | Avg 36.6%
698
+ * **Memory Utilization (RAM):** Min 36.4% | Max 40.3% | Avg 37.9%
699
+ * **Graphics Card Utilization (GPU):** Min 4% | Max 6% | Avg 5.1%
700
+ * **Disk Read Speed:** Max 10.3 MB/s | Avg 1.29 MB/s
701
+ * **Disk Write Speed:** Max 49.29 MB/s | Avg 6.94 MB/s
51
702
 
52
- For full API documentation, architecture details, and the high-performance Native Node.js equivalent, please refer to the [Vollcrypt Main Repository](https://github.com/BeratVural/vollcrypt).
53
703
 
54
- ## License
55
704
 
56
- This project is dual-licensed under:
57
- - **GPL-3.0-only** (for open-source distribution) — see the [LICENSE-GPL](https://github.com/BeratVural/vollcrypt/blob/main/LICENSE-GPL) file.
58
- - **Commercial License** (for proprietary software integrations) — see the [LICENSE-COMMERCIAL.md](https://github.com/BeratVural/vollcrypt/blob/main/LICENSE-COMMERCIAL.md) file.
59
705
 
60
- For inquiries regarding commercial license purchases, pricing tiers, or custom enterprise terms, please contact Berat Vural at [berat.vural.tr@gmail.com](mailto:berat.vural.tr@gmail.com).