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,60 @@
|
|
|
1
|
+
import type { SlhDsaParams } from './params.js';
|
|
2
|
+
import { type PreHashAlgorithm } from './prehash.js';
|
|
3
|
+
/**
|
|
4
|
+
* FIPS 205 §10.2.1 Algorithm 22 line 1 / §10.2.2 Algorithm 23 line 1,
|
|
5
|
+
* ctx must be ≤ 255 bytes (the byte that follows the domain separator in
|
|
6
|
+
* M' encodes |ctx|, so longer values cannot be represented). Throws
|
|
7
|
+
* `SigningError('sig-ctx-too-long')` per the SigningError discriminator contract.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateContext(ctx: Uint8Array): void;
|
|
10
|
+
/**
|
|
11
|
+
* FIPS 205 §3.6.2, public key must be exactly pkBytes long for its
|
|
12
|
+
* parameter set. Throws here; the public verify*() surfaces catch the
|
|
13
|
+
* throw and return false so a wrong-length pk reads as "not a valid
|
|
14
|
+
* signature" rather than a caller error.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePublicKey(pk: Uint8Array, params: SlhDsaParams): void;
|
|
17
|
+
/**
|
|
18
|
+
* Signing key must be exactly skBytes long for its parameter set. Wrong
|
|
19
|
+
* length is a caller error (the caller produced this sk via keygen* or
|
|
20
|
+
* loaded it from storage they own); throw RangeError unconditionally.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateSigningKey(sk: Uint8Array, params: SlhDsaParams): void;
|
|
23
|
+
/**
|
|
24
|
+
* FIPS 205 §3.6.2, signature must be exactly sigBytes long for its
|
|
25
|
+
* parameter set. Throws here; the public verify*() surfaces catch and
|
|
26
|
+
* return false (same protocol shape as wrong-length pk).
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateSignature(sig: Uint8Array, params: SlhDsaParams): void;
|
|
29
|
+
/**
|
|
30
|
+
* FIPS 205 §3.4 / §9.2 Algorithm 19 line 4, opt_rand (addrnd) must be
|
|
31
|
+
* exactly n bytes for the parameter set. Used by signDerand (the
|
|
32
|
+
* testing / CAVP API). Hedged sign supplies opt_rand internally;
|
|
33
|
+
* deterministic sign substitutes PK.seed; only signDerand exposes
|
|
34
|
+
* opt_rand to the caller.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateRnd(rnd: Uint8Array, params: SlhDsaParams): void;
|
|
37
|
+
/**
|
|
38
|
+
* Confirms M is a Uint8Array. FIPS 205 places no length restriction on
|
|
39
|
+
* the message; M is absorbed into Hmsg via SHAKE256 / SHA-2 depending on
|
|
40
|
+
* the chosen instantiation (§11.2 SHAKE family in the shipped scope), so
|
|
41
|
+
* any byte length is admissible.
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateMessage(M: Uint8Array): void;
|
|
44
|
+
/**
|
|
45
|
+
* FIPS 205 §10.2.2 Algorithm 23 lines 9-20, the prehash PH_M passed to
|
|
46
|
+
* HashSLH-DSA's Sign_internal / Verify_internal must be exactly the
|
|
47
|
+
* digest size of `algo`. Used by the `*Prehashed` family where the
|
|
48
|
+
* caller computes PH externally; the non-prehashed family produces PH
|
|
49
|
+
* internally so this check is implicit.
|
|
50
|
+
*
|
|
51
|
+
* Throws `SigningError('sig-malformed-input')` on mismatch. The verify
|
|
52
|
+
* surface intercepts this to return false (a wrong-size digest is a
|
|
53
|
+
* structural mismatch, indistinguishable from a wrong signature), while
|
|
54
|
+
* the sign surface lets the throw propagate (the caller supplied bad
|
|
55
|
+
* input; that is a contract violation).
|
|
56
|
+
*
|
|
57
|
+
* Duplicated from `src/ts/mldsa/validate.ts:validateDigest`; extraction is
|
|
58
|
+
* deferred until a third consumer materialises.
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateDigest(digest: Uint8Array, algo: PreHashAlgorithm): void;
|
|
@@ -0,0 +1,127 @@
|
|
|
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/slhdsa/validate.ts
|
|
23
|
+
//
|
|
24
|
+
// SLH-DSA caller-side input validation. Pure length / type checks. Mirrors
|
|
25
|
+
// `src/ts/mldsa/validate.ts`. Length checks throw RangeError; the public
|
|
26
|
+
// verify() / verifyHash() / verifyHashPrehashed() surfaces intercept pk /
|
|
27
|
+
// sig / digest length mismatches and return false instead of propagating the
|
|
28
|
+
// throw (FIPS 205 §3.6.2 / §10 structural-mismatch posture). ctx, sk, and
|
|
29
|
+
// rnd / opt_rand are caller contract violations and let the throw propagate.
|
|
30
|
+
import { SigningError } from '../errors.js';
|
|
31
|
+
import { digestSize } from './prehash.js';
|
|
32
|
+
/**
|
|
33
|
+
* FIPS 205 §10.2.1 Algorithm 22 line 1 / §10.2.2 Algorithm 23 line 1,
|
|
34
|
+
* ctx must be ≤ 255 bytes (the byte that follows the domain separator in
|
|
35
|
+
* M' encodes |ctx|, so longer values cannot be represented). Throws
|
|
36
|
+
* `SigningError('sig-ctx-too-long')` per the SigningError discriminator contract.
|
|
37
|
+
*/
|
|
38
|
+
export function validateContext(ctx) {
|
|
39
|
+
if (!(ctx instanceof Uint8Array))
|
|
40
|
+
throw new TypeError('leviathan-crypto: ctx must be a Uint8Array');
|
|
41
|
+
if (ctx.length > 255)
|
|
42
|
+
throw new SigningError('sig-ctx-too-long', `leviathan-crypto: ctx must be ≤ 255 bytes (got ${ctx.length})`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* FIPS 205 §3.6.2, public key must be exactly pkBytes long for its
|
|
46
|
+
* parameter set. Throws here; the public verify*() surfaces catch the
|
|
47
|
+
* throw and return false so a wrong-length pk reads as "not a valid
|
|
48
|
+
* signature" rather than a caller error.
|
|
49
|
+
*/
|
|
50
|
+
export function validatePublicKey(pk, params) {
|
|
51
|
+
if (!(pk instanceof Uint8Array))
|
|
52
|
+
throw new TypeError('leviathan-crypto: public key must be a Uint8Array');
|
|
53
|
+
if (pk.length !== params.pkBytes)
|
|
54
|
+
throw new RangeError(`leviathan-crypto: public key must be ${params.pkBytes} bytes for ${params.paramSet} `
|
|
55
|
+
+ `(got ${pk.length})`);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Signing key must be exactly skBytes long for its parameter set. Wrong
|
|
59
|
+
* length is a caller error (the caller produced this sk via keygen* or
|
|
60
|
+
* loaded it from storage they own); throw RangeError unconditionally.
|
|
61
|
+
*/
|
|
62
|
+
export function validateSigningKey(sk, params) {
|
|
63
|
+
if (!(sk instanceof Uint8Array))
|
|
64
|
+
throw new TypeError('leviathan-crypto: signing key must be a Uint8Array');
|
|
65
|
+
if (sk.length !== params.skBytes)
|
|
66
|
+
throw new RangeError(`leviathan-crypto: signing key must be ${params.skBytes} bytes for ${params.paramSet} `
|
|
67
|
+
+ `(got ${sk.length})`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* FIPS 205 §3.6.2, signature must be exactly sigBytes long for its
|
|
71
|
+
* parameter set. Throws here; the public verify*() surfaces catch and
|
|
72
|
+
* return false (same protocol shape as wrong-length pk).
|
|
73
|
+
*/
|
|
74
|
+
export function validateSignature(sig, params) {
|
|
75
|
+
if (!(sig instanceof Uint8Array))
|
|
76
|
+
throw new TypeError('leviathan-crypto: signature must be a Uint8Array');
|
|
77
|
+
if (sig.length !== params.sigBytes)
|
|
78
|
+
throw new RangeError(`leviathan-crypto: signature must be ${params.sigBytes} bytes for ${params.paramSet} `
|
|
79
|
+
+ `(got ${sig.length})`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* FIPS 205 §3.4 / §9.2 Algorithm 19 line 4, opt_rand (addrnd) must be
|
|
83
|
+
* exactly n bytes for the parameter set. Used by signDerand (the
|
|
84
|
+
* testing / CAVP API). Hedged sign supplies opt_rand internally;
|
|
85
|
+
* deterministic sign substitutes PK.seed; only signDerand exposes
|
|
86
|
+
* opt_rand to the caller.
|
|
87
|
+
*/
|
|
88
|
+
export function validateRnd(rnd, params) {
|
|
89
|
+
if (!(rnd instanceof Uint8Array))
|
|
90
|
+
throw new TypeError('leviathan-crypto: opt_rand must be a Uint8Array');
|
|
91
|
+
if (rnd.length !== params.n)
|
|
92
|
+
throw new RangeError(`leviathan-crypto: opt_rand must be ${params.n} bytes for ${params.paramSet} `
|
|
93
|
+
+ `(got ${rnd.length})`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Confirms M is a Uint8Array. FIPS 205 places no length restriction on
|
|
97
|
+
* the message; M is absorbed into Hmsg via SHAKE256 / SHA-2 depending on
|
|
98
|
+
* the chosen instantiation (§11.2 SHAKE family in the shipped scope), so
|
|
99
|
+
* any byte length is admissible.
|
|
100
|
+
*/
|
|
101
|
+
export function validateMessage(M) {
|
|
102
|
+
if (!(M instanceof Uint8Array))
|
|
103
|
+
throw new TypeError('leviathan-crypto: message must be a Uint8Array');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* FIPS 205 §10.2.2 Algorithm 23 lines 9-20, the prehash PH_M passed to
|
|
107
|
+
* HashSLH-DSA's Sign_internal / Verify_internal must be exactly the
|
|
108
|
+
* digest size of `algo`. Used by the `*Prehashed` family where the
|
|
109
|
+
* caller computes PH externally; the non-prehashed family produces PH
|
|
110
|
+
* internally so this check is implicit.
|
|
111
|
+
*
|
|
112
|
+
* Throws `SigningError('sig-malformed-input')` on mismatch. The verify
|
|
113
|
+
* surface intercepts this to return false (a wrong-size digest is a
|
|
114
|
+
* structural mismatch, indistinguishable from a wrong signature), while
|
|
115
|
+
* the sign surface lets the throw propagate (the caller supplied bad
|
|
116
|
+
* input; that is a contract violation).
|
|
117
|
+
*
|
|
118
|
+
* Duplicated from `src/ts/mldsa/validate.ts:validateDigest`; extraction is
|
|
119
|
+
* deferred until a third consumer materialises.
|
|
120
|
+
*/
|
|
121
|
+
export function validateDigest(digest, algo) {
|
|
122
|
+
if (!(digest instanceof Uint8Array))
|
|
123
|
+
throw new SigningError('sig-malformed-input', 'digest must be a Uint8Array');
|
|
124
|
+
const expected = digestSize(algo);
|
|
125
|
+
if (digest.length !== expected)
|
|
126
|
+
throw new SigningError('sig-malformed-input', `digest length ${digest.length} != ${expected} for ${algo}`);
|
|
127
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SlhDsaExports } from './types.js';
|
|
2
|
+
import type { SlhDsaParams } from './params.js';
|
|
3
|
+
import { type PreHashAlgorithm } from './prehash.js';
|
|
4
|
+
/**
|
|
5
|
+
* Drive slhVerifyInternal with caller-built M' bytes.
|
|
6
|
+
*
|
|
7
|
+
* Layout written into INPUT (per src/asm/slhdsa/slh.ts §slhVerifyInternal):
|
|
8
|
+
* INPUT = pk (2n) ‖ M' (msgLen) ‖ sig (sigBytes)
|
|
9
|
+
*
|
|
10
|
+
* The caller has already filtered wrong-length pk and sig (the public
|
|
11
|
+
* verify*() surface returns false for those before calling this driver).
|
|
12
|
+
* Inside this function, length-mismatch only manifests if pk / sig were
|
|
13
|
+
* truncated post-validation; we defensively re-check to keep the WASM
|
|
14
|
+
* call within its declared INPUT bounds.
|
|
15
|
+
*
|
|
16
|
+
* Returns true iff slhVerifyInternal returns 1 (the §9.3 constant-time
|
|
17
|
+
* PK.root comparison succeeded plus all FORS / hypertree path checks).
|
|
18
|
+
*/
|
|
19
|
+
export declare function slhVerifyInternalTs(x: SlhDsaExports, params: SlhDsaParams, pk: Uint8Array, MPrime: Uint8Array, sig: Uint8Array): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* HashSLH-DSA verify, post-prehash. FIPS 205 §10.3 Algorithm 25 lines
|
|
22
|
+
* 16-19. Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
23
|
+
* Verify_internal.
|
|
24
|
+
*
|
|
25
|
+
* Same return / throw posture as `slhVerifyInternalTs`: returns a pure
|
|
26
|
+
* boolean for every signature outcome. Caller (in index.ts) is expected
|
|
27
|
+
* to have already filtered wrong-length vk / sig / digest with the
|
|
28
|
+
* appropriate verdict (false) before calling this helper.
|
|
29
|
+
*
|
|
30
|
+
* The caller owns `prehash`; this helper never wipes it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyWithPrehash(x: SlhDsaExports, params: SlhDsaParams, pk: Uint8Array, prehash: Uint8Array, sig: Uint8Array, algo: PreHashAlgorithm, ctx: Uint8Array): boolean;
|
|
@@ -0,0 +1,107 @@
|
|
|
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/slhdsa/verify.ts
|
|
23
|
+
//
|
|
24
|
+
// SLH-DSA verify drivers, FIPS 205 §10.3 wrappers around the WASM
|
|
25
|
+
// slhVerifyInternal entry point (FIPS 205 §9.3 Algorithm 20).
|
|
26
|
+
//
|
|
27
|
+
// Two drivers:
|
|
28
|
+
// • slhVerifyInternalTs, writes (pk ‖ M' ‖ sig) into INPUT, runs
|
|
29
|
+
// slhVerifyInternal, returns a boolean. M' is the already-domain-
|
|
30
|
+
// separated bytes built by the caller.
|
|
31
|
+
// • verifyWithPrehash, builds the HashSLH-DSA M' from
|
|
32
|
+
// (digest, oid(ph), ctx) and forwards. Mirrors
|
|
33
|
+
// src/ts/mldsa/verify.ts:verifyWithPrehash.
|
|
34
|
+
//
|
|
35
|
+
// Posture: returns boolean. Any WASM exception during the inner call is
|
|
36
|
+
// swallowed and converted to false; FIPS 205 §10.3 / §3.6.2 model verify
|
|
37
|
+
// as a pure predicate. Caller-side contract violations (ctx > 255 bytes,
|
|
38
|
+
// unsupported ph) are surfaced at the public-method layer, not here.
|
|
39
|
+
//
|
|
40
|
+
// Buffer hygiene: INPUT held pk + M' + sig (all public inputs). The WASM
|
|
41
|
+
// wipe is still applied for consistency with the sign path and to clear
|
|
42
|
+
// any sk residue that might have lingered from a previous sign call.
|
|
43
|
+
import { wipe } from '../utils.js';
|
|
44
|
+
import { constructMPrimeHash } from './prehash.js';
|
|
45
|
+
/**
|
|
46
|
+
* Drive slhVerifyInternal with caller-built M' bytes.
|
|
47
|
+
*
|
|
48
|
+
* Layout written into INPUT (per src/asm/slhdsa/slh.ts §slhVerifyInternal):
|
|
49
|
+
* INPUT = pk (2n) ‖ M' (msgLen) ‖ sig (sigBytes)
|
|
50
|
+
*
|
|
51
|
+
* The caller has already filtered wrong-length pk and sig (the public
|
|
52
|
+
* verify*() surface returns false for those before calling this driver).
|
|
53
|
+
* Inside this function, length-mismatch only manifests if pk / sig were
|
|
54
|
+
* truncated post-validation; we defensively re-check to keep the WASM
|
|
55
|
+
* call within its declared INPUT bounds.
|
|
56
|
+
*
|
|
57
|
+
* Returns true iff slhVerifyInternal returns 1 (the §9.3 constant-time
|
|
58
|
+
* PK.root comparison succeeded plus all FORS / hypertree path checks).
|
|
59
|
+
*/
|
|
60
|
+
export function slhVerifyInternalTs(x, params, pk, MPrime, sig) {
|
|
61
|
+
const inOff = x.getInputOffset();
|
|
62
|
+
const pkLen = params.pkBytes;
|
|
63
|
+
const msgLen = MPrime.length;
|
|
64
|
+
const sigLen = sig.length;
|
|
65
|
+
const inputTotal = pkLen + msgLen + sigLen;
|
|
66
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
67
|
+
try {
|
|
68
|
+
params.wasmSelector();
|
|
69
|
+
mem.set(pk, inOff);
|
|
70
|
+
mem.set(MPrime, inOff + pkLen);
|
|
71
|
+
mem.set(sig, inOff + pkLen + msgLen);
|
|
72
|
+
return x.slhVerifyInternal(msgLen) === 1;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// FIPS 205 §10.3 verify is a pure predicate. Any unexpected
|
|
76
|
+
// exception (out-of-memory, kernel argument error) is treated
|
|
77
|
+
// as "did not authenticate". The public surface already filters
|
|
78
|
+
// caller contract violations (ctx > 255) before reaching this
|
|
79
|
+
// driver, so swallowing here cannot mask a contract bug.
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
mem.fill(0, inOff, inOff + inputTotal);
|
|
84
|
+
x.wipeBuffers();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* HashSLH-DSA verify, post-prehash. FIPS 205 §10.3 Algorithm 25 lines
|
|
89
|
+
* 16-19. Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
90
|
+
* Verify_internal.
|
|
91
|
+
*
|
|
92
|
+
* Same return / throw posture as `slhVerifyInternalTs`: returns a pure
|
|
93
|
+
* boolean for every signature outcome. Caller (in index.ts) is expected
|
|
94
|
+
* to have already filtered wrong-length vk / sig / digest with the
|
|
95
|
+
* appropriate verdict (false) before calling this helper.
|
|
96
|
+
*
|
|
97
|
+
* The caller owns `prehash`; this helper never wipes it.
|
|
98
|
+
*/
|
|
99
|
+
export function verifyWithPrehash(x, params, pk, prehash, sig, algo, ctx) {
|
|
100
|
+
const MPrime = constructMPrimeHash(prehash, algo, ctx);
|
|
101
|
+
try {
|
|
102
|
+
return slhVerifyInternalTs(x, params, pk, MPrime, sig);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
wipe(MPrime);
|
|
106
|
+
}
|
|
107
|
+
}
|
package/dist/slhdsa.wasm
ADDED
|
Binary file
|
package/dist/stream/header.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
//
|
|
24
24
|
// Wire format header encoding/decoding and counter nonce construction.
|
|
25
25
|
import { CHUNK_MAX, CHUNK_MIN, FLAG_FRAMED, HEADER_SIZE, TAG_DATA, TAG_FINAL } from './constants.js';
|
|
26
|
-
// The 16-byte nonce is a HKDF salt
|
|
26
|
+
// The 16-byte nonce is a HKDF salt, not a direct cipher nonce.
|
|
27
27
|
// Both XChaCha20Cipher and SerpentCipher derive their actual key material
|
|
28
28
|
// and nonces from this value via HKDF-SHA-256. The 16-byte size is chosen
|
|
29
29
|
// to satisfy HChaCha20's 16-byte input requirement while also serving as a
|
|
@@ -49,7 +49,7 @@ export function readHeader(header) {
|
|
|
49
49
|
throw new RangeError(`header must be exactly ${HEADER_SIZE} bytes (got ${header.length})`);
|
|
50
50
|
const byte0 = header[0];
|
|
51
51
|
if (byte0 & 0x40)
|
|
52
|
-
throw new RangeError(`header has reserved bit 6 set (byte0=0x${byte0.toString(16).padStart(2, '0')})
|
|
52
|
+
throw new RangeError(`header has reserved bit 6 set (byte0=0x${byte0.toString(16).padStart(2, '0')}), unknown or malformed wire format`);
|
|
53
53
|
return {
|
|
54
54
|
formatEnum: byte0 & 0x3f,
|
|
55
55
|
framed: !!(byte0 & FLAG_FRAMED),
|
|
@@ -65,7 +65,7 @@ export function makeCounterNonce(counter, finalFlag) {
|
|
|
65
65
|
throw new RangeError(`finalFlag must be TAG_DATA (0x00) or TAG_FINAL (0x01) (got 0x${finalFlag.toString(16).padStart(2, '0')})`);
|
|
66
66
|
const n = new Uint8Array(12);
|
|
67
67
|
// Write counter as 11-byte big-endian.
|
|
68
|
-
// JS safe integers fit in 53 bits
|
|
68
|
+
// JS safe integers fit in 53 bits, we only need the lower 53 bits.
|
|
69
69
|
// Pack from the right (byte 10 down to byte 0).
|
|
70
70
|
let c = counter;
|
|
71
71
|
for (let i = 10; i >= 0; i--) {
|
package/dist/stream/index.d.ts
CHANGED
package/dist/stream/index.js
CHANGED
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/stream/open-stream.ts
|
|
23
23
|
//
|
|
24
|
-
// OpenStream
|
|
24
|
+
// OpenStream, cipher-agnostic streaming decryption using the STREAM
|
|
25
25
|
// construction (Hoang/Reyhanitabar/Rogaway/Vizár, CRYPTO 2015).
|
|
26
26
|
import { isInitialized } from '../init.js';
|
|
27
|
+
import { constantTimeEqual } from '../utils.js';
|
|
28
|
+
import { AuthenticationError } from '../errors.js';
|
|
27
29
|
import { TAG_DATA, TAG_FINAL, CHUNK_MIN, CHUNK_MAX, HEADER_SIZE } from './constants.js';
|
|
28
30
|
import { readHeader, makeCounterNonce } from './header.js';
|
|
29
31
|
export class OpenStream {
|
|
@@ -37,15 +39,16 @@ export class OpenStream {
|
|
|
37
39
|
constructor(cipher, key, preamble) {
|
|
38
40
|
this.cipher = cipher;
|
|
39
41
|
if (!isInitialized('sha2'))
|
|
40
|
-
throw new Error('leviathan-crypto: stream layer requires sha2 for key derivation
|
|
42
|
+
throw new Error('leviathan-crypto: stream layer requires sha2 for key derivation, '
|
|
41
43
|
+ 'call init({ sha2: ... }) before creating an OpenStream');
|
|
42
44
|
const decKeySize = cipher.decKeySize ?? cipher.keySize;
|
|
43
45
|
if (key.length !== decKeySize)
|
|
44
46
|
throw new RangeError(`key must be ${decKeySize} bytes (got ${key.length})`);
|
|
45
|
-
const expectedPreambleLen = HEADER_SIZE + cipher.kemCtSize;
|
|
47
|
+
const expectedPreambleLen = HEADER_SIZE + cipher.kemCtSize + cipher.commitmentSize;
|
|
46
48
|
if (preamble.length !== expectedPreambleLen)
|
|
47
49
|
throw new RangeError(`preamble must be exactly ${expectedPreambleLen} bytes (got ${preamble.length})`);
|
|
48
|
-
const
|
|
50
|
+
const headerBytes = preamble.subarray(0, HEADER_SIZE);
|
|
51
|
+
const h = readHeader(headerBytes);
|
|
49
52
|
if (h.formatEnum !== cipher.formatEnum)
|
|
50
53
|
throw new Error(`expected format 0x${cipher.formatEnum.toString(16).padStart(2, '0')} (${cipher.formatName}), `
|
|
51
54
|
+ `got 0x${h.formatEnum.toString(16).padStart(2, '0')}`);
|
|
@@ -54,7 +57,25 @@ export class OpenStream {
|
|
|
54
57
|
const kemCt = cipher.kemCtSize > 0
|
|
55
58
|
? preamble.subarray(HEADER_SIZE, HEADER_SIZE + cipher.kemCtSize)
|
|
56
59
|
: undefined;
|
|
57
|
-
|
|
60
|
+
const commitmentOffset = HEADER_SIZE + cipher.kemCtSize;
|
|
61
|
+
const recvCommitment = cipher.commitmentSize > 0
|
|
62
|
+
? preamble.subarray(commitmentOffset, commitmentOffset + cipher.commitmentSize)
|
|
63
|
+
: undefined;
|
|
64
|
+
this.keys = cipher.deriveKeys(key, h.nonce, kemCt, headerBytes);
|
|
65
|
+
// Verify commitment before any chunk is processed. Wrong key fails
|
|
66
|
+
// fast with AuthenticationError, before Poly1305 is consulted.
|
|
67
|
+
if (cipher.commitmentSize > 0) {
|
|
68
|
+
const derivedCommitment = this.keys.commitment;
|
|
69
|
+
if (!derivedCommitment || derivedCommitment.length !== cipher.commitmentSize) {
|
|
70
|
+
cipher.wipeKeys(this.keys);
|
|
71
|
+
throw new Error(`leviathan-crypto: ${cipher.formatName}.deriveKeys returned `
|
|
72
|
+
+ `${derivedCommitment?.length ?? 'no'} commitment bytes, expected ${cipher.commitmentSize}`);
|
|
73
|
+
}
|
|
74
|
+
if (!recvCommitment || !constantTimeEqual(derivedCommitment, recvCommitment)) {
|
|
75
|
+
cipher.wipeKeys(this.keys);
|
|
76
|
+
throw new AuthenticationError(`commitment-${cipher.formatName}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
58
79
|
this.chunkSize = h.chunkSize;
|
|
59
80
|
this.framed = h.framed;
|
|
60
81
|
// Max ciphertext chunk: padded plaintext + tag
|
|
@@ -113,7 +134,7 @@ export class OpenStream {
|
|
|
113
134
|
this.cipher.wipeKeys(this.keys);
|
|
114
135
|
this.state = 'finalized';
|
|
115
136
|
}
|
|
116
|
-
// 'failed' already wiped keys; 'finalized' already wiped keys
|
|
137
|
+
// 'failed' already wiped keys; 'finalized' already wiped keys, no-op.
|
|
117
138
|
}
|
|
118
139
|
seek(index) {
|
|
119
140
|
if (this.state !== 'ready')
|
|
@@ -121,17 +142,17 @@ export class OpenStream {
|
|
|
121
142
|
if (!Number.isSafeInteger(index) || index < 0)
|
|
122
143
|
throw new RangeError(`seek index must be a non-negative safe integer ≤ Number.MAX_SAFE_INTEGER (got ${index})`);
|
|
123
144
|
if (index < this.counter)
|
|
124
|
-
throw new RangeError(`OpenStream: seek is forward-only
|
|
145
|
+
throw new RangeError(`OpenStream: seek is forward-only, current counter ${this.counter}, requested ${index}. `
|
|
125
146
|
+ 'Backward seeks would permit plaintext replay; construct a new OpenStream to restart.');
|
|
126
147
|
this.counter = index;
|
|
127
148
|
}
|
|
128
149
|
_stripFrame(chunk) {
|
|
129
150
|
if (chunk.length < 4)
|
|
130
|
-
throw new RangeError(`framed chunk too short
|
|
151
|
+
throw new RangeError(`framed chunk too short, need at least 4 bytes (got ${chunk.length})`);
|
|
131
152
|
const dv = new DataView(chunk.buffer, chunk.byteOffset);
|
|
132
153
|
const payloadLen = dv.getUint32(0, false);
|
|
133
154
|
if (payloadLen !== chunk.length - 4)
|
|
134
|
-
throw new RangeError(`framed chunk length mismatch
|
|
155
|
+
throw new RangeError(`framed chunk length mismatch, prefix says ${payloadLen}, actual payload is ${chunk.length - 4}`);
|
|
135
156
|
return chunk.subarray(4);
|
|
136
157
|
}
|
|
137
158
|
toTransformStream() {
|
|
@@ -155,7 +176,7 @@ export class OpenStream {
|
|
|
155
176
|
controller.enqueue(this.finalize(buffered));
|
|
156
177
|
}
|
|
157
178
|
else {
|
|
158
|
-
this.dispose(); // no chunks piped
|
|
179
|
+
this.dispose(); // no chunks piped, wipe keys, emit nothing
|
|
159
180
|
}
|
|
160
181
|
}
|
|
161
182
|
catch (err) {
|