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,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-128/192/256 in CTR mode.
|
|
3
|
+
*
|
|
4
|
+
* **WARNING: CTR mode is unauthenticated.** An attacker can flip ciphertext
|
|
5
|
+
* bits without detection. Always pair with HMAC-SHA256 (Encrypt-then-MAC)
|
|
6
|
+
* or use an authenticated cipher (`AESGCM`, `AESGCMSIV`, or `Seal` with
|
|
7
|
+
* `AESGCMSIVCipher` / `SerpentCipher` / `XChaCha20Cipher`) instead.
|
|
8
|
+
*
|
|
9
|
+
* The constructor requires `{ dangerUnauthenticated: true }` so callers
|
|
10
|
+
* cannot reach the unauthenticated path by accident, same gate as
|
|
11
|
+
* `AESCbc` and `SerpentCtr`.
|
|
12
|
+
*
|
|
13
|
+
* The counter is 128-bit big-endian (SP 800-38A Appendix B.1 / §F.5).
|
|
14
|
+
*
|
|
15
|
+
* Stateful, the counter advances across `encrypt`/`decrypt` calls. Reset
|
|
16
|
+
* with `setNonce()` before each new message. Holds exclusive access to the
|
|
17
|
+
* `aes` WASM module from construction until `dispose()`.
|
|
18
|
+
*/
|
|
19
|
+
export declare class AESCtr {
|
|
20
|
+
private readonly x;
|
|
21
|
+
private _tok;
|
|
22
|
+
constructor(opts?: {
|
|
23
|
+
dangerUnauthenticated: true;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Expand `key` into the WASM key schedule. Must be called before
|
|
27
|
+
* `setNonce` / `encrypt` / `decrypt`.
|
|
28
|
+
* @param key 16, 24, or 32 bytes (AES-128 / 192 / 256)
|
|
29
|
+
*/
|
|
30
|
+
loadKey(key: Uint8Array): void;
|
|
31
|
+
/**
|
|
32
|
+
* Set the 128-bit initial counter block (the full IC, not a separate
|
|
33
|
+
* nonce/counter split). Resets the working counter so subsequent
|
|
34
|
+
* encrypt/decrypt calls start at this value.
|
|
35
|
+
* @param nonce 16 bytes, must be unique per (key, message)
|
|
36
|
+
*/
|
|
37
|
+
setNonce(nonce: Uint8Array): void;
|
|
38
|
+
/**
|
|
39
|
+
* XOR `plaintext` with AES CTR keystream. The counter advances by
|
|
40
|
+
* ceil(plaintext.length / 16) blocks; counter state persists across
|
|
41
|
+
* calls until `setNonce()` resets it.
|
|
42
|
+
* @param plaintext any length; internally chunked to WASM CHUNK_SIZE
|
|
43
|
+
* @returns ciphertext of the same length
|
|
44
|
+
*/
|
|
45
|
+
encrypt(plaintext: Uint8Array): Uint8Array;
|
|
46
|
+
/** Alias for `encrypt`, CTR mode is symmetric. */
|
|
47
|
+
decrypt(ciphertext: Uint8Array): Uint8Array;
|
|
48
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
49
|
+
dispose(): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
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/aes/aes-ctr.ts
|
|
23
|
+
//
|
|
24
|
+
// AESCtr, AES-128/192/256 in CTR mode, stateful TS wrapper.
|
|
25
|
+
// SP 800-38A §6.5 (mode), Appendix B.1 (counter increment).
|
|
26
|
+
// Counter direction: 128-bit big-endian, matching the SP 800-38A §F.5
|
|
27
|
+
// worked examples and the canonical AES CTR convention. Configured in
|
|
28
|
+
// the underlying WASM (`src/asm/aes/ctr.ts`).
|
|
29
|
+
import { getInstance, _acquireModule, _releaseModule } from '../init.js';
|
|
30
|
+
/** Returns the raw AES WASM export object. @internal */
|
|
31
|
+
function getExports() {
|
|
32
|
+
return getInstance('aes').exports;
|
|
33
|
+
}
|
|
34
|
+
// ── AESCtr ──────────────────────────────────────────────────────────────────
|
|
35
|
+
/**
|
|
36
|
+
* AES-128/192/256 in CTR mode.
|
|
37
|
+
*
|
|
38
|
+
* **WARNING: CTR mode is unauthenticated.** An attacker can flip ciphertext
|
|
39
|
+
* bits without detection. Always pair with HMAC-SHA256 (Encrypt-then-MAC)
|
|
40
|
+
* or use an authenticated cipher (`AESGCM`, `AESGCMSIV`, or `Seal` with
|
|
41
|
+
* `AESGCMSIVCipher` / `SerpentCipher` / `XChaCha20Cipher`) instead.
|
|
42
|
+
*
|
|
43
|
+
* The constructor requires `{ dangerUnauthenticated: true }` so callers
|
|
44
|
+
* cannot reach the unauthenticated path by accident, same gate as
|
|
45
|
+
* `AESCbc` and `SerpentCtr`.
|
|
46
|
+
*
|
|
47
|
+
* The counter is 128-bit big-endian (SP 800-38A Appendix B.1 / §F.5).
|
|
48
|
+
*
|
|
49
|
+
* Stateful, the counter advances across `encrypt`/`decrypt` calls. Reset
|
|
50
|
+
* with `setNonce()` before each new message. Holds exclusive access to the
|
|
51
|
+
* `aes` WASM module from construction until `dispose()`.
|
|
52
|
+
*/
|
|
53
|
+
export class AESCtr {
|
|
54
|
+
x;
|
|
55
|
+
_tok;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
if (!opts?.dangerUnauthenticated) {
|
|
58
|
+
throw new Error('leviathan-crypto: AESCtr is unauthenticated, use Seal with AESGCMSIVCipher, SerpentCipher, or XChaCha20Cipher instead. ' +
|
|
59
|
+
'To use AESCtr directly, pass { dangerUnauthenticated: true }.');
|
|
60
|
+
}
|
|
61
|
+
this.x = getExports();
|
|
62
|
+
this._tok = _acquireModule('aes');
|
|
63
|
+
}
|
|
64
|
+
/** View over WASM linear memory. Rebind on every access, memory can be detached after grow. @internal */
|
|
65
|
+
get mem() {
|
|
66
|
+
return new Uint8Array(this.x.memory.buffer);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Expand `key` into the WASM key schedule. Must be called before
|
|
70
|
+
* `setNonce` / `encrypt` / `decrypt`.
|
|
71
|
+
* @param key 16, 24, or 32 bytes (AES-128 / 192 / 256)
|
|
72
|
+
*/
|
|
73
|
+
loadKey(key) {
|
|
74
|
+
if (this._tok === undefined)
|
|
75
|
+
throw new Error('AESCtr: instance has been disposed');
|
|
76
|
+
if (key.length !== 16 && key.length !== 24 && key.length !== 32)
|
|
77
|
+
throw new RangeError(`AES key must be 16, 24, or 32 bytes (got ${key.length})`);
|
|
78
|
+
this.mem.set(key, this.x.getKeyOffset());
|
|
79
|
+
if (this.x.loadKey(key.length) !== 0) {
|
|
80
|
+
this.x.wipeBuffers();
|
|
81
|
+
throw new Error('loadKey failed');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Set the 128-bit initial counter block (the full IC, not a separate
|
|
86
|
+
* nonce/counter split). Resets the working counter so subsequent
|
|
87
|
+
* encrypt/decrypt calls start at this value.
|
|
88
|
+
* @param nonce 16 bytes, must be unique per (key, message)
|
|
89
|
+
*/
|
|
90
|
+
setNonce(nonce) {
|
|
91
|
+
if (this._tok === undefined)
|
|
92
|
+
throw new Error('AESCtr: instance has been disposed');
|
|
93
|
+
if (nonce.length !== 16)
|
|
94
|
+
throw new RangeError(`AES CTR nonce must be 16 bytes (got ${nonce.length})`);
|
|
95
|
+
this.mem.set(nonce, this.x.getNonceOffset());
|
|
96
|
+
this.x.resetCounter();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* XOR `plaintext` with AES CTR keystream. The counter advances by
|
|
100
|
+
* ceil(plaintext.length / 16) blocks; counter state persists across
|
|
101
|
+
* calls until `setNonce()` resets it.
|
|
102
|
+
* @param plaintext any length; internally chunked to WASM CHUNK_SIZE
|
|
103
|
+
* @returns ciphertext of the same length
|
|
104
|
+
*/
|
|
105
|
+
encrypt(plaintext) {
|
|
106
|
+
if (this._tok === undefined)
|
|
107
|
+
throw new Error('AESCtr: instance has been disposed');
|
|
108
|
+
const output = new Uint8Array(plaintext.length);
|
|
109
|
+
if (plaintext.length === 0)
|
|
110
|
+
return output;
|
|
111
|
+
const ptOff = this.x.getChunkPtOffset();
|
|
112
|
+
const ctOff = this.x.getChunkCtOffset();
|
|
113
|
+
const maxChunk = this.x.getChunkSize();
|
|
114
|
+
for (let off = 0; off < plaintext.length; off += maxChunk) {
|
|
115
|
+
const chunk = plaintext.subarray(off, Math.min(off + maxChunk, plaintext.length));
|
|
116
|
+
this.mem.set(chunk, ptOff);
|
|
117
|
+
const ret = this.x.encryptChunk_simd(chunk.length);
|
|
118
|
+
if (ret < 0)
|
|
119
|
+
throw new RangeError(`encryptChunk_simd rejected len=${chunk.length}` +
|
|
120
|
+
` (WASM CHUNK_SIZE=${this.x.getChunkSize()})`);
|
|
121
|
+
output.set(new Uint8Array(this.x.memory.buffer).subarray(ctOff, ctOff + chunk.length), off);
|
|
122
|
+
}
|
|
123
|
+
return output;
|
|
124
|
+
}
|
|
125
|
+
/** Alias for `encrypt`, CTR mode is symmetric. */
|
|
126
|
+
decrypt(ciphertext) {
|
|
127
|
+
return this.encrypt(ciphertext);
|
|
128
|
+
}
|
|
129
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
130
|
+
dispose() {
|
|
131
|
+
if (this._tok === undefined)
|
|
132
|
+
return;
|
|
133
|
+
try {
|
|
134
|
+
this.x.wipeBuffers();
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
_releaseModule('aes', this._tok);
|
|
138
|
+
this._tok = undefined;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-128-GCM-SIV / AES-256-GCM-SIV (RFC 8452). Nonce-misuse-resistant
|
|
3
|
+
* authenticated AEAD with a 128-bit tag. AES-192 keys are rejected
|
|
4
|
+
* (RFC 8452 §6, no AES-192-GCM-SIV variant exists).
|
|
5
|
+
*
|
|
6
|
+
* Single-shot only: each `seal` / `open` call processes one complete
|
|
7
|
+
* message bounded by 64 KiB of plaintext. Larger messages are out of
|
|
8
|
+
* scope for this primitive; a future streaming variant will lift the
|
|
9
|
+
* cap via the seal/sealstream layer.
|
|
10
|
+
*
|
|
11
|
+
* `seal(nonce, plaintext, aad?)` returns `ciphertext || tag` (length
|
|
12
|
+
* pt.length + 16). `open(nonce, sealed, aad?)` verifies the tag and
|
|
13
|
+
* returns the plaintext; throws `AuthenticationError('siv')` on any
|
|
14
|
+
* verification failure.
|
|
15
|
+
*
|
|
16
|
+
* Atomic, does not hold exclusive access between calls. `dispose()`
|
|
17
|
+
* wipes the stored key from the JS-side cache.
|
|
18
|
+
*/
|
|
19
|
+
export declare class AESGCMSIV {
|
|
20
|
+
private readonly _key;
|
|
21
|
+
private _disposed;
|
|
22
|
+
/**
|
|
23
|
+
* @param key 16 bytes (AES-128-GCM-SIV) or 32 bytes (AES-256-GCM-SIV).
|
|
24
|
+
* 24-byte keys are rejected, RFC 8452 §6 does not define
|
|
25
|
+
* an AES-192-GCM-SIV variant.
|
|
26
|
+
*/
|
|
27
|
+
constructor(key: Uint8Array);
|
|
28
|
+
/**
|
|
29
|
+
* Authenticated encryption.
|
|
30
|
+
*
|
|
31
|
+
* @param nonce exactly 12 bytes (RFC 8452 §6 fixes nonce length)
|
|
32
|
+
* @param plaintext any length up to 64 KiB; may be empty
|
|
33
|
+
* @param aad any length up to 64 KiB; may be empty
|
|
34
|
+
* @returns ciphertext concatenated with the 128-bit tag
|
|
35
|
+
* (length = plaintext.length + 16)
|
|
36
|
+
*
|
|
37
|
+
* @throws RangeError if any input length violates the spec or the
|
|
38
|
+
* buffer-bounded API.
|
|
39
|
+
*/
|
|
40
|
+
seal(nonce: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
41
|
+
/**
|
|
42
|
+
* Authenticated decryption. `sealed` is the output of a matching
|
|
43
|
+
* `seal(nonce, plaintext, aad)` call.
|
|
44
|
+
*
|
|
45
|
+
* Verification routes through `constantTimeEqual` from
|
|
46
|
+
* `../utils.js` (the dedicated `ct` WASM module). On mismatch the
|
|
47
|
+
* WASM `sivWipeOnFail` helper zeroes the decrypted-but-
|
|
48
|
+
* unauthenticated plaintext at CHUNK_PT_OFFSET before this method
|
|
49
|
+
* throws, the bytes never become reachable from JS.
|
|
50
|
+
*
|
|
51
|
+
* @throws AuthenticationError('siv') if the tag fails to verify, or
|
|
52
|
+
* if `sealed` is too short, or any input length violates the
|
|
53
|
+
* spec.
|
|
54
|
+
*/
|
|
55
|
+
open(nonce: Uint8Array, sealed: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
56
|
+
/**
|
|
57
|
+
* Wipe the in-memory copy of the key. Idempotent. Subsequent calls
|
|
58
|
+
* to `seal` / `open` throw. WASM-side state is wiped at the end of
|
|
59
|
+
* every successful operation regardless of `dispose()`.
|
|
60
|
+
*/
|
|
61
|
+
dispose(): void;
|
|
62
|
+
private _mem;
|
|
63
|
+
private _assertAlive;
|
|
64
|
+
private _validate;
|
|
65
|
+
/** Push KGK + nonce + AAD into WASM memory. Common to seal/open. */
|
|
66
|
+
private _stage;
|
|
67
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
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/aes/aes-gcm-siv.ts
|
|
23
|
+
import { getInstance, _acquireModule, _releaseModule } from '../init.js';
|
|
24
|
+
import { constantTimeEqual, wipe } from '../utils.js';
|
|
25
|
+
import { AuthenticationError } from '../errors.js';
|
|
26
|
+
// RFC 8452 §6: K_LEN ∈ {16, 32}, AES-128-GCM-SIV or AES-256-GCM-SIV.
|
|
27
|
+
const KEY_LEN_128 = 16;
|
|
28
|
+
const KEY_LEN_256 = 32;
|
|
29
|
+
// RFC 8452 §6: nonce length is fixed at 96 bits (12 bytes).
|
|
30
|
+
const NONCE_LEN = 12;
|
|
31
|
+
// Single-shot bound: plaintext fits in CHUNK_PT (64 KiB). Larger inputs
|
|
32
|
+
// would need a streaming SIV API; not currently in scope.
|
|
33
|
+
const MAX_PT_BYTES = 65536;
|
|
34
|
+
// AAD is bounded by the dedicated AAD_BUFFER (64 KiB).
|
|
35
|
+
const MAX_AAD_BYTES = 65536;
|
|
36
|
+
// 16-byte authentication tag.
|
|
37
|
+
const TAG_LEN = 16;
|
|
38
|
+
/** Returns the raw AES WASM export object. @internal */
|
|
39
|
+
function getExports() {
|
|
40
|
+
return getInstance('aes').exports;
|
|
41
|
+
}
|
|
42
|
+
// ── AESGCMSIV ───────────────────────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* AES-128-GCM-SIV / AES-256-GCM-SIV (RFC 8452). Nonce-misuse-resistant
|
|
45
|
+
* authenticated AEAD with a 128-bit tag. AES-192 keys are rejected
|
|
46
|
+
* (RFC 8452 §6, no AES-192-GCM-SIV variant exists).
|
|
47
|
+
*
|
|
48
|
+
* Single-shot only: each `seal` / `open` call processes one complete
|
|
49
|
+
* message bounded by 64 KiB of plaintext. Larger messages are out of
|
|
50
|
+
* scope for this primitive; a future streaming variant will lift the
|
|
51
|
+
* cap via the seal/sealstream layer.
|
|
52
|
+
*
|
|
53
|
+
* `seal(nonce, plaintext, aad?)` returns `ciphertext || tag` (length
|
|
54
|
+
* pt.length + 16). `open(nonce, sealed, aad?)` verifies the tag and
|
|
55
|
+
* returns the plaintext; throws `AuthenticationError('siv')` on any
|
|
56
|
+
* verification failure.
|
|
57
|
+
*
|
|
58
|
+
* Atomic, does not hold exclusive access between calls. `dispose()`
|
|
59
|
+
* wipes the stored key from the JS-side cache.
|
|
60
|
+
*/
|
|
61
|
+
export class AESGCMSIV {
|
|
62
|
+
_key;
|
|
63
|
+
_disposed = false;
|
|
64
|
+
/**
|
|
65
|
+
* @param key 16 bytes (AES-128-GCM-SIV) or 32 bytes (AES-256-GCM-SIV).
|
|
66
|
+
* 24-byte keys are rejected, RFC 8452 §6 does not define
|
|
67
|
+
* an AES-192-GCM-SIV variant.
|
|
68
|
+
*/
|
|
69
|
+
constructor(key) {
|
|
70
|
+
if (key.length !== KEY_LEN_128 && key.length !== KEY_LEN_256) {
|
|
71
|
+
throw new RangeError(`AESGCMSIV key must be 16 or 32 bytes (got ${key.length}); `
|
|
72
|
+
+ 'AES-192-GCM-SIV is not defined by RFC 8452');
|
|
73
|
+
}
|
|
74
|
+
// Defensive copy so external mutation cannot change the live key.
|
|
75
|
+
this._key = new Uint8Array(key);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Authenticated encryption.
|
|
79
|
+
*
|
|
80
|
+
* @param nonce exactly 12 bytes (RFC 8452 §6 fixes nonce length)
|
|
81
|
+
* @param plaintext any length up to 64 KiB; may be empty
|
|
82
|
+
* @param aad any length up to 64 KiB; may be empty
|
|
83
|
+
* @returns ciphertext concatenated with the 128-bit tag
|
|
84
|
+
* (length = plaintext.length + 16)
|
|
85
|
+
*
|
|
86
|
+
* @throws RangeError if any input length violates the spec or the
|
|
87
|
+
* buffer-bounded API.
|
|
88
|
+
*/
|
|
89
|
+
seal(nonce, plaintext, aad = new Uint8Array(0)) {
|
|
90
|
+
this._assertAlive();
|
|
91
|
+
this._validate(nonce, plaintext.length, aad);
|
|
92
|
+
const x = getExports();
|
|
93
|
+
const tok = _acquireModule('aes');
|
|
94
|
+
try {
|
|
95
|
+
this._stage(x, nonce, aad);
|
|
96
|
+
this._mem(x).set(plaintext, x.getChunkPtOffset());
|
|
97
|
+
x.sivDeriveKeys(x.getNonceOffset());
|
|
98
|
+
x.sivSeal(aad.length, plaintext.length);
|
|
99
|
+
const ctOff = x.getChunkPtOffset();
|
|
100
|
+
const tagOff = x.getTagOffset();
|
|
101
|
+
const out = new Uint8Array(plaintext.length + TAG_LEN);
|
|
102
|
+
out.set(this._mem(x).subarray(ctOff, ctOff + plaintext.length), 0);
|
|
103
|
+
out.set(this._mem(x).subarray(tagOff, tagOff + TAG_LEN), plaintext.length);
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
x.wipeBuffers();
|
|
108
|
+
_releaseModule('aes', tok);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Authenticated decryption. `sealed` is the output of a matching
|
|
113
|
+
* `seal(nonce, plaintext, aad)` call.
|
|
114
|
+
*
|
|
115
|
+
* Verification routes through `constantTimeEqual` from
|
|
116
|
+
* `../utils.js` (the dedicated `ct` WASM module). On mismatch the
|
|
117
|
+
* WASM `sivWipeOnFail` helper zeroes the decrypted-but-
|
|
118
|
+
* unauthenticated plaintext at CHUNK_PT_OFFSET before this method
|
|
119
|
+
* throws, the bytes never become reachable from JS.
|
|
120
|
+
*
|
|
121
|
+
* @throws AuthenticationError('siv') if the tag fails to verify, or
|
|
122
|
+
* if `sealed` is too short, or any input length violates the
|
|
123
|
+
* spec.
|
|
124
|
+
*/
|
|
125
|
+
open(nonce, sealed, aad = new Uint8Array(0)) {
|
|
126
|
+
this._assertAlive();
|
|
127
|
+
if (sealed.length < TAG_LEN) {
|
|
128
|
+
throw new AuthenticationError('siv');
|
|
129
|
+
}
|
|
130
|
+
const ctLen = sealed.length - TAG_LEN;
|
|
131
|
+
try {
|
|
132
|
+
this._validate(nonce, ctLen, aad);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Same generic error so failure modes are indistinguishable.
|
|
136
|
+
throw new AuthenticationError('siv');
|
|
137
|
+
}
|
|
138
|
+
const ct = sealed.subarray(0, ctLen);
|
|
139
|
+
const providedTag = sealed.subarray(ctLen, sealed.length);
|
|
140
|
+
const x = getExports();
|
|
141
|
+
const tok = _acquireModule('aes');
|
|
142
|
+
try {
|
|
143
|
+
this._stage(x, nonce, aad);
|
|
144
|
+
this._mem(x).set(ct, x.getChunkCtOffset());
|
|
145
|
+
// Stage the provided tag at SIV_IC_OFFSET, sivOpen will
|
|
146
|
+
// read it from there as the input to the CTR initial counter.
|
|
147
|
+
this._mem(x).set(providedTag, x.getSivIcOffset());
|
|
148
|
+
x.sivDeriveKeys(x.getNonceOffset());
|
|
149
|
+
x.sivOpen(aad.length, ctLen);
|
|
150
|
+
// Read the EXPECTED tag computed by sivOpen from TAG_OFFSET.
|
|
151
|
+
// Use slice() rather than subarray() so the buffer survives
|
|
152
|
+
// any subsequent WASM memory growth or wipe.
|
|
153
|
+
const expectedTag = this._mem(x).slice(x.getTagOffset(), x.getTagOffset() + TAG_LEN);
|
|
154
|
+
// Defensive copy of providedTag for the constant-time compare,
|
|
155
|
+
// the input may be a view over a caller-controlled buffer.
|
|
156
|
+
const providedTagCopy = new Uint8Array(providedTag);
|
|
157
|
+
const ok = constantTimeEqual(expectedTag, providedTagCopy);
|
|
158
|
+
if (!ok) {
|
|
159
|
+
// Wipe before finally; covers JS-heap tag copies.
|
|
160
|
+
x.sivWipeOnFail();
|
|
161
|
+
wipe(expectedTag);
|
|
162
|
+
wipe(providedTagCopy);
|
|
163
|
+
throw new AuthenticationError('siv');
|
|
164
|
+
}
|
|
165
|
+
// Match, read PT before wiping. pt is a JS-heap slice copy.
|
|
166
|
+
const ptOff = x.getChunkPtOffset();
|
|
167
|
+
const pt = this._mem(x).slice(ptOff, ptOff + ctLen);
|
|
168
|
+
wipe(expectedTag);
|
|
169
|
+
wipe(providedTagCopy);
|
|
170
|
+
return pt;
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
x.wipeBuffers();
|
|
174
|
+
_releaseModule('aes', tok);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Wipe the in-memory copy of the key. Idempotent. Subsequent calls
|
|
179
|
+
* to `seal` / `open` throw. WASM-side state is wiped at the end of
|
|
180
|
+
* every successful operation regardless of `dispose()`.
|
|
181
|
+
*/
|
|
182
|
+
dispose() {
|
|
183
|
+
if (this._disposed)
|
|
184
|
+
return;
|
|
185
|
+
this._key.fill(0);
|
|
186
|
+
this._disposed = true;
|
|
187
|
+
}
|
|
188
|
+
// ── Internal helpers ───────────────────────────────────────────────────
|
|
189
|
+
_mem(x) {
|
|
190
|
+
return new Uint8Array(x.memory.buffer);
|
|
191
|
+
}
|
|
192
|
+
_assertAlive() {
|
|
193
|
+
if (this._disposed)
|
|
194
|
+
throw new Error('AESGCMSIV: instance has been disposed');
|
|
195
|
+
}
|
|
196
|
+
_validate(nonce, dataLen, aad) {
|
|
197
|
+
if (nonce.length !== NONCE_LEN)
|
|
198
|
+
throw new RangeError(`AESGCMSIV nonce must be ${NONCE_LEN} bytes (got ${nonce.length})`);
|
|
199
|
+
if (dataLen > MAX_PT_BYTES)
|
|
200
|
+
throw new RangeError(`AESGCMSIV plaintext must be ≤ ${MAX_PT_BYTES} bytes (got ${dataLen})`);
|
|
201
|
+
if (aad.length > MAX_AAD_BYTES)
|
|
202
|
+
throw new RangeError(`AESGCMSIV AAD must be ≤ ${MAX_AAD_BYTES} bytes (got ${aad.length})`);
|
|
203
|
+
}
|
|
204
|
+
/** Push KGK + nonce + AAD into WASM memory. Common to seal/open. */
|
|
205
|
+
_stage(x, nonce, aad) {
|
|
206
|
+
const mem = this._mem(x);
|
|
207
|
+
mem.set(this._key, x.getKeyOffset());
|
|
208
|
+
if (x.loadKey(this._key.length) !== 0) {
|
|
209
|
+
x.wipeBuffers();
|
|
210
|
+
throw new Error('AESGCMSIV: loadKey failed');
|
|
211
|
+
}
|
|
212
|
+
mem.set(nonce, x.getNonceOffset());
|
|
213
|
+
if (aad.length > 0) {
|
|
214
|
+
mem.set(aad, x.getAadOffset());
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-128/192/256 in GCM mode (SP 800-38D §7). Authenticated AEAD with
|
|
3
|
+
* 128-bit tag. Tag length is fixed; shorter tags (32/64/96/104/112/120)
|
|
4
|
+
* are out of scope for this version.
|
|
5
|
+
*
|
|
6
|
+
* `seal(key, iv, aad, pt)` returns `ciphertext || tag` (length pt.length + 16).
|
|
7
|
+
* `open(key, iv, aad, sealed)` verifies the tag and returns the plaintext;
|
|
8
|
+
* throws `RangeError('authentication failed')` on any verification failure
|
|
9
|
+
* (the same generic error as a tag mismatch, no detail leak).
|
|
10
|
+
*
|
|
11
|
+
* Holds exclusive access to the `aes` WASM module from construction until
|
|
12
|
+
* `dispose()`. Constructing a second AES-using class while this instance is
|
|
13
|
+
* live throws. Always dispose when done so key material is wiped.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AESGCM {
|
|
16
|
+
private readonly x;
|
|
17
|
+
private _tok;
|
|
18
|
+
constructor();
|
|
19
|
+
/**
|
|
20
|
+
* Authenticated encryption.
|
|
21
|
+
*
|
|
22
|
+
* @param key 16, 24, or 32 bytes (AES-128 / 192 / 256)
|
|
23
|
+
* @param iv 1+ bytes; 12-byte (96-bit) IV is the recommended fast path
|
|
24
|
+
* @param aad any length up to 64 KiB; may be empty
|
|
25
|
+
* @param pt any length up to 2^36 - 32 bytes; may be empty
|
|
26
|
+
* @returns ciphertext concatenated with the 128-bit tag
|
|
27
|
+
* (length = pt.length + 16)
|
|
28
|
+
*
|
|
29
|
+
* @throws RangeError if key/iv/aad/pt lengths violate the spec or the
|
|
30
|
+
* buffer-bounded API.
|
|
31
|
+
*/
|
|
32
|
+
seal(key: Uint8Array, iv: Uint8Array, aad: Uint8Array, pt: Uint8Array): Uint8Array;
|
|
33
|
+
/**
|
|
34
|
+
* Authenticated decryption.
|
|
35
|
+
*
|
|
36
|
+
* Performs verify-before-decrypt (SP 800-38D §7.2 permits the tag check
|
|
37
|
+
* to precede plaintext computation): the entire ciphertext is absorbed
|
|
38
|
+
* into GHASH, the tag is computed and constant-time-compared with the
|
|
39
|
+
* received tag, and only then is the ciphertext decrypted to plaintext.
|
|
40
|
+
* This avoids leaking decrypted bytes to higher layers when the tag
|
|
41
|
+
* fails to verify.
|
|
42
|
+
*
|
|
43
|
+
* @param key same constraints as `seal`
|
|
44
|
+
* @param iv same iv used during the matching `seal` call
|
|
45
|
+
* @param aad same aad used during the matching `seal` call
|
|
46
|
+
* @param sealed output of a previous `seal` call (ciphertext || tag)
|
|
47
|
+
* @returns plaintext (length = sealed.length - 16)
|
|
48
|
+
*
|
|
49
|
+
* @throws RangeError('authentication failed') if the tag fails to
|
|
50
|
+
* verify, or if the sealed input is too short, or any input
|
|
51
|
+
* length violates the spec. The same generic error covers all
|
|
52
|
+
* failure modes, no detail is leaked about which check failed.
|
|
53
|
+
*/
|
|
54
|
+
open(key: Uint8Array, iv: Uint8Array, aad: Uint8Array, sealed: Uint8Array): Uint8Array;
|
|
55
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
56
|
+
dispose(): void;
|
|
57
|
+
private _validateInputs;
|
|
58
|
+
private _loadKey;
|
|
59
|
+
private _writeIv;
|
|
60
|
+
private _writeAad;
|
|
61
|
+
}
|