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 CHANGED
@@ -1,8 +1,13 @@
1
- # pqfill
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: P1 — engine + core JS layer.** Raw key formats only (`raw-public`, `raw-seed`); spki/pkcs8/jwk, wrapKey, and DHKEM are on the way.
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 (`pqfill_engine.c`) to WebAssembly:
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/pqfill-engine.mjs` (+ `.wasm` sidecar, CSP-clean) and `pqfill-engine.single.mjs` (self-contained, requires `'wasm-unsafe-eval'` under strict CSP). 75 KB wasm covers all six parameter sets.
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.1.1",
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/pqfill.git"
10
+ "url": "git+https://github.com/vesvault/subtlepq.git"
11
11
  },
12
- "homepage": "https://github.com/vesvault/pqfill#readme",
12
+ "homepage": "https://github.com/vesvault/subtlepq#readme",
13
13
  "bugs": {
14
- "url": "https://github.com/vesvault/pqfill/issues"
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
- "wasm/dist"
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
- "ml-dsa",
35
- "webcrypto",
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
- * pqfill: algorithm registry and AlgorithmIdentifier normalization
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
- * pqfill: lazy wasm engine loader + typed wrappers over pqfill_engine.c
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/pqfill-engine.mjs");
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
- * pqfill: DOMException helpers with WebCrypto-mandated error names
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
- * pqfill: post-quantum polyfill for the Web Cryptography API
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] = function (...args) {
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
- return Promise.reject(E.notSupported(op + " is not supported"));
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
- * pqfill: true-polyfill installer -- patches globalThis.crypto.subtle
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
- * pqfill keys are handled here while every other call reaches the saved
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("pqfill: crypto.subtle is unavailable (insecure context?)");
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 = async function (...args) {
48
+ const wrapped = shape(op, (args, self) => {
49
49
  if (isOurs(args)) return ours[op](...args);
50
- if (typeof orig === "function") return orig.apply(this, args);
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
- * pqfill: forged CryptoKey factory + private key-material store
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
- __pqfill: {
33
- value: Symbol("pqfill key: not structured-cloneable; export and re-import instead"),
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
- * pqfill: dispatch rules shared by the ponyfill (index.js) and the
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 pqfill handle this call? -- keyed by SubtleCrypto method */
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
- * pqfill: spec-shaped ML-KEM / ML-DSA operations
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
- if (format === "raw-public") {
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
- if (format === "raw-seed") {
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
- /* spki / pkcs8 / jwk land in P2 */
133
- throw E.notSupported("key format " + format + " is not supported yet");
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
- if (format === "raw-public") {
141
- if (key.type !== "public") throw E.invalidAccess("raw-public exports public keys");
142
- return m.pub.slice().buffer;
143
- }
144
- if (format === "raw-seed") {
145
- if (key.type !== "private") throw E.invalidAccess("raw-seed exports private keys");
146
- return m.seed.slice().buffer;
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 yet");
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