leviathan-crypto 2.0.1 → 3.0.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 +88 -281
- package/LICENSE +4 -0
- package/README.md +275 -87
- package/dist/aes/aes-cbc.d.ts +40 -0
- package/dist/aes/aes-cbc.js +158 -0
- package/dist/aes/aes-ctr.d.ts +50 -0
- package/dist/aes/aes-ctr.js +141 -0
- package/dist/aes/aes-gcm-siv.d.ts +67 -0
- package/dist/aes/aes-gcm-siv.js +217 -0
- package/dist/aes/aes-gcm.d.ts +61 -0
- package/dist/aes/aes-gcm.js +226 -0
- package/dist/aes/cipher-suite.d.ts +21 -0
- package/dist/aes/cipher-suite.js +179 -0
- package/dist/aes/embedded.d.ts +1 -0
- package/dist/aes/embedded.js +26 -0
- package/dist/aes/generator.d.ts +14 -0
- package/dist/aes/generator.js +103 -0
- package/dist/aes/index.d.ts +58 -0
- package/dist/aes/index.js +125 -0
- package/dist/aes/ops.d.ts +60 -0
- package/dist/aes/ops.js +164 -0
- package/dist/aes/pool-worker.d.ts +1 -0
- package/dist/aes/pool-worker.js +92 -0
- package/dist/aes/types.d.ts +1 -0
- package/dist/aes/types.js +23 -0
- package/dist/aes.wasm +0 -0
- package/dist/blake3/embedded.d.ts +1 -0
- package/dist/blake3/embedded.js +26 -0
- package/dist/blake3/index.d.ts +143 -0
- package/dist/blake3/index.js +620 -0
- package/dist/blake3/types.d.ts +102 -0
- package/dist/blake3/types.js +31 -0
- package/dist/blake3/validate.d.ts +29 -0
- package/dist/blake3/validate.js +80 -0
- package/dist/blake3.wasm +0 -0
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +98 -13
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +100 -3
- package/dist/chacha20/index.js +169 -35
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +107 -27
- package/dist/chacha20/pool-worker.js +14 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -0
- package/dist/cte.wasm +0 -0
- package/dist/curve25519.wasm +0 -0
- package/dist/ecdsa/der.d.ts +23 -0
- package/dist/ecdsa/der.js +192 -0
- package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
- package/dist/ecdsa/ecprivatekey-der.js +230 -0
- package/dist/ecdsa/embedded.d.ts +1 -0
- package/dist/ecdsa/embedded.js +25 -0
- package/dist/ecdsa/index.d.ts +124 -0
- package/dist/ecdsa/index.js +366 -0
- package/dist/ecdsa/types.d.ts +31 -0
- package/dist/ecdsa/types.js +28 -0
- package/dist/ecdsa/validate.d.ts +18 -0
- package/dist/ecdsa/validate.js +92 -0
- package/dist/ed25519/embedded.d.ts +1 -0
- package/dist/ed25519/embedded.js +31 -0
- package/dist/ed25519/index.d.ts +70 -0
- package/dist/ed25519/index.js +308 -0
- package/dist/ed25519/types.d.ts +27 -0
- package/dist/ed25519/types.js +27 -0
- package/dist/ed25519/validate.d.ts +7 -0
- package/dist/ed25519/validate.js +77 -0
- package/dist/embedded/aes-pool-worker.d.ts +1 -0
- package/dist/embedded/aes-pool-worker.js +5 -0
- package/dist/embedded/aes.d.ts +1 -0
- package/dist/embedded/aes.js +3 -0
- package/dist/embedded/blake3.d.ts +1 -0
- package/dist/embedded/blake3.js +3 -0
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -2
- package/dist/embedded/curve25519.d.ts +1 -0
- package/dist/embedded/curve25519.js +3 -0
- package/dist/embedded/mldsa.d.ts +1 -0
- package/dist/embedded/mldsa.js +3 -0
- package/dist/embedded/mlkem.d.ts +1 -0
- package/dist/embedded/mlkem.js +3 -0
- package/dist/embedded/p256.d.ts +1 -0
- package/dist/embedded/p256.js +3 -0
- 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 +2 -2
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -2
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -2
- package/dist/embedded/slhdsa.d.ts +1 -0
- package/dist/embedded/slhdsa.js +3 -0
- package/dist/errors.d.ts +92 -1
- package/dist/errors.js +111 -1
- package/dist/fortuna.d.ts +18 -12
- package/dist/fortuna.js +166 -99
- package/dist/index.d.ts +42 -11
- package/dist/index.js +65 -20
- package/dist/init.d.ts +1 -3
- package/dist/init.js +73 -7
- package/dist/keccak/embedded.js +1 -1
- package/dist/keccak/index.d.ts +2 -0
- package/dist/keccak/index.js +4 -2
- package/dist/loader.d.ts +1 -19
- package/dist/loader.js +26 -32
- package/dist/merkle/blake3-tree.d.ts +35 -0
- package/dist/merkle/blake3-tree.js +187 -0
- package/dist/merkle/checkpoint.d.ts +58 -0
- package/dist/merkle/checkpoint.js +217 -0
- package/dist/merkle/index.d.ts +19 -0
- package/dist/merkle/index.js +37 -0
- package/dist/merkle/merkle-log.d.ts +130 -0
- package/dist/merkle/merkle-log.js +207 -0
- package/dist/merkle/merkle-verifier.d.ts +126 -0
- package/dist/merkle/merkle-verifier.js +296 -0
- package/dist/merkle/proof.d.ts +70 -0
- package/dist/merkle/proof.js +300 -0
- package/dist/merkle/sha256-tree.d.ts +33 -0
- package/dist/merkle/sha256-tree.js +145 -0
- package/dist/merkle/signed-log.d.ts +156 -0
- package/dist/merkle/signed-log.js +356 -0
- package/dist/merkle/signed-note.d.ts +309 -0
- package/dist/merkle/signed-note.js +648 -0
- package/dist/merkle/sth.d.ts +31 -0
- package/dist/merkle/sth.js +31 -0
- package/dist/merkle/storage.d.ts +40 -0
- package/dist/merkle/storage.js +71 -0
- package/dist/merkle/tree.d.ts +68 -0
- package/dist/merkle/tree.js +94 -0
- package/dist/mldsa/embedded.d.ts +1 -0
- package/dist/{kyber → mldsa}/embedded.js +5 -5
- package/dist/mldsa/expand.d.ts +53 -0
- package/dist/mldsa/expand.js +188 -0
- package/dist/mldsa/format.d.ts +16 -0
- package/dist/mldsa/format.js +68 -0
- package/dist/mldsa/hashvariant.d.ts +32 -0
- package/dist/mldsa/hashvariant.js +248 -0
- package/dist/mldsa/index.d.ts +142 -0
- package/dist/mldsa/index.js +463 -0
- package/dist/mldsa/keygen.d.ts +16 -0
- package/dist/mldsa/keygen.js +232 -0
- package/dist/mldsa/params.d.ts +21 -0
- package/dist/mldsa/params.js +55 -0
- package/dist/mldsa/sha3-helpers.d.ts +30 -0
- package/dist/mldsa/sha3-helpers.js +124 -0
- package/dist/mldsa/sign.d.ts +36 -0
- package/dist/mldsa/sign.js +380 -0
- package/dist/mldsa/types.d.ts +91 -0
- package/dist/mldsa/types.js +25 -0
- package/dist/mldsa/validate.d.ts +55 -0
- package/dist/mldsa/validate.js +125 -0
- package/dist/mldsa/verify.d.ts +29 -0
- package/dist/mldsa/verify.js +269 -0
- package/dist/mldsa.wasm +0 -0
- package/dist/mlkem/embedded.d.ts +1 -0
- package/dist/mlkem/embedded.js +27 -0
- package/dist/mlkem/indcpa.d.ts +49 -0
- package/dist/{kyber → mlkem}/indcpa.js +48 -48
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +41 -31
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +48 -13
- package/dist/{kyber → mlkem}/params.d.ts +4 -4
- package/dist/{kyber → mlkem}/params.js +2 -2
- package/dist/mlkem/suite.d.ts +12 -0
- package/dist/{kyber → mlkem}/suite.js +17 -12
- package/dist/{kyber → mlkem}/types.d.ts +4 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/mlkem/validate.d.ts +23 -0
- package/dist/{kyber → mlkem}/validate.js +24 -20
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +8 -0
- package/dist/ratchet/index.js +38 -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 +144 -56
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +62 -1
- package/dist/serpent/index.js +97 -21
- package/dist/serpent/pool-worker.js +28 -102
- package/dist/serpent/serpent-cbc.d.ts +16 -6
- package/dist/serpent/serpent-cbc.js +58 -37
- package/dist/serpent/shared-ops.d.ts +63 -0
- package/dist/serpent/shared-ops.js +178 -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/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +22 -1
- package/dist/sha2/index.js +80 -11
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +87 -3
- package/dist/sha3/index.js +317 -19
- package/dist/sha3/kmac.d.ts +121 -0
- package/dist/sha3/kmac.js +800 -0
- package/dist/sha3.wasm +0 -0
- package/dist/shared/pkcs7.d.ts +22 -0
- package/dist/shared/pkcs7.js +84 -0
- package/dist/sign/ctx.d.ts +41 -0
- package/dist/sign/ctx.js +102 -0
- package/dist/sign/envelope.d.ts +45 -0
- package/dist/sign/envelope.js +152 -0
- package/dist/sign/hasher.d.ts +9 -0
- package/dist/sign/hasher.js +132 -0
- package/dist/sign/index.d.ts +11 -0
- package/dist/sign/index.js +34 -0
- package/dist/sign/sign-stream.d.ts +25 -0
- package/dist/sign/sign-stream.js +112 -0
- package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
- package/dist/sign/suites/ecdsa-p256.js +120 -0
- package/dist/sign/suites/ed25519.d.ts +3 -0
- package/dist/sign/suites/ed25519.js +165 -0
- package/dist/sign/suites/hybrid-classical.d.ts +23 -0
- package/dist/sign/suites/hybrid-classical.js +526 -0
- package/dist/sign/suites/hybrid-pq.d.ts +4 -0
- package/dist/sign/suites/hybrid-pq.js +234 -0
- package/dist/sign/suites/mldsa.d.ts +7 -0
- package/dist/sign/suites/mldsa.js +161 -0
- package/dist/sign/suites/slhdsa.d.ts +7 -0
- package/dist/sign/suites/slhdsa.js +176 -0
- package/dist/sign/types.d.ts +106 -0
- package/dist/sign/types.js +28 -0
- package/dist/sign/verify-stream.d.ts +30 -0
- package/dist/sign/verify-stream.js +227 -0
- package/dist/slhdsa/embedded.d.ts +1 -0
- package/dist/slhdsa/embedded.js +26 -0
- package/dist/slhdsa/index.d.ts +149 -0
- package/dist/slhdsa/index.js +493 -0
- package/dist/slhdsa/params.d.ts +26 -0
- package/dist/slhdsa/params.js +70 -0
- package/dist/slhdsa/prehash.d.ts +68 -0
- package/dist/slhdsa/prehash.js +307 -0
- package/dist/slhdsa/sign.d.ts +39 -0
- package/dist/slhdsa/sign.js +116 -0
- package/dist/slhdsa/types.d.ts +129 -0
- package/dist/slhdsa/types.js +27 -0
- package/dist/slhdsa/validate.d.ts +60 -0
- package/dist/slhdsa/validate.js +127 -0
- package/dist/slhdsa/verify.d.ts +32 -0
- package/dist/slhdsa/verify.js +107 -0
- package/dist/slhdsa.wasm +0 -0
- package/dist/stream/header.js +8 -8
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +65 -22
- package/dist/stream/seal-stream-pool.d.ts +2 -0
- package/dist/stream/seal-stream-pool.js +100 -33
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +48 -19
- package/dist/stream/seal.js +6 -6
- package/dist/stream/types.d.ts +3 -1
- package/dist/stream/types.js +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +9 -10
- package/dist/utils.js +84 -59
- package/dist/wasm-source.d.ts +9 -8
- package/dist/wasm-source.js +1 -1
- package/dist/x25519/embedded.d.ts +1 -0
- package/dist/x25519/embedded.js +31 -0
- package/dist/x25519/index.d.ts +43 -0
- package/dist/x25519/index.js +159 -0
- package/dist/x25519/types.d.ts +25 -0
- package/dist/x25519/types.js +27 -0
- package/dist/x25519/validate.d.ts +2 -0
- package/dist/x25519/validate.js +39 -0
- package/package.json +123 -64
- package/SECURITY.md +0 -276
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +0 -323
- package/dist/docs/architecture.md +0 -932
- package/dist/docs/argon2id.md +0 -302
- package/dist/docs/chacha20.md +0 -674
- package/dist/docs/exports.md +0 -241
- package/dist/docs/fortuna.md +0 -313
- package/dist/docs/init.md +0 -302
- package/dist/docs/loader.md +0 -161
- package/dist/docs/serpent.md +0 -519
- package/dist/docs/sha2.md +0 -613
- package/dist/docs/sha3.md +0 -546
- package/dist/docs/types.md +0 -276
- package/dist/docs/utils.md +0 -367
- package/dist/embedded/kyber.d.ts +0 -1
- package/dist/embedded/kyber.js +0 -3
- package/dist/kyber/embedded.d.ts +0 -1
- package/dist/kyber/indcpa.d.ts +0 -49
- package/dist/kyber/index.d.ts +0 -38
- package/dist/kyber/kem.d.ts +0 -21
- package/dist/kyber/suite.d.ts +0 -13
- package/dist/kyber/validate.d.ts +0 -19
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/** BLAKE3 WASM exports. */
|
|
2
|
+
export interface Blake3Exports {
|
|
3
|
+
memory: WebAssembly.Memory;
|
|
4
|
+
getModuleId: () => number;
|
|
5
|
+
getMemoryPages: () => number;
|
|
6
|
+
getInputStagingOffset: () => number;
|
|
7
|
+
getOutputStagingOffset: () => number;
|
|
8
|
+
getCvOffset: () => number;
|
|
9
|
+
getMsgOffset: () => number;
|
|
10
|
+
getCounterOffset: () => number;
|
|
11
|
+
getBlockLenOffset: () => number;
|
|
12
|
+
getFlagsOffset: () => number;
|
|
13
|
+
getCompressOutOffset: () => number;
|
|
14
|
+
getKeyedKeyOffset: () => number;
|
|
15
|
+
getDeriveCvOffset: () => number;
|
|
16
|
+
getCompress4CvInOffset: () => number;
|
|
17
|
+
getCompress4MsgInOffset: () => number;
|
|
18
|
+
getCompress4CtrInOffset: () => number;
|
|
19
|
+
getCompress4OutOffset: () => number;
|
|
20
|
+
getCompress4BlenInOffset: () => number;
|
|
21
|
+
getCompress4FlagsInOffset: () => number;
|
|
22
|
+
getModeCvOffset: () => number;
|
|
23
|
+
FLAG_CHUNK_START: {
|
|
24
|
+
value: number;
|
|
25
|
+
};
|
|
26
|
+
FLAG_CHUNK_END: {
|
|
27
|
+
value: number;
|
|
28
|
+
};
|
|
29
|
+
FLAG_PARENT: {
|
|
30
|
+
value: number;
|
|
31
|
+
};
|
|
32
|
+
FLAG_ROOT: {
|
|
33
|
+
value: number;
|
|
34
|
+
};
|
|
35
|
+
FLAG_KEYED_HASH: {
|
|
36
|
+
value: number;
|
|
37
|
+
};
|
|
38
|
+
FLAG_DERIVE_KEY_CONTEXT: {
|
|
39
|
+
value: number;
|
|
40
|
+
};
|
|
41
|
+
FLAG_DERIVE_KEY_MATERIAL: {
|
|
42
|
+
value: number;
|
|
43
|
+
};
|
|
44
|
+
BLAKE3_IV0: {
|
|
45
|
+
value: number;
|
|
46
|
+
};
|
|
47
|
+
BLAKE3_IV1: {
|
|
48
|
+
value: number;
|
|
49
|
+
};
|
|
50
|
+
BLAKE3_IV2: {
|
|
51
|
+
value: number;
|
|
52
|
+
};
|
|
53
|
+
BLAKE3_IV3: {
|
|
54
|
+
value: number;
|
|
55
|
+
};
|
|
56
|
+
BLAKE3_IV4: {
|
|
57
|
+
value: number;
|
|
58
|
+
};
|
|
59
|
+
BLAKE3_IV5: {
|
|
60
|
+
value: number;
|
|
61
|
+
};
|
|
62
|
+
BLAKE3_IV6: {
|
|
63
|
+
value: number;
|
|
64
|
+
};
|
|
65
|
+
BLAKE3_IV7: {
|
|
66
|
+
value: number;
|
|
67
|
+
};
|
|
68
|
+
compress: (cvOff: number, blockOff: number, counterLo: number, counterHi: number, blockLen: number, flags: number, outOff: number) => void;
|
|
69
|
+
compress4: () => void;
|
|
70
|
+
chunkInit: (chunkIndex: bigint) => void;
|
|
71
|
+
chunkUpdate: (blockOff: number, blockLen: number) => void;
|
|
72
|
+
chunkFinalize: (outCvOff: number, isRootSoloChunk: number) => void;
|
|
73
|
+
treeInit: () => void;
|
|
74
|
+
treePushChunk: (chunkCvOff: number) => void;
|
|
75
|
+
treeFinalizeRoot: (outOff: number) => void;
|
|
76
|
+
hash: (inputOff: number, inputLen: number, outOff: number, outLen: number) => void;
|
|
77
|
+
hashKeyed: (keyOff: number, inputOff: number, inputLen: number, outOff: number, outLen: number) => void;
|
|
78
|
+
deriveKey: (contextOff: number, contextLen: number, materialOff: number, materialLen: number, outOff: number, outLen: number) => void;
|
|
79
|
+
squeezeXofBlock: (counterLo: number, counterHi: number, outOff: number) => void;
|
|
80
|
+
wipeBuffers: () => void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* BLAKE3 WASM internal test exports. NOT part of the consumer surface,
|
|
84
|
+
* NOT re-exported from `src/ts/blake3/index.ts`. Wired exclusively for
|
|
85
|
+
* the tree-internals test suite (`test/unit/blake3/blake3-tree-internals
|
|
86
|
+
* .test.ts`) and the Merkle-tree substrate
|
|
87
|
+
* (`src/ts/merkle/blake3-tree.ts`) which casts
|
|
88
|
+
* `Blake3Exports & Blake3TestExports` inside the merkle module.
|
|
89
|
+
*
|
|
90
|
+
* Tests obtain these via `test/unit/blake3/helpers.ts`, which casts the
|
|
91
|
+
* public `Blake3Exports` to `Blake3Exports & Blake3TestExports`. Consumer
|
|
92
|
+
* code never sees the `_test*` surface.
|
|
93
|
+
*/
|
|
94
|
+
export interface Blake3TestExports {
|
|
95
|
+
_testChunkCV: (inputOff: number, inputLen: number, chunkIndex: bigint, startCvOff: number, modeFlags: number, outCvOff: number) => void;
|
|
96
|
+
_testParentCV: (leftCvOff: number, rightCvOff: number, startCvOff: number, modeFlags: number, isRoot: number, outCvOff: number) => void;
|
|
97
|
+
_testDeriveContextCV: (contextOff: number, contextLen: number, outCcvOff: number) => void;
|
|
98
|
+
_getBatch4CallCount: () => number;
|
|
99
|
+
_resetBatch4CallCount: () => void;
|
|
100
|
+
_getParentBatch4CallCount: () => number;
|
|
101
|
+
_resetParentBatch4CallCount: () => void;
|
|
102
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/blake3/types.ts
|
|
23
|
+
//
|
|
24
|
+
// BLAKE3 WASM exports interface. Mirrors the AssemblyScript surface in
|
|
25
|
+
// `src/asm/blake3/index.ts`. The consumer-facing TS surface (BLAKE3,
|
|
26
|
+
// BLAKE3Stream and the keyed_hash / derive_key variants) calls the
|
|
27
|
+
// top-level `hash` / `hashKeyed` / `deriveKey` entry points. Lower-level
|
|
28
|
+
// primitives (`compress`, flag constants, buffer accessors) are typed
|
|
29
|
+
// here for completeness and so test code can drive each layer in
|
|
30
|
+
// isolation if needed.
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BLAKE3 §2.3 Modes: keyed_hash takes a 32-byte key. The key seeds the chunk
|
|
3
|
+
* machine in place of the BLAKE3 IV and every compress carries the
|
|
4
|
+
* KEYED_HASH flag. A key of any other length is a contract violation.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateKey(key: Uint8Array): void;
|
|
7
|
+
/**
|
|
8
|
+
* BLAKE3 §2.3 Modes: derive_key takes a context string and produces a
|
|
9
|
+
* derived key. The context string is a domain separator and is
|
|
10
|
+
* conventionally a UTF-8 hardcoded application constant. An empty context
|
|
11
|
+
* defeats the domain separation §2.3 is designed to provide; reject it.
|
|
12
|
+
*
|
|
13
|
+
* Accepts a JS string (UTF-8 encoded here) or a Uint8Array (passed
|
|
14
|
+
* through). No upper cap on length.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateContext(context: string | Uint8Array): Uint8Array;
|
|
17
|
+
/**
|
|
18
|
+
* BLAKE3 §2.6 XOF: default-length output is 32 bytes; the XOF can in principle
|
|
19
|
+
* produce up to 2^64 - 1 bytes. The one-shot path (BLAKE3.hash /
|
|
20
|
+
* BLAKE3KeyedHash.hash / BLAKE3DeriveKey.derive and the streaming
|
|
21
|
+
* finalize(outLen) counterparts) writes outLen bytes through a single
|
|
22
|
+
* WASM call sized by the OUTPUT_STAGING region, so the practical
|
|
23
|
+
* upper bound is `OUTPUT_STAGING_SIZE` (1024 bytes); larger consumers
|
|
24
|
+
* use `finalizeXof()` and stream from `BLAKE3OutputReader.read(n)`
|
|
25
|
+
* which squeezes 64 bytes at a time off the WASM-side root snapshot.
|
|
26
|
+
* This validator rejects nonsense (zero, negative, non-finite,
|
|
27
|
+
* non-integer); the one-shot wrappers enforce the per-call ceiling.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateOutputLen(outLen: number): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/blake3/validate.ts
|
|
23
|
+
//
|
|
24
|
+
// BLAKE3 caller-side input validation. Pure length / type checks, no
|
|
25
|
+
// crypto. BLAKE3 is a hash family, not a signature scheme; rejected
|
|
26
|
+
// inputs throw `RangeError` / `TypeError` (no `SigningError`).
|
|
27
|
+
/**
|
|
28
|
+
* BLAKE3 §2.3 Modes: keyed_hash takes a 32-byte key. The key seeds the chunk
|
|
29
|
+
* machine in place of the BLAKE3 IV and every compress carries the
|
|
30
|
+
* KEYED_HASH flag. A key of any other length is a contract violation.
|
|
31
|
+
*/
|
|
32
|
+
export function validateKey(key) {
|
|
33
|
+
if (!(key instanceof Uint8Array))
|
|
34
|
+
throw new TypeError('leviathan-crypto: blake3 key must be a Uint8Array');
|
|
35
|
+
if (key.length !== 32)
|
|
36
|
+
throw new RangeError(`leviathan-crypto: blake3 key must be 32 bytes (got ${key.length})`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* BLAKE3 §2.3 Modes: derive_key takes a context string and produces a
|
|
40
|
+
* derived key. The context string is a domain separator and is
|
|
41
|
+
* conventionally a UTF-8 hardcoded application constant. An empty context
|
|
42
|
+
* defeats the domain separation §2.3 is designed to provide; reject it.
|
|
43
|
+
*
|
|
44
|
+
* Accepts a JS string (UTF-8 encoded here) or a Uint8Array (passed
|
|
45
|
+
* through). No upper cap on length.
|
|
46
|
+
*/
|
|
47
|
+
export function validateContext(context) {
|
|
48
|
+
let bytes;
|
|
49
|
+
if (typeof context === 'string') {
|
|
50
|
+
bytes = new TextEncoder().encode(context);
|
|
51
|
+
}
|
|
52
|
+
else if (context instanceof Uint8Array) {
|
|
53
|
+
bytes = context;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new TypeError('leviathan-crypto: blake3 derive_key context must be a string or Uint8Array');
|
|
57
|
+
}
|
|
58
|
+
if (bytes.length === 0)
|
|
59
|
+
throw new RangeError('leviathan-crypto: blake3 derive_key context must be non-empty '
|
|
60
|
+
+ '(empty context defeats §2.3 domain separation)');
|
|
61
|
+
return bytes;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* BLAKE3 §2.6 XOF: default-length output is 32 bytes; the XOF can in principle
|
|
65
|
+
* produce up to 2^64 - 1 bytes. The one-shot path (BLAKE3.hash /
|
|
66
|
+
* BLAKE3KeyedHash.hash / BLAKE3DeriveKey.derive and the streaming
|
|
67
|
+
* finalize(outLen) counterparts) writes outLen bytes through a single
|
|
68
|
+
* WASM call sized by the OUTPUT_STAGING region, so the practical
|
|
69
|
+
* upper bound is `OUTPUT_STAGING_SIZE` (1024 bytes); larger consumers
|
|
70
|
+
* use `finalizeXof()` and stream from `BLAKE3OutputReader.read(n)`
|
|
71
|
+
* which squeezes 64 bytes at a time off the WASM-side root snapshot.
|
|
72
|
+
* This validator rejects nonsense (zero, negative, non-finite,
|
|
73
|
+
* non-integer); the one-shot wrappers enforce the per-call ceiling.
|
|
74
|
+
*/
|
|
75
|
+
export function validateOutputLen(outLen) {
|
|
76
|
+
if (typeof outLen !== 'number' || !Number.isFinite(outLen) || !Number.isInteger(outLen))
|
|
77
|
+
throw new RangeError(`leviathan-crypto: blake3 outLen must be a finite integer (got ${String(outLen)})`);
|
|
78
|
+
if (outLen < 1)
|
|
79
|
+
throw new RangeError(`leviathan-crypto: blake3 outLen must be >= 1 (got ${outLen})`);
|
|
80
|
+
}
|
package/dist/blake3.wasm
ADDED
|
Binary file
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { CipherSuite } from '../stream/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `CipherSuite` implementation for the stream construction using XChaCha20-Poly1305.
|
|
4
|
+
*
|
|
5
|
+
* Each chunk is encrypted with ChaCha20-Poly1305 using a HChaCha20 subkey
|
|
6
|
+
* derived via HKDF-SHA-256 + HChaCha20. This is the recommended cipher suite
|
|
7
|
+
* for `SealStream` / `OpenStream` / `SealStreamPool`.
|
|
8
|
+
*
|
|
9
|
+
* Pass to `SealStream` / `OpenStream` / `SealStreamPool` instead of constructing
|
|
10
|
+
* this object directly. Use `XChaCha20Cipher.keygen()` to generate a 32-byte key.
|
|
11
|
+
*/
|
|
2
12
|
export declare const XChaCha20Cipher: CipherSuite & {
|
|
3
13
|
keygen(): Uint8Array;
|
|
4
14
|
};
|
|
@@ -21,40 +21,101 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/chacha20/cipher-suite.ts
|
|
23
23
|
//
|
|
24
|
-
// XChaCha20Cipher
|
|
24
|
+
// XChaCha20Cipher, CipherSuite implementation for the STREAM construction.
|
|
25
25
|
// HKDF-SHA-256 key derivation → HChaCha20 subkey → ChaCha20-Poly1305 per chunk.
|
|
26
|
-
import { getInstance } from '../init.js';
|
|
26
|
+
import { getInstance, _assertNotOwned } from '../init.js';
|
|
27
27
|
import { HKDF_SHA256 } from '../sha2/index.js';
|
|
28
28
|
import { aeadEncrypt, aeadDecrypt, deriveSubkey } from './ops.js';
|
|
29
|
-
import { wipe, randomBytes } from '../utils.js';
|
|
30
|
-
|
|
29
|
+
import { wipe, randomBytes, concat } from '../utils.js';
|
|
30
|
+
import { HEADER_SIZE } from '../stream/constants.js';
|
|
31
|
+
import { WORKER_SOURCE } from '../embedded/chacha20-pool-worker.js';
|
|
32
|
+
const INFO = new TextEncoder().encode('xchacha20-sealstream-v3');
|
|
33
|
+
/** Returns the raw chacha20 WASM export object. @internal */
|
|
31
34
|
function getExports() {
|
|
32
35
|
return getInstance('chacha20').exports;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* `CipherSuite` implementation for the stream construction using XChaCha20-Poly1305.
|
|
39
|
+
*
|
|
40
|
+
* Each chunk is encrypted with ChaCha20-Poly1305 using a HChaCha20 subkey
|
|
41
|
+
* derived via HKDF-SHA-256 + HChaCha20. This is the recommended cipher suite
|
|
42
|
+
* for `SealStream` / `OpenStream` / `SealStreamPool`.
|
|
43
|
+
*
|
|
44
|
+
* Pass to `SealStream` / `OpenStream` / `SealStreamPool` instead of constructing
|
|
45
|
+
* this object directly. Use `XChaCha20Cipher.keygen()` to generate a 32-byte key.
|
|
46
|
+
*/
|
|
34
47
|
export const XChaCha20Cipher = {
|
|
35
|
-
formatEnum:
|
|
48
|
+
formatEnum: 0x03,
|
|
36
49
|
formatName: 'xchacha20',
|
|
37
|
-
hkdfInfo: 'xchacha20-sealstream-
|
|
50
|
+
hkdfInfo: 'xchacha20-sealstream-v3',
|
|
38
51
|
keySize: 32,
|
|
39
52
|
kemCtSize: 0,
|
|
53
|
+
commitmentSize: 32,
|
|
40
54
|
tagSize: 16,
|
|
41
55
|
padded: false,
|
|
42
56
|
wasmChunkSize: 65536, // src/asm/chacha20/buffers.ts CHUNK_SIZE
|
|
43
57
|
wasmModules: ['chacha20'],
|
|
58
|
+
/** Generate a random 32-byte master key suitable for use with `XChaCha20Cipher`. @returns 32 cryptographically random bytes */
|
|
44
59
|
keygen() {
|
|
45
60
|
return randomBytes(32);
|
|
46
61
|
},
|
|
47
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Derive a 32-byte HChaCha20 subkey and a 32-byte key commitment from
|
|
64
|
+
* `masterKey` and `nonce` via HKDF-SHA-256 followed by HChaCha20 subkey
|
|
65
|
+
* derivation. The full 20-byte preamble header is appended to the HKDF
|
|
66
|
+
* info string, binding `formatEnum`, framed flag, nonce, and chunkSize
|
|
67
|
+
* into the derived material, header tampering causes derived keys to
|
|
68
|
+
* differ and AEAD fails on the first chunk.
|
|
69
|
+
*
|
|
70
|
+
* The 64-byte HKDF output is split: bytes 0..32 feed HChaCha20 subkey
|
|
71
|
+
* derivation, bytes 32..64 are the key commitment that ends up in the
|
|
72
|
+
* preamble. Verifying the commitment before any chunk is processed
|
|
73
|
+
* closes the Invisible Salamanders attack surface, Poly1305 alone is
|
|
74
|
+
* not key-committing, so without this an adversary with control over
|
|
75
|
+
* two master keys could craft a single ciphertext + tag that decrypts
|
|
76
|
+
* validly under both.
|
|
77
|
+
*
|
|
78
|
+
* @param masterKey 32-byte master key
|
|
79
|
+
* @param nonce Stream nonce (16 bytes, also used as HChaCha20 input)
|
|
80
|
+
* @param _kemCt Unused for symmetric XChaCha20; KEM wrappers pass it through
|
|
81
|
+
* @param header 20-byte preamble header, required (throws otherwise)
|
|
82
|
+
* @returns `DerivedKeys` holding the 32-byte HChaCha20 subkey and 32-byte commitment
|
|
83
|
+
*/
|
|
84
|
+
deriveKeys(masterKey, nonce, _kemCt, header) {
|
|
85
|
+
if (!header || header.length !== HEADER_SIZE)
|
|
86
|
+
throw new Error(`XChaCha20Cipher.deriveKeys: header binding required (got ${header?.length ?? 'undefined'} bytes)`);
|
|
87
|
+
_assertNotOwned('chacha20');
|
|
48
88
|
const hkdf = new HKDF_SHA256();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
let okm;
|
|
90
|
+
try {
|
|
91
|
+
// INFO || header, binds formatEnum, framed flag, nonce, chunkSize into the KDF.
|
|
92
|
+
// Any header tampering produces different keys, AEAD fails on the first chunk.
|
|
93
|
+
const info = concat(INFO, header);
|
|
94
|
+
okm = hkdf.derive(masterKey, nonce, info, 64);
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
hkdf.dispose();
|
|
98
|
+
}
|
|
99
|
+
// Bytes 0..32: streamKey for HChaCha20 subkey derivation.
|
|
100
|
+
// Bytes 32..64: key commitment for the seal preamble.
|
|
101
|
+
const streamKey = okm.subarray(0, 32);
|
|
102
|
+
const commitment = okm.slice(32, 64); // independent backing, survives okm wipe
|
|
52
103
|
const x = getExports();
|
|
53
104
|
const subkey = deriveSubkey(x, streamKey, nonce);
|
|
54
|
-
wipe(
|
|
55
|
-
return { bytes: subkey };
|
|
105
|
+
wipe(okm); // wipe both halves of okm; commitment is safe (independent backing)
|
|
106
|
+
return { bytes: subkey, commitment };
|
|
56
107
|
},
|
|
108
|
+
/**
|
|
109
|
+
* Encrypt and authenticate one stream chunk with ChaCha20-Poly1305.
|
|
110
|
+
* Output: ciphertext || 16-byte Poly1305 tag.
|
|
111
|
+
* @param keys Derived keys from `deriveKeys`
|
|
112
|
+
* @param counterNonce 12-byte per-chunk nonce (unique per chunk in the stream)
|
|
113
|
+
* @param chunk Plaintext chunk
|
|
114
|
+
* @param aad Optional additional authenticated data
|
|
115
|
+
* @returns Authenticated ciphertext
|
|
116
|
+
*/
|
|
57
117
|
sealChunk(keys, counterNonce, chunk, aad) {
|
|
118
|
+
_assertNotOwned('chacha20');
|
|
58
119
|
const x = getExports();
|
|
59
120
|
const { ciphertext, tag } = aeadEncrypt(x, keys.bytes, counterNonce, chunk, aad ?? new Uint8Array(0));
|
|
60
121
|
const out = new Uint8Array(ciphertext.length + 16);
|
|
@@ -62,7 +123,16 @@ export const XChaCha20Cipher = {
|
|
|
62
123
|
out.set(tag, ciphertext.length);
|
|
63
124
|
return out;
|
|
64
125
|
},
|
|
126
|
+
/**
|
|
127
|
+
* Verify and decrypt one stream chunk. Throws `AuthenticationError` on tag mismatch.
|
|
128
|
+
* @param keys Derived keys from `deriveKeys`
|
|
129
|
+
* @param counterNonce 12-byte per-chunk nonce, must match the value used by `sealChunk`
|
|
130
|
+
* @param chunk Ciphertext || 16-byte Poly1305 tag
|
|
131
|
+
* @param aad Optional additional authenticated data
|
|
132
|
+
* @returns Plaintext
|
|
133
|
+
*/
|
|
65
134
|
openChunk(keys, counterNonce, chunk, aad) {
|
|
135
|
+
_assertNotOwned('chacha20');
|
|
66
136
|
if (chunk.length < 16)
|
|
67
137
|
throw new RangeError(`chunk too short for 16-byte tag (got ${chunk.length})`);
|
|
68
138
|
const x = getExports();
|
|
@@ -70,10 +140,25 @@ export const XChaCha20Cipher = {
|
|
|
70
140
|
const tag = chunk.subarray(chunk.length - 16);
|
|
71
141
|
return aeadDecrypt(x, keys.bytes, counterNonce, ct, tag, aad ?? new Uint8Array(0), 'xchacha20-poly1305');
|
|
72
142
|
},
|
|
143
|
+
/**
|
|
144
|
+
* Zero all derived key material in `keys`. Called by the stream layer on
|
|
145
|
+
* teardown and after auth failure.
|
|
146
|
+
* @param keys Derived keys to wipe
|
|
147
|
+
*/
|
|
73
148
|
wipeKeys(keys) {
|
|
74
149
|
wipe(keys.bytes);
|
|
75
150
|
},
|
|
151
|
+
/**
|
|
152
|
+
* Spawn an XChaCha20 pool worker from the embedded IIFE bundle.
|
|
153
|
+
* The worker holds its own chacha20 WASM instance and derived subkey.
|
|
154
|
+
* @returns Newly constructed `Worker` instance
|
|
155
|
+
*/
|
|
76
156
|
createPoolWorker() {
|
|
77
|
-
|
|
157
|
+
// See docs/architecture.md#pool-worker-spawn-pattern.
|
|
158
|
+
const blob = new Blob([WORKER_SOURCE], { type: 'application/javascript' });
|
|
159
|
+
const url = URL.createObjectURL(blob);
|
|
160
|
+
const w = new Worker(url);
|
|
161
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
162
|
+
return w;
|
|
78
163
|
},
|
|
79
164
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Generator } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ChaCha20 counter-mode PRF for Fortuna's generator slot.
|
|
4
|
+
*
|
|
5
|
+
* The 32-bit counter is read from `counter` as a little-endian u32. Each call
|
|
6
|
+
* to `generate()` encrypts zero blocks to produce keystream output (RFC 8439 §2.3).
|
|
7
|
+
* The nonce is fixed to zero, Fortuna's counter is the only varying input.
|
|
8
|
+
*
|
|
9
|
+
* Pass to `Fortuna.create({ generator: ChaCha20Generator, ... })`, do not
|
|
10
|
+
* call `generate()` directly outside of Fortuna.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ChaCha20Generator: Generator;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/chacha20/generator.ts
|
|
23
|
+
//
|
|
24
|
+
// RFC 8439 §2.3, ChaCha20 block function as PRF
|
|
25
|
+
// ChaCha20 counter-mode PRF for Fortuna's generator slot.
|
|
26
|
+
//
|
|
27
|
+
// Counter is treated as a 32-bit LE integer. Fortuna's 4-byte genCnt provides
|
|
28
|
+
// 2^32 blocks × 64 bytes = 256 GiB of output between reseeds.
|
|
29
|
+
import { _assertNotOwned, getInstance } from '../init.js';
|
|
30
|
+
/**
|
|
31
|
+
* ChaCha20 counter-mode PRF for Fortuna's generator slot.
|
|
32
|
+
*
|
|
33
|
+
* The 32-bit counter is read from `counter` as a little-endian u32. Each call
|
|
34
|
+
* to `generate()` encrypts zero blocks to produce keystream output (RFC 8439 §2.3).
|
|
35
|
+
* The nonce is fixed to zero, Fortuna's counter is the only varying input.
|
|
36
|
+
*
|
|
37
|
+
* Pass to `Fortuna.create({ generator: ChaCha20Generator, ... })`, do not
|
|
38
|
+
* call `generate()` directly outside of Fortuna.
|
|
39
|
+
*/
|
|
40
|
+
export const ChaCha20Generator = {
|
|
41
|
+
keySize: 32,
|
|
42
|
+
blockSize: 64,
|
|
43
|
+
counterSize: 4,
|
|
44
|
+
wasmModules: ['chacha20'],
|
|
45
|
+
/**
|
|
46
|
+
* Generate `n` pseudorandom bytes using ChaCha20 with a zero nonce.
|
|
47
|
+
* @param key 32-byte ChaCha20 key
|
|
48
|
+
* @param counter 4-byte counter (little-endian u32)
|
|
49
|
+
* @param n Number of bytes to generate (0 ≤ n ≤ 2^30)
|
|
50
|
+
* @returns `n` pseudorandom bytes
|
|
51
|
+
*/
|
|
52
|
+
generate(key, counter, n) {
|
|
53
|
+
_assertNotOwned('chacha20');
|
|
54
|
+
if (key.length !== 32)
|
|
55
|
+
throw new RangeError(`ChaCha20Generator: key must be 32 bytes (got ${key.length})`);
|
|
56
|
+
if (counter.length !== 4)
|
|
57
|
+
throw new RangeError(`ChaCha20Generator: counter must be 4 bytes (got ${counter.length})`);
|
|
58
|
+
if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 30)
|
|
59
|
+
throw new RangeError(`ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got ${n})`);
|
|
60
|
+
const x = getInstance('chacha20').exports;
|
|
61
|
+
const c = new DataView(counter.buffer, counter.byteOffset, 4).getUint32(0, true);
|
|
62
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
63
|
+
try {
|
|
64
|
+
mem.set(key, x.getKeyOffset());
|
|
65
|
+
// Fixed zero nonce, Fortuna's counter is the only varying input
|
|
66
|
+
mem.fill(0, x.getChachaNonceOffset(), x.getChachaNonceOffset() + 12);
|
|
67
|
+
x.chachaSetCounter(c);
|
|
68
|
+
x.chachaLoadKey();
|
|
69
|
+
const output = new Uint8Array(n);
|
|
70
|
+
let remaining = n;
|
|
71
|
+
let outPos = 0;
|
|
72
|
+
const maxChunk = x.getChunkSize();
|
|
73
|
+
const ptOff = x.getChunkPtOffset();
|
|
74
|
+
const ctOff = x.getChunkCtOffset();
|
|
75
|
+
while (remaining > 0) {
|
|
76
|
+
const chunkLen = Math.min(remaining, maxChunk);
|
|
77
|
+
mem.fill(0, ptOff, ptOff + chunkLen);
|
|
78
|
+
x.chachaEncryptChunk_simd(chunkLen);
|
|
79
|
+
output.set(mem.subarray(ctOff, ctOff + chunkLen), outPos);
|
|
80
|
+
outPos += chunkLen;
|
|
81
|
+
remaining -= chunkLen;
|
|
82
|
+
}
|
|
83
|
+
return output;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
// Wipe the WASM key/state/keystream scratch so secret-derived bytes
|
|
87
|
+
// do not outlive this call in the shared chacha20 linear memory.
|
|
88
|
+
x.wipeBuffers();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|