subtlepq 0.1.1 → 0.2.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/README.md +9 -4
- package/package.json +21 -19
- package/src/algorithms.js +1 -1
- package/src/engine.js +2 -2
- package/src/errors.js +1 -1
- package/src/formats.js +174 -0
- package/src/index.js +5 -6
- package/src/install.js +7 -8
- package/src/keystore.js +3 -3
- package/src/router.js +43 -2
- package/src/subtle.js +161 -16
- package/wasm/dist/subtlepq-engine.mjs +2 -0
- package/wasm/dist/subtlepq-engine.single.mjs +0 -0
- package/wasm/dist/subtlepq-engine.wasm +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# subtlepq
|
|
2
2
|
|
|
3
3
|
Post-quantum polyfill for the Web Cryptography API: ML-KEM (FIPS 203) and ML-DSA (FIPS 204) per the [WICG Modern Algorithms draft](https://wicg.github.io/webcrypto-modern-algos/), with native delegation where the platform already supports it.
|
|
4
4
|
|
|
5
|
-
**Status:
|
|
5
|
+
**Status: P2 — engine, core JS layer, all key formats, key wrapping.** DHKEM is on the way.
|
|
6
|
+
|
|
7
|
+
Key formats: `raw-public`, `raw-seed`, `spki`, `pkcs8` (seed-only encoding per the LAMPS
|
|
8
|
+
drafts; the `both` CHOICE is accepted on import and cross-checked), and `jwk` (kty `"AKP"`,
|
|
9
|
+
`pub`/`priv` with `priv` = seed). `wrapKey`/`unwrapKey` compose export/import with the native
|
|
10
|
+
wrapping algorithm, so AES-GCM, AES-KW, and RSA-OAEP wrapping of ML-* keys just work.
|
|
6
11
|
|
|
7
12
|
## Use
|
|
8
13
|
|
|
@@ -31,7 +36,7 @@ Known limits (inherent to a script polyfill, made loud rather than silent): ML-*
|
|
|
31
36
|
|
|
32
37
|
## Engine
|
|
33
38
|
|
|
34
|
-
`wasm/` builds a minimal [liboqs](https://github.com/open-quantum-safe/liboqs) (pinned submodule, ML-KEM + ML-DSA only) plus a thin shim (`
|
|
39
|
+
`wasm/` builds a minimal [liboqs](https://github.com/open-quantum-safe/liboqs) (pinned submodule, ML-KEM + ML-DSA only) plus a thin shim (`subtlepq_engine.c`) to WebAssembly:
|
|
35
40
|
|
|
36
41
|
```
|
|
37
42
|
git submodule update --init
|
|
@@ -39,7 +44,7 @@ wasm/build.sh # needs emsdk; override location with EMSDK=...
|
|
|
39
44
|
npm test
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
Outputs `wasm/dist/
|
|
47
|
+
Outputs `wasm/dist/subtlepq-engine.mjs` (+ `.wasm` sidecar, CSP-clean) and `subtlepq-engine.single.mjs` (self-contained, requires `'wasm-unsafe-eval'` under strict CSP). 75 KB wasm covers all six parameter sets.
|
|
43
48
|
|
|
44
49
|
Seeded operations liboqs does not expose publicly (ML-DSA keygen from seed, KAT-deterministic encapsulation/signing) use a bracketed RNG-playback override — the same technique liboqs' own ACVP tests use.
|
|
45
50
|
|
package/package.json
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subtlepq",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Post-quantum polyfill for the Web Cryptography API: ML-KEM (FIPS 203) and ML-DSA (FIPS 204) per the WICG Modern Algorithms draft, with native delegation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "VESvault Corp",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/vesvault/
|
|
10
|
+
"url": "git+https://github.com/vesvault/subtlepq.git"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://github.com/vesvault/
|
|
12
|
+
"homepage": "https://github.com/vesvault/subtlepq#readme",
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/vesvault/
|
|
14
|
+
"url": "https://github.com/vesvault/subtlepq/issues"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"sideEffects": false,
|
|
20
20
|
"files": [
|
|
21
|
-
"src",
|
|
22
|
-
"
|
|
21
|
+
"src/algorithms.js",
|
|
22
|
+
"src/engine.js",
|
|
23
|
+
"src/errors.js",
|
|
24
|
+
"src/formats.js",
|
|
25
|
+
"src/index.js",
|
|
26
|
+
"src/install.js",
|
|
27
|
+
"src/keystore.js",
|
|
28
|
+
"src/router.js",
|
|
29
|
+
"src/subtle.js",
|
|
30
|
+
"wasm/dist/subtlepq-engine.mjs",
|
|
31
|
+
"wasm/dist/subtlepq-engine.single.mjs",
|
|
32
|
+
"wasm/dist/subtlepq-engine.wasm"
|
|
23
33
|
],
|
|
24
34
|
"exports": {
|
|
25
35
|
".": "./src/index.js",
|
|
@@ -27,20 +37,12 @@
|
|
|
27
37
|
},
|
|
28
38
|
"scripts": {
|
|
29
39
|
"build": "wasm/build.sh",
|
|
40
|
+
"prepack": "node -e \"const p=require('./package.json'),fs=require('fs');for(const f of p.files)if(!fs.existsSync(f))throw new Error(f+' missing - run wasm/build.sh before packing')\"",
|
|
30
41
|
"test": "node test/engine-test.mjs && node test/polyfill-test.mjs"
|
|
31
42
|
},
|
|
32
43
|
"keywords": [
|
|
33
|
-
"ml-kem",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"polyfill",
|
|
37
|
-
"post-quantum",
|
|
38
|
-
"fips203",
|
|
39
|
-
"fips204",
|
|
40
|
-
"kyber",
|
|
41
|
-
"dilithium",
|
|
42
|
-
"subtlecrypto",
|
|
43
|
-
"encapsulation",
|
|
44
|
-
"kem"
|
|
44
|
+
"ml-kem", "ml-dsa", "webcrypto", "polyfill", "post-quantum",
|
|
45
|
+
"fips203", "fips204", "kyber", "dilithium", "subtlecrypto",
|
|
46
|
+
"encapsulation", "kem"
|
|
45
47
|
]
|
|
46
|
-
}
|
|
48
|
+
}
|
package/src/algorithms.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: algorithm registry and AlgorithmIdentifier normalization
|
|
3
3
|
*
|
|
4
4
|
* (c) 2026 VESvault Corp
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
package/src/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: lazy wasm engine loader + typed wrappers over subtlepq_engine.c
|
|
3
3
|
*
|
|
4
4
|
* (c) 2026 VESvault Corp
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -18,7 +18,7 @@ export function getEngine() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async function load() {
|
|
21
|
-
const { default: init } = await import("../wasm/dist/
|
|
21
|
+
const { default: init } = await import("../wasm/dist/subtlepq-engine.mjs");
|
|
22
22
|
return new Engine(await init());
|
|
23
23
|
}
|
|
24
24
|
|
package/src/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: DOMException helpers with WebCrypto-mandated error names
|
|
3
3
|
*
|
|
4
4
|
* (c) 2026 VESvault Corp
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
package/src/formats.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/***************************************************************************
|
|
2
|
+
* subtlepq: spki / pkcs8 / jwk codecs (fixed ASN.1 templates)
|
|
3
|
+
*
|
|
4
|
+
* (c) 2026 VESvault Corp
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*
|
|
7
|
+
* DER shapes per draft-ietf-lamps-kyber/dilithium-certificates as adopted
|
|
8
|
+
* by the WICG draft: SPKI AlgorithmIdentifier has NO parameters; PKCS#8
|
|
9
|
+
* privateKey is the seed-only CHOICE, an implicit [0] primitive holding
|
|
10
|
+
* the seed. Import also accepts the "both" CHOICE (seed + expandedKey,
|
|
11
|
+
* verified against each other); expandedKey-only is NotSupportedError.
|
|
12
|
+
***************************************************************************/
|
|
13
|
+
|
|
14
|
+
import * as E from "./errors.js";
|
|
15
|
+
|
|
16
|
+
/* NIST algorithm arc: 2.16.840.1.101.3.4.{4=kem,3=sig}.n */
|
|
17
|
+
const OIDS = {
|
|
18
|
+
"ML-KEM-512": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x01],
|
|
19
|
+
"ML-KEM-768": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x02],
|
|
20
|
+
"ML-KEM-1024": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x03],
|
|
21
|
+
"ML-DSA-44": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x11],
|
|
22
|
+
"ML-DSA-65": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12],
|
|
23
|
+
"ML-DSA-87": [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x13],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function oidToName(body) {
|
|
27
|
+
for (const [name, oid] of Object.entries(OIDS)) {
|
|
28
|
+
if (oid.length === body.length && oid.every((b, i) => b === body[i])) return name;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ---- DER write ---- */
|
|
34
|
+
|
|
35
|
+
function tlv(tag, ...parts) {
|
|
36
|
+
const len = parts.reduce((n, p) => n + p.length, 0);
|
|
37
|
+
const hdr = [tag];
|
|
38
|
+
if (len < 0x80) {
|
|
39
|
+
hdr.push(len);
|
|
40
|
+
} else {
|
|
41
|
+
const b = [];
|
|
42
|
+
for (let n = len; n; n >>>= 8) b.unshift(n & 0xff);
|
|
43
|
+
hdr.push(0x80 | b.length, ...b);
|
|
44
|
+
}
|
|
45
|
+
const out = new Uint8Array(hdr.length + len);
|
|
46
|
+
out.set(hdr);
|
|
47
|
+
let p = hdr.length;
|
|
48
|
+
for (const part of parts) { out.set(part, p); p += part.length; }
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function algorithmIdentifier(name) {
|
|
53
|
+
return tlv(0x30, tlv(0x06, OIDS[name]));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ---- DER read ---- */
|
|
57
|
+
|
|
58
|
+
class Reader {
|
|
59
|
+
constructor(u8, what) { this.u8 = u8; this.p = 0; this.what = what; }
|
|
60
|
+
bad(m) { return E.dataError("malformed " + this.what + ": " + m); }
|
|
61
|
+
tlv() {
|
|
62
|
+
if (this.p + 2 > this.u8.length) throw this.bad("truncated");
|
|
63
|
+
const tag = this.u8[this.p++];
|
|
64
|
+
let len = this.u8[this.p++];
|
|
65
|
+
if (len & 0x80) {
|
|
66
|
+
const n = len & 0x7f;
|
|
67
|
+
if (n < 1 || n > 4) throw this.bad("bad length encoding");
|
|
68
|
+
len = 0;
|
|
69
|
+
for (let i = 0; i < n; i++) {
|
|
70
|
+
if (this.p >= this.u8.length) throw this.bad("truncated");
|
|
71
|
+
len = len * 256 + this.u8[this.p++];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (this.p + len > this.u8.length) throw this.bad("truncated");
|
|
75
|
+
const val = this.u8.subarray(this.p, this.p + len);
|
|
76
|
+
this.p += len;
|
|
77
|
+
return { tag, val };
|
|
78
|
+
}
|
|
79
|
+
expect(tag, m) {
|
|
80
|
+
const t = this.tlv();
|
|
81
|
+
if (t.tag !== tag) throw this.bad("expected " + m);
|
|
82
|
+
return t.val;
|
|
83
|
+
}
|
|
84
|
+
get done() { return this.p >= this.u8.length; }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readAlgorithm(r, what) {
|
|
88
|
+
const seq = new Reader(r.expect(0x30, "AlgorithmIdentifier"), what);
|
|
89
|
+
const name = oidToName(seq.expect(0x06, "algorithm OID"));
|
|
90
|
+
if (!name) throw E.dataError(what + " algorithm OID is not an ML-KEM/ML-DSA algorithm");
|
|
91
|
+
if (!seq.done) throw E.dataError(what + " AlgorithmIdentifier parameters must be absent");
|
|
92
|
+
return name;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ---- SubjectPublicKeyInfo ---- */
|
|
96
|
+
|
|
97
|
+
export function encodeSpki(name, pub) {
|
|
98
|
+
const bits = new Uint8Array(pub.length + 1);
|
|
99
|
+
bits.set(pub, 1);
|
|
100
|
+
return tlv(0x30, algorithmIdentifier(name), tlv(0x03, bits));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function decodeSpki(bytes) {
|
|
104
|
+
const outer = new Reader(bytes, "SPKI");
|
|
105
|
+
const r = new Reader(outer.expect(0x30, "SubjectPublicKeyInfo"), "SPKI");
|
|
106
|
+
if (!outer.done) throw outer.bad("trailing bytes");
|
|
107
|
+
const name = readAlgorithm(r, "SPKI");
|
|
108
|
+
const bits = r.expect(0x03, "subjectPublicKey BIT STRING");
|
|
109
|
+
if (!r.done) throw r.bad("trailing bytes");
|
|
110
|
+
if (bits.length < 1 || bits[0] !== 0) throw r.bad("subjectPublicKey unused bits");
|
|
111
|
+
return { name, pub: bits.slice(1) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ---- PrivateKeyInfo (seed-only CHOICE) ---- */
|
|
115
|
+
|
|
116
|
+
export function encodePkcs8(name, seed) {
|
|
117
|
+
return tlv(0x30,
|
|
118
|
+
tlv(0x02, [0]),
|
|
119
|
+
algorithmIdentifier(name),
|
|
120
|
+
tlv(0x04, tlv(0x80, seed)));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Returns { name, seed, expanded? }; expanded (from the "both" CHOICE) is
|
|
124
|
+
* the caller's to verify against the seed-derived key. */
|
|
125
|
+
export function decodePkcs8(bytes) {
|
|
126
|
+
const outer = new Reader(bytes, "PKCS#8");
|
|
127
|
+
const r = new Reader(outer.expect(0x30, "PrivateKeyInfo"), "PKCS#8");
|
|
128
|
+
if (!outer.done) throw outer.bad("trailing bytes");
|
|
129
|
+
const ver = r.expect(0x02, "version INTEGER");
|
|
130
|
+
if (ver.length !== 1 || ver[0] > 1) throw r.bad("unsupported version");
|
|
131
|
+
const name = readAlgorithm(r, "PKCS#8");
|
|
132
|
+
const inner = new Reader(r.expect(0x04, "privateKey OCTET STRING"), "PKCS#8");
|
|
133
|
+
/* anything after privateKey (attributes, [1] publicKey) is ignored */
|
|
134
|
+
const t = inner.tlv();
|
|
135
|
+
if (t.tag === 0x80) { /* seed [0] */
|
|
136
|
+
if (!inner.done) throw inner.bad("trailing bytes after seed");
|
|
137
|
+
return { name, seed: t.val.slice() };
|
|
138
|
+
}
|
|
139
|
+
if (t.tag === 0x04) { /* expandedKey */
|
|
140
|
+
throw E.notSupported(
|
|
141
|
+
"expandedKey-only ML private keys are not supported; provide the seed");
|
|
142
|
+
}
|
|
143
|
+
if (t.tag === 0x30) { /* both */
|
|
144
|
+
const b = new Reader(t.val, "PKCS#8");
|
|
145
|
+
const seed = b.expect(0x04, "seed OCTET STRING");
|
|
146
|
+
const expanded = b.expect(0x04, "expandedKey OCTET STRING");
|
|
147
|
+
if (!b.done || !inner.done) throw b.bad("trailing bytes");
|
|
148
|
+
return { name, seed: seed.slice(), expanded: expanded.slice() };
|
|
149
|
+
}
|
|
150
|
+
throw inner.bad("unrecognized private key CHOICE");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* ---- JWK base64url ---- */
|
|
154
|
+
|
|
155
|
+
export function b64u(bytes) {
|
|
156
|
+
let s = "";
|
|
157
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
158
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function unb64u(s, what) {
|
|
162
|
+
if (typeof s !== "string" || /[^A-Za-z0-9_-]/.test(s)) {
|
|
163
|
+
throw E.dataError("JWK " + what + " is not base64url");
|
|
164
|
+
}
|
|
165
|
+
let bin;
|
|
166
|
+
try {
|
|
167
|
+
bin = atob(s.replace(/-/g, "+").replace(/_/g, "/"));
|
|
168
|
+
} catch {
|
|
169
|
+
throw E.dataError("JWK " + what + " is not base64url");
|
|
170
|
+
}
|
|
171
|
+
const out = new Uint8Array(bin.length);
|
|
172
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
173
|
+
return out;
|
|
174
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: post-quantum polyfill for the Web Cryptography API
|
|
3
3
|
* ML-KEM (FIPS 203) + ML-DSA (FIPS 204) per the WICG Modern Algorithms
|
|
4
4
|
* draft, with native delegation.
|
|
5
5
|
*
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
***************************************************************************/
|
|
13
13
|
|
|
14
14
|
import * as ours from "./subtle.js";
|
|
15
|
-
import { ROUTES } from "./router.js";
|
|
15
|
+
import { ROUTES, shape } from "./router.js";
|
|
16
16
|
import * as E from "./errors.js";
|
|
17
17
|
|
|
18
18
|
export { install, uninstall } from "./install.js";
|
|
@@ -21,13 +21,12 @@ export { supports } from "./subtle.js";
|
|
|
21
21
|
const routedFns = {};
|
|
22
22
|
function routed(op) {
|
|
23
23
|
if (!routedFns[op]) {
|
|
24
|
-
routedFns[op] =
|
|
24
|
+
routedFns[op] = shape(op, (args) => {
|
|
25
25
|
if (ROUTES[op](args)) return ours[op](...args);
|
|
26
26
|
const ns = globalThis.crypto && globalThis.crypto.subtle;
|
|
27
27
|
if (ns && typeof ns[op] === "function") return ns[op](...args);
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
Object.defineProperty(routedFns[op], "name", { value: op });
|
|
28
|
+
throw E.notSupported(op + " is not supported");
|
|
29
|
+
});
|
|
31
30
|
}
|
|
32
31
|
return routedFns[op];
|
|
33
32
|
}
|
package/src/install.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: true-polyfill installer -- patches globalThis.crypto.subtle
|
|
3
3
|
*
|
|
4
4
|
* (c) 2026 VESvault Corp
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*
|
|
7
7
|
* install() wraps SubtleCrypto.prototype methods so ML-* algorithms and
|
|
8
|
-
*
|
|
8
|
+
* subtlepq keys are handled here while every other call reaches the saved
|
|
9
9
|
* native original with untouched arguments. Methods and algorithms the
|
|
10
10
|
* platform already supports natively are left alone (self-retiring).
|
|
11
11
|
* uninstall() restores the originals.
|
|
12
12
|
***************************************************************************/
|
|
13
13
|
|
|
14
14
|
import * as ours from "./subtle.js";
|
|
15
|
-
import { ROUTES } from "./router.js";
|
|
15
|
+
import { ROUTES, shape } from "./router.js";
|
|
16
16
|
import * as E from "./errors.js";
|
|
17
17
|
|
|
18
18
|
let saved = null;
|
|
@@ -30,7 +30,7 @@ export function install() {
|
|
|
30
30
|
if (saved) return;
|
|
31
31
|
const subtle = globalThis.crypto && globalThis.crypto.subtle;
|
|
32
32
|
if (!subtle) {
|
|
33
|
-
throw new Error("
|
|
33
|
+
throw new Error("subtlepq: crypto.subtle is unavailable (insecure context?)");
|
|
34
34
|
}
|
|
35
35
|
const proto = Object.getPrototypeOf(subtle);
|
|
36
36
|
const ctor = subtle.constructor;
|
|
@@ -45,12 +45,11 @@ export function install() {
|
|
|
45
45
|
for (const [op, isOurs] of Object.entries(ROUTES)) {
|
|
46
46
|
const orig = proto[op];
|
|
47
47
|
saved.methods[op] = Object.getOwnPropertyDescriptor(proto, op);
|
|
48
|
-
const wrapped =
|
|
48
|
+
const wrapped = shape(op, (args, self) => {
|
|
49
49
|
if (isOurs(args)) return ours[op](...args);
|
|
50
|
-
if (typeof orig === "function") return orig.apply(
|
|
50
|
+
if (typeof orig === "function") return orig.apply(self, args);
|
|
51
51
|
throw E.notSupported(op + " is not supported");
|
|
52
|
-
};
|
|
53
|
-
Object.defineProperty(wrapped, "name", { value: op });
|
|
52
|
+
});
|
|
54
53
|
Object.defineProperty(proto, op, {
|
|
55
54
|
value: wrapped, writable: true, configurable: true,
|
|
56
55
|
});
|
package/src/keystore.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: forged CryptoKey factory + private key-material store
|
|
3
3
|
*
|
|
4
4
|
* (c) 2026 VESvault Corp
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -29,8 +29,8 @@ export function forgeKey(type, algName, extractable, usages, material) {
|
|
|
29
29
|
extractable: { value: !!extractable, enumerable: true },
|
|
30
30
|
algorithm: { value: Object.freeze({ name: algName }), enumerable: true },
|
|
31
31
|
usages: { value: Object.freeze([...usages]), enumerable: true },
|
|
32
|
-
|
|
33
|
-
value: Symbol("
|
|
32
|
+
__subtlepq: {
|
|
33
|
+
value: Symbol("subtlepq key: not structured-cloneable; export and re-import instead"),
|
|
34
34
|
enumerable: true,
|
|
35
35
|
},
|
|
36
36
|
});
|
package/src/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: dispatch rules shared by the ponyfill (index.js) and the
|
|
3
3
|
* polyfill installer (install.js)
|
|
4
4
|
*
|
|
5
5
|
* (c) 2026 VESvault Corp
|
|
@@ -15,7 +15,7 @@ import { isOurKey } from "./keystore.js";
|
|
|
15
15
|
|
|
16
16
|
const ours = (alg) => !!normalizeAlg(alg);
|
|
17
17
|
|
|
18
|
-
/* args => should
|
|
18
|
+
/* args => should subtlepq handle this call? -- keyed by SubtleCrypto method */
|
|
19
19
|
export const ROUTES = {
|
|
20
20
|
/* wrapped existing methods */
|
|
21
21
|
generateKey: (args) => ours(args[0]),
|
|
@@ -23,6 +23,10 @@ export const ROUTES = {
|
|
|
23
23
|
exportKey: (args) => isOurKey(args[1]),
|
|
24
24
|
sign: (args) => isOurKey(args[1]) || ours(args[0]),
|
|
25
25
|
verify: (args) => isOurKey(args[1]) || ours(args[0]),
|
|
26
|
+
/* wrap/unwrap: ours when the wrapped key or either key handle is ours;
|
|
27
|
+
* (format, key, wrappingKey, ...) / (format, data, unwrappingKey, wrapAlg, keyAlg, ...) */
|
|
28
|
+
wrapKey: (args) => isOurKey(args[1]) || isOurKey(args[2]),
|
|
29
|
+
unwrapKey: (args) => ours(args[4]) || isOurKey(args[2]),
|
|
26
30
|
/* methods added by the WICG draft */
|
|
27
31
|
encapsulateBits: (args) => ours(args[0]) || isOurKey(args[1]),
|
|
28
32
|
encapsulateKey: (args) => ours(args[0]) || isOurKey(args[1]),
|
|
@@ -30,3 +34,40 @@ export const ROUTES = {
|
|
|
30
34
|
decapsulateKey: (args) => ours(args[0]) || isOurKey(args[1]),
|
|
31
35
|
getPublicKey: (args) => isOurKey(args[0]),
|
|
32
36
|
};
|
|
37
|
+
|
|
38
|
+
/* Wrapper factories carrying the spec parameter lists, so consoles show real
|
|
39
|
+
* argument hints (and fn.length matches native) instead of (...args). Written
|
|
40
|
+
* out statically -- Function() codegen would break under strict CSP. Each
|
|
41
|
+
* forwards (arguments, this) to the routing handler; the async wrapper turns
|
|
42
|
+
* sync throws into rejections, as native SubtleCrypto does. */
|
|
43
|
+
export const shape = (op, h) => SHAPES[op](h);
|
|
44
|
+
|
|
45
|
+
const SHAPES = {
|
|
46
|
+
generateKey: (h) => async function generateKey(
|
|
47
|
+
algorithm, extractable, keyUsages) { return h(arguments, this); },
|
|
48
|
+
importKey: (h) => async function importKey(
|
|
49
|
+
format, keyData, algorithm, extractable, keyUsages) { return h(arguments, this); },
|
|
50
|
+
exportKey: (h) => async function exportKey(
|
|
51
|
+
format, key) { return h(arguments, this); },
|
|
52
|
+
sign: (h) => async function sign(
|
|
53
|
+
algorithm, key, data) { return h(arguments, this); },
|
|
54
|
+
verify: (h) => async function verify(
|
|
55
|
+
algorithm, key, signature, data) { return h(arguments, this); },
|
|
56
|
+
encapsulateBits: (h) => async function encapsulateBits(
|
|
57
|
+
algorithm, encapsulationKey) { return h(arguments, this); },
|
|
58
|
+
encapsulateKey: (h) => async function encapsulateKey(
|
|
59
|
+
algorithm, encapsulationKey, sharedKeyAlgorithm, extractable, keyUsages) {
|
|
60
|
+
return h(arguments, this); },
|
|
61
|
+
decapsulateBits: (h) => async function decapsulateBits(
|
|
62
|
+
algorithm, decapsulationKey, ciphertext) { return h(arguments, this); },
|
|
63
|
+
decapsulateKey: (h) => async function decapsulateKey(
|
|
64
|
+
algorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, keyUsages) {
|
|
65
|
+
return h(arguments, this); },
|
|
66
|
+
getPublicKey: (h) => async function getPublicKey(
|
|
67
|
+
key, keyUsages) { return h(arguments, this); },
|
|
68
|
+
wrapKey: (h) => async function wrapKey(
|
|
69
|
+
format, key, wrappingKey, wrapAlgorithm) { return h(arguments, this); },
|
|
70
|
+
unwrapKey: (h) => async function unwrapKey(
|
|
71
|
+
format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm,
|
|
72
|
+
extractable, keyUsages) { return h(arguments, this); },
|
|
73
|
+
};
|
package/src/subtle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/***************************************************************************
|
|
2
|
-
*
|
|
2
|
+
* subtlepq: spec-shaped ML-KEM / ML-DSA operations
|
|
3
3
|
* per https://wicg.github.io/webcrypto-modern-algos/
|
|
4
4
|
*
|
|
5
5
|
* (c) 2026 VESvault Corp
|
|
@@ -17,6 +17,9 @@ import {
|
|
|
17
17
|
normalizeAlg, KEM_PUB_USAGES, KEM_PRIV_USAGES,
|
|
18
18
|
} from "./algorithms.js";
|
|
19
19
|
import { forgeKey, materialOf, isOurKey } from "./keystore.js";
|
|
20
|
+
import {
|
|
21
|
+
encodeSpki, decodeSpki, encodePkcs8, decodePkcs8, b64u, unb64u,
|
|
22
|
+
} from "./formats.js";
|
|
20
23
|
import * as E from "./errors.js";
|
|
21
24
|
|
|
22
25
|
const rng = (n) => globalThis.crypto.getRandomValues(new Uint8Array(n));
|
|
@@ -113,39 +116,110 @@ export async function importKey(format, keyData, algorithm, extractable, keyUsag
|
|
|
113
116
|
const pubUsages = a.kind === "kem" ? KEM_PUB_USAGES : ["verify"];
|
|
114
117
|
const privUsages = a.kind === "kem" ? KEM_PRIV_USAGES : ["sign"];
|
|
115
118
|
|
|
116
|
-
|
|
119
|
+
const importPub = (pub) => {
|
|
117
120
|
checkUsages(keyUsages, pubUsages);
|
|
118
|
-
const pub = toBytes(keyData, "keyData");
|
|
119
121
|
if (pub.length !== L.pk) throw E.dataError("bad " + a.name + " public key length");
|
|
120
122
|
return forgeKey("public", a.name, extractable, keyUsages, { pub });
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
+
};
|
|
124
|
+
/* expanded (pkcs8 "both" / jwk pub): must agree with the seed-derived value */
|
|
125
|
+
const importSeed = (seed, expanded, expect) => {
|
|
123
126
|
checkUsages(keyUsages, privUsages);
|
|
124
127
|
if (!keyUsages.length) throw E.syntaxError("private key usages must not be empty");
|
|
125
|
-
const seed = toBytes(keyData, "keyData");
|
|
126
128
|
if (seed.length !== L.seed) throw E.dataError("bad " + a.name + " seed length");
|
|
127
129
|
const { pub, sk } = a.kind === "kem"
|
|
128
130
|
? eng.kemKeypairDerand(a.name, seed)
|
|
129
131
|
: eng.sigKeypairDerand(a.name, seed);
|
|
132
|
+
if (expanded && !bytesEqual(expanded, expect === "pub" ? pub : sk)) {
|
|
133
|
+
throw E.dataError(a.name + " seed and " +
|
|
134
|
+
(expect === "pub" ? "public key" : "expandedKey") + " do not match");
|
|
135
|
+
}
|
|
130
136
|
return forgeKey("private", a.name, extractable, keyUsages, { pub, seed, sk });
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
if (format === "raw-public") return importPub(toBytes(keyData, "keyData"));
|
|
140
|
+
if (format === "raw-seed") return importSeed(toBytes(keyData, "keyData"));
|
|
141
|
+
if (format === "spki") {
|
|
142
|
+
const { name, pub } = decodeSpki(toBytes(keyData, "keyData"));
|
|
143
|
+
if (name !== a.name) throw E.dataError("SPKI algorithm is " + name + ", not " + a.name);
|
|
144
|
+
return importPub(pub);
|
|
145
|
+
}
|
|
146
|
+
if (format === "pkcs8") {
|
|
147
|
+
const { name, seed, expanded } = decodePkcs8(toBytes(keyData, "keyData"));
|
|
148
|
+
if (name !== a.name) throw E.dataError("PKCS#8 algorithm is " + name + ", not " + a.name);
|
|
149
|
+
return importSeed(seed, expanded, "sk");
|
|
131
150
|
}
|
|
132
|
-
|
|
133
|
-
throw E.notSupported("key format " + format + " is not supported
|
|
151
|
+
if (format === "jwk") return importJwk(keyData, a, extractable, importPub, importSeed);
|
|
152
|
+
throw E.notSupported("key format " + format + " is not supported");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function bytesEqual(x, y) {
|
|
156
|
+
if (x.length !== y.length) return false;
|
|
157
|
+
let d = 0;
|
|
158
|
+
for (let i = 0; i < x.length; i++) d |= x[i] ^ y[i];
|
|
159
|
+
return d === 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function importJwk(jwk, a, extractable, importPub, importSeed) {
|
|
163
|
+
if (!jwk || typeof jwk !== "object" || ArrayBuffer.isView(jwk) ||
|
|
164
|
+
jwk instanceof ArrayBuffer) {
|
|
165
|
+
throw E.dataError("jwk import expects a JsonWebKey object");
|
|
166
|
+
}
|
|
167
|
+
if (jwk.kty !== "AKP") throw E.dataError('JWK kty must be "AKP"');
|
|
168
|
+
if (jwk.alg !== undefined && jwk.alg !== a.name) {
|
|
169
|
+
throw E.dataError("JWK alg does not match " + a.name);
|
|
170
|
+
}
|
|
171
|
+
if (jwk.ext === false && extractable) {
|
|
172
|
+
throw E.dataError("JWK ext forbids an extractable key");
|
|
173
|
+
}
|
|
174
|
+
const checkOps = (usages) => {
|
|
175
|
+
if (jwk.key_ops === undefined) return;
|
|
176
|
+
if (!Array.isArray(jwk.key_ops)) throw E.dataError("JWK key_ops must be an array");
|
|
177
|
+
for (const u of usages) {
|
|
178
|
+
if (!jwk.key_ops.includes(u)) {
|
|
179
|
+
throw E.dataError("JWK key_ops does not permit " + u);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
if (jwk.priv !== undefined) {
|
|
184
|
+
const key = importSeed(unb64u(jwk.priv, "priv"),
|
|
185
|
+
jwk.pub === undefined ? null : unb64u(jwk.pub, "pub"), "pub");
|
|
186
|
+
checkOps(key.usages);
|
|
187
|
+
return key;
|
|
188
|
+
}
|
|
189
|
+
if (jwk.pub === undefined) throw E.dataError("JWK has neither pub nor priv");
|
|
190
|
+
const key = importPub(unb64u(jwk.pub, "pub"));
|
|
191
|
+
checkOps(key.usages);
|
|
192
|
+
return key;
|
|
134
193
|
}
|
|
135
194
|
|
|
136
195
|
export async function exportKey(format, key) {
|
|
137
196
|
const a = requireAlg(key && key.algorithm && key.algorithm.name);
|
|
138
197
|
const m = requireKey(key, a, key.type, null);
|
|
139
198
|
if (!key.extractable) throw E.invalidAccess("key is not extractable");
|
|
140
|
-
|
|
141
|
-
if (key.type !== "public") throw E.invalidAccess("
|
|
142
|
-
return m.pub
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (key.type !== "private") throw E.invalidAccess("
|
|
146
|
-
return m.seed
|
|
199
|
+
const pub = () => {
|
|
200
|
+
if (key.type !== "public") throw E.invalidAccess(format + " exports public keys");
|
|
201
|
+
return m.pub;
|
|
202
|
+
};
|
|
203
|
+
const seed = () => {
|
|
204
|
+
if (key.type !== "private") throw E.invalidAccess(format + " exports private keys");
|
|
205
|
+
return m.seed;
|
|
206
|
+
};
|
|
207
|
+
if (format === "raw-public") return pub().slice().buffer;
|
|
208
|
+
if (format === "raw-seed") return seed().slice().buffer;
|
|
209
|
+
if (format === "spki") return encodeSpki(a.name, pub()).buffer;
|
|
210
|
+
if (format === "pkcs8") return encodePkcs8(a.name, seed()).buffer;
|
|
211
|
+
if (format === "jwk") {
|
|
212
|
+
const jwk = {
|
|
213
|
+
kty: "AKP",
|
|
214
|
+
alg: a.name,
|
|
215
|
+
pub: b64u(m.pub),
|
|
216
|
+
key_ops: [...key.usages],
|
|
217
|
+
ext: key.extractable,
|
|
218
|
+
};
|
|
219
|
+
if (key.type === "private") jwk.priv = b64u(seed());
|
|
220
|
+
return jwk;
|
|
147
221
|
}
|
|
148
|
-
throw E.notSupported("key format " + format + " is not supported
|
|
222
|
+
throw E.notSupported("key format " + format + " is not supported");
|
|
149
223
|
}
|
|
150
224
|
|
|
151
225
|
export async function getPublicKey(key, keyUsages = []) {
|
|
@@ -216,6 +290,77 @@ export async function verify(algorithm, key, signature, data) {
|
|
|
216
290
|
a.name, m.pub, toBytes(data, "data"), ctx, toBytes(signature, "signature"));
|
|
217
291
|
}
|
|
218
292
|
|
|
293
|
+
/* ---- wrapKey / unwrapKey: exportKey + native wrap composition ----
|
|
294
|
+
*
|
|
295
|
+
* The exported bytes ride through the native wrapKey/unwrapKey inside a
|
|
296
|
+
* throwaway raw-imported HMAC key, so the wrap algorithm gets the native
|
|
297
|
+
* spec-defined treatment (encrypt for AES-GCM/RSA-OAEP, key wrap for
|
|
298
|
+
* AES-KW) and the wrapping key's own usages are enforced natively. */
|
|
299
|
+
|
|
300
|
+
function nativeSubtle() {
|
|
301
|
+
const ns = globalThis.crypto && globalThis.crypto.subtle;
|
|
302
|
+
if (!ns) throw E.notSupported("no native SubtleCrypto for key wrapping");
|
|
303
|
+
return ns;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const CARRIER = { name: "HMAC", hash: "SHA-256" };
|
|
307
|
+
|
|
308
|
+
export async function wrapKey(format, key, wrappingKey, wrapAlgorithm) {
|
|
309
|
+
if (isOurKey(wrappingKey)) {
|
|
310
|
+
throw E.invalidAccess("ML-KEM/ML-DSA keys cannot wrap other keys");
|
|
311
|
+
}
|
|
312
|
+
if (!isOurKey(key)) {
|
|
313
|
+
throw E.invalidAccess("key is not from this polyfill");
|
|
314
|
+
}
|
|
315
|
+
const exported = await exportKey(format, key);
|
|
316
|
+
let bytes = format === "jwk"
|
|
317
|
+
? new TextEncoder().encode(JSON.stringify(exported))
|
|
318
|
+
: new Uint8Array(exported);
|
|
319
|
+
/* WebCrypto pads JWK JSON with spaces to the AES-KW 8-byte multiple */
|
|
320
|
+
const wrapName = wrapAlgorithm && wrapAlgorithm.name || wrapAlgorithm;
|
|
321
|
+
if (format === "jwk" && bytes.length % 8 &&
|
|
322
|
+
String(wrapName).toUpperCase() === "AES-KW") {
|
|
323
|
+
const padded = new Uint8Array(Math.ceil(bytes.length / 8) * 8).fill(0x20);
|
|
324
|
+
padded.set(bytes);
|
|
325
|
+
bytes.fill(0);
|
|
326
|
+
bytes = padded;
|
|
327
|
+
}
|
|
328
|
+
const ns = nativeSubtle();
|
|
329
|
+
try {
|
|
330
|
+
const carrier = await ns.importKey("raw", bytes, CARRIER, true, ["sign"]);
|
|
331
|
+
return await ns.wrapKey("raw", carrier, wrappingKey, wrapAlgorithm);
|
|
332
|
+
} finally {
|
|
333
|
+
bytes.fill(0);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export async function unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgorithm,
|
|
338
|
+
unwrappedKeyAlgorithm, extractable, keyUsages) {
|
|
339
|
+
if (isOurKey(unwrappingKey)) {
|
|
340
|
+
throw E.invalidAccess("ML-KEM/ML-DSA keys cannot unwrap other keys");
|
|
341
|
+
}
|
|
342
|
+
requireAlg(unwrappedKeyAlgorithm);
|
|
343
|
+
const ns = nativeSubtle();
|
|
344
|
+
const carrier = await ns.unwrapKey("raw", wrappedKey, unwrappingKey,
|
|
345
|
+
unwrapAlgorithm, CARRIER, true, ["sign"]);
|
|
346
|
+
const bytes = new Uint8Array(await ns.exportKey("raw", carrier));
|
|
347
|
+
let keyData = bytes;
|
|
348
|
+
if (format === "jwk") {
|
|
349
|
+
try {
|
|
350
|
+
keyData = JSON.parse(new TextDecoder().decode(bytes));
|
|
351
|
+
} catch {
|
|
352
|
+
throw E.dataError("unwrapped data is not JWK JSON");
|
|
353
|
+
} finally {
|
|
354
|
+
bytes.fill(0);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
return await importKey(format, keyData, unwrappedKeyAlgorithm, extractable, keyUsages);
|
|
359
|
+
} finally {
|
|
360
|
+
if (keyData instanceof Uint8Array) keyData.fill(0);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
219
364
|
/* ---- feature detection ---- */
|
|
220
365
|
|
|
221
366
|
const OPS = {
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
async function SubtlePQEngine(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var readyPromiseResolve,readyPromiseReject;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["f"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("subtlepq-engine.wasm")}return new URL("subtlepq-engine.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var printCharBuffers=[null,[],[]];var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var _random_get=(buffer,size)=>randomFill(HEAPU8.subarray(buffer,buffer+size));{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}var _pqf_kem_lengths,_free,_malloc,_pqf_kem_keypair,_pqf_kem_keypair_derand,_pqf_kem_encaps,_pqf_kem_encaps_derand,_pqf_kem_decaps,_pqf_sig_lengths,_pqf_sig_keypair,_pqf_sig_keypair_derand,_pqf_sig_sign,_pqf_sig_sign_derand,_pqf_sig_verify,_pqf_liboqs_version,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_pqf_kem_lengths=Module["_pqf_kem_lengths"]=wasmExports["g"];_free=Module["_free"]=wasmExports["h"];_malloc=Module["_malloc"]=wasmExports["i"];_pqf_kem_keypair=Module["_pqf_kem_keypair"]=wasmExports["j"];_pqf_kem_keypair_derand=Module["_pqf_kem_keypair_derand"]=wasmExports["k"];_pqf_kem_encaps=Module["_pqf_kem_encaps"]=wasmExports["l"];_pqf_kem_encaps_derand=Module["_pqf_kem_encaps_derand"]=wasmExports["m"];_pqf_kem_decaps=Module["_pqf_kem_decaps"]=wasmExports["n"];_pqf_sig_lengths=Module["_pqf_sig_lengths"]=wasmExports["o"];_pqf_sig_keypair=Module["_pqf_sig_keypair"]=wasmExports["p"];_pqf_sig_keypair_derand=Module["_pqf_sig_keypair_derand"]=wasmExports["q"];_pqf_sig_sign=Module["_pqf_sig_sign"]=wasmExports["r"];_pqf_sig_sign_derand=Module["_pqf_sig_sign_derand"]=wasmExports["s"];_pqf_sig_verify=Module["_pqf_sig_verify"]=wasmExports["t"];_pqf_liboqs_version=Module["_pqf_liboqs_version"]=wasmExports["u"];memory=wasmMemory=wasmExports["e"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={c:_emscripten_resize_heap,a:_exit,b:_fd_write,d:_random_get};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
|
|
2
|
+
;return moduleRtn}export default SubtlePQEngine;
|
|
Binary file
|
|
Binary file
|