leviathan-crypto 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +88 -281
- package/LICENSE +4 -0
- package/README.md +275 -87
- package/dist/aes/aes-cbc.d.ts +40 -0
- package/dist/aes/aes-cbc.js +158 -0
- package/dist/aes/aes-ctr.d.ts +50 -0
- package/dist/aes/aes-ctr.js +141 -0
- package/dist/aes/aes-gcm-siv.d.ts +67 -0
- package/dist/aes/aes-gcm-siv.js +217 -0
- package/dist/aes/aes-gcm.d.ts +61 -0
- package/dist/aes/aes-gcm.js +226 -0
- package/dist/aes/cipher-suite.d.ts +21 -0
- package/dist/aes/cipher-suite.js +179 -0
- package/dist/aes/embedded.d.ts +1 -0
- package/dist/aes/embedded.js +26 -0
- package/dist/aes/generator.d.ts +14 -0
- package/dist/aes/generator.js +103 -0
- package/dist/aes/index.d.ts +58 -0
- package/dist/aes/index.js +125 -0
- package/dist/aes/ops.d.ts +60 -0
- package/dist/aes/ops.js +164 -0
- package/dist/aes/pool-worker.d.ts +1 -0
- package/dist/aes/pool-worker.js +92 -0
- package/dist/aes/types.d.ts +1 -0
- package/dist/aes/types.js +23 -0
- package/dist/aes.wasm +0 -0
- package/dist/blake3/embedded.d.ts +1 -0
- package/dist/blake3/embedded.js +26 -0
- package/dist/blake3/index.d.ts +143 -0
- package/dist/blake3/index.js +620 -0
- package/dist/blake3/types.d.ts +102 -0
- package/dist/blake3/types.js +31 -0
- package/dist/blake3/validate.d.ts +29 -0
- package/dist/blake3/validate.js +80 -0
- package/dist/blake3.wasm +0 -0
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +98 -13
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +100 -3
- package/dist/chacha20/index.js +169 -35
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +107 -27
- package/dist/chacha20/pool-worker.js +14 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -0
- package/dist/cte.wasm +0 -0
- package/dist/curve25519.wasm +0 -0
- package/dist/ecdsa/der.d.ts +23 -0
- package/dist/ecdsa/der.js +192 -0
- package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
- package/dist/ecdsa/ecprivatekey-der.js +230 -0
- package/dist/ecdsa/embedded.d.ts +1 -0
- package/dist/ecdsa/embedded.js +25 -0
- package/dist/ecdsa/index.d.ts +124 -0
- package/dist/ecdsa/index.js +366 -0
- package/dist/ecdsa/types.d.ts +31 -0
- package/dist/ecdsa/types.js +28 -0
- package/dist/ecdsa/validate.d.ts +18 -0
- package/dist/ecdsa/validate.js +92 -0
- package/dist/ed25519/embedded.d.ts +1 -0
- package/dist/ed25519/embedded.js +31 -0
- package/dist/ed25519/index.d.ts +70 -0
- package/dist/ed25519/index.js +308 -0
- package/dist/ed25519/types.d.ts +27 -0
- package/dist/ed25519/types.js +27 -0
- package/dist/ed25519/validate.d.ts +7 -0
- package/dist/ed25519/validate.js +77 -0
- package/dist/embedded/aes-pool-worker.d.ts +1 -0
- package/dist/embedded/aes-pool-worker.js +5 -0
- package/dist/embedded/aes.d.ts +1 -0
- package/dist/embedded/aes.js +3 -0
- package/dist/embedded/blake3.d.ts +1 -0
- package/dist/embedded/blake3.js +3 -0
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -2
- package/dist/embedded/curve25519.d.ts +1 -0
- package/dist/embedded/curve25519.js +3 -0
- package/dist/embedded/mldsa.d.ts +1 -0
- package/dist/embedded/mldsa.js +3 -0
- package/dist/embedded/mlkem.d.ts +1 -0
- package/dist/embedded/mlkem.js +3 -0
- package/dist/embedded/p256.d.ts +1 -0
- package/dist/embedded/p256.js +3 -0
- package/dist/embedded/serpent-pool-worker.d.ts +1 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +2 -2
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -2
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -2
- package/dist/embedded/slhdsa.d.ts +1 -0
- package/dist/embedded/slhdsa.js +3 -0
- package/dist/errors.d.ts +92 -1
- package/dist/errors.js +111 -1
- package/dist/fortuna.d.ts +18 -12
- package/dist/fortuna.js +166 -99
- package/dist/index.d.ts +42 -11
- package/dist/index.js +65 -20
- package/dist/init.d.ts +1 -3
- package/dist/init.js +73 -7
- package/dist/keccak/embedded.js +1 -1
- package/dist/keccak/index.d.ts +2 -0
- package/dist/keccak/index.js +4 -2
- package/dist/loader.d.ts +1 -19
- package/dist/loader.js +26 -32
- package/dist/merkle/blake3-tree.d.ts +35 -0
- package/dist/merkle/blake3-tree.js +187 -0
- package/dist/merkle/checkpoint.d.ts +58 -0
- package/dist/merkle/checkpoint.js +217 -0
- package/dist/merkle/index.d.ts +19 -0
- package/dist/merkle/index.js +37 -0
- package/dist/merkle/merkle-log.d.ts +130 -0
- package/dist/merkle/merkle-log.js +207 -0
- package/dist/merkle/merkle-verifier.d.ts +126 -0
- package/dist/merkle/merkle-verifier.js +296 -0
- package/dist/merkle/proof.d.ts +70 -0
- package/dist/merkle/proof.js +300 -0
- package/dist/merkle/sha256-tree.d.ts +33 -0
- package/dist/merkle/sha256-tree.js +145 -0
- package/dist/merkle/signed-log.d.ts +156 -0
- package/dist/merkle/signed-log.js +356 -0
- package/dist/merkle/signed-note.d.ts +309 -0
- package/dist/merkle/signed-note.js +648 -0
- package/dist/merkle/sth.d.ts +31 -0
- package/dist/merkle/sth.js +31 -0
- package/dist/merkle/storage.d.ts +40 -0
- package/dist/merkle/storage.js +71 -0
- package/dist/merkle/tree.d.ts +68 -0
- package/dist/merkle/tree.js +94 -0
- package/dist/mldsa/embedded.d.ts +1 -0
- package/dist/{kyber → mldsa}/embedded.js +5 -5
- package/dist/mldsa/expand.d.ts +53 -0
- package/dist/mldsa/expand.js +188 -0
- package/dist/mldsa/format.d.ts +16 -0
- package/dist/mldsa/format.js +68 -0
- package/dist/mldsa/hashvariant.d.ts +32 -0
- package/dist/mldsa/hashvariant.js +248 -0
- package/dist/mldsa/index.d.ts +142 -0
- package/dist/mldsa/index.js +463 -0
- package/dist/mldsa/keygen.d.ts +16 -0
- package/dist/mldsa/keygen.js +232 -0
- package/dist/mldsa/params.d.ts +21 -0
- package/dist/mldsa/params.js +55 -0
- package/dist/mldsa/sha3-helpers.d.ts +30 -0
- package/dist/mldsa/sha3-helpers.js +124 -0
- package/dist/mldsa/sign.d.ts +36 -0
- package/dist/mldsa/sign.js +380 -0
- package/dist/mldsa/types.d.ts +91 -0
- package/dist/mldsa/types.js +25 -0
- package/dist/mldsa/validate.d.ts +55 -0
- package/dist/mldsa/validate.js +125 -0
- package/dist/mldsa/verify.d.ts +29 -0
- package/dist/mldsa/verify.js +269 -0
- package/dist/mldsa.wasm +0 -0
- package/dist/mlkem/embedded.d.ts +1 -0
- package/dist/mlkem/embedded.js +27 -0
- package/dist/mlkem/indcpa.d.ts +49 -0
- package/dist/{kyber → mlkem}/indcpa.js +48 -48
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +41 -31
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +48 -13
- package/dist/{kyber → mlkem}/params.d.ts +4 -4
- package/dist/{kyber → mlkem}/params.js +2 -2
- package/dist/mlkem/suite.d.ts +12 -0
- package/dist/{kyber → mlkem}/suite.js +17 -12
- package/dist/{kyber → mlkem}/types.d.ts +4 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/mlkem/validate.d.ts +23 -0
- package/dist/{kyber → mlkem}/validate.js +24 -20
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +8 -0
- package/dist/ratchet/index.js +38 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +144 -56
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +62 -1
- package/dist/serpent/index.js +97 -21
- package/dist/serpent/pool-worker.js +28 -102
- package/dist/serpent/serpent-cbc.d.ts +16 -6
- package/dist/serpent/serpent-cbc.js +58 -37
- package/dist/serpent/shared-ops.d.ts +63 -0
- package/dist/serpent/shared-ops.js +178 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +22 -1
- package/dist/sha2/index.js +80 -11
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +87 -3
- package/dist/sha3/index.js +317 -19
- package/dist/sha3/kmac.d.ts +121 -0
- package/dist/sha3/kmac.js +800 -0
- package/dist/sha3.wasm +0 -0
- package/dist/shared/pkcs7.d.ts +22 -0
- package/dist/shared/pkcs7.js +84 -0
- package/dist/sign/ctx.d.ts +41 -0
- package/dist/sign/ctx.js +102 -0
- package/dist/sign/envelope.d.ts +45 -0
- package/dist/sign/envelope.js +152 -0
- package/dist/sign/hasher.d.ts +9 -0
- package/dist/sign/hasher.js +132 -0
- package/dist/sign/index.d.ts +11 -0
- package/dist/sign/index.js +34 -0
- package/dist/sign/sign-stream.d.ts +25 -0
- package/dist/sign/sign-stream.js +112 -0
- package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
- package/dist/sign/suites/ecdsa-p256.js +120 -0
- package/dist/sign/suites/ed25519.d.ts +3 -0
- package/dist/sign/suites/ed25519.js +165 -0
- package/dist/sign/suites/hybrid-classical.d.ts +23 -0
- package/dist/sign/suites/hybrid-classical.js +526 -0
- package/dist/sign/suites/hybrid-pq.d.ts +4 -0
- package/dist/sign/suites/hybrid-pq.js +234 -0
- package/dist/sign/suites/mldsa.d.ts +7 -0
- package/dist/sign/suites/mldsa.js +161 -0
- package/dist/sign/suites/slhdsa.d.ts +7 -0
- package/dist/sign/suites/slhdsa.js +176 -0
- package/dist/sign/types.d.ts +106 -0
- package/dist/sign/types.js +28 -0
- package/dist/sign/verify-stream.d.ts +30 -0
- package/dist/sign/verify-stream.js +227 -0
- package/dist/slhdsa/embedded.d.ts +1 -0
- package/dist/slhdsa/embedded.js +26 -0
- package/dist/slhdsa/index.d.ts +149 -0
- package/dist/slhdsa/index.js +493 -0
- package/dist/slhdsa/params.d.ts +26 -0
- package/dist/slhdsa/params.js +70 -0
- package/dist/slhdsa/prehash.d.ts +68 -0
- package/dist/slhdsa/prehash.js +307 -0
- package/dist/slhdsa/sign.d.ts +39 -0
- package/dist/slhdsa/sign.js +116 -0
- package/dist/slhdsa/types.d.ts +129 -0
- package/dist/slhdsa/types.js +27 -0
- package/dist/slhdsa/validate.d.ts +60 -0
- package/dist/slhdsa/validate.js +127 -0
- package/dist/slhdsa/verify.d.ts +32 -0
- package/dist/slhdsa/verify.js +107 -0
- package/dist/slhdsa.wasm +0 -0
- package/dist/stream/header.js +8 -8
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +65 -22
- package/dist/stream/seal-stream-pool.d.ts +2 -0
- package/dist/stream/seal-stream-pool.js +100 -33
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +48 -19
- package/dist/stream/seal.js +6 -6
- package/dist/stream/types.d.ts +3 -1
- package/dist/stream/types.js +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +9 -10
- package/dist/utils.js +84 -59
- package/dist/wasm-source.d.ts +9 -8
- package/dist/wasm-source.js +1 -1
- package/dist/x25519/embedded.d.ts +1 -0
- package/dist/x25519/embedded.js +31 -0
- package/dist/x25519/index.d.ts +43 -0
- package/dist/x25519/index.js +159 -0
- package/dist/x25519/types.d.ts +25 -0
- package/dist/x25519/types.js +27 -0
- package/dist/x25519/validate.d.ts +2 -0
- package/dist/x25519/validate.js +39 -0
- package/package.json +123 -64
- package/SECURITY.md +0 -276
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +0 -323
- package/dist/docs/architecture.md +0 -932
- package/dist/docs/argon2id.md +0 -302
- package/dist/docs/chacha20.md +0 -674
- package/dist/docs/exports.md +0 -241
- package/dist/docs/fortuna.md +0 -313
- package/dist/docs/init.md +0 -302
- package/dist/docs/loader.md +0 -161
- package/dist/docs/serpent.md +0 -519
- package/dist/docs/sha2.md +0 -613
- package/dist/docs/sha3.md +0 -546
- package/dist/docs/types.md +0 -276
- package/dist/docs/utils.md +0 -367
- package/dist/embedded/kyber.d.ts +0 -1
- package/dist/embedded/kyber.js +0 -3
- package/dist/kyber/embedded.d.ts +0 -1
- package/dist/kyber/indcpa.d.ts +0 -49
- package/dist/kyber/index.d.ts +0 -38
- package/dist/kyber/kem.d.ts +0 -21
- package/dist/kyber/suite.d.ts +0 -13
- package/dist/kyber/validate.d.ts +0 -19
|
@@ -0,0 +1,230 @@
|
|
|
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/ecdsa/ecprivatekey-der.ts
|
|
23
|
+
//
|
|
24
|
+
// DER `ECPrivateKey` (RFC 5915 §3, Elliptic Curve Private Key Structure)
|
|
25
|
+
// codec for P-256. The library's WASM ABI stores secrets as 32-byte raw
|
|
26
|
+
// scalars; DER `ECPrivateKey` is the X.509 / PKCS#8-adjacent wire form
|
|
27
|
+
// some interop contexts require (PKCS#8 wraps an ECPrivateKey OCTET
|
|
28
|
+
// STRING inside its `privateKey` field; X.509 self-signed certs reference
|
|
29
|
+
// it through their algorithm OID).
|
|
30
|
+
//
|
|
31
|
+
// ECPrivateKey ::= SEQUENCE {
|
|
32
|
+
// version INTEGER (1),
|
|
33
|
+
// privateKey OCTET STRING,
|
|
34
|
+
// parameters [0] EXPLICIT ECParameters OPTIONAL,
|
|
35
|
+
// publicKey [1] EXPLICIT BIT STRING OPTIONAL
|
|
36
|
+
// }
|
|
37
|
+
//
|
|
38
|
+
// Encoder side: emits the exact 51-byte DER form with version 1, the
|
|
39
|
+
// 32-byte raw scalar in `privateKey`, and the named-curve OID
|
|
40
|
+
// `1.2.840.10045.3.1.7` (secp256r1, SP 800-186 §3.2.1.3) wrapped in
|
|
41
|
+
// `parameters [0]`. `publicKey` is omitted; consumers that need the
|
|
42
|
+
// public key alongside should re-derive via `EcdsaP256.keygenDerand` and
|
|
43
|
+
// the SEC 1 §2.3.4 uncompressed encoding (see `pointDecompress` in
|
|
44
|
+
// `./index.ts`). Byte-stable: same scalar in, same DER out.
|
|
45
|
+
//
|
|
46
|
+
// Decoder side: accepts any conforming RFC 5915 `ECPrivateKey` (not just
|
|
47
|
+
// leviathan's exact byte layout). Strict-DER posture per X.690 §10
|
|
48
|
+
// (Restrictions on the BER) rejects:
|
|
49
|
+
// - non-minimal length encodings (long-form when short-form suffices)
|
|
50
|
+
// - non-minimal INTEGER encodings on the `version` field
|
|
51
|
+
// - SEQUENCE / OCTET STRING / parameters [0] / publicKey [1] length
|
|
52
|
+
// prefixes that disagree with the on-the-wire content size
|
|
53
|
+
// - any curve OID inside `parameters [0]` other than secp256r1
|
|
54
|
+
// - trailing bytes after the outer SEQUENCE
|
|
55
|
+
// - wrong tags or missing required fields
|
|
56
|
+
//
|
|
57
|
+
// Lenient on `publicKey [1]`: some encoders include the derived pk
|
|
58
|
+
// alongside the scalar; the decoder skips past the field after a
|
|
59
|
+
// minimal-length check. The scalar is the only return value; callers
|
|
60
|
+
// who need the embedded pk should parse the DER themselves.
|
|
61
|
+
//
|
|
62
|
+
// No third-party ASN.1 library. Hand-rolled against X.690 §8.1 (TLV
|
|
63
|
+
// structure), §8.3 (INTEGER), §8.7 (OCTET STRING), §8.14 (tagged types),
|
|
64
|
+
// §10 (DER restrictions), mirroring the strict-parser hygiene of
|
|
65
|
+
// `./der.ts` (Ecdsa-Sig-Value codec).
|
|
66
|
+
const SEQUENCE_TAG = 0x30;
|
|
67
|
+
const INTEGER_TAG = 0x02;
|
|
68
|
+
const OCTET_STRING_TAG = 0x04;
|
|
69
|
+
const OID_TAG = 0x06;
|
|
70
|
+
const TAG_PARAMETERS_0 = 0xA0; // [0] EXPLICIT, constructed
|
|
71
|
+
const TAG_PUBLIC_KEY_1 = 0xA1; // [1] EXPLICIT, constructed
|
|
72
|
+
// DER OID for secp256r1: 1.2.840.10045.3.1.7. X.690 §8.19 collapses the
|
|
73
|
+
// first two arcs (1.2) into a single byte 0x2A (= 1 * 40 + 2); the
|
|
74
|
+
// remaining arcs (840, 10045, 3, 1, 7) base-128 encode as 86 48, CE 3D,
|
|
75
|
+
// 03, 01, 07. Wrapped in the OBJECT IDENTIFIER TLV (tag 0x06, len 0x08).
|
|
76
|
+
const SECP256R1_OID_DER = new Uint8Array([
|
|
77
|
+
OID_TAG, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
78
|
+
]);
|
|
79
|
+
// Exact 51-byte DER shape for a P-256 ECPrivateKey with no embedded
|
|
80
|
+
// public key:
|
|
81
|
+
// 30 31 SEQUENCE, 49 bytes content
|
|
82
|
+
// 02 01 01 INTEGER, version = 1
|
|
83
|
+
// 04 20 <32 bytes scalar> OCTET STRING, 32 bytes
|
|
84
|
+
// A0 0A [0] EXPLICIT, 10 bytes content
|
|
85
|
+
// 06 08 2A 86 48 CE 3D 03 01 07 OBJECT IDENTIFIER, secp256r1
|
|
86
|
+
const ENCODED_LEN = 51;
|
|
87
|
+
const SEQUENCE_CONTENT_LEN = 49;
|
|
88
|
+
const SCALAR_LEN = 32;
|
|
89
|
+
// Absolute minimum input size that exposes every required field's TLV
|
|
90
|
+
// header without index-out-of-bounds on the parser side. SEQUENCE
|
|
91
|
+
// header (2) + version INTEGER TLV (3) + OCTET STRING tag + len byte
|
|
92
|
+
// (2) = 7. The decoder's field-level checks surface more specific
|
|
93
|
+
// rejections beyond this floor (wrong scalar length, missing required
|
|
94
|
+
// fields, etc).
|
|
95
|
+
const MIN_INPUT_LEN = 7;
|
|
96
|
+
function reject(detail) {
|
|
97
|
+
throw new Error(`leviathan-crypto: ecdsa-p256 ECPrivateKey DER ${detail}`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Encode a 32-byte P-256 secret scalar as DER `ECPrivateKey` per
|
|
101
|
+
* RFC 5915 §3. Output length is exactly 51 bytes; version is 1, the
|
|
102
|
+
* named-curve OID for secp256r1 (`1.2.840.10045.3.1.7`,
|
|
103
|
+
* SP 800-186 §3.2.1.3) is included in `parameters [0]`, and the
|
|
104
|
+
* `publicKey [1]` field is omitted.
|
|
105
|
+
*
|
|
106
|
+
* Byte-stable: same input scalar produces byte-identical output. No
|
|
107
|
+
* canonicalisation pass needed because every field has a single legal
|
|
108
|
+
* DER encoding under the strict-DER rules of X.690 §10.
|
|
109
|
+
*
|
|
110
|
+
* @throws TypeError if `scalar` is not a Uint8Array
|
|
111
|
+
* @throws RangeError if `scalar.length !== 32`
|
|
112
|
+
*/
|
|
113
|
+
export function encodeEcPrivateKey(scalar) {
|
|
114
|
+
if (!(scalar instanceof Uint8Array))
|
|
115
|
+
throw new TypeError('leviathan-crypto: ecdsa-p256 ECPrivateKey scalar must be a Uint8Array');
|
|
116
|
+
if (scalar.length !== SCALAR_LEN)
|
|
117
|
+
throw new RangeError(`leviathan-crypto: ecdsa-p256 ECPrivateKey scalar must be ${SCALAR_LEN} bytes (got ${scalar.length})`);
|
|
118
|
+
const out = new Uint8Array(ENCODED_LEN);
|
|
119
|
+
let p = 0;
|
|
120
|
+
// SEQUENCE header
|
|
121
|
+
out[p++] = SEQUENCE_TAG;
|
|
122
|
+
out[p++] = SEQUENCE_CONTENT_LEN;
|
|
123
|
+
// version INTEGER (1)
|
|
124
|
+
out[p++] = INTEGER_TAG;
|
|
125
|
+
out[p++] = 0x01;
|
|
126
|
+
out[p++] = 0x01;
|
|
127
|
+
// privateKey OCTET STRING (32 bytes)
|
|
128
|
+
out[p++] = OCTET_STRING_TAG;
|
|
129
|
+
out[p++] = SCALAR_LEN;
|
|
130
|
+
out.set(scalar, p);
|
|
131
|
+
p += SCALAR_LEN;
|
|
132
|
+
// parameters [0] EXPLICIT { secp256r1 OID }
|
|
133
|
+
out[p++] = TAG_PARAMETERS_0;
|
|
134
|
+
out[p++] = SECP256R1_OID_DER.length;
|
|
135
|
+
out.set(SECP256R1_OID_DER, p);
|
|
136
|
+
// p + SECP256R1_OID_DER.length now equals ENCODED_LEN by construction.
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Decode a DER `ECPrivateKey` (RFC 5915 §3) and return the 32-byte
|
|
141
|
+
* raw P-256 secret scalar.
|
|
142
|
+
*
|
|
143
|
+
* Strict-DER per X.690 §10 (Restrictions on the BER). Rejects the
|
|
144
|
+
* cases enumerated in the file-level header. Accepts (and ignores)
|
|
145
|
+
* an optional `publicKey [1]` field per RFC 5915 §3 lenient input;
|
|
146
|
+
* the scalar is the only return value.
|
|
147
|
+
*
|
|
148
|
+
* Any curve OID inside `parameters [0]` other than secp256r1
|
|
149
|
+
* (`1.2.840.10045.3.1.7`) is rejected. Decoders that need other
|
|
150
|
+
* curves must use a different parser; this library is P-256 only.
|
|
151
|
+
*
|
|
152
|
+
* @throws TypeError if `der` is not a Uint8Array
|
|
153
|
+
* @throws Error on any DER syntax violation or unsupported parameter
|
|
154
|
+
*/
|
|
155
|
+
export function decodeEcPrivateKey(der) {
|
|
156
|
+
if (!(der instanceof Uint8Array))
|
|
157
|
+
throw new TypeError('leviathan-crypto: ecdsa-p256 ECPrivateKey DER must be a Uint8Array');
|
|
158
|
+
// Floor at the bytes needed for the SEQUENCE header + version
|
|
159
|
+
// INTEGER TLV + privateKey OCTET STRING TLV header (no content
|
|
160
|
+
// required at the floor). Specific field-level checks below
|
|
161
|
+
// surface more accurate rejections beyond this.
|
|
162
|
+
if (der.length < MIN_INPUT_LEN)
|
|
163
|
+
reject(`is shorter than the ${MIN_INPUT_LEN}-byte minimum (got ${der.length} bytes)`);
|
|
164
|
+
if (der[0] !== SEQUENCE_TAG)
|
|
165
|
+
reject(`outer tag is 0x${der[0].toString(16).padStart(2, '0')}, expected 0x30 (SEQUENCE)`);
|
|
166
|
+
const seqLen = der[1];
|
|
167
|
+
// Strict DER short-form: outer SEQUENCE content for a P-256
|
|
168
|
+
// ECPrivateKey is at most 51 - 2 = 49 bytes with no public key, or
|
|
169
|
+
// roughly 51 + 67 - 2 = 116 with a publicKey [1] field; both fit
|
|
170
|
+
// in short-form. Long-form here would be a non-minimal encoding
|
|
171
|
+
// for content < 128 bytes.
|
|
172
|
+
if (seqLen & 0x80)
|
|
173
|
+
reject('uses long-form length encoding for the outer SEQUENCE (forbidden for content < 128 bytes)');
|
|
174
|
+
if (2 + seqLen !== der.length)
|
|
175
|
+
reject(`outer SEQUENCE length ${seqLen} does not match input size (${der.length} bytes total)`);
|
|
176
|
+
let off = 2;
|
|
177
|
+
// version INTEGER (1). Strict: exactly `02 01 01`. Any other
|
|
178
|
+
// length, leading-zero pad, or value rejects.
|
|
179
|
+
if (der[off] !== INTEGER_TAG)
|
|
180
|
+
reject(`version tag at offset ${off} is 0x${der[off].toString(16).padStart(2, '0')}, expected 0x02 (INTEGER)`);
|
|
181
|
+
if (der[off + 1] !== 0x01)
|
|
182
|
+
reject(`version length at offset ${off + 1} is ${der[off + 1]}, expected 1 (single-byte INTEGER)`);
|
|
183
|
+
if (der[off + 2] !== 0x01)
|
|
184
|
+
reject(`version value at offset ${off + 2} is ${der[off + 2]}, expected 1`);
|
|
185
|
+
off += 3;
|
|
186
|
+
// privateKey OCTET STRING (32 bytes for P-256).
|
|
187
|
+
if (der[off] !== OCTET_STRING_TAG)
|
|
188
|
+
reject(`privateKey tag at offset ${off} is 0x${der[off].toString(16).padStart(2, '0')}, expected 0x04 (OCTET STRING)`);
|
|
189
|
+
const skLen = der[off + 1];
|
|
190
|
+
if (skLen & 0x80)
|
|
191
|
+
reject(`privateKey OCTET STRING at offset ${off + 1} uses long-form length encoding (forbidden for content < 128 bytes)`);
|
|
192
|
+
if (skLen !== SCALAR_LEN)
|
|
193
|
+
reject(`privateKey OCTET STRING length at offset ${off + 1} is ${skLen}, expected ${SCALAR_LEN} (P-256 scalar)`);
|
|
194
|
+
if (off + 2 + skLen > der.length)
|
|
195
|
+
reject('privateKey OCTET STRING content extends past the outer SEQUENCE end');
|
|
196
|
+
const scalar = der.slice(off + 2, off + 2 + skLen);
|
|
197
|
+
off += 2 + skLen;
|
|
198
|
+
// Optional parameters [0] EXPLICIT. Per RFC 5915 §3 this field is
|
|
199
|
+
// OPTIONAL; when present it carries the curve OID. We require
|
|
200
|
+
// secp256r1 specifically; any other OID rejects.
|
|
201
|
+
if (off < der.length && der[off] === TAG_PARAMETERS_0) {
|
|
202
|
+
const paramLen = der[off + 1];
|
|
203
|
+
if (paramLen & 0x80)
|
|
204
|
+
reject(`parameters [0] at offset ${off + 1} uses long-form length encoding (forbidden for content < 128 bytes)`);
|
|
205
|
+
if (off + 2 + paramLen > der.length)
|
|
206
|
+
reject('parameters [0] content extends past the outer SEQUENCE end');
|
|
207
|
+
if (paramLen !== SECP256R1_OID_DER.length)
|
|
208
|
+
reject(`parameters [0] content is ${paramLen} bytes, expected ${SECP256R1_OID_DER.length} `
|
|
209
|
+
+ '(secp256r1 OID TLV); other curves are not supported');
|
|
210
|
+
for (let i = 0; i < paramLen; i++) {
|
|
211
|
+
if (der[off + 2 + i] !== SECP256R1_OID_DER[i])
|
|
212
|
+
reject('parameters [0] OID does not match secp256r1 (1.2.840.10045.3.1.7); '
|
|
213
|
+
+ 'other curves are not supported');
|
|
214
|
+
}
|
|
215
|
+
off += 2 + paramLen;
|
|
216
|
+
}
|
|
217
|
+
// RFC 5915 §3 OPTIONAL: skip past after minimal length check.
|
|
218
|
+
if (off < der.length && der[off] === TAG_PUBLIC_KEY_1) {
|
|
219
|
+
const pkLen = der[off + 1];
|
|
220
|
+
if (pkLen & 0x80)
|
|
221
|
+
reject(`publicKey [1] at offset ${off + 1} uses long-form length encoding (forbidden for content < 128 bytes)`);
|
|
222
|
+
if (off + 2 + pkLen > der.length)
|
|
223
|
+
reject('publicKey [1] content extends past the outer SEQUENCE end');
|
|
224
|
+
off += 2 + pkLen;
|
|
225
|
+
}
|
|
226
|
+
// No trailing bytes after the (optional) tail fields.
|
|
227
|
+
if (off !== der.length)
|
|
228
|
+
reject(`has ${der.length - off} trailing byte(s) after the optional fields`);
|
|
229
|
+
return scalar;
|
|
230
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WASM_GZ_BASE64 as p256Wasm, WASM_GZ_BASE64 as ecdsaP256Wasm, } from '../embedded/p256.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
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/ecdsa/embedded.ts
|
|
23
|
+
//
|
|
24
|
+
// `p256Wasm` canonical, `ecdsaP256Wasm` alias.
|
|
25
|
+
export { WASM_GZ_BASE64 as p256Wasm, WASM_GZ_BASE64 as ecdsaP256Wasm, } from '../embedded/p256.js';
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { isInitialized } from '../init.js';
|
|
2
|
+
import type { WasmSource } from '../wasm-source.js';
|
|
3
|
+
import type { EcdsaP256KeyPair } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Initialise the p256 WASM module. Loads the underlying binary
|
|
6
|
+
* (scalar, no SIMD) into the `p256` slot.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ecdsaP256Init(source: WasmSource): Promise<void>;
|
|
9
|
+
export type { WasmSource };
|
|
10
|
+
export type { EcdsaP256KeyPair, EcdsaP256Exports } from './types.js';
|
|
11
|
+
export { isInitialized };
|
|
12
|
+
export { encodeEcPrivateKey, decodeEcPrivateKey } from './ecprivatekey-der.js';
|
|
13
|
+
/**
|
|
14
|
+
* Decompress a 33-byte SEC 1 §2.3.3 compressed P-256 public key to the
|
|
15
|
+
* 65-byte SEC 1 §2.3.4 uncompressed encoding `0x04 || X || Y`.
|
|
16
|
+
*
|
|
17
|
+
* The compressed form encodes only the affine x coordinate plus a
|
|
18
|
+
* single parity bit (in the prefix byte: 0x02 even-y, 0x03 odd-y).
|
|
19
|
+
* Recovery of y solves the curve equation
|
|
20
|
+
* `y² = x³ - 3x + b mod p` (SP 800-186 §3.2.1.3, P-256 has a = -3)
|
|
21
|
+
* and selects the y root whose parity matches the prefix. The
|
|
22
|
+
* substrate runs the modular square root inside the p256 WASM
|
|
23
|
+
* (`feSqrt` via the p ≡ 3 (mod 4) shortcut, x^((p+1)/4)); rejecting
|
|
24
|
+
* invalid inputs that have no square root or whose recovered (x, y)
|
|
25
|
+
* lies off-curve.
|
|
26
|
+
*
|
|
27
|
+
* Rejection cases (all throw `SigningError('sig-malformed-input')`):
|
|
28
|
+
* - prefix byte not in {0x02, 0x03}
|
|
29
|
+
* - x coordinate is not the x of any on-curve point (no quadratic
|
|
30
|
+
* residue exists for `x³ - 3x + b mod p`)
|
|
31
|
+
*
|
|
32
|
+
* Length / shape rejections throw `TypeError` / `RangeError` per the
|
|
33
|
+
* usual leviathan-crypto contract-violation posture.
|
|
34
|
+
*
|
|
35
|
+
* Requires `init({ p256: ... })`. Uses the same p256 module singleton
|
|
36
|
+
* as `EcdsaP256`; concurrency-safe alongside non-stateful uses (the
|
|
37
|
+
* `_assertNotOwned` check fires if a stateful instance is holding
|
|
38
|
+
* the module).
|
|
39
|
+
*
|
|
40
|
+
* @param pk33 33-byte compressed pk per SEC 1 §2.3.3
|
|
41
|
+
* @returns 65-byte uncompressed pk per SEC 1 §2.3.4 (0x04 || X || Y)
|
|
42
|
+
*/
|
|
43
|
+
export declare function pointDecompress(pk33: Uint8Array): Uint8Array;
|
|
44
|
+
export declare class EcdsaP256 {
|
|
45
|
+
constructor();
|
|
46
|
+
private get mx();
|
|
47
|
+
/**
|
|
48
|
+
* Deterministic ECDSA-P256 key generation from a 32-byte seed.
|
|
49
|
+
* d = seed mod n per FIPS 186-5 §A.4.2 (testing-candidates style,
|
|
50
|
+
* single candidate). pk = [d]G compressed to 33 bytes per SEC 1
|
|
51
|
+
* §2.3.3. The vanishingly rare seed mod n == 0 case traps in the
|
|
52
|
+
* WASM and surfaces as a SigningError here.
|
|
53
|
+
*
|
|
54
|
+
* @param seed 32-byte BE input
|
|
55
|
+
* @returns 33-byte compressed pk and a fresh 32-byte copy of the
|
|
56
|
+
* secret scalar d (sk === seed for this derivation, the
|
|
57
|
+
* caller may use either as the private value).
|
|
58
|
+
*/
|
|
59
|
+
keygenDerand(seed: Uint8Array): EcdsaP256KeyPair;
|
|
60
|
+
/** Random ECDSA-P256 key generation, wraps `keygenDerand` with `randomBytes(32)`. */
|
|
61
|
+
keygen(): EcdsaP256KeyPair;
|
|
62
|
+
/**
|
|
63
|
+
* Key generation that returns the public key in the 65-byte SEC 1
|
|
64
|
+
* §2.3.4 uncompressed encoding `0x04 || X || Y`, rather than the
|
|
65
|
+
* 33-byte compressed form `keygen` / `keygenDerand` return. The
|
|
66
|
+
* secret-key half is the same 32-byte raw scalar `d`.
|
|
67
|
+
*
|
|
68
|
+
* Internally runs `keygen` (or `keygenDerand` if a seed is supplied)
|
|
69
|
+
* to obtain the compressed pk, then `pointDecompress` to expand it.
|
|
70
|
+
* The compressed intermediate is wiped before return.
|
|
71
|
+
*
|
|
72
|
+
* @param seed Optional 32-byte seed; passes through to `keygenDerand`
|
|
73
|
+
* when present, falls back to `keygen` (CSPRNG seed) when
|
|
74
|
+
* omitted.
|
|
75
|
+
*/
|
|
76
|
+
keygenUncompressed(seed?: Uint8Array): EcdsaP256KeyPair;
|
|
77
|
+
/**
|
|
78
|
+
* Hedged-or-deterministic ECDSA-P256 sign per FIPS 186-5 §6.4 with
|
|
79
|
+
* RFC 6979 §3.5 low-S normalisation. The K nonce is derived per
|
|
80
|
+
* RFC 6979 §3.2 (deterministic) when `rnd` is all-zero, or per
|
|
81
|
+
* draft-irtf-cfrg-det-sigs-with-noise-05 (hedged) otherwise. The
|
|
82
|
+
* hedged path is the recommended default; pass `randomBytes(32)`.
|
|
83
|
+
*
|
|
84
|
+
* The WASM re-derives pk = [d]G internally and compares it against
|
|
85
|
+
* the caller-supplied `pk`. A mismatch traps via `unreachable` and
|
|
86
|
+
* is rethrown as `SigningError('sig-malformed-input')`. This
|
|
87
|
+
* defends against fault injection that would bias the per-signature
|
|
88
|
+
* randomness derivation by forcing the caller to also know pk.
|
|
89
|
+
*
|
|
90
|
+
* @param sk 32-byte secret scalar d
|
|
91
|
+
* @param pk 33-byte compressed or 65-byte uncompressed pk;
|
|
92
|
+
* cross-checked by WASM after derivation
|
|
93
|
+
* @param msgHash 32-byte SHA-256(M) digest (caller-computed)
|
|
94
|
+
* @param rnd 32-byte per-call entropy Z; all-zero selects
|
|
95
|
+
* deterministic RFC 6979 §3.2, non-zero selects
|
|
96
|
+
* the hedged path
|
|
97
|
+
* @returns 64-byte raw r || s signature, low-S normalised
|
|
98
|
+
* @throws SigningError('sig-malformed-input') on pk-mismatch
|
|
99
|
+
* (fault-injection trap)
|
|
100
|
+
*/
|
|
101
|
+
sign(sk: Uint8Array, pk: Uint8Array, msgHash: Uint8Array, rnd: Uint8Array): Uint8Array;
|
|
102
|
+
/**
|
|
103
|
+
* Suite-only: hedged-or-deterministic sign that derives pk
|
|
104
|
+
* internally and skips the fault-injection cross-check. See
|
|
105
|
+
* AGENTS.md "SignatureSuite lifecycle". Underscore-prefixed,
|
|
106
|
+
* not part of the public API.
|
|
107
|
+
*/
|
|
108
|
+
_signInternalPk(sk: Uint8Array, msgHash: Uint8Array, rnd: Uint8Array): Uint8Array;
|
|
109
|
+
/**
|
|
110
|
+
* Strict ECDSA-P256 verify per FIPS 186-5 §6.5 with low-S
|
|
111
|
+
* enforcement (RFC 6979 §3.5). Returns `true` on success, `false`
|
|
112
|
+
* on every signature failure mode: off-curve / identity pk, r or
|
|
113
|
+
* s out of [1, n-1], high-S, or the signature equation failing.
|
|
114
|
+
* Throws only on caller-side contract violations (wrong-length
|
|
115
|
+
* inputs).
|
|
116
|
+
*
|
|
117
|
+
* @param pk 33-byte compressed or 65-byte uncompressed pk
|
|
118
|
+
* @param msgHash 32-byte SHA-256(M) digest
|
|
119
|
+
* @param sig 64-byte raw r || s (use `ecdsaSignatureFromDer`
|
|
120
|
+
* to convert DER-encoded signatures first)
|
|
121
|
+
*/
|
|
122
|
+
verify(pk: Uint8Array, msgHash: Uint8Array, sig: Uint8Array): boolean;
|
|
123
|
+
dispose(): void;
|
|
124
|
+
}
|