leviathan-crypto 2.0.0 → 2.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/CLAUDE.md +171 -7
- package/LICENSE +4 -0
- package/README.md +109 -54
- package/SECURITY.md +125 -233
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +66 -2
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +97 -1
- package/dist/chacha20/index.js +139 -11
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +93 -13
- package/dist/chacha20/pool-worker.js +12 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/ct-wasm.js +1 -1
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +69 -26
- package/dist/docs/architecture.md +600 -520
- package/dist/docs/argon2id.md +17 -14
- package/dist/docs/chacha20.md +146 -39
- package/dist/docs/exports.md +46 -10
- package/dist/docs/fortuna.md +339 -122
- package/dist/docs/init.md +24 -25
- package/dist/docs/loader.md +142 -47
- package/dist/docs/serpent.md +139 -41
- package/dist/docs/sha2.md +77 -19
- package/dist/docs/sha3.md +81 -15
- package/dist/docs/types.md +156 -15
- package/dist/docs/utils.md +171 -81
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/kyber.d.ts +1 -1
- package/dist/embedded/kyber.js +1 -1
- package/dist/embedded/serpent-pool-worker.d.ts +1 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +1 -1
- package/dist/fortuna.d.ts +14 -8
- package/dist/fortuna.js +144 -50
- package/dist/index.d.ts +8 -6
- package/dist/index.js +6 -5
- package/dist/init.d.ts +0 -2
- package/dist/init.js +83 -3
- package/dist/kyber/indcpa.js +4 -4
- package/dist/kyber/index.js +25 -5
- package/dist/kyber/kem.js +56 -1
- package/dist/kyber/suite.d.ts +1 -2
- package/dist/kyber/suite.js +1 -0
- package/dist/kyber/types.d.ts +1 -0
- package/dist/kyber/validate.d.ts +8 -4
- package/dist/kyber/validate.js +18 -14
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +7 -2
- package/dist/loader.js +25 -28
- package/dist/ratchet/index.d.ts +6 -0
- package/dist/ratchet/index.js +37 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +136 -50
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +61 -1
- package/dist/serpent/index.js +92 -7
- package/dist/serpent/pool-worker.js +25 -95
- package/dist/serpent/serpent-cbc.d.ts +14 -4
- package/dist/serpent/serpent-cbc.js +58 -34
- package/dist/serpent/shared-ops.d.ts +83 -0
- package/dist/serpent/shared-ops.js +213 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/index.d.ts +1 -0
- package/dist/sha2/index.js +15 -1
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +17 -2
- package/dist/sha3/index.js +79 -7
- package/dist/stream/header.js +5 -5
- package/dist/stream/open-stream.js +36 -14
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +47 -8
- package/dist/stream/seal-stream.js +29 -11
- package/dist/stream/types.d.ts +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/utils.d.ts +7 -8
- package/dist/utils.js +73 -40
- package/dist/wasm-source.d.ts +9 -8
- package/package.json +79 -64
|
@@ -1,293 +1,425 @@
|
|
|
1
|
-
# Architecture
|
|
2
1
|
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
### Architecture
|
|
3
|
+
|
|
4
|
+
Overview of Leviathan Crypto's architecture design, comprising six independent WASM modules unified by a misuse-resistant TypeScript API, which delivers both Serpent's paranoia and ChaCha's elegance as a zero-dependency, tree-shakable, post-quantum library.
|
|
5
5
|
|
|
6
6
|
> ### Table of Contents
|
|
7
|
+
>
|
|
7
8
|
> - [Vision](#vision)
|
|
8
9
|
> - [Scope](#scope)
|
|
9
10
|
> - [Repository Structure](#repository-structure)
|
|
10
11
|
> - [Architecture: TypeScript over WASM](#architecture-typescript-over-wasm)
|
|
11
|
-
> - [
|
|
12
|
+
> - [Six Independent WASM Modules](#six-independent-wasm-modules)
|
|
12
13
|
> - [init() API](#init-api)
|
|
13
14
|
> - [Public API Classes](#public-api-classes)
|
|
14
15
|
> - [Build Pipeline](#build-pipeline)
|
|
15
|
-
> - [Module
|
|
16
|
+
> - [Module Relationships](#module-relationships)
|
|
16
17
|
> - [npm Package](#npm-package)
|
|
17
18
|
> - [Buffer Layouts](#buffer-layouts)
|
|
18
19
|
> - [Test Suite](#test-suite)
|
|
19
20
|
> - [Correctness Contract](#correctness-contract)
|
|
20
21
|
> - [Known Limitations](#known-limitations)
|
|
21
22
|
|
|
22
|
-
---
|
|
23
|
-
|
|
24
23
|
## Vision
|
|
25
24
|
|
|
26
|
-
`leviathan-crypto` is a
|
|
27
|
-
|
|
25
|
+
`leviathan-crypto` is a post-quantum WASM cryptography library with zero dependencies, tree-shakable, and side-effect free.
|
|
26
|
+
|
|
27
|
+
**JS is the problem, SIMD WASM is the solution.** JavaScript engines offer no formal constant-time guarantees. JIT compilers optimize based on runtime patterns, which leak secrets through cache access and instruction timing. By contrast, [WebAssembly](https://github.com/xero/leviathan-crypto/wiki/wasm) executes outside the JIT entirely, running compiled bytecode with linear memory you control. No speculative optimization, no value-dependent branches between source and execution.
|
|
28
28
|
|
|
29
|
-
**
|
|
29
|
+
**WebAssembly is the correctness layer.** All algorithm logic lives in WASM. Six AssemblyScript modules ([`serpent`](https://github.com/xero/leviathan-crypto/wiki/asm_serpent), [`chacha20`](https://github.com/xero/leviathan-crypto/wiki/asm_chacha), [`sha2`](https://github.com/xero/leviathan-crypto/wiki/asm_sha2), [`sha3`](https://github.com/xero/leviathan-crypto/wiki/asm_sha3), [`kyber`](https://github.com/xero/leviathan-crypto/wiki/asm_kyber), and [`ct`](https://github.com/xero/leviathan-crypto/wiki/asm_ct)) compile independently to WASM with SIMD where it pays off. Each module is its own instance with its own linear memory. Within a module, stateful primitives share the instance, and a runtime exclusivity model keeps them from interfering with each other.
|
|
30
30
|
|
|
31
|
-
**
|
|
31
|
+
**TypeScript is the ergonomics layer.** The strongly-typed public API covers [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal), [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream), [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool), [`Fortuna`](https://github.com/xero/leviathan-crypto/wiki/fortuna), [`HKDF`](https://github.com/xero/leviathan-crypto/wiki/sha2#hkdf_sha256), [`SkippedKeyStore`](https://github.com/xero/leviathan-crypto/wiki/ratchet#skippedkeystore), and others. The design is misuse-resistant by default. Authentication is verify-then-decrypt; key material wipes on dispose; validation runs before any crypto path; one-shot AEADs lock on first call. TypeScript never implements cryptographic algorithms. It orchestrates the WASM layer and enforces best practice through API shape, not convention.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
execution engine from leviathan-wasm. Developers get ergonomic, well-typed classes.
|
|
35
|
-
The runtime gets deterministic cryptographic computation outside the JIT.
|
|
33
|
+
**[Serpent-256](https://github.com/xero/leviathan-crypto/wiki/serpent_reference): maximum paranoia.** 32 rounds of S-boxes in pure Boolean logic with no table lookups. An ouroboros devouring every bit, in every block, through every round.
|
|
36
34
|
|
|
37
|
-
**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
layer handles API ergonomics; the WASM layer handles all cryptographic computation.
|
|
35
|
+
**[XChaCha20-Poly1305](https://github.com/xero/leviathan-crypto/wiki/chacha_reference): precise elegance.** 20 rounds of add-rotate-XOR, choreography without S-boxes or cache-timing leakage. A dance closing with Poly1305's unconditional forgery bound.
|
|
36
|
+
|
|
37
|
+
**Two ciphers, one interface.** Both share the [`CipherSuite`](https://github.com/xero/leviathan-crypto/wiki/ciphersuite) shape and slot into [`Seal`](https://github.com/xero/leviathan-crypto/wiki/aead#seal), [`SealStream`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstream), and [`SealStreamPool`](https://github.com/xero/leviathan-crypto/wiki/aead#sealstreampool) interchangeably. Post-quantum extends the same model, [`KyberSuite`](https://github.com/xero/leviathan-crypto/wiki/ciphersuite#kybersuite) wraps [`MlKem512`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets), [`MlKem768`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets), or [`MlKem1024`](https://github.com/xero/leviathan-crypto/wiki/kyber#parameter-sets) around either cipher, and the [SPQR ratchet](https://github.com/xero/leviathan-crypto/wiki/ratchet) builds forward-secret sessions on top.
|
|
41
38
|
|
|
42
39
|
---
|
|
43
40
|
|
|
44
41
|
## Scope
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
**Primitives.** WASM algorithms with their TypeScript wrapper classes.
|
|
44
|
+
|
|
45
|
+
| Module | Algorithms | TypeScript API |
|
|
46
|
+
| ----------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| [`serpent`](./asm_serpent.md) | Serpent-256 block cipher: ECB, CTR, CBC | [`Serpent`](./serpent.md) (cipher class for mode operations) |
|
|
48
|
+
| [`chacha20`](./asm_chacha.md) | ChaCha20, Poly1305, ChaCha20-Poly1305, XChaCha20-Poly1305 | [`ChaCha20`](./chacha20.md#chacha20), [`Poly1305`](./chacha20.md#poly1305), [`XChaCha20Poly1305`](./chacha20.md#xchacha20poly1305) |
|
|
49
|
+
| [`sha2`](./asm_sha2.md) | SHA-256, SHA-384, SHA-512, HMAC variants, HKDF variants | [`SHA256`](./sha2.md#sha256), [`SHA384`](./sha2.md#sha384), [`SHA512`](./sha2.md#sha512), [`HMAC_SHA256`](./sha2.md#hmac_sha256), [`HMAC_SHA384`](./sha2.md#hmac_sha384), [`HMAC_SHA512`](./sha2.md#hmac_sha512), [`HKDF_SHA256`](./sha2.md#hkdf_sha256), [`HKDF_SHA512`](./sha2.md#hkdf_sha512) |
|
|
50
|
+
| [`sha3`](./asm_sha3.md) | SHA3-224/256/384/512, SHAKE128, SHAKE256 (XOFs) | [`SHA3_256`](./sha3.md#sha3_256), [`SHA3_512`](./sha3.md#sha3_512), [`SHAKE256`](./sha3.md#shake256) |
|
|
51
|
+
| [`kyber`](./asm_kyber.md) | MlKem512, MlKem768, MlKem1024 | [`MlKem512`](./kyber.md#parameter-sets), [`MlKem768`](./kyber.md#parameter-sets), [`MlKem1024`](./kyber.md#parameter-sets) |
|
|
52
|
+
| [`ct`](./asm_ct.md) | Constant-time comparison primitives | [`constantTimeEqual`](./utils.md#constanttimeequal) |
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
**Cipher Suites.** Composition of WASM modules into complete cipher packages.
|
|
56
|
+
|
|
57
|
+
| Suite | Composition | Use case |
|
|
58
|
+
| ------------------------------------- | ------------------------------------ | ----------------------------------- |
|
|
59
|
+
| [`SerpentCipher`](./ciphersuite.md#serpentcipher) | `serpent` + `sha2` (CBC+HMAC-SHA256) | Authenticated encryption via STREAM |
|
|
60
|
+
| [`XChaCha20Cipher`](./ciphersuite.md#xchacha20cipher) | `chacha20` (XChaCha20-Poly1305 AEAD) | Streaming authenticated encryption |
|
|
61
|
+
| [`KyberSuite`](./ciphersuite.md#kybersuite) | `kyber` + (any cipher) | Post-quantum key encapsulation |
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
-
| `serpent` | Serpent-256 block cipher: ECB, CTR mode, CBC mode |
|
|
51
|
-
| `serpent` + `sha2` | `SerpentCipher` (CipherSuite for STREAM construction: CBC+HMAC-SHA256) |
|
|
52
|
-
| `chacha20` | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD, `XChaCha20Cipher` (CipherSuite for streaming AEAD) |
|
|
53
|
-
| `sha2` | SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, HKDF-SHA256, HKDF-SHA512 |
|
|
54
|
-
| `sha3` / `keccak` | SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 (XOFs, multi-squeeze). `'keccak'` is an alias for `'sha3'`; same binary, same instance slot. |
|
|
55
|
-
| `kyber` | `MlKem512`, `MlKem768`, `MlKem1024`. Requires `sha3` for Keccak sponge operations. |
|
|
56
|
-
| `stream` | `SealStream`, `OpenStream` (cipher-agnostic STREAM construction), `SealStreamPool` (worker-based parallelism) |
|
|
63
|
+
**High-Level Constructs.** Pure TypeScript abstractions over cipher suites.
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
| API | Dependencies | Purpose |
|
|
66
|
+
| ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | -------------------------------------------- |
|
|
67
|
+
| [`Seal`](./aead.md#seal) / [`SealStream`](./aead.md#sealstream) / [`SealStreamPool`](./aead.md#sealstreampool) | Any CipherSuite | One-shot, streaming, and parallel encryption |
|
|
68
|
+
| [`ratchetInit`](./ratchet.md#ratchetinitsk-context), [`KDFChain`](./ratchet.md#kdfchain), [`kemRatchetEncap`](./ratchet.md#kemratchetencapkem-rk-peerek-context)/[`kemRatchetDecap`](./ratchet.md#kemratchetdecapkem-rk-dk-kemct-ownek-context) | `sha2`; `kyber` + `sha3` for KEM | Forward-secret session ratcheting (SPQR) |
|
|
69
|
+
| [`Fortuna`](./fortuna.md) | Cipher PRF + HashFn | Cryptographically-secure RNG |
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
**Utilities.** Pure TypeScript helpers, no `init()` dependency.
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
| Utility | Purpose |
|
|
74
|
+
| --------------------------------------------------- | ------------------------------------------------ |
|
|
75
|
+
| [`hexToBytes`](./utils.md#hextobytes), [`bytesToHex`](./utils.md#bytestohex) | Hex/byte conversions |
|
|
76
|
+
| [`wipe`](./utils.md#wipe) | Secure memory zeroing |
|
|
77
|
+
| [`xor`](./utils.md#xor), [`concat`](./utils.md#concat) | Byte operations |
|
|
78
|
+
| [`randomBytes`](./utils.md#randombytes) | One-off random byte generation |
|
|
79
|
+
| [`constantTimeEqual`](./utils.md#constanttimeequal) | Timing-attack resistant comparison (WASM-backed) |
|
|
64
80
|
|
|
65
81
|
---
|
|
66
82
|
|
|
67
83
|
## Repository Structure
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
│
|
|
148
|
-
│
|
|
149
|
-
│
|
|
150
|
-
│
|
|
151
|
-
│
|
|
152
|
-
│
|
|
153
|
-
|
|
154
|
-
│
|
|
155
|
-
|
|
156
|
-
│
|
|
157
|
-
│
|
|
158
|
-
│
|
|
159
|
-
│
|
|
160
|
-
│
|
|
161
|
-
│
|
|
162
|
-
│
|
|
163
|
-
│
|
|
164
|
-
│
|
|
165
|
-
│
|
|
166
|
-
│
|
|
167
|
-
│
|
|
168
|
-
|
|
169
|
-
│
|
|
170
|
-
│
|
|
171
|
-
│
|
|
172
|
-
├──
|
|
173
|
-
│ ├──
|
|
174
|
-
│
|
|
175
|
-
│
|
|
176
|
-
│
|
|
177
|
-
│
|
|
178
|
-
|
|
179
|
-
│
|
|
180
|
-
│
|
|
181
|
-
│
|
|
182
|
-
│
|
|
183
|
-
│
|
|
184
|
-
│
|
|
185
|
-
|
|
186
|
-
├──
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
85
|
+
|
|
86
|
+
Source lives under `src/`, split between AssemblyScript primitives in `src/asm/` and the TypeScript API in `src/ts/`. Tests are in `test/`. Build, codegen, and tooling scripts go in `scripts/`. CI/CD configuration sits in `.github/`. The repository root holds project documentation, package metadata, and tool configs. Each subsection below shows the relevant tree and notes the conventions that apply across files in that tier.
|
|
87
|
+
|
|
88
|
+
### CI/CD
|
|
89
|
+
|
|
90
|
+
`.github/` holds GitHub-specific repository configuration: workflow definitions, the CI image build context, and platform metadata. Workflows split along functional lines.
|
|
91
|
+
|
|
92
|
+
**Merge gate.** `build.yml`, `lint.yml`, `e2e.yml`, `test-suite.yml`. `test-suite.yml` orchestrates the per-domain unit runners (`unit-*.yml`) plus `verify-vectors.yml` for parallel execution and per-domain failure isolation.
|
|
93
|
+
|
|
94
|
+
**Test vectors.** `verify-vectors.yml` validates the corpus against `SHA256SUMS`.
|
|
95
|
+
|
|
96
|
+
**Release flow.** Manual `release.yml` bumps the version and creates the tag; the resulting `v*` tag push triggers `publish.yml`, which runs the npm publish with provenance attestations. `npm-remove.yml` is the manual deprecate/unpublish escape hatch.
|
|
97
|
+
|
|
98
|
+
**Wiki.** `wiki.yml` syncs `docs/` to the GitHub Wiki on every merge to main.
|
|
99
|
+
|
|
100
|
+
**CI image.** `ci-image.yml` rebuilds the test-runner container from `ci.Dockerfile` whenever the Dockerfile changes.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
.github/
|
|
104
|
+
├── ci.Dockerfile
|
|
105
|
+
└── workflows/
|
|
106
|
+
├── build.yml
|
|
107
|
+
├── ci-image.yml
|
|
108
|
+
├── e2e.yml
|
|
109
|
+
├── lint.yml
|
|
110
|
+
├── npm-remove.yml
|
|
111
|
+
├── publish.yml
|
|
112
|
+
├── release.yml
|
|
113
|
+
├── test-suite.yml
|
|
114
|
+
├── unit-chacha20.yml
|
|
115
|
+
├── unit-core.yml
|
|
116
|
+
├── unit-hashing.yml
|
|
117
|
+
├── unit-kyber.yml
|
|
118
|
+
├── unit-montecarlo-cbc.yml
|
|
119
|
+
├── unit-montecarlo-ecb.yml
|
|
120
|
+
├── unit-nessie.yml
|
|
121
|
+
├── unit-ratchet.yml
|
|
122
|
+
├── unit-serpent.yml
|
|
123
|
+
├── unit-stream.yml
|
|
124
|
+
├── verify-vectors.yml
|
|
125
|
+
└── wiki.yml
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Build pipeline
|
|
129
|
+
|
|
130
|
+
`scripts/` holds the build, codegen, and tooling scripts that produce `dist/` and the test-vector corpus. Three categories.
|
|
131
|
+
|
|
132
|
+
**Build orchestration.** `build-asm.js` drives AssemblyScript compilation across the six modules. `embed-wasm.ts` produces the gzip+base64 blob for each `.wasm`. `embed-workers.ts` bundles each pool worker into a self-contained IIFE via esbuild. `copy-docs.ts` ships the consumer doc subset into `dist/`. See [Build Pipeline](#build-pipeline) for the full sequence.
|
|
133
|
+
|
|
134
|
+
**Codegen.** `generate_simd.ts` produces `src/asm/serpent/serpent_simd.ts` from a template by translating S-box gate logic into v128 ops; the generator and its output are both committed and the output is never edited by hand. `gen-seal-vectors.ts`, `gen-sealstream-vectors.ts`, `gen-fortuna-vectors.ts`, and `gen-ratchet-vectors.ts` produce known-answer-test vectors for their respective primitives.
|
|
135
|
+
|
|
136
|
+
**Tooling.** `gen-changelog.ts` generates `CHANGELOG` entries. `lint-asm.js` lints the AssemblyScript sources. `pin-actions.ts` pins every GitHub Action reference to a SHA, run via `bun pin` after workflow changes.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
scripts/
|
|
140
|
+
├── build-asm.js
|
|
141
|
+
├── copy-docs.ts
|
|
142
|
+
├── embed-wasm.ts
|
|
143
|
+
├── embed-workers.ts
|
|
144
|
+
├── gen-changelog.ts
|
|
145
|
+
├── gen-fortuna-vectors.ts
|
|
146
|
+
├── gen-ratchet-vectors.ts
|
|
147
|
+
├── gen-seal-vectors.ts
|
|
148
|
+
├── gen-sealstream-vectors.ts
|
|
149
|
+
├── generate_simd.ts
|
|
150
|
+
├── lint-asm.js
|
|
151
|
+
└── pin-actions.ts
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### AssemblyScript layer
|
|
155
|
+
|
|
156
|
+
`src/asm/` holds the AssemblyScript sources for each WASM binary. Every subdirectory compiles to its own `.wasm` with fully independent linear memory and no cross-module imports.
|
|
157
|
+
|
|
158
|
+
**Per-module conventions.** Every module exposes an `index.ts` as the asc entry point; it re-exports the public surface that becomes the WASM exports. Every module except `ct/` has a `buffers.ts` that defines the static memory layout and the offset getters that all other files in that module import. The `ct/` module is intentionally minimal: a single `index.ts` whose layout is implicit in its single 64 KB page.
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
src/asm/
|
|
162
|
+
├── chacha20/
|
|
163
|
+
│ ├── index.ts
|
|
164
|
+
│ ├── chacha20.ts ← block function (RFC 8439)
|
|
165
|
+
│ ├── chacha20_simd_4x.ts ← SIMD 4-wide inter-block keystream
|
|
166
|
+
│ ├── poly1305.ts ← one-time MAC
|
|
167
|
+
│ ├── wipe.ts ← module-wide buffer zeroizer
|
|
168
|
+
│ └── buffers.ts
|
|
169
|
+
├── ct/
|
|
170
|
+
│ └── index.ts ← v128 XOR-accumulate constant-time compare
|
|
171
|
+
├── kyber/
|
|
172
|
+
│ ├── index.ts
|
|
173
|
+
│ ├── ntt.ts ← scalar NTT/invNTT + zetas table
|
|
174
|
+
│ ├── ntt_simd.ts ← v128 NTT butterflies, fqmul_8x, barrett_reduce_8x
|
|
175
|
+
│ ├── reduce.ts ← Montgomery/Barrett reduction, fqmul
|
|
176
|
+
│ ├── poly.ts ← polynomial serialization, compression, basemul
|
|
177
|
+
│ ├── poly_simd.ts ← SIMD poly add/sub/reduce/ntt wrappers
|
|
178
|
+
│ ├── polyvec.ts ← k-wide polyvec operations
|
|
179
|
+
│ ├── cbd.ts ← centered binomial distribution (η=2, η=3)
|
|
180
|
+
│ ├── sampling.ts ← uniform rejection sampling
|
|
181
|
+
│ ├── verify.ts ← constant-time compare and conditional move
|
|
182
|
+
│ ├── params.ts ← Q, QINV, MONT, Barrett/compression constants
|
|
183
|
+
│ └── buffers.ts
|
|
184
|
+
├── serpent/
|
|
185
|
+
│ ├── index.ts
|
|
186
|
+
│ ├── serpent.ts ← block function + key schedule
|
|
187
|
+
│ ├── serpent_unrolled.ts ← unrolled S-boxes and round functions
|
|
188
|
+
│ ├── serpent_simd.ts ← SIMD bitsliced block operations
|
|
189
|
+
│ ├── cbc.ts ← CBC mode
|
|
190
|
+
│ ├── cbc_simd.ts ← SIMD CBC decrypt
|
|
191
|
+
│ ├── ctr.ts ← CTR mode
|
|
192
|
+
│ ├── ctr_simd.ts ← SIMD CTR 4-wide inter-block
|
|
193
|
+
│ └── buffers.ts
|
|
194
|
+
├── sha2/
|
|
195
|
+
│ ├── index.ts
|
|
196
|
+
│ ├── sha256.ts
|
|
197
|
+
│ ├── sha512.ts ← shared by SHA-512 and SHA-384
|
|
198
|
+
│ ├── hmac.ts ← HMAC-SHA256
|
|
199
|
+
│ ├── hmac512.ts ← HMAC-SHA512 and HMAC-SHA384
|
|
200
|
+
│ └── buffers.ts
|
|
201
|
+
└── sha3/
|
|
202
|
+
├── index.ts
|
|
203
|
+
├── keccak.ts ← Keccak-f[1600] permutation, sponge absorb/squeeze
|
|
204
|
+
└── buffers.ts
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### TypeScript layer
|
|
208
|
+
|
|
209
|
+
`src/ts/` is the public API layer. Each subdirectory is a published npm subpath; top-level files cover cross-cutting concerns and standalone utilities.
|
|
210
|
+
|
|
211
|
+
**Subpath conventions.** Every cipher and hash module has an `index.ts` barrel, a `types.ts` for TypeScript-only declarations, and an `embedded.ts` that re-exports its gzip+base64 WASM blob from `src/ts/embedded/`. The `keccak/` alias subpath omits `types.ts` and re-exports sha3's instead. The `ratchet/` and `stream/` modules have no `embedded.ts` because they compose other modules and ship no WASM of their own.
|
|
212
|
+
|
|
213
|
+
**Cipher modules** (`serpent/`, `chacha20/`) add a `cipher-suite.ts` (the `CipherSuite` implementation for STREAM), a `pool-worker.ts` (Web Worker source for `SealStreamPool`), a `generator.ts` (Fortuna `Generator`), and a `shared-ops.ts` (serpent) or `ops.ts` (chacha20) holding pure primitive functions shared between the cipher-suite and the pool worker.
|
|
214
|
+
|
|
215
|
+
**Hash modules** (`sha2/`, `sha3/`) add a `hash.ts` (the stateless Fortuna `HashFn`).
|
|
216
|
+
|
|
217
|
+
**Build artifacts.** `ct-wasm.ts` and the `embedded/` directory hold auto-generated outputs that only exist after `bun run build`. Both are gitignored. `ct-wasm.ts` is the inline raw byte array of the ct WASM module. `embedded/` holds gzip+base64 blobs of each WASM binary (from `scripts/embed-wasm.ts`) and IIFE source strings for each pool worker (from `scripts/embed-workers.ts`).
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
src/ts/
|
|
221
|
+
├── chacha20/
|
|
222
|
+
│ ├── cipher-suite.ts
|
|
223
|
+
│ ├── embedded.ts
|
|
224
|
+
│ ├── generator.ts
|
|
225
|
+
│ ├── index.ts
|
|
226
|
+
│ ├── ops.ts
|
|
227
|
+
│ ├── pool-worker.ts
|
|
228
|
+
│ └── types.ts
|
|
229
|
+
├── ct-wasm.ts ← gitignored build artifact: raw ct WASM bytes
|
|
230
|
+
├── embedded/ ← gitignored build artifacts
|
|
231
|
+
│ ├── chacha20-pool-worker.ts ← ChaCha20 pool-worker IIFE source string
|
|
232
|
+
│ ├── chacha20.ts ← chacha20.wasm gzip+base64 blob
|
|
233
|
+
│ ├── kyber.ts ← kyber.wasm gzip+base64 blob
|
|
234
|
+
│ ├── serpent-pool-worker.ts ← Serpent pool-worker IIFE source string
|
|
235
|
+
│ ├── serpent.ts ← serpent.wasm gzip+base64 blob
|
|
236
|
+
│ ├── sha2.ts ← sha2.wasm gzip+base64 blob
|
|
237
|
+
│ └── sha3.ts ← sha3.wasm gzip+base64 blob
|
|
238
|
+
├── errors.ts ← AuthenticationError
|
|
239
|
+
├── fortuna.ts ← Fortuna CSPRNG (composes pluggable Generator + HashFn)
|
|
240
|
+
├── index.ts ← root barrel + dispatching init()
|
|
241
|
+
├── init.ts ← initModule(), module cache, isInitialized
|
|
242
|
+
├── keccak/ ← alias subpath; same WASM and instance slot as sha3
|
|
243
|
+
│ ├── embedded.ts
|
|
244
|
+
│ └── index.ts
|
|
245
|
+
├── kyber/
|
|
246
|
+
│ ├── embedded.ts
|
|
247
|
+
│ ├── indcpa.ts ← IND-CPA encrypt/decrypt + matrix generation
|
|
248
|
+
│ ├── index.ts
|
|
249
|
+
│ ├── kem.ts ← Fujisaki-Okamoto transform (keygen, encaps, decaps)
|
|
250
|
+
│ ├── params.ts ← MLKEM512, MLKEM768, MLKEM1024 parameter sets
|
|
251
|
+
│ ├── suite.ts ← KyberSuite (hybrid KEM+AEAD CipherSuite factory)
|
|
252
|
+
│ ├── types.ts
|
|
253
|
+
│ └── validate.ts ← key validation (FIPS 203 §7.2, §7.3)
|
|
254
|
+
├── loader.ts ← loadWasm()/compileWasm() WasmSource dispatch
|
|
255
|
+
├── ratchet/
|
|
256
|
+
│ ├── index.ts
|
|
257
|
+
│ ├── kdf-chain.ts ← KDFChain (per-message KDF chain, DR §5.2)
|
|
258
|
+
│ ├── ratchet-keypair.ts ← RatchetKeypair (single-use ek/dk wrapper)
|
|
259
|
+
│ ├── root-kdf.ts ← ratchetInit, kemRatchetEncap, kemRatchetDecap (DR §7.2)
|
|
260
|
+
│ ├── skipped-key-store.ts ← SkippedKeyStore (MKSKIPPED cache, DR §3.2/§3.5)
|
|
261
|
+
│ └── types.ts
|
|
262
|
+
├── serpent/
|
|
263
|
+
│ ├── cipher-suite.ts
|
|
264
|
+
│ ├── embedded.ts
|
|
265
|
+
│ ├── generator.ts
|
|
266
|
+
│ ├── index.ts
|
|
267
|
+
│ ├── pool-worker.ts
|
|
268
|
+
│ ├── serpent-cbc.ts ← SerpentCbc (broken out to avoid circular import)
|
|
269
|
+
│ ├── shared-ops.ts
|
|
270
|
+
│ └── types.ts
|
|
271
|
+
├── sha2/
|
|
272
|
+
│ ├── embedded.ts
|
|
273
|
+
│ ├── hash.ts
|
|
274
|
+
│ ├── hkdf.ts ← HKDF_SHA256, HKDF_SHA512 (pure TS over HMAC)
|
|
275
|
+
│ ├── index.ts
|
|
276
|
+
│ └── types.ts
|
|
277
|
+
├── sha3/
|
|
278
|
+
│ ├── embedded.ts
|
|
279
|
+
│ ├── hash.ts
|
|
280
|
+
│ ├── index.ts
|
|
281
|
+
│ └── types.ts
|
|
282
|
+
├── stream/
|
|
283
|
+
│ ├── constants.ts ← HEADER_SIZE, CHUNK_MIN/MAX, TAG_DATA/FINAL, FLAG_FRAMED
|
|
284
|
+
│ ├── header.ts ← wire format header encode/decode, counter nonce
|
|
285
|
+
│ ├── index.ts
|
|
286
|
+
│ ├── open-stream.ts ← OpenStream (cipher-agnostic streaming decryption)
|
|
287
|
+
│ ├── seal-stream-pool.ts ← SealStreamPool (worker-based parallel batch)
|
|
288
|
+
│ ├── seal-stream.ts ← SealStream (cipher-agnostic streaming encryption)
|
|
289
|
+
│ ├── seal.ts ← Seal (static one-shot AEAD)
|
|
290
|
+
│ └── types.ts
|
|
291
|
+
├── types.ts ← shared interfaces: Hash, KeyedHash, Blockcipher, Streamcipher, AEAD, Generator, HashFn
|
|
292
|
+
├── utils.ts ← encoding, wipe, randomBytes, constantTimeEqual, CT_MAX_BYTES, hasSIMD
|
|
293
|
+
└── wasm-source.ts ← WasmSource union type
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Tests
|
|
297
|
+
|
|
298
|
+
`test/` holds three independent categories of files, used by separate workflows.
|
|
299
|
+
|
|
300
|
+
**Unit tests** (`unit/`) are Vitest suites that compile to a JS target for fast local iteration. The directory mirrors `src/ts/` structure with one folder per module, plus a handful of top-level `.test.ts` files for cross-cutting concerns (init, errors, utils, fortuna). CI splits these by domain via `unit-*.yml` for parallel execution.
|
|
301
|
+
|
|
302
|
+
**End-to-end tests** (`e2e/`) are Playwright suites that exercise the actual WASM artifacts across V8, SpiderMonkey, and JavaScriptCore. They run after the full build, including pool-worker bundling.
|
|
303
|
+
|
|
304
|
+
**Test vectors** (`vectors/`) is the immutable known-answer-test corpus. Files are read-only reference data. Some come from authoritative specifications (FIPS, RFCs, ACVP, NIST CAVP); others are self generated as regression vectors by `scripts/gen-*-vectors.ts`. CI validates KAT file integrity against `SHA256SUMS`.
|
|
305
|
+
|
|
306
|
+
See [test-suite.md](./test-suite.md) for full testing methodology, vector corpus inventory with provenance, and gate discipline.
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
test/
|
|
310
|
+
├── e2e/ ← Playwright suites against built WASM in V8, SpiderMonkey, JSC
|
|
311
|
+
├── unit/
|
|
312
|
+
│ ├── chacha20/
|
|
313
|
+
│ ├── ct/
|
|
314
|
+
│ ├── errors.test.ts
|
|
315
|
+
│ ├── fortuna/
|
|
316
|
+
│ ├── fortuna.test.ts
|
|
317
|
+
│ ├── helpers.ts
|
|
318
|
+
│ ├── init/
|
|
319
|
+
│ ├── init.test.ts
|
|
320
|
+
│ ├── kyber/
|
|
321
|
+
│ ├── loader/
|
|
322
|
+
│ ├── ratchet/
|
|
323
|
+
│ ├── serpent/
|
|
324
|
+
│ ├── sha2/
|
|
325
|
+
│ ├── sha3/
|
|
326
|
+
│ ├── stream/
|
|
327
|
+
│ └── utils.test.ts
|
|
328
|
+
└── vectors/ ← KAT corpus; integrity verified against SHA256SUMS
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Project files
|
|
332
|
+
|
|
333
|
+
The repository root holds project documentation, package metadata, and tool configuration. Build artifacts that only exist after `bun run build` are listed at the end.
|
|
334
|
+
|
|
335
|
+
**Documentation.** `README.md` is the entry point. `SECURITY.md` covers the vulnerability disclosure policy. `AGENTS.md` is the agent contract that governs how AI agents work in the repo. `CHANGELOG` tracks release history and `LICENSE` is MIT. The `docs/` directory holds the full API reference, audits, benchmarks, and architecture notes (this file lives there).
|
|
336
|
+
|
|
337
|
+
**Package metadata.** `package.json` declares the npm manifest, subpath exports, and scripts. `package-lock.json` and `bun.lock` are the lockfiles for npm and bun respectively; both ship checked in so either tool can install reproducibly.
|
|
338
|
+
|
|
339
|
+
**Tool configs.** `asconfig.json` configures AssemblyScript compilation. `eslint.config.ts` is the active linter, run via `bun fix`. `playwright.config.ts` and `vitest.config.ts` configure the e2e and unit test runners. `tsconfig.json` is the base TypeScript config; `tsconfig.test.json` and `tsconfig.e2e.json` extend it for the test targets. `tslint.json` is a TSLint config (older format).
|
|
340
|
+
|
|
341
|
+
**Build artifacts** (gitignored; only exist after `bun run build`). `build/` holds the raw `.wasm` outputs from AssemblyScript compilation. `dist/` is the published npm package contents (compiled JS, declarations, copied WASM, embedded blobs, doc subset).
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
.
|
|
345
|
+
├── build/ ← gitignored: .wasm outputs from AS compilation
|
|
346
|
+
├── dist/ ← gitignored: published npm package contents
|
|
347
|
+
├── docs/ ← API reference, audits, benchmarks (this file lives here)
|
|
348
|
+
├── README.md
|
|
349
|
+
├── SECURITY.md
|
|
350
|
+
├── AGENTS.md
|
|
351
|
+
├── CHANGELOG
|
|
352
|
+
├── LICENSE
|
|
196
353
|
├── package.json
|
|
197
|
-
├──
|
|
198
|
-
├──
|
|
199
|
-
├──
|
|
354
|
+
├── package-lock.json
|
|
355
|
+
├── bun.lock
|
|
356
|
+
├── asconfig.json
|
|
357
|
+
├── eslint.config.ts
|
|
200
358
|
├── playwright.config.ts
|
|
201
|
-
├──
|
|
202
|
-
|
|
359
|
+
├── tsconfig.json
|
|
360
|
+
├── tsconfig.e2e.json
|
|
361
|
+
├── tsconfig.test.json
|
|
362
|
+
├── tslint.json
|
|
363
|
+
└── vitest.config.ts
|
|
203
364
|
```
|
|
204
365
|
|
|
205
366
|
---
|
|
206
367
|
|
|
207
368
|
## Architecture: TypeScript over WASM
|
|
208
369
|
|
|
370
|
+
|
|
209
371
|
The TypeScript layer never implements cryptographic algorithms. It manages the boundary between JavaScript and WebAssembly by writing inputs into WASM linear memory, calling exported functions, and reading back outputs. All algorithm logic resides within AssemblyScript.
|
|
210
372
|
|
|
211
373
|
Higher-level classes like `Seal`, `SealStream`, and `SealStreamPool` are pure TypeScript, but they compose WASM-backed primitives (Serpent-CBC, HMAC-SHA256, ChaCha20-Poly1305, and HKDF-SHA256) rather than implementing new cryptographic logic. TypeScript orchestrates, while WASM computes. Pool workers instantiate their own WASM modules and directly call primitives, bypassing the main-thread module cache.
|
|
212
374
|
|
|
213
375
|
---
|
|
214
376
|
|
|
215
|
-
##
|
|
377
|
+
## Six Independent WASM Modules
|
|
216
378
|
|
|
217
|
-
Each primitive family compiles to its own `.wasm` binary.
|
|
218
|
-
independent, separate linear memories, separate buffer layouts, no shared state.
|
|
379
|
+
Each primitive family compiles to its own `.wasm` binary with fully independent linear memory and buffer layouts. No shared state, no cross-module interference. Five of the six modules load through `init()`. The sixth, `ct`, sits outside the public `Module` union and the `init()` gate; it occupies a single 64 KB memory page and lazy-loads on the first call to `constantTimeEqual`. The ct module backs the public `constantTimeEqual` and `CT_MAX_BYTES` exports from the root barrel; neither requires an `init()` call.
|
|
219
380
|
|
|
220
|
-
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
381
|
+
|Module|Binary|Primitives|
|
|
382
|
+
|---|---|---|
|
|
383
|
+
|`serpent`|`serpent.wasm`|Serpent-256 block cipher: ECB, CTR mode, CBC mode|
|
|
384
|
+
|`chacha20`|`chacha20.wasm`|ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD, XChaCha20-Poly1305 AEAD|
|
|
385
|
+
|`sha2`|`sha2.wasm`|SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, HMAC-SHA512|
|
|
386
|
+
|`sha3`|`sha3.wasm`|SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256|
|
|
387
|
+
|`kyber`|`kyber.wasm`|ML-KEM polynomial arithmetic: SIMD NTT/invNTT (v128 butterflies with scalar tail), basemul, Montgomery/Barrett, CBD, compress, CT verify/cmov|
|
|
388
|
+
|`ct`|`ct.wasm`|SIMD constant-time byte comparison. Backs `constantTimeEqual` and `CT_MAX_BYTES`, lazy-loaded outside `init()`. Single 64 KB page.|
|
|
227
389
|
|
|
228
390
|
**Size.** Consumers who only use Serpent don't load the SHA-3 binary.
|
|
229
391
|
|
|
230
392
|
**Isolation.** Key material in `serpent.wasm` memory cannot bleed into `sha3.wasm` memory even in theory.
|
|
231
393
|
|
|
232
|
-
Each module's buffer layout starts at offset 0 and is defined in its own
|
|
233
|
-
`buffers.ts`. Buffer layouts are fully independent across modules.
|
|
394
|
+
Each module's buffer layout starts at offset 0 and is defined in its own `buffers.ts`. Buffer layouts are fully independent across modules.
|
|
234
395
|
|
|
235
396
|
### Module contents
|
|
236
397
|
|
|
237
|
-
**`serpent.wasm`**
|
|
238
|
-
Serpent-256 block cipher. Key schedule, block encrypt, block decrypt. CTR mode
|
|
239
|
-
chunked streaming encrypt/decrypt. CBC mode chunked encrypt/decrypt. SIMD
|
|
240
|
-
variants for CTR 4-wide inter-block and CBC decrypt parallelism.
|
|
241
|
-
Source: `src/asm/serpent/`
|
|
242
|
-
|
|
243
|
-
The serpent TypeScript module includes `SerpentCipher` (CipherSuite implementation
|
|
244
|
-
for the STREAM construction: Serpent-CBC + HMAC-SHA256 with HKDF key derivation).
|
|
245
|
-
Requires `serpent` and `sha2` to be initialized.
|
|
246
|
-
|
|
247
|
-
**`chacha20.wasm`**
|
|
248
|
-
ChaCha20 stream cipher (RFC 8439). Poly1305 MAC (RFC 8439 §2.5). ChaCha20-Poly1305
|
|
249
|
-
AEAD (RFC 8439 §2.8). XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha).
|
|
250
|
-
HChaCha20 subkey derivation. SIMD 4-wide inter-block parallelism.
|
|
251
|
-
Source: `src/asm/chacha20/`
|
|
252
|
-
|
|
253
|
-
The chacha20 TypeScript module includes `XChaCha20Cipher` (CipherSuite implementation
|
|
254
|
-
for the STREAM construction: XChaCha20-Poly1305 with HKDF key derivation).
|
|
255
|
-
Pool workers are internal, loaded by `SealStreamPool` at runtime, not registered in the package exports map.
|
|
256
|
-
|
|
257
|
-
**`sha2.wasm`**
|
|
258
|
-
SHA-256 and SHA-512 (FIPS 180-4). SHA-384 (SHA-512 with different IVs, truncated
|
|
259
|
-
output, shares all SHA-512 buffers and compress function). HMAC-SHA256,
|
|
260
|
-
HMAC-SHA512, HMAC-SHA384 (RFC 2104). HKDF-SHA256 and HKDF-SHA512 (RFC 5869)
|
|
261
|
-
are pure TypeScript compositions over HMAC, with no new WASM logic.
|
|
262
|
-
Source: `src/asm/sha2/`
|
|
263
|
-
|
|
264
|
-
**`sha3.wasm`**
|
|
265
|
-
Keccak-f[1600] permutation (FIPS 202). SHA3-224, SHA3-256, SHA3-384, SHA3-512.
|
|
266
|
-
SHAKE128, SHAKE256 (XOFs, multi-squeeze capable, unbounded output length).
|
|
267
|
-
All six variants share one permutation, differing only in rate, domain
|
|
268
|
-
separation byte, and output length.
|
|
269
|
-
Source: `src/asm/sha3/`
|
|
270
|
-
|
|
271
|
-
**`kyber.wasm`**
|
|
272
|
-
ML-KEM (FIPS 203) polynomial arithmetic. Montgomery and Barrett reduction,
|
|
273
|
-
7-layer NTT and inverse NTT, basemul in Z_q[X]/(X²-ζ), centered binomial
|
|
274
|
-
distribution sampling (η=2, η=3), division-free compression/decompression
|
|
275
|
-
(all 5 bit-width paths: 4, 5, 10, 11, 1-bit), rejection sampling for matrix
|
|
276
|
-
generation, constant-time byte comparison and conditional move. Requires
|
|
277
|
-
WebAssembly SIMD (`v128` instructions) for NTT and polynomial arithmetic.
|
|
278
|
-
3 pages (192 KB) linear memory with 10 poly slots, 8 polyvec slots, and
|
|
279
|
-
dedicated byte buffers for keys/ciphertexts.
|
|
280
|
-
Source: `src/asm/kyber/`
|
|
398
|
+
**`serpent.wasm`** implements Serpent-256, a 128-bit block cipher. It handles key scheduling, block encryption and decryption, and both CTR and CBC streaming modes with SIMD variants for inter-block parallelism. See: [Serpent-256 WASM Module Reference](./asm_serpent.md)
|
|
281
399
|
|
|
282
|
-
The
|
|
283
|
-
|
|
284
|
-
|
|
400
|
+
[The TypeScript module](./serpent.md) wraps this with `SerpentCipher`, a CipherSuite that combines Serpent-CBC with HMAC-SHA256 and HKDF key derivation for the STREAM construction. Primitive operations (HMAC, CBC, PKCS7 padding) live in `serpent/shared-ops.ts` and are reused by both the main thread and pool workers, guaranteeing byte-identical output and consistent Vaudenay 2002 padding normalization. Requires `serpent` and `sha2` to be initialized.
|
|
401
|
+
|
|
402
|
+
**`chacha20.wasm`** implements the full ChaCha20-Poly1305 AEAD family per RFC 8439 and draft-irtf-cfrg-xchacha. It includes ChaCha20 stream cipher, Poly1305 one-time MAC, the AEAD construction, HChaCha20 for nonce extension, and SIMD 4-wide inter-block parallelism. See: [ChaCha20/Poly1305 WASM Reference](./asm_chacha.md)
|
|
403
|
+
|
|
404
|
+
[The TypeScript module](./chacha20.md) exports `XChaCha20Cipher`, a CipherSuite implementation for STREAM using XChaCha20-Poly1305 with HKDF key derivation. Pool workers load internally via `SealStreamPool` at runtime and don't appear in the package exports map.
|
|
405
|
+
|
|
406
|
+
**`sha2.wasm`** implements SHA-256 and SHA-512 per FIPS 180-4, plus SHA-384 (which reuses SHA-512's buffer and compress function with different IVs and truncation). It also provides HMAC per RFC 2104 for all three variants. HKDF-SHA256 and HKDF-SHA512 (RFC 5869) are pure TypeScript compositions over HMAC with no new WASM logic. See: [SHA-2 WASM Reference](./asm_sha2.md)
|
|
407
|
+
|
|
408
|
+
**`sha3.wasm`** implements the Keccak-f[1600] permutation per FIPS 202. All SHA3 variants (SHA3-224, SHA3-256, SHA3-384, SHA3-512) and XOF variants (SHAKE128, SHAKE256) share a single permutation, differing only in rate, domain separation byte, and output length. SHAKE supports unbounded multi-squeeze output. See: [SHA-3 WASM Reference](./asm_sha3.md)
|
|
409
|
+
|
|
410
|
+
**`kyber.wasm`** implements ML-KEM polynomial arithmetic per FIPS 203. It includes Montgomery and Barrett reduction, 7-layer NTT and inverse NTT with SIMD butterflies, basemul in Z_q[X]/(X²-ζ), centered binomial distribution sampling (η=2 and η=3), compression and decompression across all five bit-width paths, rejection sampling for matrix generation, and constant-time byte comparison and conditional move. Requires WebAssembly SIMD (`v128` instructions). Uses 3 memory pages (192 KB) with 10 polynomial slots, 8 polynomial vector slots, and dedicated buffers for keys and ciphertexts. See: [Kyber WASM Reference](./asm_kyber.md)
|
|
411
|
+
|
|
412
|
+
[The TypeScript module](./kyber.md) exports `MlKem512`, `MlKem768`, and `MlKem1024`—KEM classes implementing the Fujisaki-Okamoto transform. All three require both `kyber` and `sha3` to be initialized; the sha3 module provides the Keccak sponge for matrix generation (SHAKE128), noise sampling (SHAKE256), and finalization (SHA3-256 for H, SHA3-512 for G).
|
|
413
|
+
|
|
414
|
+
**`ct.wasm`** implements constant-time byte array equality with a single SIMD-only primitive. The module exports `compare(aOff, bOff, len)`, which reads both arrays directly from caller-specified offsets in linear memory and returns 1 if all bytes match, 0 otherwise. Comparison is zero-copy: no internal staging buffers, no buffer slots, no `wipeBuffers` export. The implementation is structurally branch-free. A `v128.xor`/`v128.or` accumulator processes 16-byte blocks, a scalar tail handles any remainder, and the final zero-test is an arithmetic shift, not a conditional. Requires WebAssembly SIMD (`v128` instructions); if the runtime lacks SIMD or compilation fails, the first call throws a branded error. See: [Constant-Time WASM Reference](asm_ct.md)
|
|
415
|
+
|
|
416
|
+
[The TypeScript module](./utils.md#constanttimeequal) exports `constantTimeEqual` and `CT_MAX_BYTES` from the root barrel. The wrapper instantiates the WASM synchronously on first call and caches it for subsequent calls. It writes both arrays into linear memory, calls `compare`, and zeroes both regions in a `finally` block before returning. `CT_MAX_BYTES` is 32 KB per side; the 64 KB page holds two equal-length inputs.
|
|
285
417
|
|
|
286
418
|
---
|
|
287
419
|
|
|
288
420
|
## `init()` API
|
|
289
421
|
|
|
290
|
-
WASM instantiation is async. `init()` is the initialization gate, call it once before using any cryptographic class. The cost is explicit and the developer controls when it is paid.
|
|
422
|
+
WASM instantiation is async. [`init()`](./init.md) is the initialization gate, call it once before using any cryptographic class. The cost is explicit and the developer controls when it is paid.
|
|
291
423
|
|
|
292
424
|
### Signature
|
|
293
425
|
|
|
@@ -308,19 +440,15 @@ async function init(
|
|
|
308
440
|
): Promise<void>
|
|
309
441
|
```
|
|
310
442
|
|
|
311
|
-
The loading strategy is inferred from the source type, so there is no need
|
|
312
|
-
for a mode string. Each module also exports its own init function, such as
|
|
313
|
-
`serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`,
|
|
314
|
-
`sha3Init(source)`, `keccakInit(source)`, and `kyberInit(source)`,
|
|
315
|
-
enabling tree-shakeable imports.
|
|
443
|
+
The loading strategy is inferred from the source type, so there is no need for a mode string. Each module also exports its own init function, such as `serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`, and `kyberInit(source)`, enabling tree-shakeable imports.
|
|
316
444
|
|
|
317
445
|
> [!NOTE]
|
|
318
|
-
> **`
|
|
446
|
+
> **`keccak` is an alias for `sha3`.** Both names are accepted by `init()`, `initModule()`, `getInstance()`, and `isInitialized()`. They share the same WASM binary and the same instance slot. The alias exists so Kyber/ML-KEM consumers can write `init({ keccak: keccakWasm })` using the semantically correct name for the underlying sponge primitive.
|
|
319
447
|
|
|
320
448
|
### Embedded subpath exports
|
|
321
449
|
|
|
322
|
-
Each module provides a `/embedded` subpath that exports the gzip+base64
|
|
323
|
-
|
|
450
|
+
Each module provides a `/embedded` subpath that exports the gzip+base64 blob as a ready-to-use `WasmSource`:
|
|
451
|
+
|
|
324
452
|
```typescript
|
|
325
453
|
import { init } from 'leviathan-crypto'
|
|
326
454
|
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
@@ -331,55 +459,50 @@ await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
|
331
459
|
|
|
332
460
|
### Behavioral contracts
|
|
333
461
|
|
|
334
|
-
**Idempotent initialization.** Calling `init()` on an already initialized
|
|
335
|
-
module is a no-op. It is safe to call `init()` from multiple locations
|
|
336
|
-
within the codebase.
|
|
462
|
+
**Idempotent initialization.** Calling `init()` on an already initialized module is a no-op. It is safe to call `init()` from multiple locations within the codebase.
|
|
337
463
|
|
|
338
|
-
**Module-scope cache.** Each `WebAssembly.Instance` is cached at module
|
|
339
|
-
scope after initial instantiation. All subsequent class constructions use
|
|
340
|
-
the cached instance with no recompilation.
|
|
464
|
+
**Module-scope cache.** Each `WebAssembly.Instance` is cached at module scope after initial instantiation. All subsequent class constructions use the cached instance with no recompilation.
|
|
341
465
|
|
|
342
|
-
**Error before initialization.** Invoking any cryptographic class before
|
|
343
|
-
calling `init()` throws a clear error prompting the developer to call
|
|
344
|
-
`init({ <module>: ... })` first.
|
|
466
|
+
**Error before initialization.** Invoking any cryptographic class before calling `init()` throws a clear error prompting the developer to call `init({ <module>: ... })` first.
|
|
345
467
|
|
|
346
|
-
**No implicit initialization.** Classes never call `init()` automatically
|
|
347
|
-
on first use. Explicit initialization is preferable to hidden costs.
|
|
468
|
+
**No implicit initialization.** Classes never call `init()` automatically on first use. Explicit initialization is preferable to hidden costs.
|
|
348
469
|
|
|
349
|
-
**Thread safety.** The main thread uses a single WASM instance per module.
|
|
350
|
-
`SealStreamPool` provides worker-based parallelism. Each pool worker owns
|
|
351
|
-
its own WASM instances with isolated linear memory, bypassing the main-thread
|
|
352
|
-
cache entirely. For other primitives, create one instance per Worker if
|
|
353
|
-
Workers are used.
|
|
470
|
+
**Thread safety.** The main thread uses a single WASM instance per module. `SealStreamPool` provides worker-based parallelism. Each pool worker is spawned from an IIFE bundled at build time and instantiates its own WASM modules with isolated linear memory, bypassing the main-thread cache entirely. For other primitives, create one instance per Worker if Workers are used.
|
|
354
471
|
|
|
355
472
|
---
|
|
356
473
|
|
|
357
474
|
## Public API Classes
|
|
358
475
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
|
362
|
-
|
|
363
|
-
| `
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
367
|
-
| `
|
|
368
|
-
| `
|
|
369
|
-
| `kyber` + `sha3` +
|
|
370
|
-
| `stream`
|
|
371
|
-
| `serpent` + `sha2`
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
476
|
+
| Module | Classes |
|
|
477
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
478
|
+
| `serpent` + `sha2` | `SerpentCipher` |
|
|
479
|
+
| `serpent` | `Serpent`, `SerpentCtr`, `SerpentCbc` |
|
|
480
|
+
| `chacha20` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher` |
|
|
481
|
+
| `sha2` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
|
|
482
|
+
| `sha3` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
|
|
483
|
+
| `kyber` + `sha3` | `MlKem512`, `MlKem768`, `MlKem1024` |
|
|
484
|
+
| `kyber` + `sha3` + inner cipher | `KyberSuite` (hybrid KEM+AEAD factory) |
|
|
485
|
+
| `sha2` | `ratchetInit`, `KDFChain`, `SkippedKeyStore` |
|
|
486
|
+
| `kyber` + `sha3` + `sha2` | `kemRatchetEncap`, `kemRatchetDecap`, `RatchetKeypair` |
|
|
487
|
+
| `stream` | `Seal`, `SealStream`, `OpenStream`, `SealStreamPool` |
|
|
488
|
+
| `serpent` + `sha2` | `Fortuna` with `SerpentGenerator` + `SHA256Hash` |
|
|
489
|
+
| `serpent` + `sha3` | `Fortuna` with `SerpentGenerator` + `SHA3_256Hash` |
|
|
490
|
+
| `chacha20` + `sha2` | `Fortuna` with `ChaCha20Generator` + `SHA256Hash` |
|
|
491
|
+
| `chacha20` + `sha3` | `Fortuna` with `ChaCha20Generator` + `SHA3_256Hash` |
|
|
492
|
+
|
|
493
|
+
>[!NOTE]
|
|
494
|
+
> Class Names match conventional cryptographic notation.
|
|
495
|
+
|
|
496
|
+
- HMAC names use underscore separator (`HMAC_SHA256`) matching RFC convention.
|
|
497
|
+
- SHA-3 names use underscore separator (`SHA3_256`) for readability.
|
|
498
|
+
- Ratchet exports are KDF primitives from Signal's Sparse Post-Quantum Ratchet spec; session state, message ordering, and header format remain application concerns.
|
|
499
|
+
- **`Fortuna`** requires `await Fortuna.create({ generator, hash })` rather than `new Fortuna()`. Required modules depend on the generator and hash you pass. See [fortuna.md](./fortuna.md) for valid combinations.
|
|
500
|
+
- `SealStream`, `OpenStream`, and `SealStreamPool` are cipher-agnostic; you select the cipher by passing `XChaCha20Cipher` or `SerpentCipher` at construction.
|
|
379
501
|
|
|
380
502
|
### Usage pattern
|
|
381
503
|
|
|
382
504
|
All WASM-backed classes follow the same pattern:
|
|
505
|
+
|
|
383
506
|
```typescript
|
|
384
507
|
import { init, Seal, SerpentCipher, SHA3_256 } from 'leviathan-crypto'
|
|
385
508
|
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
@@ -399,35 +522,38 @@ const digest = hasher.hash(message)
|
|
|
399
522
|
|
|
400
523
|
Pure TypeScript utilities ship alongside the WASM-backed primitives:
|
|
401
524
|
|
|
402
|
-
|
|
|
403
|
-
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
|
|
|
525
|
+
|Category|Exports|
|
|
526
|
+
|---|---|
|
|
527
|
+
|Encoding|`hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`|
|
|
528
|
+
|Security|`constantTimeEqual`, `CT_MAX_BYTES`, `wipe`, `xor`|
|
|
529
|
+
|Helpers|`concat`, `randomBytes`, `hasSIMD`|
|
|
530
|
+
|Types|`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`|
|
|
408
531
|
|
|
409
532
|
---
|
|
410
533
|
|
|
411
534
|
## Build Pipeline
|
|
412
535
|
|
|
536
|
+
|
|
413
537
|
1. `npm run build:asm`: AssemblyScript compiler reads `src/asm/*/index.ts`, emits `build/*.wasm`
|
|
414
538
|
2. `npm run build:embed`: `scripts/embed-wasm.ts` reads each `.wasm`, gzip compresses, base64 encodes, writes to `src/ts/embedded/*.ts` and per-module `src/ts/*/embedded.ts`
|
|
415
|
-
3. `npm run build:
|
|
416
|
-
4. `
|
|
417
|
-
5.
|
|
418
|
-
6. At runtime (
|
|
539
|
+
3. `npm run build:embed-workers`: `scripts/embed-workers.ts` bundles each pool worker into a self-contained IIFE via esbuild and writes the source to `src/ts/embedded/<cipher>-pool-worker.ts` as a string export
|
|
540
|
+
4. `npm run build:ts`: TypeScript compiler emits `dist/`
|
|
541
|
+
5. `cp build/*.wasm dist/`: WASM binaries copied for URL-based consumers
|
|
542
|
+
6. At runtime (subpath): `serpentInit(serpentWasm)` → `initModule()` → `loadWasm(source)` → decode gzip+base64 → `WebAssembly.instantiate` → cache in `init.ts`
|
|
543
|
+
7. At runtime (root): `init({ serpent: serpentWasm, sha2: sha2Wasm })` → dispatches to each module's init function via `Promise.all` → same path as step 6 per module
|
|
419
544
|
|
|
420
|
-
`src/ts/embedded/` is gitignored; these files are build artifacts
|
|
545
|
+
`src/ts/embedded/` is gitignored; these files are build artifacts. The WASM blobs (`<module>.ts`) derive from the AssemblyScript source in `src/asm/`. The pool-worker bundles (`<cipher>-pool-worker.ts`) derive from the worker source in `src/ts/<cipher>/pool-worker.ts`, bundled as a self-contained IIFE by `scripts/embed-workers.ts`.
|
|
421
546
|
|
|
422
547
|
---
|
|
423
548
|
|
|
424
|
-
## Module
|
|
549
|
+
## Module Relationships
|
|
425
550
|
|
|
426
551
|
### ASM layer: internal import graph
|
|
427
552
|
|
|
428
553
|
Each WASM module is fully independent. No cross-module imports exist.
|
|
429
554
|
|
|
430
555
|
**Serpent (`src/asm/serpent/`)**
|
|
556
|
+
|
|
431
557
|
```
|
|
432
558
|
buffers.ts
|
|
433
559
|
<- serpent.ts (offsets for key, block, subkey, work, CBC IV)
|
|
@@ -454,6 +580,7 @@ index.ts
|
|
|
454
580
|
```
|
|
455
581
|
|
|
456
582
|
**ChaCha (`src/asm/chacha20/`)**
|
|
583
|
+
|
|
457
584
|
```
|
|
458
585
|
buffers.ts
|
|
459
586
|
<- chacha20.ts (key, nonce, counter, block, state, poly key, xchacha offsets)
|
|
@@ -539,158 +666,128 @@ index.ts
|
|
|
539
666
|
|
|
540
667
|
### TS layer: internal import graph
|
|
541
668
|
|
|
542
|
-
```
|
|
543
|
-
+---------------------+
|
|
544
|
-
| index.ts | <- barrel: dispatching init()
|
|
545
|
-
| (public API root) | + re-exports everything
|
|
546
|
-
+--+--+--+--+--+--+--+
|
|
547
|
-
| | | | | |
|
|
548
|
-
+-------------------------+ | | | | +----------------------+
|
|
549
|
-
| +----------------+ | | +----------+ |
|
|
550
|
-
v v v v v v
|
|
551
|
-
serpent/ chacha20/ sha2/ sha3/ fortuna.ts stream/
|
|
552
|
-
index.ts index.ts index.ts index.ts index.ts
|
|
553
|
-
| | | | | | |
|
|
554
|
-
| | +-> ops.ts | | +-> init.ts |
|
|
555
|
-
| | | | +-> serpent/ |
|
|
556
|
-
| +-> cipher- | | +-> sha2/ +-> seal-stream.ts
|
|
557
|
-
| suite.ts | | +-> utils.ts +-> open-stream.ts
|
|
558
|
-
+-> cipher- | | | +-> seal-stream-pool.ts
|
|
559
|
-
suite.ts +-> pool- +-> hkdf.ts +-> header.ts
|
|
560
|
-
| worker.ts +-> constants.ts
|
|
561
|
-
+-> pool- | +-> types.ts
|
|
562
|
-
worker.ts |
|
|
563
|
-
|
|
|
564
|
-
All module index.ts files ──────────────────────────> init.ts <── getInstance()
|
|
565
|
-
initModule()
|
|
566
|
-
All */embedded.ts files ──────────────────────────> embedded/*.ts (gzip+base64 blobs)
|
|
567
|
-
```
|
|
568
669
|
|
|
569
|
-
Each module's init function (`serpentInit`, `chacha20Init`, `sha2Init`,
|
|
570
|
-
`sha3Init`, `kyberInit`) calls `initModule()` from `init.ts`, passing a `WasmSource`.
|
|
571
|
-
`initModule()` delegates to `loadWasm(source)` in `loader.ts`. The loader
|
|
572
|
-
infers the loading strategy from the source type, with no mode string and no knowledge of module names or embedded file paths.
|
|
670
|
+
Each module's init function (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`, `kyberInit`) calls `initModule()` from `init.ts`, passing a `WasmSource`. `initModule()` delegates to `loadWasm(source)` in `loader.ts`. The loader infers the loading strategy from the source type, with no mode string and no knowledge of module names or embedded file paths.
|
|
573
671
|
|
|
574
|
-
Pool workers (`serpent/pool-worker.ts`, `chacha20/pool-worker.ts`) instantiate
|
|
575
|
-
their own WASM modules from pre-compiled `WebAssembly.Module` objects passed
|
|
576
|
-
via `postMessage`. They do not use `initModule()` or the main-thread cache.
|
|
672
|
+
Pool workers (`serpent/pool-worker.ts`, `chacha20/pool-worker.ts`) instantiate their own WASM modules from pre-compiled `WebAssembly.Module` objects passed via `postMessage`. They do not use `initModule()` or the main-thread cache. Workers are spawned from blob URLs constructed in `cipher-suite.ts` over an IIFE source string built at lib build time (`src/ts/embedded/<cipher>-pool-worker.ts`). The `pool-worker.ts` file itself is the source the bundler reads, not the runtime spawn entry.
|
|
577
673
|
|
|
578
674
|
---
|
|
579
675
|
|
|
580
676
|
### TS-to-WASM mapping
|
|
581
677
|
|
|
582
|
-
Each TS wrapper class maps to one WASM module and specific exported functions.
|
|
583
|
-
Tier 2 composition classes are pure TypeScript; they call Tier 1 classes rather than WASM functions directly.
|
|
678
|
+
Each TS wrapper class maps to one WASM module and specific exported functions. Tier 2 composition classes are pure TypeScript; they call Tier 1 classes rather than WASM functions directly.
|
|
584
679
|
|
|
585
680
|
**serpent/index.ts → asm/serpent/ (Tier 1: direct WASM callers)**
|
|
586
681
|
|
|
587
|
-
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
682
|
+
|TS Class|WASM functions called|
|
|
683
|
+
|---|---|
|
|
684
|
+
|`Serpent`|`loadKey`, `encryptBlock`, `decryptBlock`, `wipeBuffers` + buffer getters|
|
|
685
|
+
|`SerpentCtr`|`loadKey`, `resetCounter`, `setCounter`, `encryptChunk`, `encryptChunk_simd`, `wipeBuffers` + buffer getters|
|
|
686
|
+
|`SerpentCbc`|`loadKey`, `cbcEncryptChunk`, `cbcDecryptChunk`, `cbcDecryptChunk_simd`, `wipeBuffers` + buffer getters|
|
|
687
|
+
|`SerpentGenerator`|`loadKey`, `encryptBlock`, `wipeBuffers` + buffer getters|
|
|
592
688
|
|
|
593
689
|
**chacha20/index.ts → asm/chacha20/ (Tier 1: direct WASM callers)**
|
|
594
690
|
|
|
595
|
-
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
691
|
+
|TS Class|WASM functions called|
|
|
692
|
+
|---|---|
|
|
693
|
+
|`ChaCha20`|`chachaLoadKey`, `chachaSetCounter`, `chachaEncryptChunk`, `chachaEncryptChunk_simd`, `wipeBuffers` + buffer getters|
|
|
694
|
+
|`Poly1305`|`polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters|
|
|
695
|
+
|`ChaCha20Poly1305`|`chachaLoadKey`, `chachaSetCounter`, `chachaGenPolyKey`, `chachaEncryptChunk`, `polyInit`, `polyUpdate`, `polyFinal`, `wipeBuffers` + buffer getters (via `ops.ts`)|
|
|
696
|
+
|`XChaCha20Poly1305`|All of `ChaCha20Poly1305` + `hchacha20` + xchacha buffer getters (via `ops.ts`)|
|
|
697
|
+
|`ChaCha20Generator`|`chachaLoadKey`, `chachaSetCounter`, `chachaEncryptChunk_simd`, `wipeBuffers` + buffer getters|
|
|
601
698
|
|
|
602
699
|
**sha2/index.ts → asm/sha2/ (Tier 1: direct WASM callers)**
|
|
603
700
|
|
|
604
|
-
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
701
|
+
|TS Class|WASM functions called|
|
|
702
|
+
|---|---|
|
|
703
|
+
|`SHA256`|`sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
|
|
704
|
+
|`SHA512`|`sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters|
|
|
705
|
+
|`SHA384`|`sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters|
|
|
706
|
+
|`HMAC_SHA256`|`hmac256Init`, `hmac256Update`, `hmac256Final`, `sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
|
|
707
|
+
|`HMAC_SHA512`|`hmac512Init`, `hmac512Update`, `hmac512Final`, `sha512Init`, `sha512Update`, `sha512Final`, `wipeBuffers` + buffer getters|
|
|
708
|
+
|`HMAC_SHA384`|`hmac384Init`, `hmac384Update`, `hmac384Final`, `sha384Init`, `sha512Update`, `sha384Final`, `wipeBuffers` + buffer getters|
|
|
709
|
+
|`SHA256Hash`|`sha256Init`, `sha256Update`, `sha256Final`, `wipeBuffers` + buffer getters|
|
|
612
710
|
|
|
613
711
|
**sha3/index.ts → asm/sha3/ (Tier 1: direct WASM callers)**
|
|
614
712
|
|
|
615
|
-
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
713
|
+
|TS Class|WASM functions called|
|
|
714
|
+
|---|---|
|
|
715
|
+
|`SHA3_224`|`sha3_224Init`, `keccakAbsorb`, `sha3_224Final`, `wipeBuffers` + buffer getters|
|
|
716
|
+
|`SHA3_256`|`sha3_256Init`, `keccakAbsorb`, `sha3_256Final`, `wipeBuffers` + buffer getters|
|
|
717
|
+
|`SHA3_384`|`sha3_384Init`, `keccakAbsorb`, `sha3_384Final`, `wipeBuffers` + buffer getters|
|
|
718
|
+
|`SHA3_512`|`sha3_512Init`, `keccakAbsorb`, `sha3_512Final`, `wipeBuffers` + buffer getters|
|
|
719
|
+
|`SHAKE128`|`shake128Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters|
|
|
720
|
+
|`SHAKE256`|`shake256Init`, `keccakAbsorb`, `shakePad`, `shakeSqueezeBlock`, `wipeBuffers` + buffer getters|
|
|
721
|
+
|`SHA3_256Hash`|`sha3_256Init`, `keccakAbsorb`, `sha3_256Final`, `wipeBuffers` + buffer getters|
|
|
623
722
|
|
|
624
723
|
**kyber/index.ts + kyber/kem.ts + kyber/indcpa.ts → asm/kyber/ (Tier 1)**
|
|
625
724
|
|
|
626
|
-
|
|
|
627
|
-
|
|
628
|
-
|
|
725
|
+
|TS Class|WASM functions called|
|
|
726
|
+
|---|---|
|
|
727
|
+
|`MlKem512`, `MlKem768`, `MlKem1024`|`polyvec_ntt`, `polyvec_invntt`, `polyvec_basemul_acc_montgomery`, `polyvec_add`, `polyvec_reduce`, `polyvec_tobytes`, `polyvec_frombytes`, `polyvec_compress`, `polyvec_decompress`, `poly_ntt`, `poly_invntt`, `poly_tomont`, `poly_add`, `poly_sub`, `poly_reduce`, `poly_basemul_montgomery`, `poly_frommsg`, `poly_tomsg`, `poly_compress`, `poly_decompress`, `poly_getnoise`, `rej_uniform`, `ct_verify`, `ct_cmov`, `wipeBuffers` + buffer getters|
|
|
629
728
|
|
|
630
729
|
All MlKem classes also call sha3 WASM via `indcpa.ts`: `sha3_256Init`, `sha3_512Init`, `shake128Init`, `shake256Init`, `keccakAbsorb`, `sha3_256Final`, `sha3_512Final`, `shakeFinal`, `shakePad`, `shakeSqueezeBlock`.
|
|
631
730
|
|
|
632
731
|
**Tier 2: pure TS composition**
|
|
633
732
|
|
|
634
|
-
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
733
|
+
|TS Class / Object|Composes|
|
|
734
|
+
|---|---|
|
|
735
|
+
|`SerpentCipher`|`SerpentCbc` + `HMAC_SHA256` + `HKDF_SHA256`|
|
|
736
|
+
|`XChaCha20Cipher`|`ChaCha20Poly1305` (via `ops.ts`) + `HKDF_SHA256`|
|
|
737
|
+
|`Seal`|`SealStream` + `OpenStream` (degenerate single-chunk case)|
|
|
738
|
+
|`SealStream`|`CipherSuite` (generic — caller provides cipher)|
|
|
739
|
+
|`OpenStream`|`CipherSuite` (generic — caller provides cipher)|
|
|
740
|
+
|`SealStreamPool`|`CipherSuite` + `compileWasm()` + Web Workers|
|
|
741
|
+
|`HKDF_SHA256`|`HMAC_SHA256` (extract + expand per RFC 5869)|
|
|
742
|
+
|`HKDF_SHA512`|`HMAC_SHA512` (extract + expand per RFC 5869)|
|
|
743
|
+
|`Fortuna`|`Generator` + `HashFn` (any compatible pair: `SerpentGenerator`/`ChaCha20Generator` × `SHA256Hash`/`SHA3_256Hash`)|
|
|
645
744
|
|
|
646
745
|
---
|
|
647
746
|
|
|
648
747
|
### Cross-module dependencies
|
|
649
748
|
|
|
650
|
-
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
|
749
|
+
|Relationship|Notes|
|
|
750
|
+
|---|---|
|
|
751
|
+
|`SerpentCipher` → `serpent` + `sha2`|Tier 2 composition: Serpent-CBC + HMAC-SHA256 + HKDF-SHA256.|
|
|
752
|
+
|`XChaCha20Cipher` → `chacha20` + `sha2`|HKDF-SHA256 for key derivation + HChaCha20 + ChaCha20-Poly1305 for per-chunk AEAD.|
|
|
753
|
+
|`KyberSuite` → `kyber` + `sha3` + inner cipher|KEM encaps/decaps + HKDF with kemCt binding + inner CipherSuite.|
|
|
754
|
+
|`SealStream`, `OpenStream` → depends on cipher|Cipher-agnostic. Module requirements are determined by the `CipherSuite` passed at construction.|
|
|
755
|
+
|`SealStreamPool` → depends on cipher|Same module requirements as the cipher, plus `WasmSource` in pool opts for worker compilation.|
|
|
756
|
+
|`Fortuna` → cipher module + hash module|Uses `Fortuna.create({ generator, hash })` static factory instead of `new`. Required modules depend on which generator and hash you pass. See [fortuna.md](./fortuna.md).|
|
|
757
|
+
|`MlKem512`, `MlKem768`, `MlKem1024` → `kyber` + `sha3`|Kyber module handles polynomial arithmetic; sha3 provides SHAKE128/256, SHA3-256/512 for G/H/J/matrix gen.|
|
|
758
|
+
|`HKDF_SHA256`, `HKDF_SHA512` → `sha2`|Pure TS composition — extract and expand steps per RFC 5869.|
|
|
759
|
+
|All other classes|Each depends on exactly **one** WASM module.|
|
|
661
760
|
|
|
662
761
|
---
|
|
663
762
|
|
|
664
763
|
### Public API barrel (`src/ts/index.ts`)
|
|
665
764
|
|
|
666
|
-
The root barrel defines and exports the dispatching `init()` function.
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
Each subpath export also exports its own module-specific init function for
|
|
686
|
-
tree-shakeable loading: `serpentInit(source)`, `chacha20Init(source)`,
|
|
687
|
-
`sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`.
|
|
765
|
+
The root barrel defines and exports the dispatching `init()` function. It is the only file that imports all four module-scoped init functions.
|
|
766
|
+
|
|
767
|
+
|Source|Exports|
|
|
768
|
+
|---|---|
|
|
769
|
+
|_(barrel itself)_|`init` (dispatching function — calls per-module init functions via `Promise.all`)|
|
|
770
|
+
|`init.ts`|`Module`, `WasmSource`, `isInitialized`|
|
|
771
|
+
|`errors.ts`|`AuthenticationError`|
|
|
772
|
+
|`serpent/index.ts`|`Serpent`, `SerpentCtr`, `SerpentCbc`, `SerpentCipher`, `_serpentReady`|
|
|
773
|
+
|`chacha20/index.ts`|`ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Cipher`, `_chachaReady`|
|
|
774
|
+
|`sha2/index.ts`|`SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`, `HKDF_SHA256`, `HKDF_SHA512`, `_sha2Ready`|
|
|
775
|
+
|`sha3/index.ts`|`SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256`, `_sha3Ready`|
|
|
776
|
+
|`keccak/index.ts`|`keccakInit` + re-exports all sha3 classes (alias subpath)|
|
|
777
|
+
|`kyber/index.ts`|`kyberInit`, `KyberSuite`, `MlKem512`, `MlKem768`, `MlKem1024`, `KyberKeyPair`, `KyberEncapsulation`, `KyberParams`, `MLKEM512`, `MLKEM768`, `MLKEM1024`|
|
|
778
|
+
|`stream/index.ts`|`Seal`, `SealStream`, `OpenStream`, `SealStreamPool`, `CipherSuite`, `DerivedKeys`, `SealStreamOpts`, `PoolOpts`, `FLAG_FRAMED`, `TAG_DATA`, `TAG_FINAL`, `HEADER_SIZE`, `CHUNK_MIN`, `CHUNK_MAX`|
|
|
779
|
+
|`fortuna.ts`|`Fortuna`|
|
|
780
|
+
|`types.ts`|`Hash`, `KeyedHash`, `Blockcipher`, `Streamcipher`, `AEAD`, `Generator`, `HashFn`|
|
|
781
|
+
|`utils.ts`|`hexToBytes`, `bytesToHex`, `utf8ToBytes`, `bytesToUtf8`, `base64ToBytes`, `bytesToBase64`, `constantTimeEqual`, `CT_MAX_BYTES`, `wipe`, `xor`, `concat`, `randomBytes`, `hasSIMD`|
|
|
782
|
+
|
|
783
|
+
Each subpath export also exports its own module-specific init function for tree-shakeable loading: `serpentInit(source)`, `chacha20Init(source)`, `sha2Init(source)`, `sha3Init(source)`, `keccakInit(source)`.
|
|
688
784
|
|
|
689
785
|
---
|
|
690
786
|
|
|
691
787
|
## npm Package
|
|
692
788
|
|
|
693
789
|
**Subpath exports:**
|
|
790
|
+
|
|
694
791
|
```json
|
|
695
792
|
{
|
|
696
793
|
"exports": {
|
|
@@ -707,17 +804,14 @@ tree-shakeable loading: `serpentInit(source)`, `chacha20Init(source)`,
|
|
|
707
804
|
"./keccak": "./dist/keccak/index.js",
|
|
708
805
|
"./keccak/embedded": "./dist/keccak/embedded.js",
|
|
709
806
|
"./kyber": "./dist/kyber/index.js",
|
|
710
|
-
"./kyber/embedded": "./dist/kyber/embedded.js"
|
|
807
|
+
"./kyber/embedded": "./dist/kyber/embedded.js",
|
|
808
|
+
"./ratchet": "./dist/ratchet/index.js"
|
|
711
809
|
}
|
|
712
810
|
}
|
|
713
811
|
```
|
|
714
812
|
|
|
715
813
|
> [!NOTE]
|
|
716
|
-
> Pool worker files (`dist/serpent/pool-worker.js`, `dist/chacha20/pool-worker.js`)
|
|
717
|
-
> ship in the package but are not in the `exports` map. They are internal Web
|
|
718
|
-
> Worker entry points loaded by `SealStreamPool` at runtime via
|
|
719
|
-
> `new URL('./pool-worker.js', import.meta.url)`. Do not import them as named
|
|
720
|
-
> subpaths.
|
|
814
|
+
> Pool worker source files (`dist/serpent/pool-worker.js`, `dist/chacha20/pool-worker.js`) ship in the package but are not in the `exports` map. They are the build inputs from which `scripts/embed-workers.ts` produces the IIFE source strings embedded in `dist/<cipher>/cipher-suite.js` at lib build time. Workers are spawned from those embedded strings via classic blob URLs. Consumers do not import the `pool-worker.js` files directly, and bundlers do not need to chunk them. Strict-CSP consumers (`worker-src 'self'`, no `blob:`) can supply their own URL-based factory by spread-overriding `createPoolWorker` on the cipher object; see [ciphersuite.md](./ciphersuite.md).
|
|
721
815
|
|
|
722
816
|
The root `.` export re-exports everything. Subpath exports allow bundlers to tree-shake at the module level; a consumer importing only `./sha3` does not include the Serpent wrapper classes or their embedded WASM binaries in their bundle.
|
|
723
817
|
|
|
@@ -727,36 +821,35 @@ The `/embedded` subpaths provide gzip+base64 WASM blobs for zero-config usage. C
|
|
|
727
821
|
|
|
728
822
|
**Published.** The npm package includes:
|
|
729
823
|
|
|
730
|
-
- `dist/`: compiled JS, TypeScript declarations, WASM binaries, pool worker
|
|
824
|
+
- `dist/`: compiled JS, TypeScript declarations, WASM binaries, pool worker source files (build inputs, not runtime spawn entries; see the NOTE above), and a subset of consumer-facing API docs for offline use.
|
|
731
825
|
- `CLAUDE.md`: agent-facing project context.
|
|
732
826
|
- `SECURITY.md`: vulnerability disclosure policy.
|
|
733
827
|
|
|
734
|
-
**Not published.** `src/`, `test/`, `build/`, `scripts/`,
|
|
828
|
+
**Not published.** `src/`, `test/`, `build/`, `scripts/`, `.github/`, editor configs.
|
|
735
829
|
|
|
736
830
|
---
|
|
737
831
|
|
|
738
832
|
## Buffer Layouts
|
|
739
833
|
|
|
740
|
-
All offsets start at 0 per module. Independent linear memory. No offsets are
|
|
741
|
-
shared or coordinated across modules.
|
|
834
|
+
All offsets start at 0 per module. Independent linear memory. No offsets are shared or coordinated across modules.
|
|
742
835
|
|
|
743
836
|
### Serpent module (3 pages, 192 KB)
|
|
744
837
|
|
|
745
838
|
Source: `src/asm/serpent/buffers.ts`
|
|
746
839
|
|
|
747
|
-
|
|
|
748
|
-
|
|
749
|
-
|
|
|
750
|
-
|
|
|
751
|
-
|
|
|
752
|
-
|
|
|
753
|
-
|
|
|
754
|
-
|
|
|
755
|
-
|
|
|
756
|
-
|
|
|
757
|
-
|
|
|
758
|
-
|
|
|
759
|
-
|
|
|
840
|
+
|Offset|Size|Name|
|
|
841
|
+
|---|---|---|
|
|
842
|
+
|0|32|`KEY_BUFFER` — key input (padded to 32 bytes for all key sizes)|
|
|
843
|
+
|32|16|`BLOCK_PT_BUFFER` — single block plaintext|
|
|
844
|
+
|48|16|`BLOCK_CT_BUFFER` — single block ciphertext|
|
|
845
|
+
|64|16|`NONCE_BUFFER` — CTR mode nonce|
|
|
846
|
+
|80|16|`COUNTER_BUFFER` — 128-bit little-endian counter|
|
|
847
|
+
|96|528|`SUBKEY_BUFFER` — key schedule output (33 rounds × 4 × 4 bytes)|
|
|
848
|
+
|624|65552|`CHUNK_PT_BUFFER` — streaming plaintext (CTR/CBC); +16 from 65536 to fit PKCS7 max overhead|
|
|
849
|
+
|66176|65552|`CHUNK_CT_BUFFER` — streaming ciphertext (CTR/CBC)|
|
|
850
|
+
|131728|20|`WORK_BUFFER` — 5 × i32 scratch registers (key schedule + S-box/LT rounds)|
|
|
851
|
+
|131748|16|`CBC_IV_BUFFER` — CBC initialization vector / chaining value|
|
|
852
|
+
|131856|—|END|
|
|
760
853
|
|
|
761
854
|
`wipeBuffers()` zeroes all 10 buffers (key, block pt/ct, nonce, counter, subkeys, work, chunk pt/ct, CBC IV).
|
|
762
855
|
|
|
@@ -764,29 +857,29 @@ Source: `src/asm/serpent/buffers.ts`
|
|
|
764
857
|
|
|
765
858
|
Source: `src/asm/chacha20/buffers.ts`
|
|
766
859
|
|
|
767
|
-
|
|
|
768
|
-
|
|
769
|
-
|
|
|
770
|
-
|
|
|
771
|
-
|
|
|
772
|
-
|
|
|
773
|
-
|
|
|
774
|
-
|
|
|
775
|
-
|
|
|
776
|
-
|
|
|
777
|
-
|
|
|
778
|
-
|
|
|
779
|
-
|
|
|
780
|
-
|
|
|
781
|
-
|
|
|
782
|
-
|
|
|
783
|
-
|
|
|
784
|
-
|
|
|
785
|
-
|
|
|
786
|
-
|
|
|
787
|
-
|
|
|
788
|
-
|
|
|
789
|
-
|
|
|
860
|
+
|Offset|Size|Name|
|
|
861
|
+
|---|---|---|
|
|
862
|
+
|0|32|`KEY_BUFFER` — ChaCha20 256-bit key|
|
|
863
|
+
|32|12|`CHACHA_NONCE_BUFFER` — 96-bit nonce (3 × u32, LE)|
|
|
864
|
+
|44|4|`CHACHA_CTR_BUFFER` — u32 block counter|
|
|
865
|
+
|48|64|`CHACHA_BLOCK_BUFFER` — 64-byte keystream block output|
|
|
866
|
+
|112|64|`CHACHA_STATE_BUFFER` — 16 × u32 initial state|
|
|
867
|
+
|176|65536|`CHUNK_PT_BUFFER` — streaming plaintext|
|
|
868
|
+
|65712|65536|`CHUNK_CT_BUFFER` — streaming ciphertext|
|
|
869
|
+
|131248|32|`POLY_KEY_BUFFER` — one-time key r‖s|
|
|
870
|
+
|131280|64|`POLY_MSG_BUFFER` — message staging (≤ 64 bytes per polyUpdate)|
|
|
871
|
+
|131344|16|`POLY_BUF_BUFFER` — partial block accumulator|
|
|
872
|
+
|131360|4|`POLY_BUF_LEN_BUFFER` — u32 bytes in partial block|
|
|
873
|
+
|131364|16|`POLY_TAG_BUFFER` — 16-byte output MAC tag|
|
|
874
|
+
|131380|40|`POLY_H_BUFFER` — accumulator h: 5 × u64|
|
|
875
|
+
|131420|40|`POLY_R_BUFFER` — clamped r: 5 × u64|
|
|
876
|
+
|131460|32|`POLY_RS_BUFFER` — precomputed 5×r[1..4]: 4 × u64|
|
|
877
|
+
|131492|16|`POLY_S_BUFFER` — s pad: 4 × u32|
|
|
878
|
+
|131508|24|`XCHACHA_NONCE_BUFFER` — full 24-byte XChaCha20 nonce|
|
|
879
|
+
|131532|32|`XCHACHA_SUBKEY_BUFFER` — HChaCha20 output (key material)|
|
|
880
|
+
|131564|4|_(padding for 16-byte SIMD alignment)_|
|
|
881
|
+
|131568|256|`CHACHA_SIMD_WORK_BUFFER` — 4-wide inter-block keystream (4 × 64 bytes)|
|
|
882
|
+
|131824|—|END|
|
|
790
883
|
|
|
791
884
|
`wipeBuffers()` zeroes all 15 buffer regions (key, chacha nonce/ctr/block/state, chunk pt/ct, poly key/msg/buf/tag/h/r/rs/s, xchacha nonce/subkey, SIMD work).
|
|
792
885
|
|
|
@@ -794,29 +887,29 @@ Source: `src/asm/chacha20/buffers.ts`
|
|
|
794
887
|
|
|
795
888
|
Source: `src/asm/sha2/buffers.ts`
|
|
796
889
|
|
|
797
|
-
|
|
|
798
|
-
|
|
799
|
-
|
|
|
800
|
-
|
|
|
801
|
-
|
|
|
802
|
-
|
|
|
803
|
-
|
|
|
804
|
-
|
|
|
805
|
-
|
|
|
806
|
-
|
|
|
807
|
-
|
|
|
808
|
-
|
|
|
809
|
-
|
|
|
810
|
-
|
|
|
811
|
-
|
|
|
812
|
-
|
|
|
813
|
-
|
|
|
814
|
-
|
|
|
815
|
-
|
|
|
816
|
-
|
|
|
817
|
-
|
|
|
818
|
-
|
|
|
819
|
-
|
|
|
890
|
+
|Offset|Size|Name|
|
|
891
|
+
|---|---|---|
|
|
892
|
+
|0|32|`SHA256_H` — SHA-256 hash state H0..H7 (8 × u32)|
|
|
893
|
+
|32|64|`SHA256_BLOCK` — SHA-256 block accumulator|
|
|
894
|
+
|96|256|`SHA256_W` — SHA-256 message schedule W[0..63] (64 × u32)|
|
|
895
|
+
|352|32|`SHA256_OUT` — SHA-256 digest output|
|
|
896
|
+
|384|64|`SHA256_INPUT` — SHA-256 user input staging (one block)|
|
|
897
|
+
|448|4|`SHA256_PARTIAL` — u32 partial block length|
|
|
898
|
+
|452|8|`SHA256_TOTAL` — u64 total bytes hashed|
|
|
899
|
+
|460|64|`HMAC256_IPAD` — HMAC-SHA256 K' XOR ipad|
|
|
900
|
+
|524|64|`HMAC256_OPAD` — HMAC-SHA256 K' XOR opad|
|
|
901
|
+
|588|32|`HMAC256_INNER` — HMAC-SHA256 inner hash|
|
|
902
|
+
|620|64|`SHA512_H` — SHA-512 hash state H0..H7 (8 × u64)|
|
|
903
|
+
|684|128|`SHA512_BLOCK` — SHA-512 block accumulator|
|
|
904
|
+
|812|640|`SHA512_W` — SHA-512 message schedule W[0..79] (80 × u64)|
|
|
905
|
+
|1452|64|`SHA512_OUT` — SHA-512 digest output (SHA-384 uses first 48 bytes)|
|
|
906
|
+
|1516|128|`SHA512_INPUT` — SHA-512 user input staging (one block)|
|
|
907
|
+
|1644|4|`SHA512_PARTIAL` — u32 partial block length|
|
|
908
|
+
|1648|8|`SHA512_TOTAL` — u64 total bytes hashed|
|
|
909
|
+
|1656|128|`HMAC512_IPAD` — HMAC-SHA512 K' XOR ipad (128-byte block size)|
|
|
910
|
+
|1784|128|`HMAC512_OPAD` — HMAC-SHA512 K' XOR opad|
|
|
911
|
+
|1912|64|`HMAC512_INNER` — HMAC-SHA512 inner hash|
|
|
912
|
+
|1976|—|END|
|
|
820
913
|
|
|
821
914
|
`wipeBuffers()` zeroes all 20 buffer regions (SHA-256 state/block/W/out/input/partial/total, HMAC-256 ipad/opad/inner, SHA-512 state/block/W/out/input/partial/total, HMAC-512 ipad/opad/inner).
|
|
822
915
|
|
|
@@ -824,15 +917,15 @@ Source: `src/asm/sha2/buffers.ts`
|
|
|
824
917
|
|
|
825
918
|
Source: `src/asm/sha3/buffers.ts`
|
|
826
919
|
|
|
827
|
-
|
|
|
828
|
-
|
|
829
|
-
|
|
|
830
|
-
|
|
|
831
|
-
|
|
|
832
|
-
|
|
|
833
|
-
|
|
|
834
|
-
|
|
|
835
|
-
|
|
|
920
|
+
|Offset|Size|Name|
|
|
921
|
+
|---|---|---|
|
|
922
|
+
|0|200|`KECCAK_STATE`: 25 × u64 Keccak-f[1600] lane matrix (5×5, row-major x+5y)|
|
|
923
|
+
|200|4|`KECCAK_RATE`: u32 rate in bytes (variant-specific: 72–168)|
|
|
924
|
+
|204|4|`KECCAK_ABSORBED`: u32 bytes absorbed into current block|
|
|
925
|
+
|208|1|`KECCAK_DSBYTE`: u8 domain separation byte (0x06 for SHA-3, 0x1f for SHAKE)|
|
|
926
|
+
|209|168|`KECCAK_INPUT`: input staging buffer (max rate = SHAKE128 at 168 bytes)|
|
|
927
|
+
|377|168|`KECCAK_OUT`: output buffer (one SHAKE128 squeeze block)|
|
|
928
|
+
|545|—|END|
|
|
836
929
|
|
|
837
930
|
`wipeBuffers()` zeroes all 6 buffer regions (state, rate, absorbed, dsbyte, input, output).
|
|
838
931
|
|
|
@@ -840,19 +933,19 @@ Source: `src/asm/sha3/buffers.ts`
|
|
|
840
933
|
|
|
841
934
|
Source: `src/asm/kyber/`
|
|
842
935
|
|
|
843
|
-
|
|
|
844
|
-
|
|
845
|
-
|
|
|
846
|
-
|
|
|
847
|
-
|
|
|
848
|
-
|
|
|
849
|
-
|
|
|
850
|
-
|
|
|
851
|
-
|
|
|
852
|
-
|
|
|
853
|
-
|
|
|
854
|
-
|
|
|
855
|
-
|
|
|
936
|
+
|Region|Offset|Size|Purpose|
|
|
937
|
+
|---|---|---|---|
|
|
938
|
+
|AS data segment|0|4096|Zetas table (128 × i16, bit-reversed Montgomery domain)|
|
|
939
|
+
|Poly slots|4096|5120|10 × 512B scratch polynomials (256 × i16 each)|
|
|
940
|
+
|Polyvec slots|9216|16384|8 × 2048B scratch polyvecs (k=4 max: 4 × 512B)|
|
|
941
|
+
|SEED buffer|25600|32|Seed ρ/σ|
|
|
942
|
+
|MSG buffer|25632|32|Message / shared secret|
|
|
943
|
+
|PK buffer|25664|1568|Encapsulation key (max k=4)|
|
|
944
|
+
|SK buffer|27232|1536|IND-CPA secret key (max k=4)|
|
|
945
|
+
|CT buffer|28768|1568|Ciphertext (max k=4)|
|
|
946
|
+
|CT_PRIME buffer|30336|1568|Decaps re-encrypt comparison (max k=4)|
|
|
947
|
+
|XOF/PRF buffer|31904|1024|SHAKE squeeze output for rej_uniform / CBD|
|
|
948
|
+
|Poly accumulator|32928|512|Internal scratch for polyvec_basemul_acc|
|
|
856
949
|
|
|
857
950
|
Total mutable: 29344 bytes (4096–33440). End = 33440 < 192 KB.
|
|
858
951
|
|
|
@@ -862,14 +955,14 @@ Total mutable: 29344 bytes (4096–33440). End = 33440 < 192 KB.
|
|
|
862
955
|
|
|
863
956
|
## Test Suite
|
|
864
957
|
|
|
865
|
-
### Structure
|
|
866
958
|
|
|
959
|
+
### Structure
|
|
867
960
|
|
|
868
961
|
For the full testing methodology and vector corpus, see: [test-suite.md](./test-suite.md)
|
|
869
962
|
|
|
870
963
|
### Gate discipline
|
|
871
964
|
|
|
872
|
-
Each primitive family has a gate test
|
|
965
|
+
**Each primitive family has a gate test:** the simplest authoritative vector for that primitive. The gate must pass before any other tests in that family are written or run. Gate tests are annotated with a `// GATE` comment.
|
|
873
966
|
|
|
874
967
|
### `init.test.ts` contracts
|
|
875
968
|
|
|
@@ -882,50 +975,37 @@ Each primitive family has a gate test: the simplest authoritative vector for tha
|
|
|
882
975
|
|
|
883
976
|
## Correctness Contract
|
|
884
977
|
|
|
885
|
-
leviathan-crypto must produce byte-identical output to the authoritative
|
|
886
|
-
specification for every known test vector. Cross-checks against the leviathan
|
|
887
|
-
TypeScript reference and external tools (OpenSSL, Python hashlib, Node.js crypto)
|
|
888
|
-
provide additional verification layers.
|
|
978
|
+
leviathan-crypto must produce byte-identical output to the authoritative specification for every known test vector. Cross-checks against the leviathan TypeScript reference and external tools (OpenSSL, Python hashlib, Node.js crypto) provide additional verification layers.
|
|
889
979
|
|
|
890
|
-
The
|
|
891
|
-
`SHA256SUMS`, expected values are sourced directly from authoritative references.
|
|
892
|
-
They are the immutable truth, and must never be modified to make tests pass.
|
|
980
|
+
The vector corpus in `test/vectors/` act as a source of immutable known-answer-test truth. KAT files are reference data from authoritative specifications (FIPS, RFCs, ACVP, NIST CAVP, NESSIE) or are self generated as regression vectors by `scripts/gen-*-vectors.ts`. CI validates integrity against `SHA256SUMS`. See [test-suite.md](./test-suite.md) for the full corpus inventory, provenance, and gate discipline.
|
|
893
981
|
|
|
894
982
|
---
|
|
895
983
|
|
|
896
984
|
## Known Limitations
|
|
897
985
|
|
|
898
|
-
- **`SerpentCbc` is unauthenticated**: use `Seal` with `SerpentCipher` for
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
- **
|
|
902
|
-
`SealStreamPool` provides Worker-based parallelism for both cipher families;
|
|
903
|
-
other primitives remain single-threaded.
|
|
904
|
-
- **Max input per WASM call**: chunk-based APIs (CTR, CBC) accept at most 64KB
|
|
905
|
-
per call. Wrappers handle splitting automatically for larger inputs.
|
|
906
|
-
- **WASM side-channel posture**: WebAssembly implementations offer the best
|
|
907
|
-
available side-channel resistance (branchless, table-free), but lack
|
|
908
|
-
hardware-level constant-time guarantees. For applications where timing
|
|
909
|
-
side channels are a primary threat, a native cryptographic library with
|
|
910
|
-
verified constant-time guarantees will be more appropriate than any
|
|
911
|
-
WASM-based implementation.
|
|
986
|
+
- **`SerpentCbc` is unauthenticated**: use `Seal` with `SerpentCipher` for authenticated Serpent encryption, or pair `SerpentCbc` with `HMAC_SHA256` (Encrypt-then-MAC) if direct CBC access is required.
|
|
987
|
+
- **Single-threaded WASM per instance**: one WASM instance per binary per thread. `SealStreamPool` provides Worker-based parallelism for both cipher families; other primitives remain single-threaded.
|
|
988
|
+
- **Max input per WASM call**: CTR accepts at most 65536 bytes per call; CBC accepts at most 65552 bytes (65536 + 16 bytes PKCS7 maximum overhead). Wrappers handle splitting automatically for larger inputs.
|
|
989
|
+
- **WASM side-channel posture**: WebAssembly implementations offer the best available side-channel resistance (branchless, table-free), but lack hardware-level constant-time guarantees. For applications where timing side channels are a primary threat, a native cryptographic library with verified constant-time guarantees will be more appropriate than any WASM-based implementation.
|
|
912
990
|
|
|
913
991
|
---
|
|
914
992
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
993
|
+
## Cross-References
|
|
994
|
+
|
|
995
|
+
|Document|Description|
|
|
996
|
+
|---|---|
|
|
997
|
+
|[index](./README.md)|Project Documentation index|
|
|
998
|
+
|[lexicon](./lexicon.md)|Glossary of cryptographic terms|
|
|
999
|
+
|[test-suite](./test-suite.md)|testing methodology, vector corpus, and gate discipline|
|
|
1000
|
+
|[init](./init.md)|`init()` API, `WasmSource`, and idempotent behavior|
|
|
1001
|
+
|[loader](./loader.md)|internal WASM binary loading strategies|
|
|
1002
|
+
|[wasm](./wasm.md)|WebAssembly primer: modules, instances, memory, and the init gate|
|
|
1003
|
+
|[types](./types.md)|public TypeScript interfaces and `CipherSuite`|
|
|
1004
|
+
|[utils](./utils.md)|encoding helpers, `constantTimeEqual`, `wipe`, `randomBytes`|
|
|
1005
|
+
|[authenticated encryption](./aead.md)|`Seal`, `SealStream`, `OpenStream`: cipher-agnostic AEAD APIs using a `CipherSuite` such as `SerpentCipher` or `XChaCha20Cipher`|
|
|
1006
|
+
|[serpent](./serpent.md)|Serpent-256 TypeScript API, SerpentCipher|
|
|
1007
|
+
|[chacha20](./chacha20.md)|ChaCha20/Poly1305 TypeScript API, XChaCha20Cipher|
|
|
1008
|
+
|[sha2](./sha2.md)|SHA-2 hashes, HMAC, and HKDF TypeScript API|
|
|
1009
|
+
|[sha3](./sha3.md)|SHA-3 hashes and SHAKE XOFs TypeScript API|
|
|
1010
|
+
|[fortuna](./fortuna.md)|Fortuna CSPRNG with forward secrecy and entropy pooling|
|
|
1011
|
+
|[argon2id](./argon2id.md)|Argon2id password hashing and key derivation|
|