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
package/dist/sha3.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const PKCS7_INVALID = "invalid ciphertext";
|
|
2
|
+
/**
|
|
3
|
+
* Apply PKCS7 padding to `data` so the result length is a multiple of 16.
|
|
4
|
+
* Padding length is always 1-16 bytes so a full pad block is appended when
|
|
5
|
+
* `data.length` is already block-aligned.
|
|
6
|
+
* @param data Input bytes of any length
|
|
7
|
+
* @returns New Uint8Array padded to the next 16-byte boundary
|
|
8
|
+
*/
|
|
9
|
+
export declare function pkcs7Pad(data: Uint8Array): Uint8Array;
|
|
10
|
+
/**
|
|
11
|
+
* Remove PKCS7 padding from a block-aligned buffer in constant time.
|
|
12
|
+
*
|
|
13
|
+
* Branch-free over all secret bits, padding length and per-byte comparisons
|
|
14
|
+
* are accumulated into a single `bad` flag with no early exit. Closes the
|
|
15
|
+
* Vaudenay 2002 padding-oracle surface. Throws a single generic
|
|
16
|
+
* `RangeError('invalid ciphertext')` for every failure mode: empty input,
|
|
17
|
+
* non-block-aligned length, padding byte out of range 1-16, and any per-byte
|
|
18
|
+
* mismatch in the padding region.
|
|
19
|
+
* @param data Block-aligned ciphertext (length must be a multiple of 16)
|
|
20
|
+
* @returns Plaintext with padding removed
|
|
21
|
+
*/
|
|
22
|
+
export declare function pkcs7Strip(data: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,84 @@
|
|
|
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/shared/pkcs7.ts
|
|
23
|
+
//
|
|
24
|
+
// Cipher-agnostic PKCS7 padding helpers (RFC 5652 §6.3). Used by every
|
|
25
|
+
// CBC mode wrapper in the library, `SerpentCbc`, `AESCbc`, the Serpent
|
|
26
|
+
// pool worker, and any future CBC-based suite. A single source of truth
|
|
27
|
+
// keeps the branch-free, Vaudenay-2002-closed padding check identical
|
|
28
|
+
// across all call sites; divergence between paths would reintroduce a
|
|
29
|
+
// padding-oracle.
|
|
30
|
+
// Generic error string used by every failure mode of `pkcs7Strip` and the
|
|
31
|
+
// length/alignment gates in CBC `decrypt` paths. No numeric leaks, no
|
|
32
|
+
// structural disclosure, a caller cannot distinguish "bad length" from
|
|
33
|
+
// "bad padding" by message or by timing.
|
|
34
|
+
export const PKCS7_INVALID = 'invalid ciphertext';
|
|
35
|
+
/**
|
|
36
|
+
* Apply PKCS7 padding to `data` so the result length is a multiple of 16.
|
|
37
|
+
* Padding length is always 1-16 bytes so a full pad block is appended when
|
|
38
|
+
* `data.length` is already block-aligned.
|
|
39
|
+
* @param data Input bytes of any length
|
|
40
|
+
* @returns New Uint8Array padded to the next 16-byte boundary
|
|
41
|
+
*/
|
|
42
|
+
export function pkcs7Pad(data) {
|
|
43
|
+
const padLen = 16 - (data.length % 16); // 1..16
|
|
44
|
+
const out = new Uint8Array(data.length + padLen);
|
|
45
|
+
out.set(data);
|
|
46
|
+
out.fill(padLen, data.length);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Remove PKCS7 padding from a block-aligned buffer in constant time.
|
|
51
|
+
*
|
|
52
|
+
* Branch-free over all secret bits, padding length and per-byte comparisons
|
|
53
|
+
* are accumulated into a single `bad` flag with no early exit. Closes the
|
|
54
|
+
* Vaudenay 2002 padding-oracle surface. Throws a single generic
|
|
55
|
+
* `RangeError('invalid ciphertext')` for every failure mode: empty input,
|
|
56
|
+
* non-block-aligned length, padding byte out of range 1-16, and any per-byte
|
|
57
|
+
* mismatch in the padding region.
|
|
58
|
+
* @param data Block-aligned ciphertext (length must be a multiple of 16)
|
|
59
|
+
* @returns Plaintext with padding removed
|
|
60
|
+
*/
|
|
61
|
+
export function pkcs7Strip(data) {
|
|
62
|
+
if (data.length === 0 || data.length % 16 !== 0)
|
|
63
|
+
throw new RangeError(PKCS7_INVALID);
|
|
64
|
+
const padLen = data[data.length - 1];
|
|
65
|
+
let bad = 0;
|
|
66
|
+
bad |= ((padLen - 1) >>> 31); // 1 if padLen == 0
|
|
67
|
+
bad |= ((16 - padLen) >>> 31); // 1 if padLen > 16
|
|
68
|
+
// Per-byte pad-region mask without branches on secret bits.
|
|
69
|
+
// inPadRegion = 0xff when i >= 16 - padLen
|
|
70
|
+
// = 0x00 otherwise
|
|
71
|
+
//
|
|
72
|
+
// (16 - padLen - i - 1) is negative iff i >= 16 - padLen. A signed
|
|
73
|
+
// arithmetic shift by 31 yields -1 for negative, 0 for non-negative;
|
|
74
|
+
// ANDing with 0xff collapses those to 0xff and 0x00.
|
|
75
|
+
for (let i = 0; i < 16; i++) {
|
|
76
|
+
const idx = data.length - 16 + i;
|
|
77
|
+
const mask = ((16 - padLen - i - 1) >> 31) & 0xff;
|
|
78
|
+
bad |= (data[idx] ^ padLen) & mask;
|
|
79
|
+
}
|
|
80
|
+
const invalid = ((bad - 1) >>> 31) ^ 1;
|
|
81
|
+
if (invalid)
|
|
82
|
+
throw new RangeError(PKCS7_INVALID);
|
|
83
|
+
return data.subarray(0, data.length - padLen);
|
|
84
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PreHashAlgorithm } from '../mldsa/hashvariant.js';
|
|
2
|
+
import type { PrehashAlgorithm } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Maximum user_ctx length in bytes. Matches the FIPS 204 (Module-Lattice-
|
|
5
|
+
* Based Digital Signature Standard) §3.6.1 native ctx cap; buildEffectiveCtx
|
|
6
|
+
* enforces it as the first check.
|
|
7
|
+
*/
|
|
8
|
+
export declare const USER_CTX_MAX = 255;
|
|
9
|
+
/** Maximum ctxDomain length in bytes; enforced by suite factories. */
|
|
10
|
+
export declare const CTX_DOMAIN_MAX = 32;
|
|
11
|
+
/**
|
|
12
|
+
* Construct effective_ctx from suite domain and user-supplied ctx.
|
|
13
|
+
*
|
|
14
|
+
* Format: [domain_len: u8][domain_bytes][user_ctx_len: u8][user_ctx_bytes]
|
|
15
|
+
*
|
|
16
|
+
* Two independent length checks gate the construction:
|
|
17
|
+
*
|
|
18
|
+
* 1. user_ctx ≤ USER_CTX_MAX (255). Mirrors FIPS 204 §3.6.1's ctx cap on
|
|
19
|
+
* the user-supplied half before any framing is applied.
|
|
20
|
+
* 2. Combined effective_ctx ≤ 255. The 1-byte length prefixes plus the
|
|
21
|
+
* domain and user_ctx bytes must fit FIPS 204 §3.6.1's ctx parameter,
|
|
22
|
+
* which is what the result is ultimately passed to. The effective
|
|
23
|
+
* per-suite user_ctx ceiling is `253 - len(domainBytes)`.
|
|
24
|
+
*
|
|
25
|
+
* Both throws share the `sig-ctx-too-long` discriminator. The absolute
|
|
26
|
+
* check fires first, so callers passing user_ctx > 255 always trip the
|
|
27
|
+
* absolute cap regardless of ctxDomain length.
|
|
28
|
+
*
|
|
29
|
+
* @throws SigningError('sig-ctx-too-long') if user_ctx exceeds USER_CTX_MAX
|
|
30
|
+
* or if the combined effective_ctx exceeds 255 bytes.
|
|
31
|
+
* @throws Error (not SigningError) if ctxDomain exceeds CTX_DOMAIN_MAX;
|
|
32
|
+
* that's a developer-time mistake, not a caller mistake.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildEffectiveCtx(ctxDomain: string, userCtx: Uint8Array): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Map the lib's lowercase, hyphenated PrehashAlgorithm to the identifier
|
|
37
|
+
* MlDsaBase's signHash / verifyHash family accepts (FIPS 204 §5.4.1).
|
|
38
|
+
* SHAKE entries deliberately use the no-hyphen spelling 'SHAKE128' /
|
|
39
|
+
* 'SHAKE256' that the mldsa layer expects.
|
|
40
|
+
*/
|
|
41
|
+
export declare function prehashAlgoToMldsa(algo: PrehashAlgorithm): PreHashAlgorithm;
|
package/dist/sign/ctx.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
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/sign/ctx.ts
|
|
23
|
+
//
|
|
24
|
+
// Effective-ctx construction and prehash-algorithm mapping. Used by
|
|
25
|
+
// every SignatureSuite factory.
|
|
26
|
+
import { utf8ToBytes } from '../utils.js';
|
|
27
|
+
import { SigningError } from '../errors.js';
|
|
28
|
+
/**
|
|
29
|
+
* Maximum user_ctx length in bytes. Matches the FIPS 204 (Module-Lattice-
|
|
30
|
+
* Based Digital Signature Standard) §3.6.1 native ctx cap; buildEffectiveCtx
|
|
31
|
+
* enforces it as the first check.
|
|
32
|
+
*/
|
|
33
|
+
export const USER_CTX_MAX = 255;
|
|
34
|
+
/** Maximum ctxDomain length in bytes; enforced by suite factories. */
|
|
35
|
+
export const CTX_DOMAIN_MAX = 32;
|
|
36
|
+
/**
|
|
37
|
+
* Maximum combined effective_ctx length in bytes. The output of
|
|
38
|
+
* buildEffectiveCtx is fed to the underlying primitive's ctx parameter,
|
|
39
|
+
* which FIPS 204 §3.6.1 caps at 255.
|
|
40
|
+
*/
|
|
41
|
+
const EFFECTIVE_CTX_MAX = 255;
|
|
42
|
+
/**
|
|
43
|
+
* Construct effective_ctx from suite domain and user-supplied ctx.
|
|
44
|
+
*
|
|
45
|
+
* Format: [domain_len: u8][domain_bytes][user_ctx_len: u8][user_ctx_bytes]
|
|
46
|
+
*
|
|
47
|
+
* Two independent length checks gate the construction:
|
|
48
|
+
*
|
|
49
|
+
* 1. user_ctx ≤ USER_CTX_MAX (255). Mirrors FIPS 204 §3.6.1's ctx cap on
|
|
50
|
+
* the user-supplied half before any framing is applied.
|
|
51
|
+
* 2. Combined effective_ctx ≤ 255. The 1-byte length prefixes plus the
|
|
52
|
+
* domain and user_ctx bytes must fit FIPS 204 §3.6.1's ctx parameter,
|
|
53
|
+
* which is what the result is ultimately passed to. The effective
|
|
54
|
+
* per-suite user_ctx ceiling is `253 - len(domainBytes)`.
|
|
55
|
+
*
|
|
56
|
+
* Both throws share the `sig-ctx-too-long` discriminator. The absolute
|
|
57
|
+
* check fires first, so callers passing user_ctx > 255 always trip the
|
|
58
|
+
* absolute cap regardless of ctxDomain length.
|
|
59
|
+
*
|
|
60
|
+
* @throws SigningError('sig-ctx-too-long') if user_ctx exceeds USER_CTX_MAX
|
|
61
|
+
* or if the combined effective_ctx exceeds 255 bytes.
|
|
62
|
+
* @throws Error (not SigningError) if ctxDomain exceeds CTX_DOMAIN_MAX;
|
|
63
|
+
* that's a developer-time mistake, not a caller mistake.
|
|
64
|
+
*/
|
|
65
|
+
export function buildEffectiveCtx(ctxDomain, userCtx) {
|
|
66
|
+
if (userCtx.length > USER_CTX_MAX)
|
|
67
|
+
throw new SigningError('sig-ctx-too-long', `user_ctx length ${userCtx.length} > ${USER_CTX_MAX}`);
|
|
68
|
+
const domainBytes = utf8ToBytes(ctxDomain);
|
|
69
|
+
if (domainBytes.length > CTX_DOMAIN_MAX)
|
|
70
|
+
throw new Error(`leviathan-crypto: ctxDomain '${ctxDomain}' encodes to ${domainBytes.length} bytes (max ${CTX_DOMAIN_MAX})`);
|
|
71
|
+
const combined = 1 + domainBytes.length + 1 + userCtx.length;
|
|
72
|
+
if (combined > EFFECTIVE_CTX_MAX)
|
|
73
|
+
throw new SigningError('sig-ctx-too-long', `effective_ctx length ${combined} > ${EFFECTIVE_CTX_MAX} (FIPS 204 §3.6.1 ctx cap; ctxDomain ${domainBytes.length} bytes + user_ctx ${userCtx.length} bytes + 2 length prefixes)`);
|
|
74
|
+
const out = new Uint8Array(combined);
|
|
75
|
+
let pos = 0;
|
|
76
|
+
out[pos++] = domainBytes.length;
|
|
77
|
+
out.set(domainBytes, pos);
|
|
78
|
+
pos += domainBytes.length;
|
|
79
|
+
out[pos++] = userCtx.length;
|
|
80
|
+
out.set(userCtx, pos);
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Map the lib's lowercase, hyphenated PrehashAlgorithm to the identifier
|
|
85
|
+
* MlDsaBase's signHash / verifyHash family accepts (FIPS 204 §5.4.1).
|
|
86
|
+
* SHAKE entries deliberately use the no-hyphen spelling 'SHAKE128' /
|
|
87
|
+
* 'SHAKE256' that the mldsa layer expects.
|
|
88
|
+
*/
|
|
89
|
+
export function prehashAlgoToMldsa(algo) {
|
|
90
|
+
switch (algo) {
|
|
91
|
+
case 'sha-256': return 'SHA2-256';
|
|
92
|
+
case 'sha-512': return 'SHA2-512';
|
|
93
|
+
case 'sha3-256': return 'SHA3-256';
|
|
94
|
+
case 'sha3-512': return 'SHA3-512';
|
|
95
|
+
case 'shake-128': return 'SHAKE128';
|
|
96
|
+
case 'shake-256': return 'SHAKE256';
|
|
97
|
+
default: {
|
|
98
|
+
const _exhaustive = algo;
|
|
99
|
+
throw new Error(`leviathan-crypto: unknown prehash algorithm ${_exhaustive}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { SignatureSuite } from './types.js';
|
|
2
|
+
export declare class Sign {
|
|
3
|
+
/**
|
|
4
|
+
* Single-shot sign. Returns the attached envelope blob.
|
|
5
|
+
*/
|
|
6
|
+
static sign(suite: SignatureSuite, sk: Uint8Array, msg: Uint8Array, ctx: Uint8Array): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Single-shot verify. Returns the extracted payload on success.
|
|
9
|
+
*
|
|
10
|
+
* @throws SigningError('sig-blob-too-short') blob cannot fit the wire shape.
|
|
11
|
+
* @throws SigningError('sig-suite-mismatch') wire suite_byte mismatch.
|
|
12
|
+
* @throws SigningError('sig-ctx-mismatch') caller ctx != wire ctx.
|
|
13
|
+
* @throws SigningError('verify-failed') suite.verify returned false.
|
|
14
|
+
*/
|
|
15
|
+
static verify(suite: SignatureSuite, pk: Uint8Array, blob: Uint8Array, ctx: Uint8Array): Uint8Array;
|
|
16
|
+
/**
|
|
17
|
+
* Detached sign. Returns just the raw signature bytes (no envelope).
|
|
18
|
+
* Caller is responsible for transmitting (suite, msg, sig, ctx)
|
|
19
|
+
* out-of-band. signDetached is the interop surface; the wire is
|
|
20
|
+
* exactly what the underlying primitive emits, no leviathan-specific
|
|
21
|
+
* framing.
|
|
22
|
+
*/
|
|
23
|
+
static signDetached(suite: SignatureSuite, sk: Uint8Array, msg: Uint8Array, ctx: Uint8Array): Uint8Array;
|
|
24
|
+
/**
|
|
25
|
+
* Detached verify. Returns boolean; does NOT throw on signature failure.
|
|
26
|
+
* Contract violations in the suite (wrong-size key, ctx too long) still
|
|
27
|
+
* throw SigningError per the suite contract.
|
|
28
|
+
*/
|
|
29
|
+
static verifyDetached(suite: SignatureSuite, pk: Uint8Array, msg: Uint8Array, sig: Uint8Array, ctx: Uint8Array): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Introspect a blob without verifying. Validates structural shape only
|
|
32
|
+
* (ctx_len and payload_len in range); does NOT call suite.verify and
|
|
33
|
+
* does NOT compare ctx. Returns offsets the caller can use to extract
|
|
34
|
+
* wire ctx, payload, and sig themselves.
|
|
35
|
+
*
|
|
36
|
+
* @throws SigningError('sig-blob-too-short') blob cannot fit the wire shape.
|
|
37
|
+
*/
|
|
38
|
+
static peek(blob: Uint8Array, suite: SignatureSuite): {
|
|
39
|
+
suiteByte: number;
|
|
40
|
+
ctx: Uint8Array;
|
|
41
|
+
payloadOffset: number;
|
|
42
|
+
payloadLength: number;
|
|
43
|
+
sigOffset: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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/sign/envelope.ts
|
|
23
|
+
//
|
|
24
|
+
// Sign, single-shot signing/verification using the attached envelope wire
|
|
25
|
+
// format. Mirrors the static-only-class pattern from stream/seal.ts.
|
|
26
|
+
//
|
|
27
|
+
// Wire format see: docs/signing.md#attached-envelope.
|
|
28
|
+
import { constantTimeEqual } from '../utils.js';
|
|
29
|
+
import { SigningError } from '../errors.js';
|
|
30
|
+
// 1 suite_byte + 1 ctx_len + 4 payload_len. Smallest legal blob carries
|
|
31
|
+
// at least these six header bytes plus the suite's sig.
|
|
32
|
+
const ENVELOPE_HEADER_FIXED = 6;
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
34
|
+
export class Sign {
|
|
35
|
+
/**
|
|
36
|
+
* Single-shot sign. Returns the attached envelope blob.
|
|
37
|
+
*/
|
|
38
|
+
static sign(suite, sk, msg, ctx) {
|
|
39
|
+
const sig = suite.sign(sk, msg, ctx);
|
|
40
|
+
return assembleBlob(suite.formatEnum, ctx, msg, sig);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Single-shot verify. Returns the extracted payload on success.
|
|
44
|
+
*
|
|
45
|
+
* @throws SigningError('sig-blob-too-short') blob cannot fit the wire shape.
|
|
46
|
+
* @throws SigningError('sig-suite-mismatch') wire suite_byte mismatch.
|
|
47
|
+
* @throws SigningError('sig-ctx-mismatch') caller ctx != wire ctx.
|
|
48
|
+
* @throws SigningError('verify-failed') suite.verify returned false.
|
|
49
|
+
*/
|
|
50
|
+
static verify(suite, pk, blob, ctx) {
|
|
51
|
+
const { suiteByte, ctxLen, payloadLen, payloadOffset, sigOffset } = parseHeader(blob, suite);
|
|
52
|
+
if (suiteByte !== suite.formatEnum)
|
|
53
|
+
throw new SigningError('sig-suite-mismatch', `wire suite 0x${suiteByte.toString(16)} != suite.formatEnum 0x${suite.formatEnum.toString(16)}`);
|
|
54
|
+
const wireCtx = blob.subarray(2, 2 + ctxLen);
|
|
55
|
+
if (!constantTimeEqual(wireCtx, ctx))
|
|
56
|
+
throw new SigningError('sig-ctx-mismatch');
|
|
57
|
+
const payload = blob.subarray(payloadOffset, payloadOffset + payloadLen);
|
|
58
|
+
const sig = blob.subarray(sigOffset, blob.length);
|
|
59
|
+
if (!suite.verify(pk, payload, sig, wireCtx))
|
|
60
|
+
throw new SigningError('verify-failed');
|
|
61
|
+
return payload;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Detached sign. Returns just the raw signature bytes (no envelope).
|
|
65
|
+
* Caller is responsible for transmitting (suite, msg, sig, ctx)
|
|
66
|
+
* out-of-band. signDetached is the interop surface; the wire is
|
|
67
|
+
* exactly what the underlying primitive emits, no leviathan-specific
|
|
68
|
+
* framing.
|
|
69
|
+
*/
|
|
70
|
+
static signDetached(suite, sk, msg, ctx) {
|
|
71
|
+
return suite.sign(sk, msg, ctx);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Detached verify. Returns boolean; does NOT throw on signature failure.
|
|
75
|
+
* Contract violations in the suite (wrong-size key, ctx too long) still
|
|
76
|
+
* throw SigningError per the suite contract.
|
|
77
|
+
*/
|
|
78
|
+
static verifyDetached(suite, pk, msg, sig, ctx) {
|
|
79
|
+
return suite.verify(pk, msg, sig, ctx);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Introspect a blob without verifying. Validates structural shape only
|
|
83
|
+
* (ctx_len and payload_len in range); does NOT call suite.verify and
|
|
84
|
+
* does NOT compare ctx. Returns offsets the caller can use to extract
|
|
85
|
+
* wire ctx, payload, and sig themselves.
|
|
86
|
+
*
|
|
87
|
+
* @throws SigningError('sig-blob-too-short') blob cannot fit the wire shape.
|
|
88
|
+
*/
|
|
89
|
+
static peek(blob, suite) {
|
|
90
|
+
const { suiteByte, ctxLen, payloadLen, payloadOffset, sigOffset } = parseHeader(blob, suite);
|
|
91
|
+
return {
|
|
92
|
+
suiteByte,
|
|
93
|
+
ctx: blob.subarray(2, 2 + ctxLen),
|
|
94
|
+
payloadOffset,
|
|
95
|
+
payloadLength: payloadLen,
|
|
96
|
+
sigOffset,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Parse the wire header. Throws SigningError('sig-blob-too-short')
|
|
102
|
+
* on every wire-shape overflow; .message carries the specifics.
|
|
103
|
+
*/
|
|
104
|
+
function parseHeader(blob, suite) {
|
|
105
|
+
if (blob.length < ENVELOPE_HEADER_FIXED)
|
|
106
|
+
throw new SigningError('sig-blob-too-short', `blob length ${blob.length} < min ${ENVELOPE_HEADER_FIXED} (suite + ctx_len + payload_len header)`);
|
|
107
|
+
const suiteByte = blob[0];
|
|
108
|
+
const ctxLen = blob[1];
|
|
109
|
+
const payloadLenOffset = 2 + ctxLen;
|
|
110
|
+
const payloadOffset = payloadLenOffset + 4;
|
|
111
|
+
if (blob.length < payloadOffset)
|
|
112
|
+
throw new SigningError('sig-blob-too-short', `blob length ${blob.length} cannot fit ctx (${ctxLen} bytes) + payload_len header`);
|
|
113
|
+
const payloadLen = readU32BE(blob, payloadLenOffset);
|
|
114
|
+
const sigOffset = payloadOffset + payloadLen;
|
|
115
|
+
if (sigOffset > blob.length)
|
|
116
|
+
throw new SigningError('sig-blob-too-short', `payload_len ${payloadLen} pushes payload past blob end (blob length ${blob.length})`);
|
|
117
|
+
if (sigOffset + suite.sigMaxSize < blob.length)
|
|
118
|
+
throw new SigningError('sig-blob-too-short', `trailing sig ${blob.length - sigOffset} > suite.sigMaxSize ${suite.sigMaxSize}`);
|
|
119
|
+
return { suiteByte, ctxLen, payloadLen, payloadOffset, sigOffset };
|
|
120
|
+
}
|
|
121
|
+
function assembleBlob(suiteByte, ctx, payload, sig) {
|
|
122
|
+
if (ctx.length > 255)
|
|
123
|
+
throw new SigningError('sig-ctx-too-long', `ctx length ${ctx.length} > 255 (wire format ctx_len is u8)`);
|
|
124
|
+
if (payload.length > 0xFFFFFFFF)
|
|
125
|
+
throw new SigningError('sig-malformed-input', `payload length ${payload.length} > 2^32 - 1 (wire format payload_len is u32)`);
|
|
126
|
+
const out = new Uint8Array(2 + ctx.length + 4 + payload.length + sig.length);
|
|
127
|
+
let pos = 0;
|
|
128
|
+
out[pos++] = suiteByte;
|
|
129
|
+
out[pos++] = ctx.length;
|
|
130
|
+
out.set(ctx, pos);
|
|
131
|
+
pos += ctx.length;
|
|
132
|
+
writeU32BE(out, pos, payload.length);
|
|
133
|
+
pos += 4;
|
|
134
|
+
out.set(payload, pos);
|
|
135
|
+
pos += payload.length;
|
|
136
|
+
out.set(sig, pos);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function readU32BE(buf, off) {
|
|
140
|
+
// Multiply by 2^24 instead of <<24 so the high byte does not
|
|
141
|
+
// sign-extend into a negative JS number, which would propagate
|
|
142
|
+
// through the subsequent arithmetic and silently bypass the
|
|
143
|
+
// payload-overflow check.
|
|
144
|
+
return (buf[off] * 0x1000000
|
|
145
|
+
+ ((buf[off + 1] << 16) | (buf[off + 2] << 8) | buf[off + 3]));
|
|
146
|
+
}
|
|
147
|
+
function writeU32BE(buf, off, value) {
|
|
148
|
+
buf[off] = (value >>> 24) & 0xFF;
|
|
149
|
+
buf[off + 1] = (value >>> 16) & 0xFF;
|
|
150
|
+
buf[off + 2] = (value >>> 8) & 0xFF;
|
|
151
|
+
buf[off + 3] = value & 0xFF;
|
|
152
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PrehashAlgorithm } from './types.js';
|
|
2
|
+
export interface RunningHash {
|
|
3
|
+
update(chunk: Uint8Array): void;
|
|
4
|
+
finalize(): Uint8Array;
|
|
5
|
+
dispose(): void;
|
|
6
|
+
}
|
|
7
|
+
export declare function sha256OneShot(msg: Uint8Array): Uint8Array;
|
|
8
|
+
export declare function sha512OneShot(msg: Uint8Array): Uint8Array;
|
|
9
|
+
export declare function createRunningHash(algo: PrehashAlgorithm): RunningHash;
|
|
@@ -0,0 +1,132 @@
|
|
|
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/sign/hasher.ts
|
|
23
|
+
//
|
|
24
|
+
// Internal running-hash abstraction keyed on PrehashAlgorithm, used by
|
|
25
|
+
// SignStream and VerifyStream. SHAKE output sizes track FIPS 204 §5.4.1
|
|
26
|
+
// (HashML-DSA) and FIPS 205 §10.2.2 (HashSLH-DSA).
|
|
27
|
+
import { SHA3_256Stream, SHA3_512Stream, SHAKE128Stream, SHAKE256Stream, } from '../sha3/index.js';
|
|
28
|
+
import { SHA256, SHA512 } from '../sha2/index.js';
|
|
29
|
+
export function sha256OneShot(msg) {
|
|
30
|
+
const h = new SHA256();
|
|
31
|
+
try {
|
|
32
|
+
return h.hash(msg);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
h.dispose();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function sha512OneShot(msg) {
|
|
39
|
+
const h = new SHA512();
|
|
40
|
+
try {
|
|
41
|
+
return h.hash(msg);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
h.dispose();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function sha256Buffered() {
|
|
48
|
+
let chunks = [];
|
|
49
|
+
let total = 0;
|
|
50
|
+
return {
|
|
51
|
+
update(chunk) {
|
|
52
|
+
const copy = new Uint8Array(chunk.length);
|
|
53
|
+
copy.set(chunk);
|
|
54
|
+
chunks.push(copy);
|
|
55
|
+
total += copy.length;
|
|
56
|
+
},
|
|
57
|
+
finalize() {
|
|
58
|
+
const buf = new Uint8Array(total);
|
|
59
|
+
let off = 0;
|
|
60
|
+
for (const c of chunks) {
|
|
61
|
+
buf.set(c, off);
|
|
62
|
+
off += c.length;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return sha256OneShot(buf);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
buf.fill(0);
|
|
69
|
+
for (const c of chunks)
|
|
70
|
+
c.fill(0);
|
|
71
|
+
chunks = [];
|
|
72
|
+
total = 0;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
dispose() {
|
|
76
|
+
for (const c of chunks)
|
|
77
|
+
c.fill(0);
|
|
78
|
+
chunks = [];
|
|
79
|
+
total = 0;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function sha512Buffered() {
|
|
84
|
+
let chunks = [];
|
|
85
|
+
let total = 0;
|
|
86
|
+
return {
|
|
87
|
+
update(chunk) {
|
|
88
|
+
const copy = new Uint8Array(chunk.length);
|
|
89
|
+
copy.set(chunk);
|
|
90
|
+
chunks.push(copy);
|
|
91
|
+
total += copy.length;
|
|
92
|
+
},
|
|
93
|
+
finalize() {
|
|
94
|
+
const buf = new Uint8Array(total);
|
|
95
|
+
let off = 0;
|
|
96
|
+
for (const c of chunks) {
|
|
97
|
+
buf.set(c, off);
|
|
98
|
+
off += c.length;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return sha512OneShot(buf);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
buf.fill(0);
|
|
105
|
+
for (const c of chunks)
|
|
106
|
+
c.fill(0);
|
|
107
|
+
chunks = [];
|
|
108
|
+
total = 0;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
dispose() {
|
|
112
|
+
for (const c of chunks)
|
|
113
|
+
c.fill(0);
|
|
114
|
+
chunks = [];
|
|
115
|
+
total = 0;
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function createRunningHash(algo) {
|
|
120
|
+
switch (algo) {
|
|
121
|
+
case 'sha3-256': return new SHA3_256Stream();
|
|
122
|
+
case 'sha3-512': return new SHA3_512Stream();
|
|
123
|
+
case 'shake-128': return new SHAKE128Stream(32);
|
|
124
|
+
case 'shake-256': return new SHAKE256Stream(64);
|
|
125
|
+
case 'sha-512': return sha512Buffered();
|
|
126
|
+
case 'sha-256': return sha256Buffered();
|
|
127
|
+
default: {
|
|
128
|
+
const _exhaustive = algo;
|
|
129
|
+
throw new Error(`leviathan-crypto: unknown prehash algorithm ${_exhaustive}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { SignatureSuite, StreamableSignatureSuite, PrehashAlgorithm, } from './types.js';
|
|
2
|
+
export { buildEffectiveCtx, prehashAlgoToMldsa, USER_CTX_MAX, CTX_DOMAIN_MAX, } from './ctx.js';
|
|
3
|
+
export { Sign } from './envelope.js';
|
|
4
|
+
export { SignStream } from './sign-stream.js';
|
|
5
|
+
export { VerifyStream } from './verify-stream.js';
|
|
6
|
+
export { Ed25519Suite, Ed25519PreHashSuite, } from './suites/ed25519.js';
|
|
7
|
+
export { EcdsaP256Suite } from './suites/ecdsa-p256.js';
|
|
8
|
+
export { MlDsa44Suite, MlDsa65Suite, MlDsa87Suite, MlDsa44PreHashSuite, MlDsa65PreHashSuite, MlDsa87PreHashSuite, } from './suites/mldsa.js';
|
|
9
|
+
export { SlhDsa128fSuite, SlhDsa192fSuite, SlhDsa256fSuite, SlhDsa128fPreHashSuite, SlhDsa192fPreHashSuite, SlhDsa256fPreHashSuite, } from './suites/slhdsa.js';
|
|
10
|
+
export { MlDsa44SlhDsa128fSuite, MlDsa65SlhDsa192fSuite, MlDsa87SlhDsa256fSuite, } from './suites/hybrid-pq.js';
|
|
11
|
+
export { MlDsa44Ed25519Suite, MlDsa65Ed25519Suite, MlDsa44EcdsaP256Suite, MlDsa65EcdsaP256Suite, } from './suites/hybrid-classical.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
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/sign/index.ts
|
|
23
|
+
//
|
|
24
|
+
// Public barrel for the v3 sign module.
|
|
25
|
+
export { buildEffectiveCtx, prehashAlgoToMldsa, USER_CTX_MAX, CTX_DOMAIN_MAX, } from './ctx.js';
|
|
26
|
+
export { Sign } from './envelope.js';
|
|
27
|
+
export { SignStream } from './sign-stream.js';
|
|
28
|
+
export { VerifyStream } from './verify-stream.js';
|
|
29
|
+
export { Ed25519Suite, Ed25519PreHashSuite, } from './suites/ed25519.js';
|
|
30
|
+
export { EcdsaP256Suite } from './suites/ecdsa-p256.js';
|
|
31
|
+
export { MlDsa44Suite, MlDsa65Suite, MlDsa87Suite, MlDsa44PreHashSuite, MlDsa65PreHashSuite, MlDsa87PreHashSuite, } from './suites/mldsa.js';
|
|
32
|
+
export { SlhDsa128fSuite, SlhDsa192fSuite, SlhDsa256fSuite, SlhDsa128fPreHashSuite, SlhDsa192fPreHashSuite, SlhDsa256fPreHashSuite, } from './suites/slhdsa.js';
|
|
33
|
+
export { MlDsa44SlhDsa128fSuite, MlDsa65SlhDsa192fSuite, MlDsa87SlhDsa256fSuite, } from './suites/hybrid-pq.js';
|
|
34
|
+
export { MlDsa44Ed25519Suite, MlDsa65Ed25519Suite, MlDsa44EcdsaP256Suite, MlDsa65EcdsaP256Suite, } from './suites/hybrid-classical.js';
|