@actana/sdk 0.3.3 → 0.4.1
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 +2 -1
- package/dist/core-client.d.ts +18 -0
- package/dist/core-client.d.ts.map +1 -1
- package/dist/core-client.js +26 -2
- package/dist/core-client.js.map +1 -1
- package/dist/core-link-frames.d.ts +77 -1
- package/dist/core-link-frames.d.ts.map +1 -1
- package/dist/core-link-frames.js +37 -1
- package/dist/core-link-frames.js.map +1 -1
- package/dist/core-pairing-csr.d.ts +21 -0
- package/dist/core-pairing-csr.d.ts.map +1 -0
- package/dist/core-pairing-csr.js +159 -0
- package/dist/core-pairing-csr.js.map +1 -0
- package/dist/core-pairing-wire.d.ts +63 -0
- package/dist/core-pairing-wire.d.ts.map +1 -0
- package/dist/core-pairing-wire.js +22 -0
- package/dist/core-pairing-wire.js.map +1 -0
- package/dist/core-pairing.d.ts +211 -0
- package/dist/core-pairing.d.ts.map +1 -0
- package/dist/core-pairing.js +630 -0
- package/dist/core-pairing.js.map +1 -0
- package/dist/core-session.d.ts +213 -5
- package/dist/core-session.d.ts.map +1 -1
- package/dist/core-session.js +326 -28
- package/dist/core-session.js.map +1 -1
- package/package.json +1 -1
|
@@ -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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"}
|