leviathan-crypto 2.1.0 → 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 +86 -443
- package/README.md +198 -65
- 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.js +47 -25
- package/dist/chacha20/generator.d.ts +2 -2
- package/dist/chacha20/generator.js +4 -4
- package/dist/chacha20/index.d.ts +16 -15
- package/dist/chacha20/index.js +52 -46
- package/dist/chacha20/ops.d.ts +7 -7
- package/dist/chacha20/ops.js +34 -34
- package/dist/chacha20/pool-worker.js +5 -3
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -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 -1
- package/dist/embedded/chacha20-pool-worker.js +2 -2
- 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 -1
- package/dist/embedded/serpent-pool-worker.js +2 -2
- 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 +5 -5
- package/dist/fortuna.js +37 -64
- package/dist/index.d.ts +38 -9
- package/dist/index.js +63 -19
- package/dist/init.d.ts +1 -1
- package/dist/init.js +11 -25
- 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 -24
- package/dist/loader.js +13 -16
- 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 +44 -44
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +24 -34
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +44 -64
- 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 +3 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/{kyber → mlkem}/validate.d.ts +7 -7
- package/dist/{kyber → mlkem}/validate.js +7 -7
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +2 -0
- package/dist/ratchet/index.js +1 -0
- package/dist/ratchet/kdf-chain.js +3 -3
- package/dist/ratchet/ratchet-keypair.js +2 -2
- package/dist/ratchet/root-kdf.js +7 -7
- package/dist/ratchet/skipped-key-store.js +4 -4
- package/dist/ratchet/types.d.ts +1 -1
- package/dist/serpent/cipher-suite.js +20 -17
- package/dist/serpent/generator.d.ts +1 -1
- package/dist/serpent/generator.js +2 -2
- package/dist/serpent/index.d.ts +8 -7
- package/dist/serpent/index.js +18 -27
- package/dist/serpent/pool-worker.js +7 -5
- package/dist/serpent/serpent-cbc.d.ts +4 -4
- package/dist/serpent/serpent-cbc.js +11 -8
- package/dist/serpent/shared-ops.d.ts +3 -23
- package/dist/serpent/shared-ops.js +50 -85
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +21 -1
- package/dist/sha2/index.js +65 -10
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/index.d.ts +72 -3
- package/dist/sha3/index.js +240 -14
- 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 +3 -3
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +31 -10
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +63 -26
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +20 -9
- 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 +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +46 -54
- package/dist/wasm-source.d.ts +7 -7
- 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 +70 -26
- package/SECURITY.md +0 -163
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/docs/aead.md +0 -363
- package/dist/docs/architecture.md +0 -1011
- package/dist/docs/argon2id.md +0 -305
- package/dist/docs/chacha20.md +0 -781
- package/dist/docs/exports.md +0 -277
- package/dist/docs/fortuna.md +0 -530
- package/dist/docs/init.md +0 -301
- package/dist/docs/loader.md +0 -256
- package/dist/docs/serpent.md +0 -617
- package/dist/docs/sha2.md +0 -671
- package/dist/docs/sha3.md +0 -612
- package/dist/docs/types.md +0 -416
- package/dist/docs/utils.md +0 -457
- 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 -12
- /package/dist/{ct.wasm → cte.wasm} +0 -0
|
@@ -0,0 +1,800 @@
|
|
|
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/sha3/kmac.ts
|
|
23
|
+
//
|
|
24
|
+
// cSHAKE and KMAC public classes, SP 800-185.
|
|
25
|
+
// Built on the existing sha3 WASM sponge primitives plus two new init
|
|
26
|
+
// exports (cshake128Init / cshake256Init). All SP 800-185 §2.3 encoding
|
|
27
|
+
// helpers live in TypeScript per the established mldsa/sha3-helpers.ts
|
|
28
|
+
// precedent for non-trivial input framing.
|
|
29
|
+
import { getInstance, _acquireModule, _releaseModule, _assertNotOwned } from '../init.js';
|
|
30
|
+
import { AuthenticationError } from '../errors.js';
|
|
31
|
+
import { constantTimeEqual, wipe } from '../utils.js';
|
|
32
|
+
function getExports() {
|
|
33
|
+
return getInstance('sha3').exports;
|
|
34
|
+
}
|
|
35
|
+
// ── SP 800-185 §2.3 encoding helpers ────────────────────────────────────────
|
|
36
|
+
// SP 800-185 §2.3.1, left_encode(x). Encodes x as n base-256 bytes (big-endian)
|
|
37
|
+
// preceded by the byte n. Cap on the input range is conservative, all
|
|
38
|
+
// realistic uses (rate ≤ 168, output bit-lengths, key bit-lengths) fit in a
|
|
39
|
+
// 32-bit signed integer.
|
|
40
|
+
function leftEncode(x) {
|
|
41
|
+
if (!Number.isInteger(x) || x < 0 || x > 0x7fffffff)
|
|
42
|
+
throw new RangeError(`leftEncode: x out of range (got ${x})`);
|
|
43
|
+
if (x === 0)
|
|
44
|
+
return new Uint8Array([0x01, 0x00]);
|
|
45
|
+
const bytes = [];
|
|
46
|
+
let v = x;
|
|
47
|
+
while (v > 0) {
|
|
48
|
+
bytes.unshift(v & 0xff);
|
|
49
|
+
v >>>= 8;
|
|
50
|
+
}
|
|
51
|
+
const out = new Uint8Array(bytes.length + 1);
|
|
52
|
+
out[0] = bytes.length;
|
|
53
|
+
for (let i = 0; i < bytes.length; i++)
|
|
54
|
+
out[i + 1] = bytes[i];
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
// SP 800-185 §2.3.1, right_encode(x). Same digit sequence as left_encode but
|
|
58
|
+
// with the n byte appended.
|
|
59
|
+
function rightEncode(x) {
|
|
60
|
+
if (!Number.isInteger(x) || x < 0 || x > 0x7fffffff)
|
|
61
|
+
throw new RangeError(`rightEncode: x out of range (got ${x})`);
|
|
62
|
+
if (x === 0)
|
|
63
|
+
return new Uint8Array([0x00, 0x01]);
|
|
64
|
+
const bytes = [];
|
|
65
|
+
let v = x;
|
|
66
|
+
while (v > 0) {
|
|
67
|
+
bytes.unshift(v & 0xff);
|
|
68
|
+
v >>>= 8;
|
|
69
|
+
}
|
|
70
|
+
const out = new Uint8Array(bytes.length + 1);
|
|
71
|
+
for (let i = 0; i < bytes.length; i++)
|
|
72
|
+
out[i] = bytes[i];
|
|
73
|
+
out[bytes.length] = bytes.length;
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
// SP 800-185 §2.3.2, encode_string(S). left_encode of the BIT length of S
|
|
77
|
+
// followed by S itself. For byte-aligned inputs len(S) = 8 * bytes.length.
|
|
78
|
+
function encodeString(s) {
|
|
79
|
+
const prefix = leftEncode(s.length * 8);
|
|
80
|
+
const out = new Uint8Array(prefix.length + s.length);
|
|
81
|
+
out.set(prefix, 0);
|
|
82
|
+
out.set(s, prefix.length);
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
// SP 800-185 §2.3.3, bytepad(x, w). Prepend left_encode(w), then zero-pad
|
|
86
|
+
// until total length is a multiple of w.
|
|
87
|
+
function bytepad(x, w) {
|
|
88
|
+
const prefix = leftEncode(w);
|
|
89
|
+
const baseLen = prefix.length + x.length;
|
|
90
|
+
const padded = Math.ceil(baseLen / w) * w;
|
|
91
|
+
const out = new Uint8Array(padded);
|
|
92
|
+
out.set(prefix, 0);
|
|
93
|
+
out.set(x, prefix.length);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
// Stream msg into INPUT_OFFSET in ≤168-byte chunks. Mirrors the local helper
|
|
97
|
+
// in src/ts/sha3/index.ts; duplicated here to keep that file's @internal
|
|
98
|
+
// surface unchanged. See also mldsa/sha3-helpers.ts sha3Absorb.
|
|
99
|
+
function absorb(x, msg) {
|
|
100
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
101
|
+
const inputOff = x.getInputOffset();
|
|
102
|
+
let pos = 0;
|
|
103
|
+
while (pos < msg.length) {
|
|
104
|
+
const chunk = Math.min(msg.length - pos, 168);
|
|
105
|
+
mem.set(msg.subarray(pos, pos + chunk), inputOff);
|
|
106
|
+
x.keccakAbsorb(chunk);
|
|
107
|
+
pos += chunk;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// SP 800-185 §4, function name "KMAC" used by KMAC{128,256} and the XOF
|
|
111
|
+
// variants when calling cSHAKE internally. ASCII 'K','M','A','C'.
|
|
112
|
+
const KMAC_N = new Uint8Array([0x4b, 0x4d, 0x41, 0x43]);
|
|
113
|
+
const EMPTY = new Uint8Array(0);
|
|
114
|
+
// ── CSHAKE128 ───────────────────────────────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* cSHAKE128, customizable SHAKE128 (SP 800-185 §3).
|
|
117
|
+
*
|
|
118
|
+
* Holds exclusive access to the `sha3` WASM module from construction until
|
|
119
|
+
* `dispose()`. Constructing any other sha3 user (SHAKE128/256, SHA3_*,
|
|
120
|
+
* KMAC*, CSHAKE*) while this instance is live throws.
|
|
121
|
+
*/
|
|
122
|
+
export class CSHAKE128 {
|
|
123
|
+
x;
|
|
124
|
+
_rate = 168;
|
|
125
|
+
_prefix;
|
|
126
|
+
_squeezing = false;
|
|
127
|
+
_block = new Uint8Array(168);
|
|
128
|
+
_blockPos = 168;
|
|
129
|
+
_tok;
|
|
130
|
+
constructor(customization) {
|
|
131
|
+
// SP 800-185 §3.3, if N and S are both empty cSHAKE collapses to
|
|
132
|
+
// SHAKE. The public API hides N (always empty); the both-empty case
|
|
133
|
+
// is therefore "customization is empty", reject and direct the
|
|
134
|
+
// caller to SHAKE128.
|
|
135
|
+
if (customization.length === 0)
|
|
136
|
+
throw new Error('CSHAKE128: customization is empty, use SHAKE128 instead');
|
|
137
|
+
// SP 800-185 §3.3, cSHAKE128 prefix:
|
|
138
|
+
// bytepad(encode_string(N) || encode_string(S), 168)
|
|
139
|
+
const en = encodeString(EMPTY);
|
|
140
|
+
const es = encodeString(customization);
|
|
141
|
+
const cat = new Uint8Array(en.length + es.length);
|
|
142
|
+
cat.set(en, 0);
|
|
143
|
+
cat.set(es, en.length);
|
|
144
|
+
this._prefix = bytepad(cat, this._rate);
|
|
145
|
+
this.x = getExports();
|
|
146
|
+
this._tok = _acquireModule('sha3');
|
|
147
|
+
try {
|
|
148
|
+
this.x.cshake128Init();
|
|
149
|
+
absorb(this.x, this._prefix);
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
_releaseModule('sha3', this._tok);
|
|
153
|
+
this._tok = undefined;
|
|
154
|
+
throw e;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
reset() {
|
|
158
|
+
if (this._tok === undefined)
|
|
159
|
+
throw new Error('CSHAKE128: instance has been disposed');
|
|
160
|
+
this.x.cshake128Init();
|
|
161
|
+
absorb(this.x, this._prefix);
|
|
162
|
+
this._squeezing = false;
|
|
163
|
+
this._block.fill(0);
|
|
164
|
+
this._blockPos = this._rate;
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
absorb(msg) {
|
|
168
|
+
if (this._tok === undefined)
|
|
169
|
+
throw new Error('CSHAKE128: instance has been disposed');
|
|
170
|
+
if (this._squeezing)
|
|
171
|
+
throw new Error('CSHAKE128: cannot absorb after squeeze, call reset() first');
|
|
172
|
+
absorb(this.x, msg);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
squeeze(n) {
|
|
176
|
+
if (this._tok === undefined)
|
|
177
|
+
throw new Error('CSHAKE128: instance has been disposed');
|
|
178
|
+
if (n < 1)
|
|
179
|
+
throw new RangeError(`squeeze length must be >= 1 (got ${n})`);
|
|
180
|
+
if (!this._squeezing) {
|
|
181
|
+
this.x.shakePad();
|
|
182
|
+
this._squeezing = true;
|
|
183
|
+
this._blockPos = this._rate;
|
|
184
|
+
}
|
|
185
|
+
const out = new Uint8Array(n);
|
|
186
|
+
let pos = 0;
|
|
187
|
+
while (pos < n) {
|
|
188
|
+
if (this._blockPos >= this._rate) {
|
|
189
|
+
this.x.shakeSqueezeBlock();
|
|
190
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
191
|
+
const off = this.x.getOutOffset();
|
|
192
|
+
this._block.set(mem.subarray(off, off + this._rate));
|
|
193
|
+
this._blockPos = 0;
|
|
194
|
+
}
|
|
195
|
+
const take = Math.min(n - pos, this._rate - this._blockPos);
|
|
196
|
+
out.set(this._block.subarray(this._blockPos, this._blockPos + take), pos);
|
|
197
|
+
this._blockPos += take;
|
|
198
|
+
pos += take;
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
hash(msg, outputLength) {
|
|
203
|
+
if (this._tok === undefined)
|
|
204
|
+
throw new Error('CSHAKE128: instance has been disposed');
|
|
205
|
+
if (outputLength < 1)
|
|
206
|
+
throw new RangeError(`outputLength must be >= 1 (got ${outputLength})`);
|
|
207
|
+
this.reset();
|
|
208
|
+
this.absorb(msg);
|
|
209
|
+
return this.squeeze(outputLength);
|
|
210
|
+
}
|
|
211
|
+
dispose() {
|
|
212
|
+
if (this._tok === undefined)
|
|
213
|
+
return;
|
|
214
|
+
this._block.fill(0);
|
|
215
|
+
try {
|
|
216
|
+
this.x.wipeBuffers();
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
_releaseModule('sha3', this._tok);
|
|
220
|
+
this._tok = undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// ── CSHAKE256 ───────────────────────────────────────────────────────────────
|
|
225
|
+
/**
|
|
226
|
+
* cSHAKE256, customizable SHAKE256 (SP 800-185 §3).
|
|
227
|
+
*
|
|
228
|
+
* Holds exclusive access to the `sha3` WASM module from construction until
|
|
229
|
+
* `dispose()`.
|
|
230
|
+
*/
|
|
231
|
+
export class CSHAKE256 {
|
|
232
|
+
x;
|
|
233
|
+
_rate = 136;
|
|
234
|
+
_prefix;
|
|
235
|
+
_squeezing = false;
|
|
236
|
+
_block = new Uint8Array(136);
|
|
237
|
+
_blockPos = 136;
|
|
238
|
+
_tok;
|
|
239
|
+
constructor(customization) {
|
|
240
|
+
if (customization.length === 0)
|
|
241
|
+
throw new Error('CSHAKE256: customization is empty, use SHAKE256 instead');
|
|
242
|
+
const en = encodeString(EMPTY);
|
|
243
|
+
const es = encodeString(customization);
|
|
244
|
+
const cat = new Uint8Array(en.length + es.length);
|
|
245
|
+
cat.set(en, 0);
|
|
246
|
+
cat.set(es, en.length);
|
|
247
|
+
this._prefix = bytepad(cat, this._rate);
|
|
248
|
+
this.x = getExports();
|
|
249
|
+
this._tok = _acquireModule('sha3');
|
|
250
|
+
try {
|
|
251
|
+
this.x.cshake256Init();
|
|
252
|
+
absorb(this.x, this._prefix);
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
_releaseModule('sha3', this._tok);
|
|
256
|
+
this._tok = undefined;
|
|
257
|
+
throw e;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
reset() {
|
|
261
|
+
if (this._tok === undefined)
|
|
262
|
+
throw new Error('CSHAKE256: instance has been disposed');
|
|
263
|
+
this.x.cshake256Init();
|
|
264
|
+
absorb(this.x, this._prefix);
|
|
265
|
+
this._squeezing = false;
|
|
266
|
+
this._block.fill(0);
|
|
267
|
+
this._blockPos = this._rate;
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
absorb(msg) {
|
|
271
|
+
if (this._tok === undefined)
|
|
272
|
+
throw new Error('CSHAKE256: instance has been disposed');
|
|
273
|
+
if (this._squeezing)
|
|
274
|
+
throw new Error('CSHAKE256: cannot absorb after squeeze, call reset() first');
|
|
275
|
+
absorb(this.x, msg);
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
squeeze(n) {
|
|
279
|
+
if (this._tok === undefined)
|
|
280
|
+
throw new Error('CSHAKE256: instance has been disposed');
|
|
281
|
+
if (n < 1)
|
|
282
|
+
throw new RangeError(`squeeze length must be >= 1 (got ${n})`);
|
|
283
|
+
if (!this._squeezing) {
|
|
284
|
+
this.x.shakePad();
|
|
285
|
+
this._squeezing = true;
|
|
286
|
+
this._blockPos = this._rate;
|
|
287
|
+
}
|
|
288
|
+
const out = new Uint8Array(n);
|
|
289
|
+
let pos = 0;
|
|
290
|
+
while (pos < n) {
|
|
291
|
+
if (this._blockPos >= this._rate) {
|
|
292
|
+
this.x.shakeSqueezeBlock();
|
|
293
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
294
|
+
const off = this.x.getOutOffset();
|
|
295
|
+
this._block.set(mem.subarray(off, off + this._rate));
|
|
296
|
+
this._blockPos = 0;
|
|
297
|
+
}
|
|
298
|
+
const take = Math.min(n - pos, this._rate - this._blockPos);
|
|
299
|
+
out.set(this._block.subarray(this._blockPos, this._blockPos + take), pos);
|
|
300
|
+
this._blockPos += take;
|
|
301
|
+
pos += take;
|
|
302
|
+
}
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
hash(msg, outputLength) {
|
|
306
|
+
if (this._tok === undefined)
|
|
307
|
+
throw new Error('CSHAKE256: instance has been disposed');
|
|
308
|
+
if (outputLength < 1)
|
|
309
|
+
throw new RangeError(`outputLength must be >= 1 (got ${outputLength})`);
|
|
310
|
+
this.reset();
|
|
311
|
+
this.absorb(msg);
|
|
312
|
+
return this.squeeze(outputLength);
|
|
313
|
+
}
|
|
314
|
+
dispose() {
|
|
315
|
+
if (this._tok === undefined)
|
|
316
|
+
return;
|
|
317
|
+
this._block.fill(0);
|
|
318
|
+
try {
|
|
319
|
+
this.x.wipeBuffers();
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
_releaseModule('sha3', this._tok);
|
|
323
|
+
this._tok = undefined;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// ── KMAC128 ─────────────────────────────────────────────────────────────────
|
|
328
|
+
/**
|
|
329
|
+
* KMAC128, keyed Keccak MAC, fixed-output (SP 800-185 §4).
|
|
330
|
+
*
|
|
331
|
+
* Bound to a specific output length at construction (the spec's right_encode(L)
|
|
332
|
+
* suffix is a function of L). Use `KMACXOF128` for arbitrary-length output.
|
|
333
|
+
*
|
|
334
|
+
* Holds exclusive access to the `sha3` WASM module from construction until
|
|
335
|
+
* `dispose()`.
|
|
336
|
+
*/
|
|
337
|
+
export class KMAC128 {
|
|
338
|
+
x;
|
|
339
|
+
_rate = 168;
|
|
340
|
+
_outLen;
|
|
341
|
+
_finalized = false;
|
|
342
|
+
_tok;
|
|
343
|
+
constructor(key, outLen, customization) {
|
|
344
|
+
if (key.length === 0)
|
|
345
|
+
throw new Error('KMAC128: empty key, use CSHAKE128 instead');
|
|
346
|
+
if (!Number.isInteger(outLen) || outLen < 1)
|
|
347
|
+
throw new RangeError(`KMAC128: outLen must be a positive integer (got ${outLen})`);
|
|
348
|
+
this._outLen = outLen;
|
|
349
|
+
// SP 800-185 §4, cSHAKE128 prefix with N = "KMAC", S = customization.
|
|
350
|
+
const en = encodeString(KMAC_N);
|
|
351
|
+
const es = encodeString(customization);
|
|
352
|
+
const csPrefix = new Uint8Array(en.length + es.length);
|
|
353
|
+
csPrefix.set(en, 0);
|
|
354
|
+
csPrefix.set(es, en.length);
|
|
355
|
+
const cshakePad = bytepad(csPrefix, this._rate);
|
|
356
|
+
// SP 800-185 §4, KMAC newX = bytepad(encode_string(K), rate) || X || right_encode(L).
|
|
357
|
+
// The key bytepad is absorbed at construction; X is absorbed via update();
|
|
358
|
+
// right_encode(L*8) is appended in finalize().
|
|
359
|
+
const keyPad = bytepad(encodeString(key), this._rate);
|
|
360
|
+
this.x = getExports();
|
|
361
|
+
this._tok = _acquireModule('sha3');
|
|
362
|
+
try {
|
|
363
|
+
this.x.cshake128Init();
|
|
364
|
+
absorb(this.x, cshakePad);
|
|
365
|
+
absorb(this.x, keyPad);
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
_releaseModule('sha3', this._tok);
|
|
369
|
+
this._tok = undefined;
|
|
370
|
+
throw e;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
update(chunk) {
|
|
374
|
+
if (this._tok === undefined)
|
|
375
|
+
throw new Error('KMAC128: instance has been disposed');
|
|
376
|
+
if (this._finalized)
|
|
377
|
+
throw new Error('KMAC128: cannot update after finalize');
|
|
378
|
+
absorb(this.x, chunk);
|
|
379
|
+
return this;
|
|
380
|
+
}
|
|
381
|
+
finalize() {
|
|
382
|
+
if (this._tok === undefined)
|
|
383
|
+
throw new Error('KMAC128: instance has been disposed');
|
|
384
|
+
if (this._finalized)
|
|
385
|
+
throw new Error('KMAC128: already finalized');
|
|
386
|
+
absorb(this.x, rightEncode(this._outLen * 8));
|
|
387
|
+
this.x.shakePad();
|
|
388
|
+
const out = new Uint8Array(this._outLen);
|
|
389
|
+
let pos = 0;
|
|
390
|
+
while (pos < this._outLen) {
|
|
391
|
+
this.x.shakeSqueezeBlock();
|
|
392
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
393
|
+
const off = this.x.getOutOffset();
|
|
394
|
+
const take = Math.min(this._outLen - pos, this._rate);
|
|
395
|
+
out.set(mem.subarray(off, off + take), pos);
|
|
396
|
+
pos += take;
|
|
397
|
+
}
|
|
398
|
+
this._finalized = true;
|
|
399
|
+
return out;
|
|
400
|
+
}
|
|
401
|
+
mac(msg) {
|
|
402
|
+
this.update(msg);
|
|
403
|
+
return this.finalize();
|
|
404
|
+
}
|
|
405
|
+
dispose() {
|
|
406
|
+
if (this._tok === undefined)
|
|
407
|
+
return;
|
|
408
|
+
try {
|
|
409
|
+
this.x.wipeBuffers();
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
_releaseModule('sha3', this._tok);
|
|
413
|
+
this._tok = undefined;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Constant-time tag verification. Throws `AuthenticationError('kmac128')`
|
|
418
|
+
* on mismatch (matches the lib's AEAD pattern). Returns `true` on success.
|
|
419
|
+
*
|
|
420
|
+
* Atomic, does not hold the sha3 module beyond the internal compute.
|
|
421
|
+
*/
|
|
422
|
+
static verify(tag, key, msg, customization) {
|
|
423
|
+
_assertNotOwned('sha3');
|
|
424
|
+
const m = new KMAC128(key, tag.length, customization);
|
|
425
|
+
let exp;
|
|
426
|
+
try {
|
|
427
|
+
m.update(msg);
|
|
428
|
+
exp = m.finalize();
|
|
429
|
+
if (!constantTimeEqual(exp, tag))
|
|
430
|
+
throw new AuthenticationError('kmac128');
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
finally {
|
|
434
|
+
if (exp !== undefined)
|
|
435
|
+
wipe(exp);
|
|
436
|
+
m.dispose();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// ── KMAC256 ─────────────────────────────────────────────────────────────────
|
|
441
|
+
/**
|
|
442
|
+
* KMAC256, 256-bit-strength keyed Keccak MAC, fixed-output (SP 800-185 §4).
|
|
443
|
+
*/
|
|
444
|
+
export class KMAC256 {
|
|
445
|
+
x;
|
|
446
|
+
_rate = 136;
|
|
447
|
+
_outLen;
|
|
448
|
+
_finalized = false;
|
|
449
|
+
_tok;
|
|
450
|
+
constructor(key, outLen, customization) {
|
|
451
|
+
if (key.length === 0)
|
|
452
|
+
throw new Error('KMAC256: empty key, use CSHAKE256 instead');
|
|
453
|
+
if (!Number.isInteger(outLen) || outLen < 1)
|
|
454
|
+
throw new RangeError(`KMAC256: outLen must be a positive integer (got ${outLen})`);
|
|
455
|
+
this._outLen = outLen;
|
|
456
|
+
const en = encodeString(KMAC_N);
|
|
457
|
+
const es = encodeString(customization);
|
|
458
|
+
const csPrefix = new Uint8Array(en.length + es.length);
|
|
459
|
+
csPrefix.set(en, 0);
|
|
460
|
+
csPrefix.set(es, en.length);
|
|
461
|
+
const cshakePad = bytepad(csPrefix, this._rate);
|
|
462
|
+
const keyPad = bytepad(encodeString(key), this._rate);
|
|
463
|
+
this.x = getExports();
|
|
464
|
+
this._tok = _acquireModule('sha3');
|
|
465
|
+
try {
|
|
466
|
+
this.x.cshake256Init();
|
|
467
|
+
absorb(this.x, cshakePad);
|
|
468
|
+
absorb(this.x, keyPad);
|
|
469
|
+
}
|
|
470
|
+
catch (e) {
|
|
471
|
+
_releaseModule('sha3', this._tok);
|
|
472
|
+
this._tok = undefined;
|
|
473
|
+
throw e;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
update(chunk) {
|
|
477
|
+
if (this._tok === undefined)
|
|
478
|
+
throw new Error('KMAC256: instance has been disposed');
|
|
479
|
+
if (this._finalized)
|
|
480
|
+
throw new Error('KMAC256: cannot update after finalize');
|
|
481
|
+
absorb(this.x, chunk);
|
|
482
|
+
return this;
|
|
483
|
+
}
|
|
484
|
+
finalize() {
|
|
485
|
+
if (this._tok === undefined)
|
|
486
|
+
throw new Error('KMAC256: instance has been disposed');
|
|
487
|
+
if (this._finalized)
|
|
488
|
+
throw new Error('KMAC256: already finalized');
|
|
489
|
+
absorb(this.x, rightEncode(this._outLen * 8));
|
|
490
|
+
this.x.shakePad();
|
|
491
|
+
const out = new Uint8Array(this._outLen);
|
|
492
|
+
let pos = 0;
|
|
493
|
+
while (pos < this._outLen) {
|
|
494
|
+
this.x.shakeSqueezeBlock();
|
|
495
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
496
|
+
const off = this.x.getOutOffset();
|
|
497
|
+
const take = Math.min(this._outLen - pos, this._rate);
|
|
498
|
+
out.set(mem.subarray(off, off + take), pos);
|
|
499
|
+
pos += take;
|
|
500
|
+
}
|
|
501
|
+
this._finalized = true;
|
|
502
|
+
return out;
|
|
503
|
+
}
|
|
504
|
+
mac(msg) {
|
|
505
|
+
this.update(msg);
|
|
506
|
+
return this.finalize();
|
|
507
|
+
}
|
|
508
|
+
dispose() {
|
|
509
|
+
if (this._tok === undefined)
|
|
510
|
+
return;
|
|
511
|
+
try {
|
|
512
|
+
this.x.wipeBuffers();
|
|
513
|
+
}
|
|
514
|
+
finally {
|
|
515
|
+
_releaseModule('sha3', this._tok);
|
|
516
|
+
this._tok = undefined;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
static verify(tag, key, msg, customization) {
|
|
520
|
+
_assertNotOwned('sha3');
|
|
521
|
+
const m = new KMAC256(key, tag.length, customization);
|
|
522
|
+
let exp;
|
|
523
|
+
try {
|
|
524
|
+
m.update(msg);
|
|
525
|
+
exp = m.finalize();
|
|
526
|
+
if (!constantTimeEqual(exp, tag))
|
|
527
|
+
throw new AuthenticationError('kmac256');
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
finally {
|
|
531
|
+
if (exp !== undefined)
|
|
532
|
+
wipe(exp);
|
|
533
|
+
m.dispose();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// ── KMACXOF128 ──────────────────────────────────────────────────────────────
|
|
538
|
+
/**
|
|
539
|
+
* KMACXOF128, XOF variant of KMAC128 (SP 800-185 §4.3.1). Output length
|
|
540
|
+
* is caller-chosen per squeeze; the spec's right_encode(0) suffix marks the
|
|
541
|
+
* XOF mode.
|
|
542
|
+
*/
|
|
543
|
+
export class KMACXOF128 {
|
|
544
|
+
x;
|
|
545
|
+
_rate = 168;
|
|
546
|
+
_squeezing = false;
|
|
547
|
+
_block = new Uint8Array(168);
|
|
548
|
+
_blockPos = 168;
|
|
549
|
+
_tok;
|
|
550
|
+
constructor(key, customization) {
|
|
551
|
+
if (key.length === 0)
|
|
552
|
+
throw new Error('KMACXOF128: empty key, use CSHAKE128 instead');
|
|
553
|
+
const en = encodeString(KMAC_N);
|
|
554
|
+
const es = encodeString(customization);
|
|
555
|
+
const csPrefix = new Uint8Array(en.length + es.length);
|
|
556
|
+
csPrefix.set(en, 0);
|
|
557
|
+
csPrefix.set(es, en.length);
|
|
558
|
+
const cshakePad = bytepad(csPrefix, this._rate);
|
|
559
|
+
const keyPad = bytepad(encodeString(key), this._rate);
|
|
560
|
+
this.x = getExports();
|
|
561
|
+
this._tok = _acquireModule('sha3');
|
|
562
|
+
try {
|
|
563
|
+
this.x.cshake128Init();
|
|
564
|
+
absorb(this.x, cshakePad);
|
|
565
|
+
absorb(this.x, keyPad);
|
|
566
|
+
}
|
|
567
|
+
catch (e) {
|
|
568
|
+
_releaseModule('sha3', this._tok);
|
|
569
|
+
this._tok = undefined;
|
|
570
|
+
throw e;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
update(chunk) {
|
|
574
|
+
if (this._tok === undefined)
|
|
575
|
+
throw new Error('KMACXOF128: instance has been disposed');
|
|
576
|
+
if (this._squeezing)
|
|
577
|
+
throw new Error('KMACXOF128: cannot update after squeeze');
|
|
578
|
+
absorb(this.x, chunk);
|
|
579
|
+
return this;
|
|
580
|
+
}
|
|
581
|
+
squeeze(n) {
|
|
582
|
+
if (this._tok === undefined)
|
|
583
|
+
throw new Error('KMACXOF128: instance has been disposed');
|
|
584
|
+
if (n < 1)
|
|
585
|
+
throw new RangeError(`squeeze length must be >= 1 (got ${n})`);
|
|
586
|
+
if (!this._squeezing) {
|
|
587
|
+
// SP 800-185 §4.3.1, right_encode(0) terminates the absorb phase.
|
|
588
|
+
absorb(this.x, rightEncode(0));
|
|
589
|
+
this.x.shakePad();
|
|
590
|
+
this._squeezing = true;
|
|
591
|
+
this._blockPos = this._rate;
|
|
592
|
+
}
|
|
593
|
+
const out = new Uint8Array(n);
|
|
594
|
+
let pos = 0;
|
|
595
|
+
while (pos < n) {
|
|
596
|
+
if (this._blockPos >= this._rate) {
|
|
597
|
+
this.x.shakeSqueezeBlock();
|
|
598
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
599
|
+
const off = this.x.getOutOffset();
|
|
600
|
+
this._block.set(mem.subarray(off, off + this._rate));
|
|
601
|
+
this._blockPos = 0;
|
|
602
|
+
}
|
|
603
|
+
const take = Math.min(n - pos, this._rate - this._blockPos);
|
|
604
|
+
out.set(this._block.subarray(this._blockPos, this._blockPos + take), pos);
|
|
605
|
+
this._blockPos += take;
|
|
606
|
+
pos += take;
|
|
607
|
+
}
|
|
608
|
+
return out;
|
|
609
|
+
}
|
|
610
|
+
mac(msg, outLen) {
|
|
611
|
+
this.update(msg);
|
|
612
|
+
return this.squeeze(outLen);
|
|
613
|
+
}
|
|
614
|
+
dispose() {
|
|
615
|
+
if (this._tok === undefined)
|
|
616
|
+
return;
|
|
617
|
+
this._block.fill(0);
|
|
618
|
+
try {
|
|
619
|
+
this.x.wipeBuffers();
|
|
620
|
+
}
|
|
621
|
+
finally {
|
|
622
|
+
_releaseModule('sha3', this._tok);
|
|
623
|
+
this._tok = undefined;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
// ── @internal, test-only cSHAKE-with-N helpers ─────────────────────────────
|
|
628
|
+
/**
|
|
629
|
+
* cSHAKE128(X, L, N, S), direct primitive with explicit function-name N.
|
|
630
|
+
*
|
|
631
|
+
* The public CSHAKE128 class hard-wires N to empty per SP 800-185 §3.4
|
|
632
|
+
* ("Users of cSHAKE should not make up their own names"). This helper
|
|
633
|
+
* exists so that ACVP cSHAKE corpora, whose records carry NIST-reserved
|
|
634
|
+
* function names ("KMAC", "TupleHash", "ParallelHash"), can be exercised
|
|
635
|
+
* against the WASM sponge. Acquires and releases the sha3 module token
|
|
636
|
+
* around the computation; not safe to call concurrently with any stateful
|
|
637
|
+
* sha3 user.
|
|
638
|
+
*
|
|
639
|
+
* @internal
|
|
640
|
+
*/
|
|
641
|
+
export function _cshake128Raw(functionName, customization, msg, outLen) {
|
|
642
|
+
if (functionName.length === 0 && customization.length === 0)
|
|
643
|
+
throw new Error('_cshake128Raw: N and S both empty, use SHAKE128 instead');
|
|
644
|
+
const rate = 168;
|
|
645
|
+
const en = encodeString(functionName);
|
|
646
|
+
const es = encodeString(customization);
|
|
647
|
+
const cat = new Uint8Array(en.length + es.length);
|
|
648
|
+
cat.set(en, 0);
|
|
649
|
+
cat.set(es, en.length);
|
|
650
|
+
const prefix = bytepad(cat, rate);
|
|
651
|
+
const x = getExports();
|
|
652
|
+
const tok = _acquireModule('sha3');
|
|
653
|
+
try {
|
|
654
|
+
x.cshake128Init();
|
|
655
|
+
absorb(x, prefix);
|
|
656
|
+
absorb(x, msg);
|
|
657
|
+
x.shakePad();
|
|
658
|
+
const out = new Uint8Array(outLen);
|
|
659
|
+
let pos = 0;
|
|
660
|
+
while (pos < outLen) {
|
|
661
|
+
x.shakeSqueezeBlock();
|
|
662
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
663
|
+
const off = x.getOutOffset();
|
|
664
|
+
const take = Math.min(outLen - pos, rate);
|
|
665
|
+
out.set(mem.subarray(off, off + take), pos);
|
|
666
|
+
pos += take;
|
|
667
|
+
}
|
|
668
|
+
return out;
|
|
669
|
+
}
|
|
670
|
+
finally {
|
|
671
|
+
x.wipeBuffers();
|
|
672
|
+
_releaseModule('sha3', tok);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* cSHAKE256(X, L, N, S), direct primitive with explicit function-name N.
|
|
677
|
+
* See `_cshake128Raw` for usage notes.
|
|
678
|
+
* @internal
|
|
679
|
+
*/
|
|
680
|
+
export function _cshake256Raw(functionName, customization, msg, outLen) {
|
|
681
|
+
if (functionName.length === 0 && customization.length === 0)
|
|
682
|
+
throw new Error('_cshake256Raw: N and S both empty, use SHAKE256 instead');
|
|
683
|
+
const rate = 136;
|
|
684
|
+
const en = encodeString(functionName);
|
|
685
|
+
const es = encodeString(customization);
|
|
686
|
+
const cat = new Uint8Array(en.length + es.length);
|
|
687
|
+
cat.set(en, 0);
|
|
688
|
+
cat.set(es, en.length);
|
|
689
|
+
const prefix = bytepad(cat, rate);
|
|
690
|
+
const x = getExports();
|
|
691
|
+
const tok = _acquireModule('sha3');
|
|
692
|
+
try {
|
|
693
|
+
x.cshake256Init();
|
|
694
|
+
absorb(x, prefix);
|
|
695
|
+
absorb(x, msg);
|
|
696
|
+
x.shakePad();
|
|
697
|
+
const out = new Uint8Array(outLen);
|
|
698
|
+
let pos = 0;
|
|
699
|
+
while (pos < outLen) {
|
|
700
|
+
x.shakeSqueezeBlock();
|
|
701
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
702
|
+
const off = x.getOutOffset();
|
|
703
|
+
const take = Math.min(outLen - pos, rate);
|
|
704
|
+
out.set(mem.subarray(off, off + take), pos);
|
|
705
|
+
pos += take;
|
|
706
|
+
}
|
|
707
|
+
return out;
|
|
708
|
+
}
|
|
709
|
+
finally {
|
|
710
|
+
x.wipeBuffers();
|
|
711
|
+
_releaseModule('sha3', tok);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// ── KMACXOF256 ──────────────────────────────────────────────────────────────
|
|
715
|
+
/**
|
|
716
|
+
* KMACXOF256, XOF variant of KMAC256 (SP 800-185 §4.3.1).
|
|
717
|
+
*/
|
|
718
|
+
export class KMACXOF256 {
|
|
719
|
+
x;
|
|
720
|
+
_rate = 136;
|
|
721
|
+
_squeezing = false;
|
|
722
|
+
_block = new Uint8Array(136);
|
|
723
|
+
_blockPos = 136;
|
|
724
|
+
_tok;
|
|
725
|
+
constructor(key, customization) {
|
|
726
|
+
if (key.length === 0)
|
|
727
|
+
throw new Error('KMACXOF256: empty key, use CSHAKE256 instead');
|
|
728
|
+
const en = encodeString(KMAC_N);
|
|
729
|
+
const es = encodeString(customization);
|
|
730
|
+
const csPrefix = new Uint8Array(en.length + es.length);
|
|
731
|
+
csPrefix.set(en, 0);
|
|
732
|
+
csPrefix.set(es, en.length);
|
|
733
|
+
const cshakePad = bytepad(csPrefix, this._rate);
|
|
734
|
+
const keyPad = bytepad(encodeString(key), this._rate);
|
|
735
|
+
this.x = getExports();
|
|
736
|
+
this._tok = _acquireModule('sha3');
|
|
737
|
+
try {
|
|
738
|
+
this.x.cshake256Init();
|
|
739
|
+
absorb(this.x, cshakePad);
|
|
740
|
+
absorb(this.x, keyPad);
|
|
741
|
+
}
|
|
742
|
+
catch (e) {
|
|
743
|
+
_releaseModule('sha3', this._tok);
|
|
744
|
+
this._tok = undefined;
|
|
745
|
+
throw e;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
update(chunk) {
|
|
749
|
+
if (this._tok === undefined)
|
|
750
|
+
throw new Error('KMACXOF256: instance has been disposed');
|
|
751
|
+
if (this._squeezing)
|
|
752
|
+
throw new Error('KMACXOF256: cannot update after squeeze');
|
|
753
|
+
absorb(this.x, chunk);
|
|
754
|
+
return this;
|
|
755
|
+
}
|
|
756
|
+
squeeze(n) {
|
|
757
|
+
if (this._tok === undefined)
|
|
758
|
+
throw new Error('KMACXOF256: instance has been disposed');
|
|
759
|
+
if (n < 1)
|
|
760
|
+
throw new RangeError(`squeeze length must be >= 1 (got ${n})`);
|
|
761
|
+
if (!this._squeezing) {
|
|
762
|
+
absorb(this.x, rightEncode(0));
|
|
763
|
+
this.x.shakePad();
|
|
764
|
+
this._squeezing = true;
|
|
765
|
+
this._blockPos = this._rate;
|
|
766
|
+
}
|
|
767
|
+
const out = new Uint8Array(n);
|
|
768
|
+
let pos = 0;
|
|
769
|
+
while (pos < n) {
|
|
770
|
+
if (this._blockPos >= this._rate) {
|
|
771
|
+
this.x.shakeSqueezeBlock();
|
|
772
|
+
const mem = new Uint8Array(this.x.memory.buffer);
|
|
773
|
+
const off = this.x.getOutOffset();
|
|
774
|
+
this._block.set(mem.subarray(off, off + this._rate));
|
|
775
|
+
this._blockPos = 0;
|
|
776
|
+
}
|
|
777
|
+
const take = Math.min(n - pos, this._rate - this._blockPos);
|
|
778
|
+
out.set(this._block.subarray(this._blockPos, this._blockPos + take), pos);
|
|
779
|
+
this._blockPos += take;
|
|
780
|
+
pos += take;
|
|
781
|
+
}
|
|
782
|
+
return out;
|
|
783
|
+
}
|
|
784
|
+
mac(msg, outLen) {
|
|
785
|
+
this.update(msg);
|
|
786
|
+
return this.squeeze(outLen);
|
|
787
|
+
}
|
|
788
|
+
dispose() {
|
|
789
|
+
if (this._tok === undefined)
|
|
790
|
+
return;
|
|
791
|
+
this._block.fill(0);
|
|
792
|
+
try {
|
|
793
|
+
this.x.wipeBuffers();
|
|
794
|
+
}
|
|
795
|
+
finally {
|
|
796
|
+
_releaseModule('sha3', this._tok);
|
|
797
|
+
this._tok = undefined;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|