@actana/sdk 0.3.3 → 0.4.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,630 @@
1
+ // Pairing, from the client's side: a key pair born here, a fingerprint checked
2
+ // before a secret moves, and a `CoreRegistrationBlob` at the end of it (#280,
3
+ // #284).
4
+ //
5
+ // An operator on the Core runs `actana pair new` and reads out two things — an
6
+ // eight-character code and the SHA-256 fingerprint of that Core's CA. This
7
+ // module is what the other machine does with them:
8
+ //
9
+ // 1. dial the Core's HTTPS surface with nothing trusted yet, and read the
10
+ // certificate chain it presents;
11
+ // 2. compute the fingerprint of the CA in that chain and compare it with the
12
+ // one the operator read out;
13
+ // 3. **only then** post the code, a client label and a CSR;
14
+ // 4. put the response together with the private key that never moved, and
15
+ // hand back the shape every other client surface already takes.
16
+ //
17
+ // **Step 2 is the whole security argument.** A client at step 1 has an address
18
+ // and a code and no trust anchor, so the dial cannot verify anything and does
19
+ // not pretend to. What it must not do is *stay* that way: the fingerprint is
20
+ // compared while nothing secret has been sent, and the redemption in step 3 is
21
+ // a second, separate connection pinned to the exact CA certificate that
22
+ // matched — `rejectUnauthorized: true`, plus a `checkServerIdentity` that
23
+ // re-runs both the hostname check and the fingerprint comparison before the
24
+ // handshake completes. There is no code path here that sends the code over an
25
+ // unverified connection, and no blob comes back pinned to a CA that was not
26
+ // the one compared.
27
+ //
28
+ // **A caller with no fingerprint is not a caller with a waived one.** Passing
29
+ // no `expectedCaFingerprint` is the first-contact case (#286's UI wants to show
30
+ // the operator's fingerprint beside the Core's before anyone types anything),
31
+ // and it is answered by reporting the presented fingerprint back — through
32
+ // {@link fetchCorePairingIdentity}, which has no code to send, or as the
33
+ // `fingerprint-unconfirmed` failure of {@link pairWithCore}, which has one and
34
+ // does not send it.
35
+ //
36
+ // **The wire types live in `core-pairing-wire.ts` and are re-exported here.**
37
+ // They are not imported from `@actana/shared`: that package is private and
38
+ // stays private ([ADR 0025][adr] D4), so an SDK that imported the pairing
39
+ // request and response from it would be a published package with a dependency
40
+ // nobody outside this repository can resolve — the arrangement that ADR
41
+ // rejected (D1: the protocol ships with the client). Nor are they declared
42
+ // here: `packages/core/src/core-pairing-routes.ts` reads and answers with the
43
+ // same shapes, and two structurally identical declarations are the mirror D3
44
+ // forbids. One import-free module, imported by both sides, is the shape D2's
45
+ // amendment allows and D3 asks for.
46
+ //
47
+ // [adr]: ../../docs/adr/0025-the-protocol-ships-with-the-client.md
48
+ import { createHash } from "node:crypto";
49
+ import { request as httpsRequest } from "node:https";
50
+ import { isIP } from "node:net";
51
+ import { checkServerIdentity as checkTlsServerIdentity, connect as tlsConnect } from "node:tls";
52
+ import { generateClientCsr } from "./core-pairing-csr.js";
53
+ // ─── The wire ───
54
+ // One definition, in an import-free module the Core imports too (ADR 0025 D2 as
55
+ // amended by #306, D3). Re-exported here so every client-side caller keeps
56
+ // reaching for them by the name it already used.
57
+ export { CORE_PAIRING_REDEEM_PATH, } from "./core-pairing-wire.js";
58
+ import { CORE_PAIRING_REDEEM_PATH } from "./core-pairing-wire.js";
59
+ /**
60
+ * A pairing attempt that did not produce a blob.
61
+ *
62
+ * One class with a {@link CorePairingFailure} rather than a class per failure:
63
+ * the CLI (#285) and the Panel (#286) both switch on the reason to write a
64
+ * sentence, and a `switch` over a union is checked by the compiler where a
65
+ * chain of `instanceof` is not.
66
+ */
67
+ export class CorePairingError extends Error {
68
+ name = "CorePairingError";
69
+ /** Which failure this is. Switch on it; do not read the message. */
70
+ failure;
71
+ /** Whatever the failure knows beyond its reason. */
72
+ detail;
73
+ // Fields are assigned rather than declared as constructor parameters: this
74
+ // package is loaded by plain `node` with nothing but type stripping
75
+ // (`__tests__/plain-node-consumption.test.ts`), and a parameter property is
76
+ // syntax that stripping cannot erase.
77
+ constructor(failure, message, detail = {}, options = {}) {
78
+ super(message, options);
79
+ this.failure = failure;
80
+ this.detail = detail;
81
+ }
82
+ }
83
+ /** How long a dial or a redemption may take before it is called unreachable. */
84
+ export const DEFAULT_PAIRING_TIMEOUT_MS = 15_000;
85
+ /**
86
+ * Dial a Core and report the CA it presents — **without a code to send**.
87
+ *
88
+ * This is the first-contact mode, and the reason it is a separate function
89
+ * rather than a flag is that it takes no code: a UI that shows the operator's
90
+ * fingerprint beside the Core's, and asks a human whether they match, cannot
91
+ * leak a secret it was never given. {@link pairWithCore} calls it too, so the
92
+ * fingerprint a caller confirms is computed by the same code that later
93
+ * enforces it.
94
+ *
95
+ * The dial is unverified, because at this point in the flow there is nothing to
96
+ * verify against — that is what the fingerprint the operator read out is for.
97
+ * Nothing is sent on this connection and it is closed as soon as the chain has
98
+ * been read.
99
+ */
100
+ export async function fetchCorePairingIdentity(opts) {
101
+ const { host, port, httpsOrigin } = parseCoreAddress(opts.address);
102
+ const chain = await presentedChain(host, port, opts.timeoutMs ?? DEFAULT_PAIRING_TIMEOUT_MS);
103
+ const ca = certificateAuthorityIn(chain);
104
+ if (!ca) {
105
+ throw new CorePairingError("no-ca-presented", `${httpsOrigin} presented a certificate chain with no certificate authority in it, so there is nothing to compare against the fingerprint`);
106
+ }
107
+ return { fingerprint: fingerprintOf(ca.raw), caCert: derToCertificatePem(ca.raw), host, port, httpsOrigin };
108
+ }
109
+ /**
110
+ * Pair with a Core and return the credential it issued.
111
+ *
112
+ * The result is a {@link CoreRegistrationBlob} and nothing downstream can tell
113
+ * it from a hand-carried one: `coreConnectionFromBlob` unpacks it,
114
+ * `httpsBaseUrlFor` derives the HTTPS origin, the PEMs go into the mTLS
115
+ * handshake and the bearer into the `auth` frame — exactly as they do today.
116
+ * `clientKey` is the key generated on this machine a few lines above; it was
117
+ * never sent, and the Core has never seen it.
118
+ *
119
+ * Throws {@link CorePairingError} for everything that is not a blob.
120
+ */
121
+ export async function pairWithCore(opts) {
122
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_PAIRING_TIMEOUT_MS;
123
+ // Read the address and the ticket first. Both are failures a caller can fix
124
+ // without a Core being involved, and neither is worth a dial to discover.
125
+ const address = parseCoreAddress(opts.address);
126
+ const ticket = parsePairingTicket(opts.code, opts.sessionId);
127
+ const expected = opts.expectedCaFingerprint ? parseFingerprint(opts.expectedCaFingerprint) : null;
128
+ const identity = await fetchCorePairingIdentity({ address: opts.address, timeoutMs });
129
+ // ── The comparison, and everything that turns on it ──
130
+ if (!expected) {
131
+ throw new CorePairingError("fingerprint-unconfirmed", `${identity.httpsOrigin} presents a certificate authority with fingerprint ${identity.fingerprint}; no expected fingerprint was given, so the pairing code was not sent`, { presentedFingerprint: identity.fingerprint, presentedCaCert: identity.caCert });
132
+ }
133
+ if (expected !== identity.fingerprint) {
134
+ throw new CorePairingError("fingerprint-mismatch", `${identity.httpsOrigin} presented a certificate authority with fingerprint ${identity.fingerprint}, but ${expected} was expected — the pairing code was not sent`, {
135
+ expectedFingerprint: expected,
136
+ presentedFingerprint: identity.fingerprint,
137
+ presentedCaCert: identity.caCert,
138
+ });
139
+ }
140
+ // Past the comparison, and only past it. The key pair is minted here rather
141
+ // than above so that a mismatch costs a dial rather than a dial and an RSA
142
+ // key generation — and so that nothing exists to be sent until the Core on
143
+ // the other end is the one the operator described.
144
+ const { csrPem, privateKeyPem } = await generateClientCsr(opts.label ?? "actana-client");
145
+ const body = {
146
+ sessionId: ticket.sessionId,
147
+ code: ticket.code,
148
+ client: {
149
+ ...(opts.label === undefined ? {} : { label: opts.label }),
150
+ ...(opts.platform === undefined ? {} : { platform: opts.platform }),
151
+ },
152
+ csr: csrPem,
153
+ };
154
+ const answer = await postRedemption({
155
+ host: address.host,
156
+ port: address.port,
157
+ origin: identity.httpsOrigin,
158
+ caCert: identity.caCert,
159
+ expectedFingerprint: expected,
160
+ body: JSON.stringify(body),
161
+ timeoutMs,
162
+ });
163
+ const issued = readRedeemResponse(answer, identity.httpsOrigin);
164
+ // The CA in the response is the one every later dial pins, so it is held to
165
+ // the same fingerprint the bootstrap dial was. A Core that presented one CA
166
+ // in its handshake and handed back another would be asking this client to
167
+ // trust something no human ever read out.
168
+ const issuedFingerprint = fingerprintOf(pemToDer(issued.caCert));
169
+ if (issuedFingerprint !== expected) {
170
+ throw new CorePairingError("fingerprint-mismatch", `${identity.httpsOrigin} answered with a certificate authority whose fingerprint is ${issuedFingerprint}, not the ${expected} it presented in the handshake`, { expectedFingerprint: expected, presentedFingerprint: issuedFingerprint });
171
+ }
172
+ return {
173
+ endpoint: issued.endpoint,
174
+ ...(opts.label === undefined ? {} : { label: opts.label }),
175
+ caCert: issued.caCert,
176
+ clientCert: issued.clientCert,
177
+ // The field that never crossed the wire, put back into the shape.
178
+ clientKey: privateKeyPem,
179
+ bearer: issued.bearer,
180
+ };
181
+ }
182
+ /**
183
+ * Read a ticket out of what a human typed.
184
+ *
185
+ * The code is checked for *shape* — eight alphanumerics, hyphens and spaces
186
+ * ignored — and not against the Core's alphabet. That is a deliberate stop:
187
+ * the alphabet is an internal of `packages/shared` (which this package may not
188
+ * import) and mirroring it here would be a copy free to drift, which ADR 0025
189
+ * D3 is about. What the shape check buys is worth having on its own: a code
190
+ * that could not be right whatever the alphabet is never spends one of the
191
+ * five attempts the operator's session has.
192
+ */
193
+ export function parsePairingTicket(input, sessionId) {
194
+ const trimmed = input.trim();
195
+ const separator = trimmed.indexOf(":");
196
+ const explicit = sessionId?.trim() ?? "";
197
+ const carried = separator === -1 ? "" : trimmed.slice(0, separator).trim();
198
+ // The prefix is stripped whenever there is one, whether or not a session id
199
+ // was also passed: a caller that always forwards `--session` while letting an
200
+ // operator paste whatever they were read out hands in both, and refusing that
201
+ // would refuse the one shape this function exists to be tolerant of.
202
+ const rawCode = separator === -1 ? trimmed : trimmed.slice(separator + 1);
203
+ if (explicit !== "" && carried !== "" && explicit !== carried) {
204
+ // Two session ids that disagree is not a shape to pick a winner from: one
205
+ // of them is a mistake, and redeeming against the wrong session refuses in
206
+ // a way that looks like a bad code.
207
+ throw new CorePairingError("bad-code", `the code names session "${carried}" and "${explicit}" was passed beside it — they must agree`);
208
+ }
209
+ const session = explicit !== "" ? explicit : carried;
210
+ if (session === "") {
211
+ throw new CorePairingError("bad-code", "a pairing code names a pairing session — pass the session id as `sessionId`, or a `<sessionId>:<XXXX-XXXX>` code");
212
+ }
213
+ const stripped = rawCode.replace(/[\s-]/g, "").toUpperCase();
214
+ if (!/^[A-Z0-9]{8}$/.test(stripped)) {
215
+ throw new CorePairingError("bad-code", `a pairing code is eight characters, written XXXX-XXXX — "${rawCode.trim()}" is not`);
216
+ }
217
+ return { sessionId: session, code: `${stripped.slice(0, 4)}-${stripped.slice(4)}` };
218
+ }
219
+ /**
220
+ * Read `host:port`, `https://host:port` or `wss://host:port`.
221
+ *
222
+ * `ws://` and `http://` are refused rather than upgraded: there is no
223
+ * certificate on a plaintext dial, so there is no fingerprint to check, and
224
+ * pairing over one would be the silent unverified exchange this module exists
225
+ * to make impossible. A caller that meant the secure port should say so.
226
+ */
227
+ export function parseCoreAddress(address) {
228
+ const trimmed = address.trim();
229
+ if (trimmed === "")
230
+ throw new CorePairingError("bad-address", "a Core address is required");
231
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
232
+ let url;
233
+ try {
234
+ url = new URL(withScheme);
235
+ }
236
+ catch {
237
+ throw new CorePairingError("bad-address", `"${address}" is not a Core address — try host:port`);
238
+ }
239
+ if (url.protocol === "ws:" || url.protocol === "http:") {
240
+ throw new CorePairingError("bad-address", `pairing needs the Core's TLS port: "${address}" names a plaintext one, and there is no certificate on it to check the fingerprint against`);
241
+ }
242
+ if (url.protocol !== "https:" && url.protocol !== "wss:") {
243
+ throw new CorePairingError("bad-address", `"${address}" is not a Core address — try host:port`);
244
+ }
245
+ const host = url.hostname.replace(/^\[|\]$/g, "");
246
+ if (host === "")
247
+ throw new CorePairingError("bad-address", `"${address}" names no host`);
248
+ const port = url.port === "" ? 443 : Number(url.port);
249
+ return { host, port, httpsOrigin: `https://${url.host}` };
250
+ }
251
+ /**
252
+ * A fingerprint as this module compares them: colon-separated uppercase hex,
253
+ * which is the form `actana pair new` prints and a human copies.
254
+ */
255
+ export function parseFingerprint(input) {
256
+ const hex = input.trim().replace(/^sha-?256[:=]/i, "").replace(/[\s:]/g, "").toUpperCase();
257
+ if (!/^[0-9A-F]{64}$/.test(hex)) {
258
+ throw new CorePairingError("bad-fingerprint", `"${input.trim()}" is not a SHA-256 fingerprint — expected 32 bytes of hex, as AA:BB:…`);
259
+ }
260
+ return groupHex(hex);
261
+ }
262
+ /** SHA-256 over a certificate's DER, in the form {@link parseFingerprint} yields. */
263
+ export function fingerprintOf(der) {
264
+ return groupHex(createHash("sha256").update(der).digest("hex").toUpperCase());
265
+ }
266
+ function groupHex(hex) {
267
+ return (hex.match(/../g) ?? []).join(":");
268
+ }
269
+ /**
270
+ * The chain a Core presents, deepest certificate last.
271
+ *
272
+ * `rejectUnauthorized: false` is here and nowhere else in this file. It is what
273
+ * "no trust anchor yet" means in code: the client has an address and a
274
+ * fingerprint on a piece of paper, and the only way to compare the two is to
275
+ * look at what the server presents. Nothing is sent on this socket, and it is
276
+ * destroyed as soon as the chain has been copied out.
277
+ */
278
+ function presentedChain(host, port, timeoutMs) {
279
+ return new Promise((resolve, reject) => {
280
+ const socket = tlsConnect({
281
+ host,
282
+ port,
283
+ rejectUnauthorized: false,
284
+ // An IP address is not a valid SNI server name (RFC 6066), and Node warns
285
+ // about sending one. The certificate is identified by its fingerprint
286
+ // here rather than by its name, so there is nothing to lose by omitting
287
+ // it in that case.
288
+ ...(isIP(host) === 0 ? { servername: host } : {}),
289
+ });
290
+ const settle = (fn) => {
291
+ clearTimeout(timer);
292
+ socket.removeAllListeners();
293
+ socket.destroy();
294
+ fn();
295
+ };
296
+ const timer = setTimeout(() => {
297
+ settle(() => reject(new CorePairingError("unreachable", `${host}:${port} did not answer within ${timeoutMs}ms`)));
298
+ }, timeoutMs);
299
+ socket.once("secureConnect", () => {
300
+ const chain = chainOf(socket.getPeerCertificate(true));
301
+ settle(() => resolve(chain));
302
+ });
303
+ socket.once("error", (err) => {
304
+ settle(() => reject(new CorePairingError("unreachable", `${host}:${port} could not be reached: ${err.message}`, {}, { cause: err })));
305
+ });
306
+ });
307
+ }
308
+ /**
309
+ * Walk a peer certificate up to the root.
310
+ *
311
+ * Node hands back the chain as a linked list through `issuerCertificate`, and
312
+ * terminates it by pointing the root at itself — so the loop stops on a
313
+ * certificate it has already seen rather than on a null, which is the shape
314
+ * that would spin forever.
315
+ */
316
+ function chainOf(leaf) {
317
+ const chain = [];
318
+ const seen = new Set();
319
+ let current = leaf;
320
+ while (current && current.raw && !seen.has(current.fingerprint256)) {
321
+ seen.add(current.fingerprint256);
322
+ chain.push(current);
323
+ current = current.issuerCertificate;
324
+ }
325
+ return chain;
326
+ }
327
+ /**
328
+ * The certificate authority in a presented chain: the last one, when it is
329
+ * self-issued.
330
+ *
331
+ * A Core presents its server certificate and the CA above it, and that CA — the
332
+ * one `actana pair new` fingerprints from `PersistedMaterial.caCert` — is the
333
+ * root of what it sends. If the chain ends somewhere else the Core has sent a
334
+ * partial chain, and there is nothing here to compare: the alternative,
335
+ * fingerprinting whatever is at the top, would compare the operator's CA
336
+ * fingerprint against a leaf and fail with a mismatch that describes the wrong
337
+ * problem.
338
+ *
339
+ * That makes "a Core presents a chain ending in its own root" a property of the
340
+ * pairing flow rather than a detail of this file: a Core that later fronts an
341
+ * intermediate, or serves a partial chain, breaks first contact for every
342
+ * client. It belongs in #280 beside the rest of the flow's properties, and is
343
+ * written here so the dependency is visible from the code that has it.
344
+ */
345
+ function certificateAuthorityIn(chain) {
346
+ const top = chain.at(-1);
347
+ if (!top)
348
+ return null;
349
+ return sameName(top.subject, top.issuer) ? top : null;
350
+ }
351
+ function sameName(a, b) {
352
+ return JSON.stringify(a) === JSON.stringify(b);
353
+ }
354
+ /**
355
+ * Post the redemption on a connection pinned to the CA that just matched.
356
+ *
357
+ * Three things hold the code to that connection, and they are deliberately not
358
+ * one thing: `ca` is the single certificate the bootstrap dial fingerprinted,
359
+ * so no other authority can complete this handshake; `rejectUnauthorized` is
360
+ * true, so the verification is enforced rather than reported; and
361
+ * `checkServerIdentity` re-runs Node's own hostname check *and* the fingerprint
362
+ * comparison, before the handshake completes and therefore before a byte of the
363
+ * body is written. The last is redundant against the first two by construction
364
+ * — which is the point of having it, since the first two are options on an
365
+ * object and the day one of them is edited away the third still refuses.
366
+ */
367
+ function postRedemption(opts) {
368
+ return new Promise((resolve, reject) => {
369
+ const req = httpsRequest({
370
+ host: opts.host,
371
+ port: opts.port,
372
+ path: CORE_PAIRING_REDEEM_PATH,
373
+ method: "POST",
374
+ ca: opts.caCert,
375
+ rejectUnauthorized: true,
376
+ agent: false,
377
+ ...(isIP(opts.host) === 0 ? { servername: opts.host } : {}),
378
+ checkServerIdentity: (host, cert) => {
379
+ const identity = checkTlsServerIdentity(host, cert);
380
+ if (identity)
381
+ return identity;
382
+ const ca = certificateAuthorityIn(chainOf(cert));
383
+ if (!ca) {
384
+ return pinFailure(`${opts.origin} presented no certificate authority on the redemption dial`);
385
+ }
386
+ const presented = fingerprintOf(ca.raw);
387
+ if (presented !== opts.expectedFingerprint) {
388
+ return pinFailure(`${opts.origin} presented ${presented} on the redemption dial, not the ${opts.expectedFingerprint} it presented before`);
389
+ }
390
+ return undefined;
391
+ },
392
+ headers: {
393
+ "content-type": "application/json",
394
+ "content-length": String(Buffer.byteLength(opts.body)),
395
+ },
396
+ }, (res) => {
397
+ const chunks = [];
398
+ res.on("data", (chunk) => chunks.push(chunk));
399
+ res.on("end", () => {
400
+ clearTimeout(timer);
401
+ const retryAfter = Number(res.headers["retry-after"]);
402
+ resolve({
403
+ status: res.statusCode ?? 0,
404
+ body: Buffer.concat(chunks).toString("utf8"),
405
+ ...(Number.isFinite(retryAfter) ? { retryAfterSeconds: retryAfter } : {}),
406
+ });
407
+ });
408
+ });
409
+ const timer = setTimeout(() => {
410
+ req.destroy(new Error(`no answer within ${opts.timeoutMs}ms`));
411
+ }, opts.timeoutMs);
412
+ req.on("error", (err) => {
413
+ clearTimeout(timer);
414
+ // Whatever this turns out to be, the body was never written: the failure
415
+ // is the handshake's, and the code went nowhere. What is decided here is
416
+ // only which of four things the operator is told — and one of them
417
+ // accuses somebody, so see {@link classifyDialFailure}.
418
+ const code = failureCode(err);
419
+ const tls = code === undefined ? {} : { tlsCode: code };
420
+ reject(dialFailure(classifyDialFailure(err), err, opts, tls));
421
+ });
422
+ req.end(opts.body);
423
+ });
424
+ }
425
+ /** One sentence per {@link DialFailure}, and the failure that carries it. */
426
+ function dialFailure(kind, err, opts, tls) {
427
+ const cause = { cause: err };
428
+ if (kind === "pin") {
429
+ return new CorePairingError("fingerprint-mismatch", `${opts.origin} did not present the certificate authority whose fingerprint was confirmed — the pairing code was not sent (${err.message})`, { ...tls, expectedFingerprint: opts.expectedFingerprint }, cause);
430
+ }
431
+ if (kind === "hostname") {
432
+ return new CorePairingError("hostname-mismatch", `${opts.origin} presented the expected certificate authority, but its certificate does not cover ${opts.host} — dial the address this Core was set up for (${err.message})`, { ...tls, expectedFingerprint: opts.expectedFingerprint }, cause);
433
+ }
434
+ if (kind === "certificate") {
435
+ return new CorePairingError("certificate-invalid", `${opts.origin} presented the expected certificate authority, but its certificate could not be used: ${err.message}`, { ...tls, expectedFingerprint: opts.expectedFingerprint }, cause);
436
+ }
437
+ return new CorePairingError("unreachable", `${opts.origin} could not be reached: ${err.message}`, tls, cause);
438
+ }
439
+ /**
440
+ * Turn an answer into a response or a {@link CorePairingError}.
441
+ *
442
+ * The mapping is the Core's status table read back: `403` is the one refusal
443
+ * that covers four states, `429` carries a `retry-after` a caller can wait out,
444
+ * `400` and its neighbours mean this client sent something wrong, and `404`
445
+ * means the Core has no pairing endpoint at all — which is what an operator who
446
+ * has not run `actana pair new` on a Core built before #282 will see.
447
+ */
448
+ function readRedeemResponse(answer, origin) {
449
+ if (answer.status !== 200)
450
+ throw refusalFor(answer, origin);
451
+ let parsed;
452
+ try {
453
+ parsed = JSON.parse(answer.body);
454
+ }
455
+ catch {
456
+ throw new CorePairingError("malformed-response", `${origin} answered 200 with something that was not JSON`, {
457
+ status: answer.status,
458
+ });
459
+ }
460
+ // `JSON.parse("null")` succeeds, and an array parses too. Both would reach
461
+ // the sweep below as something a field can be read off without complaint from
462
+ // the compiler, and `null` would throw a raw `TypeError` out of a function
463
+ // whose whole contract is that failures arrive as a `CorePairingError`.
464
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
465
+ throw new CorePairingError("malformed-response", `${origin} answered 200 with something that was not an object`, {
466
+ status: answer.status,
467
+ });
468
+ }
469
+ const fields = parsed;
470
+ const missing = ["endpoint", "caCert", "clientCert", "bearer"].filter((field) => typeof fields[field] !== "string" || fields[field].length === 0);
471
+ if (missing.length > 0) {
472
+ throw new CorePairingError("malformed-response", `${origin} answered 200 without ${missing.join(", ")}`, { status: answer.status });
473
+ }
474
+ // The endpoint decides whether anything later is protected at all.
475
+ // `coreConnectionFromBlob` reads TLS off the scheme and nothing else: a
476
+ // `ws://` endpoint yields `tls: null` **and still carries the bearer**, so a
477
+ // Core that answered with one — misconfigured, or hostile — would hand back a
478
+ // credential whose every later dial ships the bearer in cleartext with no
479
+ // client certificate. That is the property this module spent a bootstrap
480
+ // dial, a fingerprint comparison and a pinned second connection to establish,
481
+ // undone by one field nobody looked at.
482
+ const endpoint = fields.endpoint.trim();
483
+ if (!endpoint.startsWith("wss://")) {
484
+ throw new CorePairingError("malformed-response", `${origin} answered with the endpoint ${endpoint}, which is not a \`wss://\` core link — a paired credential is only a credential on one`, { status: answer.status });
485
+ }
486
+ return {
487
+ endpoint,
488
+ caCert: fields.caCert,
489
+ clientCert: fields.clientCert,
490
+ bearer: fields.bearer,
491
+ };
492
+ }
493
+ function refusalFor(answer, origin) {
494
+ const body = safeRefusal(answer.body);
495
+ const detail = {
496
+ status: answer.status,
497
+ ...(body.code === undefined ? {} : { coreCode: body.code }),
498
+ };
499
+ const said = body.error ?? `HTTP ${answer.status}`;
500
+ if (answer.status === 429) {
501
+ return new CorePairingError("rate-limited", `${origin} is refusing pairing attempts for now: ${said}`, { ...detail, ...(answer.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: answer.retryAfterSeconds }) });
502
+ }
503
+ if (answer.status === 403) {
504
+ return new CorePairingError("refused", `${origin} refused the pairing code: it is wrong, expired, already used, or the session is out of attempts`, detail);
505
+ }
506
+ if (answer.status === 404) {
507
+ return new CorePairingError("not-pairable", `${origin} has no pairing endpoint — ${said}`, detail);
508
+ }
509
+ if (answer.status >= 500) {
510
+ return new CorePairingError("core-error", `${origin} failed to handle the redemption: ${said}`, detail);
511
+ }
512
+ return new CorePairingError("rejected", `${origin} would not accept the redemption: ${said}`, detail);
513
+ }
514
+ /**
515
+ * The `code` this module's own `checkServerIdentity` refusals carry.
516
+ *
517
+ * Node reports a refusal from `checkServerIdentity` by destroying the socket
518
+ * with the returned error, which arrives at the request as an ordinary
519
+ * `error` event indistinguishable from a connection reset. Tagging it is what
520
+ * lets {@link classifyDialFailure} tell "this is not the Core you confirmed"
521
+ * from "nothing answered" — and from the two failures that are neither.
522
+ */
523
+ const PIN_FAILURE_CODE = "ERR_ACTANA_PAIRING_PIN";
524
+ /** An error for `checkServerIdentity` to refuse a handshake with. */
525
+ function pinFailure(message) {
526
+ return Object.assign(new Error(message), { code: PIN_FAILURE_CODE });
527
+ }
528
+ /**
529
+ * Was this failure about the certificate at all?
530
+ *
531
+ * OpenSSL's verdicts reach Node as `code` — `UNABLE_TO_VERIFY_LEAF_SIGNATURE`,
532
+ * `CERT_HAS_EXPIRED` and the rest of a list too long and too version-dependent
533
+ * to enumerate — beside the `ERR_TLS_*` family Node raises itself. Matched by
534
+ * prefix, deliberately: a verification failure this predicate did not recognise
535
+ * would be reported as `unreachable`, which sends an operator looking at their
536
+ * network for a problem that is in their certificates.
537
+ *
538
+ * It answers only that question. Which certificate failure it was — the pin,
539
+ * the hostname, or an unusable certificate — is {@link classifyDialFailure}'s,
540
+ * and that one is enumerated rather than guessed.
541
+ */
542
+ function certificateFailure(code) {
543
+ return (code.startsWith("ERR_TLS_") ||
544
+ code.startsWith("ERR_SSL_") ||
545
+ code.includes("CERT") ||
546
+ code.startsWith("UNABLE_TO_") ||
547
+ code.startsWith("DEPTH_ZERO_") ||
548
+ code.startsWith("SELF_SIGNED_"));
549
+ }
550
+ /**
551
+ * OpenSSL verdicts that mean **this chain does not lead to the pinned CA**.
552
+ *
553
+ * Every one of them is a statement about who signed what: no issuer, an issuer
554
+ * that is not the one supplied, a signature that does not check out, a chain
555
+ * that ends in a self-signed certificate that is not the pinned root. On a dial
556
+ * whose `ca` is the single certificate the operator's fingerprint matched,
557
+ * that is the pin refusing — the same event `checkServerIdentity` reports when
558
+ * it gets far enough to run.
559
+ *
560
+ * Enumerated rather than prefix-matched, because this is the set that decides
561
+ * whether a person is told they are being attacked.
562
+ */
563
+ const CHAIN_FAILURE_CODES = new Set([
564
+ "UNABLE_TO_GET_ISSUER_CERT",
565
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
566
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
567
+ "SELF_SIGNED_CERT_IN_CHAIN",
568
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
569
+ "CERT_SIGNATURE_FAILURE",
570
+ "CERT_UNTRUSTED",
571
+ "INVALID_CA",
572
+ ]);
573
+ /** Node's own verdict when a certificate does not cover the address dialled. */
574
+ const HOSTNAME_FAILURE_CODE = "ERR_TLS_CERT_ALTNAME_INVALID";
575
+ /**
576
+ * Classify a failure on the pinned dial.
577
+ *
578
+ * The distinction this draws is the module's most consequential sentence, and
579
+ * it used to be drawn wrong: any code containing `CERT` was reported as a
580
+ * fingerprint mismatch, so `ERR_TLS_CERT_ALTNAME_INVALID` — a Core set up for
581
+ * one address and reached at another, which `core-cert-material.ts` makes an
582
+ * ordinary configuration rather than an exotic one — told the operator they
583
+ * were being intercepted. So did an expired server certificate. **A
584
+ * misconfigured Core must not be reported as an attack**: the person who reads
585
+ * that goes looking for an attacker instead of for their own SAN list.
586
+ *
587
+ * So the width and the accusation are now two separate decisions.
588
+ * {@link certificateFailure} still decides broadly whether the failure was
589
+ * about the certificate at all — being wrong there only costs the wrong noun.
590
+ * Whether it was the *pin* is decided by an enumerated set plus the tagged
591
+ * refusal, and anything certificate-shaped that is in neither is reported as an
592
+ * invalid certificate. Nothing is lost by that default: the exchange is refused
593
+ * either way and the code was never written to the socket — the only thing that
594
+ * changes is which sentence a person is shown.
595
+ */
596
+ function classifyDialFailure(err) {
597
+ const code = String(err?.code ?? "");
598
+ if (code === PIN_FAILURE_CODE)
599
+ return "pin";
600
+ if (CHAIN_FAILURE_CODES.has(code))
601
+ return "pin";
602
+ if (code === HOSTNAME_FAILURE_CODE)
603
+ return "hostname";
604
+ return certificateFailure(code) ? "certificate" : "transport";
605
+ }
606
+ /** The `code` a dial failure carried, for {@link CorePairingErrorDetail.tlsCode}. */
607
+ function failureCode(err) {
608
+ const code = err?.code;
609
+ return typeof code === "string" && code.length > 0 ? code : undefined;
610
+ }
611
+ function safeRefusal(body) {
612
+ try {
613
+ const parsed = JSON.parse(body);
614
+ return parsed && typeof parsed === "object" ? parsed : {};
615
+ }
616
+ catch {
617
+ return {};
618
+ }
619
+ }
620
+ /** DER to a PEM `CERTIFICATE`, in the 64-column form every PEM reader expects. */
621
+ function derToCertificatePem(der) {
622
+ const body = Buffer.from(der).toString("base64").replace(/(.{64})/g, "$1\n").trimEnd();
623
+ return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----\n`;
624
+ }
625
+ /** The first certificate in a PEM, as DER. Throws nothing: an empty PEM yields no bytes. */
626
+ function pemToDer(pem) {
627
+ const match = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/.exec(pem);
628
+ return new Uint8Array(Buffer.from((match?.[1] ?? "").replace(/\s+/g, ""), "base64"));
629
+ }
630
+ //# sourceMappingURL=core-pairing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-pairing.js","sourceRoot":"","sources":["../src/core-pairing.ts"],"names":[],"mappings":"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"}