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/utils.js
CHANGED
|
@@ -21,15 +21,18 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/utils.ts
|
|
23
23
|
//
|
|
24
|
-
// Pure TypeScript utilities
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
/** Hex string to Uint8Array. Accepts lowercase/uppercase, optional 0x prefix. Throws RangeError on odd-length input. */
|
|
24
|
+
// Pure TypeScript utilities, no init() dependency.
|
|
25
|
+
// ── Encoding ────────────────────────────────────────────────────────────────
|
|
26
|
+
/** Hex string to Uint8Array. Accepts lowercase/uppercase, optional 0x prefix. Throws RangeError on odd-length or non-hex input. */
|
|
28
27
|
export const hexToBytes = (hex) => {
|
|
29
28
|
if (hex.startsWith('0x') || hex.startsWith('0X'))
|
|
30
29
|
hex = hex.slice(2);
|
|
31
30
|
if (hex.length % 2)
|
|
32
|
-
throw new RangeError(`hexToBytes: odd-length string (${hex.length} chars)
|
|
31
|
+
throw new RangeError(`hexToBytes: odd-length string (${hex.length} chars), input must be an even-length hex string`);
|
|
32
|
+
// parseInt('0g', 16) returns 0 (not NaN) because it stops at the first
|
|
33
|
+
// invalid char, silent wrong-answer. Reject non-hex chars up front.
|
|
34
|
+
if (hex.length > 0 && !/^[0-9a-fA-F]*$/.test(hex))
|
|
35
|
+
throw new RangeError('hexToBytes: input contains non-hex characters');
|
|
33
36
|
const bin = new Uint8Array(hex.length >>> 1);
|
|
34
37
|
for (let i = 0, len = hex.length >>> 1; i < len; i++)
|
|
35
38
|
bin[i] = parseInt(hex.slice(i << 1, (i << 1) + 2), 16);
|
|
@@ -51,20 +54,20 @@ export const utf8ToBytes = (str) => {
|
|
|
51
54
|
export const bytesToUtf8 = (bytes) => {
|
|
52
55
|
return new TextDecoder().decode(bytes);
|
|
53
56
|
};
|
|
54
|
-
/** Base64 or base64url string to Uint8Array. Handles padded, unpadded, and legacy %3d padding.
|
|
57
|
+
/** Base64 or base64url string to Uint8Array. Handles padded, unpadded, and legacy %3d padding. Throws RangeError on invalid input. */
|
|
55
58
|
export const base64ToBytes = (b64) => {
|
|
56
59
|
// Normalise base64url → base64
|
|
57
60
|
b64 = b64.replace(/-/g, '+').replace(/_/g, '/').replace(/%3d/gi, '=');
|
|
58
61
|
// Re-pad if unpadded (RFC 4648 §5 base64url omits '=')
|
|
59
62
|
const rem = b64.length % 4;
|
|
60
63
|
if (rem === 1)
|
|
61
|
-
|
|
64
|
+
throw new RangeError('base64ToBytes: invalid base64 input'); // no valid b64 produces this
|
|
62
65
|
if (rem === 2)
|
|
63
66
|
b64 += '==';
|
|
64
67
|
if (rem === 3)
|
|
65
68
|
b64 += '=';
|
|
66
69
|
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(b64))
|
|
67
|
-
|
|
70
|
+
throw new RangeError('base64ToBytes: invalid base64 input');
|
|
68
71
|
let strlen = b64.length / 4 * 3;
|
|
69
72
|
if (b64.charAt(b64.length - 1) === '=')
|
|
70
73
|
strlen--;
|
|
@@ -75,7 +78,7 @@ export const base64ToBytes = (b64) => {
|
|
|
75
78
|
return new Uint8Array(atob(b64).split('').map(c => c.charCodeAt(0)));
|
|
76
79
|
}
|
|
77
80
|
catch {
|
|
78
|
-
|
|
81
|
+
throw new RangeError('base64ToBytes: invalid base64 input');
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
// Fallback: manual decode
|
|
@@ -110,7 +113,7 @@ export const base64ToBytes = (b64) => {
|
|
|
110
113
|
}
|
|
111
114
|
return bin;
|
|
112
115
|
};
|
|
113
|
-
/** Uint8Array to base64 string. Pass url=true for base64url (RFC 4648 §5
|
|
116
|
+
/** Uint8Array to base64 string. Pass url=true for base64url (RFC 4648 §5, no padding characters). */
|
|
114
117
|
export const bytesToBase64 = (bytes, url = false) => {
|
|
115
118
|
if (typeof btoa !== 'undefined') {
|
|
116
119
|
const raw = btoa(String.fromCharCode.apply(null, Array.from(bytes)));
|
|
@@ -136,65 +139,83 @@ export const bytesToBase64 = (bytes, url = false) => {
|
|
|
136
139
|
}
|
|
137
140
|
return base64;
|
|
138
141
|
};
|
|
139
|
-
// ── Constant-time comparison
|
|
140
|
-
import {
|
|
141
|
-
let
|
|
142
|
-
let
|
|
143
|
-
let
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
142
|
+
// ── Constant-time comparison ────────────────────────────────────────────────
|
|
143
|
+
import { CTE_WASM } from './cte-wasm.js';
|
|
144
|
+
let _cteCompare = null;
|
|
145
|
+
let _cteMem = null;
|
|
146
|
+
let _cteMemView = null;
|
|
147
|
+
let _cteInit = false;
|
|
148
|
+
let _cteInitError = null;
|
|
149
|
+
export const CTE_MAX_BYTES = 32768;
|
|
150
|
+
function _initCte() {
|
|
151
|
+
if (_cteInit) {
|
|
152
|
+
if (_cteInitError)
|
|
153
|
+
throw _cteInitError;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
_cteInit = true;
|
|
157
|
+
if (!hasSIMD()) {
|
|
158
|
+
_cteInitError = new Error('leviathan-crypto: constantTimeEqual requires WebAssembly SIMD, '
|
|
159
|
+
+ 'this runtime does not support it');
|
|
160
|
+
throw _cteInitError;
|
|
161
|
+
}
|
|
154
162
|
try {
|
|
155
|
-
|
|
156
|
-
return false;
|
|
157
|
-
_ctMem = new WebAssembly.Memory({ initial: 1, maximum: 1 });
|
|
158
|
-
const buf = CT_WASM.buffer.slice(CT_WASM.byteOffset, CT_WASM.byteOffset + CT_WASM.byteLength);
|
|
163
|
+
const buf = CTE_WASM.buffer.slice(CTE_WASM.byteOffset, CTE_WASM.byteOffset + CTE_WASM.byteLength);
|
|
159
164
|
const mod = new WebAssembly.Module(buf);
|
|
160
|
-
const inst = new WebAssembly.Instance(mod
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
const inst = new WebAssembly.Instance(mod);
|
|
166
|
+
const exports = inst.exports;
|
|
167
|
+
_cteMem = exports.memory;
|
|
168
|
+
_cteMemView = new Uint8Array(_cteMem.buffer);
|
|
169
|
+
_cteCompare = exports.compare;
|
|
163
170
|
}
|
|
164
|
-
catch {
|
|
165
|
-
|
|
171
|
+
catch (cause) {
|
|
172
|
+
_cteInitError = new Error(`leviathan-crypto: cte WASM module failed to instantiate: ${cause.message}`);
|
|
173
|
+
throw _cteInitError;
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
/**
|
|
169
177
|
* Constant-time byte-array equality.
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* Max input size: 32768 bytes per side (enforced regardless of code path).
|
|
178
|
+
* Runs entirely inside a WASM SIMD module (v128 XOR accumulate with
|
|
179
|
+
* branch-free reduction). Throws on runtimes without SIMD support,
|
|
180
|
+
* no JS fallback. Length check is not constant-time (length is
|
|
181
|
+
* non-secret in all protocols). Max input size: 32768 bytes per side.
|
|
175
182
|
*/
|
|
176
183
|
export const constantTimeEqual = (a, b) => {
|
|
177
184
|
if (a.length !== b.length)
|
|
178
185
|
return false;
|
|
179
|
-
if (a.length >
|
|
180
|
-
throw new RangeError(`constantTimeEqual: max ${
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
186
|
+
if (a.length > CTE_MAX_BYTES)
|
|
187
|
+
throw new RangeError(`constantTimeEqual: max ${CTE_MAX_BYTES} bytes (got ${a.length})`);
|
|
188
|
+
_initCte();
|
|
189
|
+
const mem = _cteMemView;
|
|
190
|
+
const compare = _cteCompare;
|
|
191
|
+
if (!mem || !compare)
|
|
192
|
+
throw new Error('leviathan-crypto: cte init invariant violated');
|
|
193
|
+
mem.set(a, 0);
|
|
194
|
+
mem.set(b, a.length);
|
|
195
|
+
try {
|
|
196
|
+
return compare(0, a.length, a.length) === 1;
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
// Wipe the full cte memory region, not just the bytes we wrote.
|
|
200
|
+
// Defense in depth against stale residue from longer prior calls.
|
|
201
|
+
// The module-private WASM memory is never read from outside this
|
|
202
|
+
// function, but we keep the surface clean regardless.
|
|
203
|
+
mem.fill(0, 0, CTE_MAX_BYTES * 2);
|
|
191
204
|
}
|
|
192
|
-
// JS fallback — best-effort constant-time via XOR accumulate
|
|
193
|
-
let diff = 0;
|
|
194
|
-
for (let i = 0; i < a.length; i++)
|
|
195
|
-
diff |= a[i] ^ b[i];
|
|
196
|
-
return diff === 0;
|
|
197
205
|
};
|
|
206
|
+
/**
|
|
207
|
+
* Reset the internal CTE WASM cache, including any cached initialization
|
|
208
|
+
* error. Exists so the test suite can force re-instantiation across
|
|
209
|
+
* describe blocks.
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
export function _cteResetForTesting() {
|
|
213
|
+
_cteInit = false;
|
|
214
|
+
_cteCompare = null;
|
|
215
|
+
_cteMem = null;
|
|
216
|
+
_cteMemView = null;
|
|
217
|
+
_cteInitError = null;
|
|
218
|
+
}
|
|
198
219
|
/** Zero a typed array in place. */
|
|
199
220
|
export const wipe = (data) => {
|
|
200
221
|
data.fill(0);
|
|
@@ -218,11 +239,15 @@ export const concat = (...arrays) => {
|
|
|
218
239
|
};
|
|
219
240
|
/** Cryptographically secure random bytes via Web Crypto API. */
|
|
220
241
|
export const randomBytes = (n) => {
|
|
242
|
+
if (typeof globalThis.crypto === 'undefined'
|
|
243
|
+
|| typeof globalThis.crypto.getRandomValues !== 'function')
|
|
244
|
+
throw new Error('leviathan-crypto: crypto.getRandomValues is required, '
|
|
245
|
+
+ 'this runtime does not expose the Web Crypto API');
|
|
221
246
|
const buf = new Uint8Array(n);
|
|
222
|
-
crypto.getRandomValues(buf);
|
|
247
|
+
globalThis.crypto.getRandomValues(buf);
|
|
223
248
|
return buf;
|
|
224
249
|
};
|
|
225
|
-
// ── SIMD detection
|
|
250
|
+
// ── SIMD detection ──────────────────────────────────────────────────────────
|
|
226
251
|
let _simd = null;
|
|
227
252
|
/**
|
|
228
253
|
* Detects WASM SIMD support once and caches the result.
|
|
@@ -236,7 +261,7 @@ export function hasSIMD() {
|
|
|
236
261
|
_simd = false;
|
|
237
262
|
return _simd;
|
|
238
263
|
}
|
|
239
|
-
// Minimal WASM module using v128
|
|
264
|
+
// Minimal WASM module using v128, validates iff SIMD is supported
|
|
240
265
|
try {
|
|
241
266
|
_simd = WebAssembly.validate(new Uint8Array([
|
|
242
267
|
0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123,
|
package/dist/wasm-source.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* All accepted forms of WASM input for init functions.
|
|
3
3
|
*
|
|
4
|
-
* - `string`
|
|
5
|
-
* - `URL`
|
|
6
|
-
* - `ArrayBuffer`
|
|
7
|
-
* - `Uint8Array`
|
|
8
|
-
* - `WebAssembly.Module`
|
|
9
|
-
* - `Response`
|
|
10
|
-
* - `
|
|
4
|
+
* - `string` , gzip+base64 embedded blob (from `/embedded` subpath)
|
|
5
|
+
* - `URL` , streaming-compiled from `fetch(url)`
|
|
6
|
+
* - `ArrayBuffer` , raw WASM bytes, compiled inline
|
|
7
|
+
* - `Uint8Array` , raw WASM bytes, compiled inline
|
|
8
|
+
* - `WebAssembly.Module` , pre-compiled module (Cloudflare Workers, edge runtimes)
|
|
9
|
+
* - `Response` , streaming-compiled from an in-flight fetch
|
|
10
|
+
* - `PromiseLike<WasmSource>` , any thenable resolving to another `WasmSource`; nesting
|
|
11
|
+
* is resolved recursively (max depth 3).
|
|
11
12
|
*/
|
|
12
|
-
export type WasmSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module | Response |
|
|
13
|
+
export type WasmSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module | Response | PromiseLike<WasmSource>;
|
package/dist/wasm-source.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WASM_GZ_BASE64 as curve25519Wasm, WASM_GZ_BASE64 as x25519Wasm, } from '../embedded/curve25519.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
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/x25519/embedded.ts
|
|
23
|
+
//
|
|
24
|
+
// Exports the gzip+base64 curve25519 WASM blob for use as a WasmSource.
|
|
25
|
+
// Ed25519 and X25519 share the same curve25519 WASM binary; both the
|
|
26
|
+
// `leviathan-crypto/ed25519/embedded` and `leviathan-crypto/x25519/embedded`
|
|
27
|
+
// subpaths re-export the same blob under three names: `curve25519Wasm`
|
|
28
|
+
// (canonical), `ed25519Wasm`, and `x25519Wasm` (aliases that read more
|
|
29
|
+
// naturally in the matching subpath context). All three resolve to the
|
|
30
|
+
// identical underlying string; tree-shaking is unaffected.
|
|
31
|
+
export { WASM_GZ_BASE64 as curve25519Wasm, WASM_GZ_BASE64 as x25519Wasm, } from '../embedded/curve25519.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isInitialized } from '../init.js';
|
|
2
|
+
import type { WasmSource } from '../wasm-source.js';
|
|
3
|
+
import type { X25519KeyPair } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Initialise the curve25519 WASM module under the `x25519` alias.
|
|
6
|
+
* Equivalent to `ed25519Init(source)`; both target the same WASM module
|
|
7
|
+
* and the init layer de-dupes when given identical sources.
|
|
8
|
+
*/
|
|
9
|
+
export declare function x25519Init(source: WasmSource): Promise<void>;
|
|
10
|
+
export type { WasmSource };
|
|
11
|
+
export type { X25519KeyPair, X25519Exports } from './types.js';
|
|
12
|
+
export { isInitialized };
|
|
13
|
+
export declare class X25519 {
|
|
14
|
+
constructor();
|
|
15
|
+
private get mx();
|
|
16
|
+
/**
|
|
17
|
+
* Deterministic X25519 key generation from a 32-byte secret per
|
|
18
|
+
* RFC 7748 §6. Clamping is applied internally on every WASM call;
|
|
19
|
+
* the returned secretKey is a fresh copy of the supplied sk bytes
|
|
20
|
+
* (NOT pre-clamped).
|
|
21
|
+
*/
|
|
22
|
+
keygenDerand(sk: Uint8Array): X25519KeyPair;
|
|
23
|
+
/** Random X25519 key generation, wraps `keygenDerand` with `randomBytes(32)`. */
|
|
24
|
+
keygen(): X25519KeyPair;
|
|
25
|
+
/**
|
|
26
|
+
* X25519 Diffie-Hellman, RFC 7748 §6.
|
|
27
|
+
*
|
|
28
|
+
* Computes the shared u-coordinate from the local secret and the
|
|
29
|
+
* peer's public key. If the resulting shared secret is all-zero
|
|
30
|
+
* (peer public key is a small-order point per RFC 7748 §7), throws
|
|
31
|
+
* `KeyAgreementError` rather than returning the degenerate value.
|
|
32
|
+
* The all-zero scan accumulates via OR across all 32 output bytes
|
|
33
|
+
* before comparing to zero, so the success path is constant-time;
|
|
34
|
+
* the throw branches off only after a known-bad outcome is observed.
|
|
35
|
+
*
|
|
36
|
+
* @param sk 32-byte local secret (NOT pre-clamped, clamped internally)
|
|
37
|
+
* @param peerPk 32-byte peer public key (u-coordinate)
|
|
38
|
+
* @returns 32-byte shared u-coordinate
|
|
39
|
+
* @throws KeyAgreementError on all-zero shared secret
|
|
40
|
+
*/
|
|
41
|
+
dh(sk: Uint8Array, peerPk: Uint8Array): Uint8Array;
|
|
42
|
+
dispose(): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
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/x25519/index.ts
|
|
23
|
+
//
|
|
24
|
+
// X25519 public API. RFC 7748 §5 (algorithm), §6 (keygen + DH), §7
|
|
25
|
+
// (security considerations and the small-order peer-pk rejection that
|
|
26
|
+
// motivates the TS-layer all-zero shared-secret check).
|
|
27
|
+
//
|
|
28
|
+
// Both Ed25519 and X25519 share the curve25519 WASM module;
|
|
29
|
+
// `ed25519Init(source)` and `x25519Init(source)` both target it and the
|
|
30
|
+
// init() layer de-dupes when given identical sources.
|
|
31
|
+
//
|
|
32
|
+
// Per-call lifecycle mirrors the Ed25519 wrapper: stage caller inputs
|
|
33
|
+
// at fixed offsets above the WASM mutable buffer region, call the
|
|
34
|
+
// underlying export, copy outputs to fresh `Uint8Array`s, wipe both
|
|
35
|
+
// the WASM-internal scratch and the TS-side I/O staging region.
|
|
36
|
+
import { getInstance, initModule, isInitialized, _assertNotOwned } from '../init.js';
|
|
37
|
+
import { randomBytes, wipe } from '../utils.js';
|
|
38
|
+
import { KeyAgreementError } from '../errors.js';
|
|
39
|
+
import { validateSecretKey, validatePublicKey } from './validate.js';
|
|
40
|
+
/**
|
|
41
|
+
* Initialise the curve25519 WASM module under the `x25519` alias.
|
|
42
|
+
* Equivalent to `ed25519Init(source)`; both target the same WASM module
|
|
43
|
+
* and the init layer de-dupes when given identical sources.
|
|
44
|
+
*/
|
|
45
|
+
export async function x25519Init(source) {
|
|
46
|
+
return initModule('curve25519', source);
|
|
47
|
+
}
|
|
48
|
+
export { isInitialized };
|
|
49
|
+
// ── I/O staging layout ─────────────────────────────────────────────────────
|
|
50
|
+
//
|
|
51
|
+
// Same base as the ed25519 wrapper, fixed offsets above the WASM mutable
|
|
52
|
+
// buffer region (BUFFER_END = 7836). Two 32-byte input slots plus one
|
|
53
|
+
// 32-byte output slot, no large variable region.
|
|
54
|
+
const IO_BASE = 8192;
|
|
55
|
+
const SK_STAGE = IO_BASE; // 32 bytes
|
|
56
|
+
const PK_STAGE = IO_BASE + 32; // 32 bytes
|
|
57
|
+
const PEER_STAGE = IO_BASE + 64; // 32 bytes
|
|
58
|
+
const SHARED_STAGE = IO_BASE + 96; // 32 bytes
|
|
59
|
+
function ioWipe(mx) {
|
|
60
|
+
// Zero the entire TS-managed staging region. wipeBuffers covers
|
|
61
|
+
// MUTABLE_START..BUFFER_END only; the I/O slots above must be
|
|
62
|
+
// scrubbed at the wrapper layer.
|
|
63
|
+
new Uint8Array(mx.memory.buffer).fill(0, IO_BASE, mx.memory.buffer.byteLength);
|
|
64
|
+
}
|
|
65
|
+
export class X25519 {
|
|
66
|
+
constructor() {
|
|
67
|
+
if (!isInitialized('curve25519'))
|
|
68
|
+
throw new Error('leviathan-crypto: call init({ x25519: ... }) before using X25519');
|
|
69
|
+
}
|
|
70
|
+
get mx() {
|
|
71
|
+
return getInstance('curve25519').exports;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Deterministic X25519 key generation from a 32-byte secret per
|
|
75
|
+
* RFC 7748 §6. Clamping is applied internally on every WASM call;
|
|
76
|
+
* the returned secretKey is a fresh copy of the supplied sk bytes
|
|
77
|
+
* (NOT pre-clamped).
|
|
78
|
+
*/
|
|
79
|
+
keygenDerand(sk) {
|
|
80
|
+
_assertNotOwned('curve25519');
|
|
81
|
+
validateSecretKey(sk);
|
|
82
|
+
const mx = this.mx;
|
|
83
|
+
const mem = new Uint8Array(mx.memory.buffer);
|
|
84
|
+
mem.set(sk, SK_STAGE);
|
|
85
|
+
try {
|
|
86
|
+
mx.x25519Keygen(SK_STAGE, PK_STAGE);
|
|
87
|
+
const publicKey = mem.slice(PK_STAGE, PK_STAGE + 32);
|
|
88
|
+
const secretKey = new Uint8Array(32);
|
|
89
|
+
secretKey.set(sk);
|
|
90
|
+
return { publicKey, secretKey };
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
ioWipe(mx);
|
|
94
|
+
mx.wipeBuffers();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Random X25519 key generation, wraps `keygenDerand` with `randomBytes(32)`. */
|
|
98
|
+
keygen() {
|
|
99
|
+
const sk = randomBytes(32);
|
|
100
|
+
try {
|
|
101
|
+
return this.keygenDerand(sk);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
wipe(sk);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* X25519 Diffie-Hellman, RFC 7748 §6.
|
|
109
|
+
*
|
|
110
|
+
* Computes the shared u-coordinate from the local secret and the
|
|
111
|
+
* peer's public key. If the resulting shared secret is all-zero
|
|
112
|
+
* (peer public key is a small-order point per RFC 7748 §7), throws
|
|
113
|
+
* `KeyAgreementError` rather than returning the degenerate value.
|
|
114
|
+
* The all-zero scan accumulates via OR across all 32 output bytes
|
|
115
|
+
* before comparing to zero, so the success path is constant-time;
|
|
116
|
+
* the throw branches off only after a known-bad outcome is observed.
|
|
117
|
+
*
|
|
118
|
+
* @param sk 32-byte local secret (NOT pre-clamped, clamped internally)
|
|
119
|
+
* @param peerPk 32-byte peer public key (u-coordinate)
|
|
120
|
+
* @returns 32-byte shared u-coordinate
|
|
121
|
+
* @throws KeyAgreementError on all-zero shared secret
|
|
122
|
+
*/
|
|
123
|
+
dh(sk, peerPk) {
|
|
124
|
+
_assertNotOwned('curve25519');
|
|
125
|
+
validateSecretKey(sk);
|
|
126
|
+
validatePublicKey(peerPk);
|
|
127
|
+
const mx = this.mx;
|
|
128
|
+
const mem = new Uint8Array(mx.memory.buffer);
|
|
129
|
+
mem.set(sk, SK_STAGE);
|
|
130
|
+
mem.set(peerPk, PEER_STAGE);
|
|
131
|
+
try {
|
|
132
|
+
mx.x25519DH(SK_STAGE, PEER_STAGE, SHARED_STAGE);
|
|
133
|
+
const shared = mem.slice(SHARED_STAGE, SHARED_STAGE + 32);
|
|
134
|
+
// Constant-time OR-accumulate scan, RFC 7748 §6.1
|
|
135
|
+
// contributory-behaviour requirement. No early exit on the
|
|
136
|
+
// first non-zero byte.
|
|
137
|
+
let acc = 0;
|
|
138
|
+
for (let i = 0; i < 32; i++)
|
|
139
|
+
acc |= shared[i];
|
|
140
|
+
if (acc === 0) {
|
|
141
|
+
wipe(shared);
|
|
142
|
+
throw new KeyAgreementError('leviathan-crypto: X25519 shared secret is all-zero '
|
|
143
|
+
+ '(peer public key is a small-order point)');
|
|
144
|
+
}
|
|
145
|
+
return shared;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
ioWipe(mx);
|
|
149
|
+
mx.wipeBuffers();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
dispose() {
|
|
153
|
+
try {
|
|
154
|
+
this.mx.wipeBuffers();
|
|
155
|
+
ioWipe(this.mx);
|
|
156
|
+
}
|
|
157
|
+
catch { /* idempotent */ }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface X25519KeyPair {
|
|
2
|
+
/** 32-byte public u-coordinate. */
|
|
3
|
+
publicKey: Uint8Array;
|
|
4
|
+
/**
|
|
5
|
+
* 32-byte secret. Per RFC 7748 §5 / §6 this is "any 32 random bytes";
|
|
6
|
+
* clamping is applied internally on every WASM call. The stored
|
|
7
|
+
* secretKey is the unclamped form so that round-tripping through
|
|
8
|
+
* keygen / external storage preserves byte-equality.
|
|
9
|
+
*/
|
|
10
|
+
secretKey: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The X25519-relevant subset of the curve25519 WASM exports. The
|
|
14
|
+
* curve25519 module is shared between Ed25519 and X25519; this interface
|
|
15
|
+
* deliberately surfaces only the X25519 high-level entry points plus the
|
|
16
|
+
* layout / wipe primitives.
|
|
17
|
+
*/
|
|
18
|
+
export interface X25519Exports {
|
|
19
|
+
memory: WebAssembly.Memory;
|
|
20
|
+
getModuleId: () => number;
|
|
21
|
+
getMemoryPages: () => number;
|
|
22
|
+
x25519Keygen: (skOff: number, pkOff: number) => void;
|
|
23
|
+
x25519DH: (skOff: number, peerPkOff: number, sharedOff: number) => void;
|
|
24
|
+
wipeBuffers: () => void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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/x25519/types.ts
|
|
23
|
+
//
|
|
24
|
+
// X25519 type surface: the WASM export interface for the curve25519
|
|
25
|
+
// module (X25519-relevant subset) and the public key-pair shape returned
|
|
26
|
+
// by keygen / keygenDerand. RFC 7748 §5 / §6.
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
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/x25519/validate.ts
|
|
23
|
+
//
|
|
24
|
+
// X25519 caller-side input validation. Pure length / type checks. Peer
|
|
25
|
+
// public-key masking happens inside the WASM (feFromBytes masks bit 255
|
|
26
|
+
// per RFC 7748 §5); the all-zero shared-secret rejection happens in the
|
|
27
|
+
// TS X25519.dh method after the WASM returns.
|
|
28
|
+
export function validateSecretKey(sk) {
|
|
29
|
+
if (!(sk instanceof Uint8Array))
|
|
30
|
+
throw new TypeError('leviathan-crypto: x25519 secret key must be a Uint8Array');
|
|
31
|
+
if (sk.length !== 32)
|
|
32
|
+
throw new RangeError(`leviathan-crypto: x25519 secret key must be 32 bytes (got ${sk.length})`);
|
|
33
|
+
}
|
|
34
|
+
export function validatePublicKey(pk) {
|
|
35
|
+
if (!(pk instanceof Uint8Array))
|
|
36
|
+
throw new TypeError('leviathan-crypto: x25519 public key must be a Uint8Array');
|
|
37
|
+
if (pk.length !== 32)
|
|
38
|
+
throw new RangeError(`leviathan-crypto: x25519 public key must be 32 bytes (got ${pk.length})`);
|
|
39
|
+
}
|