@polyjose/node 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "darwin-arm64": "2fddd1beba8c3bda8c3c6e7ebf84cb19b601ff3cf03db0c2b9ee421b17c5164e",
3
+ "darwin-x64": "b5a0c5b0fa18ffcb372944782f5ebaa01a1353d4734fab372db0d99b673f9866",
4
+ "linux-arm64-gnu": "197e9632aa6737a059789038366b0cbd3fb49ee4e8b09ec1194b7187c6f7d53a",
5
+ "linux-arm64-musl": "1ad5baebf6a0fa75d24ae343b9b5e180876edef836b033e8dbec5dd686df5396",
6
+ "linux-x64-gnu": "69c828632292671c0e3f8004b39de3fc4065cf2b734df85844280ddb35767bad",
7
+ "linux-x64-musl": "cb411795274b60909e022d080431b80494112df6ccf507a50538c12cab60b5d7",
8
+ "win32-arm64-msvc": "157a25122817918748b02eb0715d7f1163254b2575452327b358ac5cb381cd16",
9
+ "win32-x64-msvc": "7c2b07e6d943ea2fd3b6a96d8105e30d10489d719a614c29dba9c4f858a168d3"
10
+ }
package/backend.d.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The napi-rs backend of `@polyjose/node` (ADR 0031 §1-§2, `bindings/js/BACKEND.md`; packaging as
3
+ * superseded by the feature 0017 ADR: one package that ships the addon of all eight targets).
4
+ *
5
+ * The native addon `polyjose.<platform>.node` already is the backend contract: one function per
6
+ * ADR 0030 §4 operation, camelCase, in the ADR's argument order, rejecting with an `Error` whose
7
+ * `code` is one of `errorCodes()` (ADR 0030 §5). Its types are the `binding.d.ts` that
8
+ * `napi build --dts` generates. So this module only picks the addon for this host, authenticates
9
+ * it, loads it and gives it the facade's `Backend` type; it adds no JOSE logic and renames nothing
10
+ * (ADR 0030 §1: loading and integrity checks are entry idiom).
11
+ *
12
+ * **No resolving loader (feature 0017; review-security.md M1, feature 0007 F02).** napi-rs's
13
+ * generated loader picks the first module that loads from an override variable, the package
14
+ * directory, every ancestor `node_modules`, `NODE_PATH` and a WASI chain, so a JavaScript or native
15
+ * file planted in any of them could become the crypto backend and "verify" any token. polyjose
16
+ * builds no such loader (`napi build --no-js`) and resolves no module name at all: this file reads
17
+ * only files of its own package directory, `dirname(import.meta.url)`, by absolute path. It never
18
+ * reads `process.env`, so neither `NODE_PATH` nor any `NAPI_RS_*` variable changes what loads.
19
+ *
20
+ * **The steps (the F07 hash-pin model, bindings security audit N-M1, the JVM's 0008 F05 bar):**
21
+ * 1. The platform name (`detectPlatform`): `darwin-<arch>`, `win32-<arch>-msvc`, or
22
+ * `linux-<arch>-gnu|musl` for x64 and arm64, with napi-rs's names. Any other host is refused
23
+ * by name, pointing at `@polyjose/wasm`, which runs everywhere.
24
+ * 2. `addon-sha256.json`, in the package directory, maps each shipped platform to its addon's
25
+ * SHA-256 (FIPS 180-4). It is generated, never hand-edited: `scripts/addon-hashes.sh`, run by
26
+ * `scripts/build.sh` for a local build and by `scripts/pack-main.sh` over the eight addons the
27
+ * package ships. No map, a malformed one, or no entry for this host refuses the load: nothing
28
+ * is trusted by default.
29
+ * 3. It reads `polyjose.<platform>.node` from the package directory once, hashes those bytes with
30
+ * `node:crypto` and refuses on a mismatch, naming the platform, the file and both hashes.
31
+ * There is no fallback to another file.
32
+ * 4. It writes the same bytes, with `O_EXCL`, into a new directory from `fs.mkdtempSync` under
33
+ * `os.tmpdir()` (mode 0700 on POSIX; on Windows the per-user `%TEMP%` ACL), under a random
34
+ * leaf name (macOS looks a leaf name up in `DYLD_LIBRARY_PATH` before the path), checks the
35
+ * copy's SHA-256 again, and `require`s that absolute path, which Node's `.node` handler hands
36
+ * to `dlopen`. So between the check and `dlopen` only this user (who can already change
37
+ * anything they run) could swap the file; whoever can write the installed package can't. The
38
+ * copy is deleted right after (a loaded library stays mapped; Windows can't delete a loaded
39
+ * DLL, so there the copy stays in the temporary directory).
40
+ * 5. What loaded must be the exports of exactly that one module in the CommonJS cache, the
41
+ * private copy, so no JavaScript module wraps or re-exports it.
42
+ * 6. It must be this package's version: `version()` equals `package.json`'s version, the
43
+ * workspace version every release keeps in step (feature 0007 T04), so an addon of another
44
+ * release is refused.
45
+ * Every refusal is an `Error` with `code` `ERR_POLYJOSE_ADDON_REFUSED`. This doesn't defend against
46
+ * code already running in the process, which can patch anything. It makes sure the package never
47
+ * selects a substitute.
48
+ *
49
+ * `test/loader.test.ts` pins what this module loads (its built-ins and its one `require`), the
50
+ * platform detection, and each refusal.
51
+ */
52
+ import type { Backend } from "./facade/backend.js";
53
+ /** The one `fs` call platform detection makes (so tests can fake it). */
54
+ interface TextReader {
55
+ readFileSync(path: string, encoding: "utf8"): string;
56
+ }
57
+ interface ProcessReport {
58
+ excludeNetwork: boolean;
59
+ getReport(): {
60
+ readonly header?: {
61
+ readonly glibcVersionRuntime?: unknown;
62
+ };
63
+ readonly sharedObjects?: unknown;
64
+ } | null;
65
+ }
66
+ /** What `detectPlatform` reads of `process`. */
67
+ interface PlatformHost {
68
+ readonly platform: string;
69
+ readonly arch: string;
70
+ readonly report?: ProcessReport;
71
+ }
72
+ /**
73
+ * The eight platforms whose addon this package ships (ADR 0031 §2's targets): the only keys a map
74
+ * may have. `test/loader.test.ts` checks them against package.json's `napi.targets` and `files`.
75
+ */
76
+ export declare const PLATFORMS: readonly ["darwin-x64", "darwin-arm64", "linux-x64-gnu", "linux-arm64-gnu", "linux-x64-musl", "linux-arm64-musl", "win32-x64-msvc", "win32-arm64-msvc"];
77
+ /**
78
+ * Step 1: this host's platform name, napi-rs's (`napi build --platform` names the addon after it):
79
+ * `darwin-x64|arm64`, `win32-x64|arm64-msvc`, `linux-x64|arm64-gnu|musl`. Any other platform or
80
+ * architecture is `<platform>-<arch>`, which `PLATFORMS` does not list. On Linux it reads
81
+ * /usr/bin/ldd and may take a process report (`isMusl`); it refuses when it cannot tell the libc.
82
+ */
83
+ export declare function detectPlatform(host: PlatformHost, fs: TextReader): string;
84
+ /** The native addon, typed as the facade's backend. */
85
+ export declare const backend: Backend;
86
+ export {};
package/backend.js ADDED
@@ -0,0 +1,253 @@
1
+ /**
2
+ * The napi-rs backend of `@polyjose/node` (ADR 0031 §1-§2, `bindings/js/BACKEND.md`; packaging as
3
+ * superseded by the feature 0017 ADR: one package that ships the addon of all eight targets).
4
+ *
5
+ * The native addon `polyjose.<platform>.node` already is the backend contract: one function per
6
+ * ADR 0030 §4 operation, camelCase, in the ADR's argument order, rejecting with an `Error` whose
7
+ * `code` is one of `errorCodes()` (ADR 0030 §5). Its types are the `binding.d.ts` that
8
+ * `napi build --dts` generates. So this module only picks the addon for this host, authenticates
9
+ * it, loads it and gives it the facade's `Backend` type; it adds no JOSE logic and renames nothing
10
+ * (ADR 0030 §1: loading and integrity checks are entry idiom).
11
+ *
12
+ * **No resolving loader (feature 0017; review-security.md M1, feature 0007 F02).** napi-rs's
13
+ * generated loader picks the first module that loads from an override variable, the package
14
+ * directory, every ancestor `node_modules`, `NODE_PATH` and a WASI chain, so a JavaScript or native
15
+ * file planted in any of them could become the crypto backend and "verify" any token. polyjose
16
+ * builds no such loader (`napi build --no-js`) and resolves no module name at all: this file reads
17
+ * only files of its own package directory, `dirname(import.meta.url)`, by absolute path. It never
18
+ * reads `process.env`, so neither `NODE_PATH` nor any `NAPI_RS_*` variable changes what loads.
19
+ *
20
+ * **The steps (the F07 hash-pin model, bindings security audit N-M1, the JVM's 0008 F05 bar):**
21
+ * 1. The platform name (`detectPlatform`): `darwin-<arch>`, `win32-<arch>-msvc`, or
22
+ * `linux-<arch>-gnu|musl` for x64 and arm64, with napi-rs's names. Any other host is refused
23
+ * by name, pointing at `@polyjose/wasm`, which runs everywhere.
24
+ * 2. `addon-sha256.json`, in the package directory, maps each shipped platform to its addon's
25
+ * SHA-256 (FIPS 180-4). It is generated, never hand-edited: `scripts/addon-hashes.sh`, run by
26
+ * `scripts/build.sh` for a local build and by `scripts/pack-main.sh` over the eight addons the
27
+ * package ships. No map, a malformed one, or no entry for this host refuses the load: nothing
28
+ * is trusted by default.
29
+ * 3. It reads `polyjose.<platform>.node` from the package directory once, hashes those bytes with
30
+ * `node:crypto` and refuses on a mismatch, naming the platform, the file and both hashes.
31
+ * There is no fallback to another file.
32
+ * 4. It writes the same bytes, with `O_EXCL`, into a new directory from `fs.mkdtempSync` under
33
+ * `os.tmpdir()` (mode 0700 on POSIX; on Windows the per-user `%TEMP%` ACL), under a random
34
+ * leaf name (macOS looks a leaf name up in `DYLD_LIBRARY_PATH` before the path), checks the
35
+ * copy's SHA-256 again, and `require`s that absolute path, which Node's `.node` handler hands
36
+ * to `dlopen`. So between the check and `dlopen` only this user (who can already change
37
+ * anything they run) could swap the file; whoever can write the installed package can't. The
38
+ * copy is deleted right after (a loaded library stays mapped; Windows can't delete a loaded
39
+ * DLL, so there the copy stays in the temporary directory).
40
+ * 5. What loaded must be the exports of exactly that one module in the CommonJS cache, the
41
+ * private copy, so no JavaScript module wraps or re-exports it.
42
+ * 6. It must be this package's version: `version()` equals `package.json`'s version, the
43
+ * workspace version every release keeps in step (feature 0007 T04), so an addon of another
44
+ * release is refused.
45
+ * Every refusal is an `Error` with `code` `ERR_POLYJOSE_ADDON_REFUSED`. This doesn't defend against
46
+ * code already running in the process, which can patch anything. It makes sure the package never
47
+ * selects a substitute.
48
+ *
49
+ * `test/loader.test.ts` pins what this module loads (its built-ins and its one `require`), the
50
+ * platform detection, and each refusal.
51
+ */
52
+ /** The addon hash map in the package directory (step 2): `{ "<platform>": "<SHA-256, 64 lowercase hex>" }`. */
53
+ const PINS = "addon-sha256.json";
54
+ /** The `code` of every load refusal. */
55
+ const REFUSED = "ERR_POLYJOSE_ADDON_REFUSED";
56
+ /**
57
+ * The eight platforms whose addon this package ships (ADR 0031 §2's targets): the only keys a map
58
+ * may have. `test/loader.test.ts` checks them against package.json's `napi.targets` and `files`.
59
+ */
60
+ export const PLATFORMS = [
61
+ "darwin-x64",
62
+ "darwin-arm64",
63
+ "linux-x64-gnu",
64
+ "linux-arm64-gnu",
65
+ "linux-x64-musl",
66
+ "linux-arm64-musl",
67
+ "win32-x64-msvc",
68
+ "win32-arm64-msvc",
69
+ ];
70
+ const SHIPPED = PLATFORMS;
71
+ /**
72
+ * A load refusal, with `code` `ERR_POLYJOSE_ADDON_REFUSED`. It names a platform, a file of the
73
+ * package, a version or a SHA-256; never a file's contents.
74
+ */
75
+ function refuse(reason) {
76
+ throw Object.assign(new Error(`@polyjose/node: ${reason}`), { code: REFUSED });
77
+ }
78
+ /**
79
+ * Step 1: this host's platform name, napi-rs's (`napi build --platform` names the addon after it):
80
+ * `darwin-x64|arm64`, `win32-x64|arm64-msvc`, `linux-x64|arm64-gnu|musl`. Any other platform or
81
+ * architecture is `<platform>-<arch>`, which `PLATFORMS` does not list. On Linux it reads
82
+ * /usr/bin/ldd and may take a process report (`isMusl`); it refuses when it cannot tell the libc.
83
+ */
84
+ export function detectPlatform(host, fs) {
85
+ const { platform, arch } = host;
86
+ if (arch !== "x64" && arch !== "arm64")
87
+ return `${platform}-${arch}`;
88
+ if (platform === "darwin")
89
+ return `darwin-${arch}`;
90
+ if (platform === "win32")
91
+ return `win32-${arch}-msvc`;
92
+ if (platform === "linux")
93
+ return `linux-${arch}-${isMusl(host, fs) ? "musl" : "gnu"}`;
94
+ return `${platform}-${arch}`;
95
+ }
96
+ /**
97
+ * musl or glibc, decided in napi-rs's order (its generated `isMusl`): `/usr/bin/ldd` names musl
98
+ * or not (glibc ships ldd as a script of its own, musl's runs `ld-musl-*`); if it cannot be read,
99
+ * the process report, taken with `excludeNetwork` set (restored after): a glibc runtime version
100
+ * means glibc, else a `libc.musl-`/`ld-musl-` shared object means musl. napi-rs's last resort,
101
+ * `ldd --version` in a child process, is not run: without a usable report this refuses instead
102
+ * of guessing.
103
+ */
104
+ function isMusl(host, fs) {
105
+ try {
106
+ return fs.readFileSync("/usr/bin/ldd", "utf8").includes("musl");
107
+ }
108
+ catch {
109
+ // Unreadable: ask the process report, as napi-rs does.
110
+ }
111
+ const report = host.report;
112
+ if (report === undefined || typeof report.getReport !== "function") {
113
+ return refuse(`cannot tell glibc from musl on linux-${host.arch} (no /usr/bin/ldd, no process report); refusing to load`);
114
+ }
115
+ const before = report.excludeNetwork;
116
+ report.excludeNetwork = true;
117
+ let r;
118
+ try {
119
+ r = report.getReport();
120
+ }
121
+ finally {
122
+ report.excludeNetwork = before;
123
+ }
124
+ if (r === null || typeof r !== "object") {
125
+ return refuse(`cannot tell glibc from musl on linux-${host.arch} (empty process report); refusing to load`);
126
+ }
127
+ if (r.header?.glibcVersionRuntime)
128
+ return false;
129
+ const objects = r.sharedObjects;
130
+ const musl = (f) => typeof f === "string" && (f.includes("libc.musl-") || f.includes("ld-musl-"));
131
+ return Array.isArray(objects) && objects.some(musl);
132
+ }
133
+ /** Step 2: the package's addon hash map, validated, as a null-prototype record. */
134
+ function readPins(fs, file) {
135
+ let text;
136
+ try {
137
+ text = fs.readFileSync(file, "utf8");
138
+ }
139
+ catch {
140
+ return refuse("addon-sha256.json is missing, so no native addon is trusted; this package was not assembled by polyjose's build");
141
+ }
142
+ let parsed;
143
+ try {
144
+ parsed = JSON.parse(text);
145
+ }
146
+ catch {
147
+ return refuse("addon-sha256.json is not valid JSON; refusing to load");
148
+ }
149
+ const pins = Object.create(null);
150
+ const valid = typeof parsed === "object" &&
151
+ parsed !== null &&
152
+ !Array.isArray(parsed) &&
153
+ Object.entries(parsed).every(([platform, hash]) => {
154
+ if (!SHIPPED.includes(platform) || typeof hash !== "string" || !/^[0-9a-f]{64}$/.test(hash))
155
+ return false;
156
+ pins[platform] = hash;
157
+ return true;
158
+ });
159
+ if (!valid) {
160
+ return refuse("addon-sha256.json is not a map of shipped platforms to lowercase SHA-256 hex; refusing to load");
161
+ }
162
+ return pins;
163
+ }
164
+ /** SHA-256 (FIPS 180-4) of `bytes`, lowercase hex. */
165
+ function sha256(crypto, bytes) {
166
+ return crypto.createHash("sha256").update(bytes).digest("hex");
167
+ }
168
+ /**
169
+ * Steps 1-4: requires a private, hash-checked copy of this package's addon for this host. Returns
170
+ * what it loaded and the copy's path (the CommonJS cache key, step 5).
171
+ */
172
+ function loadPinned(host, req, b, dir) {
173
+ const platform = detectPlatform(host, b.fs);
174
+ if (!SHIPPED.includes(platform)) {
175
+ return refuse(`there is no native addon for ${platform} (this package ships ${SHIPPED.join(", ")}); ` +
176
+ "use @polyjose/wasm, which has the same API and runs on every platform");
177
+ }
178
+ const pins = readPins(b.fs, b.path.join(dir, PINS));
179
+ const expected = pins[platform];
180
+ if (expected === undefined) {
181
+ const listed = Object.keys(pins).sort().join(", ");
182
+ return refuse(`this package has no pinned SHA-256 for ${platform} (addon-sha256.json lists ${listed || "none"}); refusing to load`);
183
+ }
184
+ const name = `polyjose.${platform}.node`;
185
+ let bytes;
186
+ try {
187
+ bytes = b.fs.readFileSync(b.path.join(dir, name));
188
+ }
189
+ catch {
190
+ return refuse(`./${name} is missing from this package or cannot be read; reinstall @polyjose/node`);
191
+ }
192
+ const actual = sha256(b.crypto, bytes);
193
+ if (actual !== expected) {
194
+ refuse(`./${name} for ${platform} has SHA-256 ${actual}, but this package pins ${expected}; refusing to load it`);
195
+ }
196
+ // The real path, as the CommonJS cache keys it (macOS's tmpdir is a symlink).
197
+ const tmp = b.fs.realpathSync(b.fs.mkdtempSync(b.path.join(b.os.tmpdir(), "polyjose-addon-")));
198
+ try {
199
+ const copy = b.path.join(tmp, `polyjose-${b.crypto.randomBytes(16).toString("hex")}.node`);
200
+ b.fs.writeFileSync(copy, bytes, { flag: "wx", mode: 0o700 });
201
+ if (sha256(b.crypto, b.fs.readFileSync(copy)) !== expected) {
202
+ refuse(`the private copy of the addon for ${platform} does not have the pinned SHA-256; refusing to load it`);
203
+ }
204
+ return { loaded: req(copy), copy };
205
+ }
206
+ finally {
207
+ try {
208
+ b.fs.rmSync(tmp, { recursive: true, force: true });
209
+ }
210
+ catch {
211
+ // Windows can't delete a DLL while it is loaded; the copy stays in the temporary directory.
212
+ }
213
+ }
214
+ }
215
+ /** Steps 5-6: the loaded binding is the private copy, at this package's version. */
216
+ function checkLoaded(req, b, dir, loaded, copy) {
217
+ if (typeof loaded !== "object" || loaded === null) {
218
+ return refuse("the native addon returned no binding object");
219
+ }
220
+ const owners = Object.keys(req.cache).filter((file) => req.cache[file]?.exports === loaded);
221
+ if (owners.length !== 1 || owners[0] !== copy) {
222
+ refuse("the loaded binding is not exactly the hash-checked native addon; refusing to load");
223
+ }
224
+ let expected;
225
+ try {
226
+ expected = JSON.parse(b.fs.readFileSync(b.path.join(dir, "package.json"), "utf8")).version;
227
+ }
228
+ catch {
229
+ expected = undefined;
230
+ }
231
+ const actual = typeof loaded.version === "function" ? loaded.version() : undefined;
232
+ if (typeof expected !== "string" || actual !== expected) {
233
+ refuse(`the native addon is version ${String(actual)}, but this package is ${String(expected)}; reinstall it`);
234
+ }
235
+ return loaded;
236
+ }
237
+ /** Loads this package's pinned native addon for this host (the module comment). */
238
+ function loadBinding(host, moduleUrl) {
239
+ const builtins = {
240
+ fs: host.getBuiltinModule("node:fs"),
241
+ crypto: host.getBuiltinModule("node:crypto"),
242
+ os: host.getBuiltinModule("node:os"),
243
+ path: host.getBuiltinModule("node:path"),
244
+ };
245
+ // The package directory: backend.js sits at its root, next to the addons and the pins.
246
+ const dir = builtins.path.dirname(host.getBuiltinModule("node:url").fileURLToPath(moduleUrl));
247
+ const req = host.getBuiltinModule("node:module").createRequire(moduleUrl);
248
+ const { loaded, copy } = loadPinned(host, req, builtins, dir);
249
+ return checkLoaded(req, builtins, dir, loaded, copy);
250
+ }
251
+ const binding = loadBinding(globalThis.process, import.meta.url);
252
+ /** The native addon, typed as the facade's backend. */
253
+ export const backend = binding;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The polyjose JS facade (ADR 0030, ADR 0031 §1, ADR 0047), shared unchanged by
3
+ * `@polyjose/node` and `@polyjose/wasm`. Each package's entry binds it to its native layer:
4
+ *
5
+ * ```ts
6
+ * export const { version, errorCodes, jwsVerifyCompact, ... } = createApi(backend);
7
+ * export { PolyjoseError, ERROR_CODES } from "./api.js";
8
+ * ```
9
+ *
10
+ * The facade adds idiom only (ADR 0030 §1): Promises, object↔JSON, JS type checks
11
+ * (`TypeError`), `null` → `undefined`, the JS clock for JWT `now`, and `PolyjoseError`.
12
+ * Policy, defaults and every JOSE check stay in the core.
13
+ */
14
+ import type { Backend } from "./backend.js";
15
+ import { type ErrorCode } from "./error-codes.js";
16
+ import type { EcCurve, JsonInput, JweDecrypted, JweDecryptOptions, JweEncryptOptions, JweJsonForm, Jwk, JwkObject, JwkSelector, JwkSet, JwsSigner, JwsSignOptions, JwsVerified, JwsVerifyOptions, JwtNestedVerified, JwtSignOptions, JwtVerified, JwtVerifyOptions, KeyFormat, OkpCurve, ThumbprintHash } from "./types.js";
17
+ export type * from "./types.js";
18
+ export type { Backend } from "./backend.js";
19
+ export { ERROR_CODES, type ErrorCode } from "./error-codes.js";
20
+ /**
21
+ * A polyjose failure (ADR 0030 §5). `code` is one of {@link ERROR_CODES}, the core's stable
22
+ * `Error::code()` (ADR 0007): match on it, never on `message`, which may be reworded. Neither
23
+ * ever contains key material or input bytes.
24
+ */
25
+ export declare class PolyjoseError extends Error {
26
+ readonly name = "PolyjoseError";
27
+ /** The stable error code. */
28
+ readonly code: ErrorCode;
29
+ constructor(code: ErrorCode, message: string);
30
+ }
31
+ /** Every operation of ADR 0030 §4, camelCase (§2), each returning a Promise (ADR 0031 §1). */
32
+ export interface PolyjoseApi {
33
+ /** The polyjose crate version. */
34
+ version(): Promise<string>;
35
+ /** The stable error codes, equal to the core's `Error::CODES` (ADR 0030 §5). */
36
+ errorCodes(): Promise<string[]>;
37
+ /** base64url without padding (RFC 4648 §5, RFC 7515 §2). */
38
+ base64urlEncode(bytes: Uint8Array): Promise<string>;
39
+ /** Strict, canonical base64url decoding (RFC 4648 §5, §3.5). */
40
+ base64urlDecode(text: string): Promise<Uint8Array>;
41
+ /** Validates a JWK (RFC 7517 §4, RFC 7518 §6, RFC 8037 §2) and returns the core's serialization. */
42
+ jwkParse(jwk: Jwk): Promise<JwkObject>;
43
+ /** The public half of a JWK (RFC 7517 §4, RFC 7518 §6.2.1, §6.3.1). */
44
+ jwkToPublic(jwk: Jwk): Promise<JwkObject>;
45
+ /**
46
+ * The JWK Thumbprint (RFC 7638 §3, §3.4), base64url: 43, 64 or 86 characters for SHA-256,
47
+ * SHA-384 or SHA-512. An absent `hash` (or `null`) is the core default, SHA-256 (ADR 0054 §9,
48
+ * RFC 9278 §5). Any other name fails with `UnsupportedAlgorithm` from the core, after the key's
49
+ * own import errors (ADR 0057 §2). Never publish the thumbprint of a password or other
50
+ * low-entropy `oct` key, or use it as a `kid`: it is an unsalted fast hash of the secret
51
+ * (ADR 0057 §4, RFC 7638 §7).
52
+ */
53
+ jwkThumbprint(jwk: Jwk, hash?: ThumbprintHash): Promise<string>;
54
+ /**
55
+ * The JWK Thumbprint URI (RFC 9278 §3), `urn:ietf:params:oauth:jwk-thumbprint:<name>:<thumbprint>`,
56
+ * always with the IANA hash name (`sha-256`, `sha-384`, `sha-512`; RFC 9278 §4). `hash` is as
57
+ * for `jwkThumbprint`.
58
+ */
59
+ jwkThumbprintUri(jwk: Jwk, hash?: ThumbprintHash): Promise<string>;
60
+ /**
61
+ * The one key of a JWK Set (RFC 7517 §5) that `selector` picks by `kid`, `alg` and `keyUse`,
62
+ * under the core rule (`JwkSet::select`, ADR 0054 §2). An absent selector (or `{}`) matches
63
+ * every member. No match rejects with `NoMatchingKey`, two or more with `MultipleMatchingKeys`
64
+ * (RFC 7517 §5.1 gives a set's order no meaning); an `alg` or `keyUse` the core refuses with
65
+ * `UnsupportedAlgorithm`, after the set's own import errors (ADR 0054 §4).
66
+ *
67
+ * The key may be private (`d`, `k`): call `jwkToPublic` before publishing it. Selection never
68
+ * authorizes an algorithm, so pass the key only to an operation with an allow-list, and pair a
69
+ * `kid` from an untrusted header with `alg` (ADR 0054 §7). A server maps `NoMatchingKey` and
70
+ * `MultipleMatchingKeys` to one opaque client error, such as `invalid_token` (RFC 6750 §3.1).
71
+ */
72
+ jwksSelect(jwks: JwkSet, selector?: JwkSelector): Promise<JwkObject>;
73
+ /** A random `oct` key of `lenBytes` octets (RFC 7518 §6.4), from the OS CSPRNG (ADR 0005). */
74
+ jwkGenerateOct(lenBytes: number): Promise<JwkObject>;
75
+ /** A random EC private key (RFC 7518 §6.2). */
76
+ jwkGenerateEc(crv: EcCurve): Promise<JwkObject>;
77
+ /** A random OKP private key (RFC 8037 §2). */
78
+ jwkGenerateOkp(crv: OkpCurve): Promise<JwkObject>;
79
+ /** A random key for a JWS `alg`, JWE `alg` or `enc` (RFC 7518 §7.1 registry), with `alg` set. */
80
+ jwkGenerateForAlg(alg: string): Promise<JwkObject>;
81
+ /** Imports one RFC 7468 PEM block (ADR 0018 §3). */
82
+ jwkFromPem(pem: string): Promise<JwkObject>;
83
+ /** Imports DER in the structure `format` names; no sniffing (ADR 0018 §3, ADR 0007). */
84
+ jwkFromDer(der: Uint8Array, format: KeyFormat): Promise<JwkObject>;
85
+ /** Exports a JWK as PEM (RFC 7468, ADR 0018 §3). */
86
+ jwkToPem(jwk: Jwk, format: KeyFormat): Promise<string>;
87
+ /** Exports a JWK as DER (ADR 0018 §3). */
88
+ jwkToDer(jwk: Jwk, format: KeyFormat): Promise<Uint8Array>;
89
+ /** Signs in the JWS Compact Serialization (RFC 7515 §5.1, §7.1). */
90
+ jwsSignCompact(payload: Uint8Array, key: Jwk, options: JwsSignOptions): Promise<string>;
91
+ /** Signs in the flattened JWS JSON Serialization (RFC 7515 §7.2.2); returns JSON text. */
92
+ jwsSignFlattened(payload: Uint8Array, signer: JwsSigner, detached?: boolean): Promise<string>;
93
+ /** Signs in the general JWS JSON Serialization (RFC 7515 §7.2.1); returns JSON text. */
94
+ jwsSignGeneral(payload: Uint8Array, signers: readonly JwsSigner[], detached?: boolean): Promise<string>;
95
+ /** Verifies a compact JWS against an explicit allow-list (RFC 7515 §5.2, RFC 8725 §3.1). */
96
+ jwsVerifyCompact(token: string, key: Jwk, options: JwsVerifyOptions): Promise<JwsVerified>;
97
+ /** Verifies a flattened or general JWS JSON Serialization (RFC 7515 §7.2, §5.2). */
98
+ jwsVerifyJson(jws: JsonInput, key: Jwk, options: JwsVerifyOptions): Promise<JwsVerified>;
99
+ /** Encrypts to one recipient in the JWE Compact Serialization (RFC 7516 §5.1, §7.1). */
100
+ jweEncryptCompact(plaintext: Uint8Array, options: JweEncryptOptions): Promise<string>;
101
+ /** Encrypts in a JWE JSON Serialization (RFC 7516 §7.2); returns JSON text. */
102
+ jweEncryptJson(plaintext: Uint8Array, options: JweEncryptOptions, form: JweJsonForm): Promise<string>;
103
+ /** Decrypts a compact JWE against explicit allow-lists (RFC 7516 §5.2, RFC 8725 §3.1). */
104
+ jweDecryptCompact(token: string, key: Jwk, options: JweDecryptOptions): Promise<JweDecrypted>;
105
+ /** Decrypts a flattened or general JWE JSON Serialization (RFC 7516 §7.2, §5.2). */
106
+ jweDecryptJson(jwe: JsonInput, key: Jwk, options: JweDecryptOptions): Promise<JweDecrypted>;
107
+ /** Issues a signed JWT (RFC 7519 §7.1; ADR 0027 §8). */
108
+ jwtSign(claims: JsonInput, key: Jwk, options: JwtSignOptions): Promise<string>;
109
+ /** Validates a signed JWT with one key (RFC 7519 §7.2; ADR 0027 policy and error order). */
110
+ jwtVerify(token: string, key: Jwk, options: JwtVerifyOptions): Promise<JwtVerified>;
111
+ /** Validates a signed JWT with a JWK Set (RFC 7517 §5; ADR 0029 selection). */
112
+ jwtVerifyWithJwks(token: string, jwks: JwkSet, options: JwtVerifyOptions): Promise<JwtVerified>;
113
+ /** Encrypts a signed JWT as a nested JWT, `cty: "JWT"` (RFC 7519 §5.2, §11.2; ADR 0027 §7). */
114
+ jwtEncryptNested(signedJwt: string, options: JweEncryptOptions): Promise<string>;
115
+ /** Decrypts and validates a nested JWT with one verification key (RFC 7519 §7.2 step 8). */
116
+ jwtDecryptNested(token: string, decryptKey: Jwk, decryptOptions: JweDecryptOptions, key: Jwk, verifyOptions: JwtVerifyOptions): Promise<JwtNestedVerified>;
117
+ /** Decrypts and validates a nested JWT with a JWK Set for the inner signature. */
118
+ jwtDecryptNestedWithJwks(token: string, decryptKey: Jwk, decryptOptions: JweDecryptOptions, jwks: JwkSet, verifyOptions: JwtVerifyOptions): Promise<JwtNestedVerified>;
119
+ /** The error class every operation rejects with for a core failure. */
120
+ readonly PolyjoseError: typeof PolyjoseError;
121
+ }
122
+ /** Binds the facade to one native layer (BACKEND.md). */
123
+ export declare function createApi(backend: Backend): PolyjoseApi;