@amritk/nish-x86_64-linux 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +45 -4
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/host.d.ts +12 -0
  6. package/runtime/core/host.mjs +74 -2
  7. package/runtime/nish.d.ts +148 -0
  8. package/runtime/nish.h +59 -0
  9. package/runtime/nish.mjs +25 -0
  10. package/runtime/node.mjs +136 -6
  11. package/runtime/runtime-net.c +44 -3
  12. package/runtime/runtime-os.c +351 -18
  13. package/scripts/build.sh +13 -3
  14. package/std/README.md +27 -10
  15. package/std/cli-args.ts +1216 -0
  16. package/std/cli-flags.ts +585 -0
  17. package/std/cli.ts +308 -19
  18. package/std/compress/checksum.ts +132 -20
  19. package/std/compress/deflate.ts +638 -248
  20. package/std/compress/gzip.ts +20 -6
  21. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  22. package/std/crypto/p256.ts +63 -1
  23. package/std/crypto/p384.ts +6044 -0
  24. package/std/crypto/pki-roots.ts +322 -0
  25. package/std/crypto/pki.ts +1207 -0
  26. package/std/crypto/rsa.ts +663 -0
  27. package/std/crypto/x509.ts +1054 -58
  28. package/std/net/dns-config.ts +734 -0
  29. package/std/net/dns-message.ts +517 -0
  30. package/std/net/dns-resolver.ts +1015 -0
  31. package/std/net/dns.ts +478 -0
  32. package/std/net/http1-client.ts +1227 -0
  33. package/std/net/http1-server.ts +7 -2
  34. package/std/net/http1.ts +9 -9
  35. package/std/net/postgres.ts +259 -26
  36. package/std/net/tls/client.ts +1094 -0
  37. package/std/net/tls/codec.ts +585 -3
  38. package/std/net/tls/record-client.ts +1192 -0
  39. package/std/net/tls/verify.ts +221 -0
  40. package/std/net/tls-tcp.ts +642 -11
  41. package/std/net/tls.ts +2 -2
  42. package/std/net/url.ts +559 -4
  43. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,1094 @@
1
+ /**
2
+ * `nish/net/tls/client` — the client side of a TLS 1.3 handshake (RFC 8446),
3
+ * from its ClientHello to its Finished, carrier-agnostic and sans-IO: the
4
+ * mirror of `nish/net/tls`'s `TlsServer`.
5
+ *
6
+ * import { TlsClient, TlsClientConfig } from "nish/net/tls/client";
7
+ *
8
+ * const client = new TlsClient(new TlsClientConfig("example.com", ["h2"]), random, x25519Key, p256Key);
9
+ * client.start();
10
+ * // send client.takeOutput(TLS_LEVEL_INITIAL); hand the server's bytes to
11
+ * // client.receive(level, bytes, 0, n); install secrets as they are known;
12
+ * // at TLS_CLIENT_STATE_WAIT_AUTH check peerCertificates() and the
13
+ * // signature, then client.authenticate(ok) (or client.reject(alert)).
14
+ *
15
+ * It lives in a module of its own, beside `nish/net/tls` and not re-exported
16
+ * by it, since Nish has no re-exports; the levels and `TLS_SECRET_*` indices
17
+ * it shares with the server are `nish/net/tls`'s.
18
+ *
19
+ * **Carrier-agnostic.** As the server does, the client reads and writes
20
+ * handshake *messages* tagged with their level — the ClientHello and the
21
+ * ServerHello at Initial, the rest of the server's flight and the client's
22
+ * Finished at Handshake — and its secrets are readable as they become known.
23
+ * `nish/net/tls/record-client` puts it on a byte stream.
24
+ *
25
+ * **Sans-IO.** The client random and both private keys, x25519's and P-256's,
26
+ * are constructor arguments. Nothing here reads a socket, a clock or the
27
+ * random device.
28
+ *
29
+ * **Authentication is the caller's.** Once the server's CertificateVerify is
30
+ * read, the client stops at `TLS_CLIENT_STATE_WAIT_AUTH` without looking at
31
+ * the chain or the signature: `peerCertificates`, `verifyScheme`,
32
+ * `verifyContent` and `verifySignature` hand them out, and only
33
+ * `authenticate(true)` goes on to the server's Finished and writes the
34
+ * client's. A caller that drives this class directly must make that call
35
+ * itself, after checking the chain against its trust anchors and the
36
+ * signature over `verifyContent()` under the leaf's key: nothing here does
37
+ * either, and nothing proves the peer is who `serverName` says until then
38
+ * (`docs/security/tls.md`, "The client").
39
+ *
40
+ * **What is offered.** TLS 1.3 only, with a 32-byte `legacy_session_id` for
41
+ * middlebox compatibility (§D.4). The suites, groups and first key share are
42
+ * the configuration's: by default the three suites this tree negotiates and
43
+ * the group x25519; `secp256r1` may be configured too, for the first share or
44
+ * for a HelloRetryRequest to pick. `signature_algorithms` lists ECDSA
45
+ * P-256 and P-384 and RSA-PSS; `server_name` is sent unless the name is an
46
+ * IP literal (RFC 6066 §3); ALPN is the configuration's. One
47
+ * HelloRetryRequest is answered (§4.1.4); resumption, 0-RTT and client
48
+ * certificates are not here, and a NewSessionTicket is read and dropped.
49
+ *
50
+ * **Refusals are alerts.** `receive`, `authenticate` and `reject` answer 0 or
51
+ * a TLS alert description (`TLS_ALERT_*` in `nish/net/tls/codec`); after one
52
+ * the client is in `TLS_CLIENT_STATE_FAILED`, every secret it held is wiped,
53
+ * and it answers the same alert to every call. Nothing the peer sends can
54
+ * make it panic.
55
+ *
56
+ * **Secrets.** Both private keys are copied into `Secret` fields and the
57
+ * caller's arrays zeroed; the one the server did not pick is wiped when it
58
+ * picks, and both once the exchange is done. The ECDHE secret is a `Secret`
59
+ * wiped on every path; the handshake, traffic and exporter secrets are the
60
+ * fields of two `TlsSecretPool`s. `fail` and `wipe` zero them all.
61
+ *
62
+ * The state is a class and a `switch`, with no closures. Written from RFC
63
+ * 8446, RFC 7301 and RFC 6066, not ported from another implementation.
64
+ */
65
+ import { Secret, expose, exposeWith, secret, wipe } from "nish:secret"
66
+ import { SHA256_SIZE } from "nish/crypto/sha256"
67
+ import { SHA384_SIZE } from "nish/crypto/sha512"
68
+ import { timingSafeEqualAt } from "nish/crypto/ct"
69
+ import { X25519_SIZE, x25519, x25519Base } from "nish/crypto/x25519"
70
+ import { P256_SCALAR_SIZE, p256Ecdh, p256PublicKey } from "nish/crypto/p256"
71
+ import {
72
+ TLS_ALERT_BAD_CERTIFICATE,
73
+ TLS_ALERT_DECODE_ERROR,
74
+ TLS_ALERT_DECRYPT_ERROR,
75
+ TLS_ALERT_ILLEGAL_PARAMETER,
76
+ TLS_ALERT_INTERNAL_ERROR,
77
+ TLS_ALERT_MISSING_EXTENSION,
78
+ TLS_ALERT_PROTOCOL_VERSION,
79
+ TLS_ALERT_UNEXPECTED_MESSAGE,
80
+ TLS_ALERT_UNSUPPORTED_EXTENSION,
81
+ TLS_GROUP_SECP256R1,
82
+ TLS_GROUP_X25519,
83
+ TLS_HANDSHAKE_CERTIFICATE,
84
+ TLS_HANDSHAKE_CERTIFICATE_VERIFY,
85
+ TLS_HANDSHAKE_ENCRYPTED_EXTENSIONS,
86
+ TLS_HANDSHAKE_FINISHED,
87
+ TLS_HANDSHAKE_NEW_SESSION_TICKET,
88
+ TLS_HANDSHAKE_SERVER_HELLO,
89
+ TLS_LEGACY_VERSION,
90
+ TLS_P256_SHARE_SIZE,
91
+ TLS_RANDOM_SIZE,
92
+ TLS_VERSION_13,
93
+ TLS_X25519_SHARE_SIZE,
94
+ TlsServerFlightView,
95
+ tlsCertificateVerifyContent,
96
+ tlsClientOffersScheme,
97
+ tlsCopyCertificates,
98
+ tlsReadCertificate,
99
+ tlsReadCertificateVerify,
100
+ tlsReadEncryptedExtensions,
101
+ tlsReadNewSessionTicket,
102
+ tlsReadServerHello,
103
+ tlsServerHelloDowngrade,
104
+ tlsCopyWindow,
105
+ tlsWindowString,
106
+ tlsWriteClientHello,
107
+ tlsWriteFinished,
108
+ } from "nish/net/tls/codec"
109
+ import {
110
+ TLS_AES_128_GCM_SHA256,
111
+ TLS_AES_256_GCM_SHA384,
112
+ TLS_CHACHA20_POLY1305_SHA256,
113
+ TlsTranscript,
114
+ tlsDeriveSecretHeld,
115
+ tlsEarlySecret,
116
+ tlsFinishedVerifyDataHeld,
117
+ tlsHandshakeSecretHeld,
118
+ tlsMasterSecretHeld,
119
+ tlsSuiteHashLength,
120
+ } from "nish/net/tls/schedule"
121
+ import {
122
+ TLS_LEVEL_APPLICATION,
123
+ TLS_LEVEL_HANDSHAKE,
124
+ TLS_LEVEL_INITIAL,
125
+ TLS_MAX_HANDSHAKE_MESSAGE,
126
+ TLS_SECRET_CLIENT_APPLICATION,
127
+ TLS_SECRET_CLIENT_HANDSHAKE,
128
+ TLS_SECRET_EXPORTER,
129
+ TLS_SECRET_HANDSHAKE,
130
+ TLS_SECRET_SERVER_APPLICATION,
131
+ TLS_SECRET_SERVER_HANDSHAKE,
132
+ TlsSecretPool,
133
+ tlsHeldCopyIn,
134
+ tlsIsZeroSecret,
135
+ tlsZeroHeld,
136
+ } from "nish/net/tls"
137
+
138
+ /** Built; `start` has not written the ClientHello yet. */
139
+ export const TLS_CLIENT_STATE_START: i32 = 0
140
+ /** The ClientHello is out; a ServerHello or HelloRetryRequest comes next, at the Initial level. */
141
+ export const TLS_CLIENT_STATE_WAIT_SERVER_HELLO: i32 = 1
142
+ /** The handshake secrets are known; EncryptedExtensions comes next, at the Handshake level. */
143
+ export const TLS_CLIENT_STATE_WAIT_ENCRYPTED_EXTENSIONS: i32 = 2
144
+ export const TLS_CLIENT_STATE_WAIT_CERTIFICATE: i32 = 3
145
+ export const TLS_CLIENT_STATE_WAIT_CERTIFICATE_VERIFY: i32 = 4
146
+ /**
147
+ * The server's chain and signature are read and wait for the caller's
148
+ * verdict: `authenticate` or `reject`. Bytes that arrive meanwhile are held,
149
+ * not read.
150
+ */
151
+ export const TLS_CLIENT_STATE_WAIT_AUTH: i32 = 5
152
+ /** The caller accepted the server; its Finished comes next. */
153
+ export const TLS_CLIENT_STATE_WAIT_FINISHED: i32 = 6
154
+ /** Both Finished are done and the application secrets known. */
155
+ export const TLS_CLIENT_STATE_CONNECTED: i32 = 7
156
+ export const TLS_CLIENT_STATE_FAILED: i32 = 8
157
+
158
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
159
+ const TLS_CLIENT_FROM: i32 = 0
160
+
161
+ /** The legacy_session_id the client sends (§4.1.2, §D.4): 32 bytes, so that middleboxes see a resumption. */
162
+ const TLS_CLIENT_SESSION_ID_SIZE: i32 = 32
163
+
164
+ /** The longest `server_name` a ClientHello carries: a DNS name's 253 characters, under the extension's own bound. */
165
+ const TLS_CLIENT_SERVER_NAME_MAX: i32 = 253
166
+
167
+ /**
168
+ * What a client is configured with, the same for every connection. The
169
+ * constructor fills in the defaults; a caller may narrow `suites` or `groups`
170
+ * or change `shareGroup` before handing it to a `TlsClient`, which checks it.
171
+ */
172
+ export class TlsClientConfig {
173
+ /** The host the client means to reach: sent as `server_name` unless it is an IP literal, and the name the caller authenticates. */
174
+ serverName: string
175
+ /** The ALPN protocols offered, most preferred first; empty sends no ALPN. Each is 1 to 255 bytes. */
176
+ alpn: string[]
177
+ /** The cipher suites offered, most preferred first: a non-empty subset of the three this tree negotiates. */
178
+ suites: i32[]
179
+ /** The groups offered in `supported_groups`: a non-empty subset of x25519 and secp256r1. */
180
+ groups: i32[]
181
+ /** The group of the one key share the first ClientHello carries; one of `groups`. */
182
+ shareGroup: i32
183
+
184
+ constructor(serverName: string, alpn: string[]) {
185
+ this.serverName = serverName
186
+ this.alpn = alpn
187
+ this.suites = [TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256]
188
+ this.groups = [TLS_GROUP_X25519]
189
+ this.shareGroup = TLS_GROUP_X25519
190
+ }
191
+ }
192
+
193
+ /** Whether `suite` is one this tree negotiates. */
194
+ const tlsClientKnowsSuite = (suite: i32): boolean => tlsSuiteHashLength(suite) !== 0
195
+
196
+ /** Whether `group` is one the client can send a share for. */
197
+ const tlsClientKnowsGroup = (group: i32): boolean =>
198
+ group === TLS_GROUP_X25519 || group === TLS_GROUP_SECP256R1
199
+
200
+ /**
201
+ * Whether `name` is an IP literal, which RFC 6066 §3 keeps out of
202
+ * `server_name`: dotted decimal (digits and dots only), or anything with a
203
+ * colon, which no DNS name has.
204
+ */
205
+ export const tlsClientIsIpLiteral = (name: string): boolean => {
206
+ let digits: boolean = true
207
+ for (let k: i32 = 0; k < toI32(name.length); k++) {
208
+ const c: i32 = toI32(name.charCodeAt(k))
209
+ if (c === 58) {
210
+ return true
211
+ }
212
+ if (c !== 46 && (c < 48 || c > 57)) {
213
+ digits = false
214
+ }
215
+ }
216
+ return digits && toI32(name.length) > 0
217
+ }
218
+
219
+ /**
220
+ * The ClientHello's bytes before its extensions, less the session id and
221
+ * suites: the message header (4), version (2), random (32), the session id's
222
+ * length (1), the suites' length (2), compression (2) and the extensions'
223
+ * own length (2).
224
+ */
225
+ const TLS_CLIENT_HELLO_FIXED: i32 = 45
226
+
227
+ /** Whether every character of `text` is ASCII, so it goes on the wire a byte each as it is. */
228
+ const tlsClientIsAscii = (text: string): boolean => {
229
+ for (let k: i32 = 0; k < toI32(text.length); k++) {
230
+ if (toI32(text.charCodeAt(k)) > 127) {
231
+ return false
232
+ }
233
+ }
234
+ return true
235
+ }
236
+
237
+ /**
238
+ * Whether `config` can be offered: suites and groups non-empty and known, the
239
+ * share's group among the groups, every ALPN protocol 1 to 255 bytes, and a
240
+ * server name of at most 253 bytes.
241
+ */
242
+ const tlsClientConfigFits = (config: TlsClientConfig): boolean => {
243
+ if (toI32(config.suites.length) === 0 || toI32(config.groups.length) === 0) {
244
+ return false
245
+ }
246
+ for (const suite of config.suites) {
247
+ if (!tlsClientKnowsSuite(suite)) {
248
+ return false
249
+ }
250
+ }
251
+ for (const group of config.groups) {
252
+ if (!tlsClientKnowsGroup(group)) {
253
+ return false
254
+ }
255
+ }
256
+ if (config.groups.indexOf(config.shareGroup) < 0) {
257
+ return false
258
+ }
259
+ for (const protocol of config.alpn) {
260
+ if (toI32(protocol.length) < 1 || toI32(protocol.length) > 255 || !tlsClientIsAscii(protocol)) {
261
+ return false
262
+ }
263
+ }
264
+ return toI32(config.serverName.length) <= TLS_CLIENT_SERVER_NAME_MAX && tlsClientIsAscii(config.serverName)
265
+ }
266
+
267
+ export class TlsClient {
268
+ config: TlsClientConfig
269
+ clientRandom: u8[]
270
+ /** The x25519 private key, copied in by the constructor; wiped once the server picks a group, unless it picked x25519, then once the exchange is done. */
271
+ readonly x25519Key: Secret<u8[]>
272
+ /** The P-256 private key, held the same way. */
273
+ readonly p256Key: Secret<u8[]>
274
+ /** The legacy_session_id sent, which the ServerHello must echo. */
275
+ sessionId: u8[]
276
+ transcript: TlsTranscript
277
+ /**
278
+ * The first ClientHello, kept plain until the ServerHello names the hash:
279
+ * the transcript feeds one hash only, so it starts from this copy.
280
+ */
281
+ firstHello: u8[]
282
+ /** The bytes received at the current level and not yet read: `input[0 .. inputLength)`, whole messages and the start of the next. */
283
+ input: u8[]
284
+ /** What the client has written at the Initial level and not handed on. */
285
+ outputInitial: u8[]
286
+ /** What the client has written at the Handshake level and not handed on: its Finished. */
287
+ outputHandshake: u8[]
288
+ secrets256: TlsSecretPool
289
+ secrets384: TlsSecretPool
290
+ /** The server message last read, as windows into `input`. */
291
+ view: TlsServerFlightView
292
+ /** The HelloRetryRequest's cookie, echoed in the second ClientHello; empty if it sent none. */
293
+ cookie: u8[]
294
+ /** The server's DER certificates, leaf first, once its Certificate is read. */
295
+ certificates: u8[][]
296
+ /** What the server's CertificateVerify signs (§4.4.3), once its Certificate is read. */
297
+ content: u8[]
298
+ /** The CertificateVerify signature, as it came on the wire. */
299
+ signature: u8[]
300
+ /** A transcript hash on its way into the schedule: HashLen bytes of it. */
301
+ hashes: u8[]
302
+ /** The ALPN protocol the server chose, one of the configuration's, or empty. */
303
+ alpnChosen: string = ""
304
+ /** Which of the negotiated pool's secrets are known: bit `1 << TLS_SECRET_*` for each, as `TlsServer.held`. */
305
+ held: i32 = 0
306
+ state: i32 = 0
307
+ /** The alert the client failed with, or 0. */
308
+ alert: i32 = 0
309
+ /** The negotiated cipher suite, or 0 before the ServerHello (or HelloRetryRequest) is read. */
310
+ suite: i32 = 0
311
+ hashLength: i32 = 0
312
+ /** The group of the key share the last ClientHello carried. */
313
+ shareGroup: i32 = 0
314
+ /** The CertificateVerify scheme. */
315
+ scheme: i32 = 0
316
+ inputLength: i32 = 0
317
+ outputInitialLength: i32 = 0
318
+ outputHandshakeLength: i32 = 0
319
+ /** Whether a HelloRetryRequest was read, so a second is fatal. */
320
+ retried: boolean = false
321
+ /** Whether `server_name` was sent, so that only then may the server acknowledge it. */
322
+ sentServerName: boolean = false
323
+
324
+ /**
325
+ * A client for one connection under `config`, with the 32-byte client
326
+ * random and the 32-byte x25519 and P-256 private keys drawn for it; both
327
+ * keys are copied in and the caller's arrays zeroed. A random or key of
328
+ * another length, or a configuration it cannot offer, leaves the client
329
+ * failed with `internal_error` before it writes anything.
330
+ */
331
+ constructor(config: TlsClientConfig, clientRandom: u8[], x25519Private: u8[], p256Private: u8[]) {
332
+ const x25519Length: i32 = toI32(x25519Private.length)
333
+ const p256Length: i32 = toI32(p256Private.length)
334
+ this.config = config
335
+ this.clientRandom =
336
+ toI32(clientRandom.length) === TLS_RANDOM_SIZE
337
+ ? tlsCopyWindow(clientRandom, TLS_CLIENT_FROM, TLS_RANDOM_SIZE)
338
+ : []
339
+ this.x25519Key = secret(new Array<u8>(X25519_SIZE))
340
+ this.p256Key = secret(new Array<u8>(P256_SCALAR_SIZE))
341
+ exposeWith(this.x25519Key, x25519Private, tlsHeldCopyIn)
342
+ exposeWith(this.p256Key, p256Private, tlsHeldCopyIn)
343
+ secureZero(x25519Private)
344
+ secureZero(p256Private)
345
+ // Public, and derived from the public random, so it gives nothing away;
346
+ // its only job is to make the hello look like a resumption (§D.4).
347
+ this.sessionId = tlsCopyWindow(this.clientRandom, TLS_CLIENT_FROM, TLS_CLIENT_SESSION_ID_SIZE)
348
+ this.transcript = new TlsTranscript(0)
349
+ this.firstHello = []
350
+ this.input = new Array<u8>(1024)
351
+ this.outputInitial = []
352
+ this.outputHandshake = new Array<u8>(4 + SHA384_SIZE)
353
+ this.secrets256 = new TlsSecretPool(SHA256_SIZE)
354
+ this.secrets384 = new TlsSecretPool(SHA384_SIZE)
355
+ this.view = new TlsServerFlightView()
356
+ this.cookie = []
357
+ this.certificates = []
358
+ this.content = []
359
+ this.signature = []
360
+ this.hashes = new Array<u8>(SHA384_SIZE)
361
+ this.shareGroup = config.shareGroup
362
+ if (
363
+ toI32(this.clientRandom.length) !== TLS_RANDOM_SIZE ||
364
+ x25519Length !== X25519_SIZE ||
365
+ p256Length !== P256_SCALAR_SIZE ||
366
+ !tlsClientConfigFits(config)
367
+ ) {
368
+ this.fail(TLS_ALERT_INTERNAL_ERROR)
369
+ }
370
+ }
371
+
372
+ /** Writes the first ClientHello at the Initial level. Answers 0, or the alert the client failed with; a second call is `internal_error`. */
373
+ start(): i32 {
374
+ if (this.state === TLS_CLIENT_STATE_FAILED) {
375
+ return this.alert
376
+ }
377
+ if (this.state !== TLS_CLIENT_STATE_START) {
378
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
379
+ }
380
+ const at: i32 = this.outputInitialLength
381
+ const end: i32 = this.writeHello()
382
+ if (end < 0) {
383
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
384
+ }
385
+ this.firstHello = tlsCopyWindow(this.outputInitial, at, end - at)
386
+ this.state = TLS_CLIENT_STATE_WAIT_SERVER_HELLO
387
+ return 0
388
+ }
389
+
390
+ /**
391
+ * Writes a ClientHello with a share of `shareGroup` and the cookie, if any,
392
+ * after what the Initial output holds, and answers where it ends; -1 when
393
+ * the key for the share gives no public key, or when the extensions would
394
+ * pass the 65,535 bytes their length can say (a HelloRetryRequest's cookie
395
+ * near that size).
396
+ */
397
+ writeHello(): i32 {
398
+ const share: u8[] | null =
399
+ this.shareGroup === TLS_GROUP_X25519 ? x25519Base(this.x25519Key) : p256PublicKey(this.p256Key)
400
+ if (share === null) {
401
+ return -1
402
+ }
403
+ const serverName: string = tlsClientIsIpLiteral(this.config.serverName) ? "" : this.config.serverName
404
+ this.sentServerName = toI32(serverName.length) > 0
405
+ const none: u8[] = []
406
+ const at: i32 = this.outputInitialLength
407
+ const length: i32 = tlsWriteClientHello(
408
+ none,
409
+ TLS_CLIENT_FROM,
410
+ this.clientRandom,
411
+ this.sessionId,
412
+ this.config.suites,
413
+ this.config.groups,
414
+ this.shareGroup,
415
+ share,
416
+ serverName,
417
+ this.config.alpn,
418
+ this.cookie
419
+ )
420
+ const before: i32 =
421
+ TLS_CLIENT_HELLO_FIXED + toI32(this.sessionId.length) + 2 * toI32(this.config.suites.length)
422
+ if (length - before > 65535) {
423
+ return -1
424
+ }
425
+ this.reserveOutput(at + length)
426
+ const end: i32 = tlsWriteClientHello(
427
+ this.outputInitial,
428
+ at,
429
+ this.clientRandom,
430
+ this.sessionId,
431
+ this.config.suites,
432
+ this.config.groups,
433
+ this.shareGroup,
434
+ share,
435
+ serverName,
436
+ this.config.alpn,
437
+ this.cookie
438
+ )
439
+ this.sent(TLS_LEVEL_INITIAL, at, end)
440
+ return end
441
+ }
442
+
443
+ /** Fails the handshake with `alert` and answers it, wiping every secret the client holds. */
444
+ fail(alert: i32): i32 {
445
+ this.wipe()
446
+ this.state = TLS_CLIENT_STATE_FAILED
447
+ this.alert = alert
448
+ return alert
449
+ }
450
+
451
+ /** The pool of the negotiated hash: SHA-384's for a SHA-384 suite, SHA-256's otherwise. */
452
+ secrets(): TlsSecretPool {
453
+ return this.hashLength === SHA384_SIZE ? this.secrets384 : this.secrets256
454
+ }
455
+
456
+ /** Whether the handshake has derived secret `index` (`TLS_SECRET_*`). */
457
+ known(index: i32): boolean {
458
+ return index >= 0 && index < 31 && (this.held & (1 << index)) !== 0
459
+ }
460
+
461
+ /** Whether the secret the client *reads* `level` under — the server's, `serverHandshake` or `serverApplication` — is known yet. */
462
+ readKnown(level: i32): boolean {
463
+ if (level === TLS_LEVEL_HANDSHAKE) {
464
+ return this.known(TLS_SECRET_SERVER_HANDSHAKE)
465
+ }
466
+ return level === TLS_LEVEL_APPLICATION && this.known(TLS_SECRET_SERVER_APPLICATION)
467
+ }
468
+
469
+ /** Whether the secret the client *writes* `level` under — `clientHandshake` or `clientApplication` — is known yet. */
470
+ writeKnown(level: i32): boolean {
471
+ if (level === TLS_LEVEL_HANDSHAKE) {
472
+ return this.known(TLS_SECRET_CLIENT_HANDSHAKE)
473
+ }
474
+ return level === TLS_LEVEL_APPLICATION && this.known(TLS_SECRET_CLIENT_APPLICATION)
475
+ }
476
+
477
+ /** Zeroes secret `index` of `secrets()` once a carrier has taken what it needs from it; it stays known. */
478
+ wipeSecret(index: i32): void {
479
+ this.secrets().zero(index)
480
+ }
481
+
482
+ /**
483
+ * Wipes every secret the client holds, both private keys and both pools,
484
+ * and fails it: a wiped client has nothing left to go on with. `fail`
485
+ * calls it, and the carriers when a connection ends; wiped without a
486
+ * failure, it answers `internal_error` from then on.
487
+ */
488
+ wipe(): void {
489
+ wipe(this.x25519Key)
490
+ wipe(this.p256Key)
491
+ this.secrets256.wipe()
492
+ this.secrets384.wipe()
493
+ this.state = TLS_CLIENT_STATE_FAILED
494
+ if (this.alert === 0) {
495
+ this.alert = TLS_ALERT_INTERNAL_ERROR
496
+ }
497
+ }
498
+
499
+ /** The level the server's next bytes must arrive at, or -1 when the state expects none. */
500
+ expectedLevel(): i32 {
501
+ switch (this.state) {
502
+ case TLS_CLIENT_STATE_WAIT_SERVER_HELLO:
503
+ return TLS_LEVEL_INITIAL
504
+ case TLS_CLIENT_STATE_WAIT_ENCRYPTED_EXTENSIONS:
505
+ case TLS_CLIENT_STATE_WAIT_CERTIFICATE:
506
+ case TLS_CLIENT_STATE_WAIT_CERTIFICATE_VERIFY:
507
+ case TLS_CLIENT_STATE_WAIT_AUTH:
508
+ case TLS_CLIENT_STATE_WAIT_FINISHED:
509
+ return TLS_LEVEL_HANDSHAKE
510
+ case TLS_CLIENT_STATE_CONNECTED:
511
+ return TLS_LEVEL_APPLICATION
512
+ default:
513
+ return -1
514
+ }
515
+ }
516
+
517
+ /** Makes `input` hold at least `need` bytes, keeping the ones it holds; it doubles, and never grows past one largest message. */
518
+ reserveInput(need: i32): void {
519
+ const capacity: i32 = toI32(this.input.length)
520
+ if (need <= capacity) {
521
+ return
522
+ }
523
+ let size: i32 = capacity
524
+ while (size < need) {
525
+ size = size * 2
526
+ }
527
+ const most: i32 = TLS_MAX_HANDSHAKE_MESSAGE + 4
528
+ const grown: u8[] = new Array<u8>(size > most ? most : size)
529
+ for (let k: i32 = 0; k < this.inputLength && k < toI32(grown.length) && k < capacity; k++) {
530
+ grown[k] = this.input[k]
531
+ }
532
+ this.input = grown
533
+ }
534
+
535
+ /** Makes the Initial output hold at least `need` bytes, keeping the ones it holds. */
536
+ reserveOutput(need: i32): void {
537
+ const capacity: i32 = toI32(this.outputInitial.length)
538
+ if (need <= capacity) {
539
+ return
540
+ }
541
+ const grown: u8[] = new Array<u8>(need)
542
+ for (let k: i32 = 0; k < toI32(grown.length) && k < this.outputInitialLength && k < capacity; k++) {
543
+ grown[k] = this.outputInitial[k]
544
+ }
545
+ this.outputInitial = grown
546
+ }
547
+
548
+ /**
549
+ * Takes the handshake bytes `data[off .. off + len)` the server sent at
550
+ * `level`, a whole message, part of one or several, and reads every
551
+ * message they complete. Answers 0, or the alert the client fails with.
552
+ *
553
+ * A level other than the one the state expects is `unexpected_message`, as
554
+ * is a byte left over once a message ends the server's flight at its level
555
+ * (a ServerHello, a HelloRetryRequest or a Finished): RFC 8446 §5.1 forbids
556
+ * a message to straddle a key change. A window outside `data` is
557
+ * `internal_error`. At `TLS_CLIENT_STATE_WAIT_AUTH` the bytes are held
558
+ * until `authenticate`. Once connected, the Application level takes
559
+ * NewSessionTickets, which are checked and dropped; anything else there is
560
+ * `unexpected_message`, KeyUpdate being the record layer's.
561
+ */
562
+ receive(level: i32, data: u8[], off: i32, len: i32): i32 {
563
+ if (this.state === TLS_CLIENT_STATE_FAILED) {
564
+ return this.alert
565
+ }
566
+ if (off < 0 || len < 0 || off > toI32(data.length) - len) {
567
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
568
+ }
569
+ if (len === 0) {
570
+ return 0
571
+ }
572
+ if (level !== this.expectedLevel()) {
573
+ return this.fail(TLS_ALERT_UNEXPECTED_MESSAGE)
574
+ }
575
+ const most: i32 = TLS_MAX_HANDSHAKE_MESSAGE + 4
576
+ let taken: i32 = 0
577
+ while (taken < len) {
578
+ // A level's bytes may hold several messages, so they are taken a
579
+ // largest message at a time, each read before the next is copied in.
580
+ if (level !== this.expectedLevel()) {
581
+ return this.fail(TLS_ALERT_UNEXPECTED_MESSAGE)
582
+ }
583
+ const room: i32 = most - this.inputLength
584
+ if (room <= 0) {
585
+ return this.fail(TLS_ALERT_DECODE_ERROR)
586
+ }
587
+ const take: i32 = len - taken < room ? len - taken : room
588
+ this.reserveInput(this.inputLength + take)
589
+ for (
590
+ let k: i32 = 0;
591
+ k < take && off + taken + k < toI32(data.length) && this.inputLength < toI32(this.input.length);
592
+ k++
593
+ ) {
594
+ this.input[this.inputLength] = data[off + taken + k]
595
+ this.inputLength = this.inputLength + 1
596
+ }
597
+ taken = taken + take
598
+ const alert: i32 = this.drain()
599
+ if (alert !== 0) {
600
+ return this.fail(alert)
601
+ }
602
+ }
603
+ return 0
604
+ }
605
+
606
+ /**
607
+ * Reads every whole message at the front of `input`, dropping each once
608
+ * read, until a partial one is left or the state waits on the caller.
609
+ * Answers 0 or the alert.
610
+ */
611
+ drain(): i32 {
612
+ while (this.state !== TLS_CLIENT_STATE_WAIT_AUTH && this.inputLength >= 4) {
613
+ const length: i32 = (toI32(this.input[1]) << 16) | (toI32(this.input[2]) << 8) | toI32(this.input[3])
614
+ if (length > TLS_MAX_HANDSHAKE_MESSAGE) {
615
+ return TLS_ALERT_DECODE_ERROR
616
+ }
617
+ const whole: i32 = 4 + length
618
+ if (this.inputLength < whole) {
619
+ return 0
620
+ }
621
+ const type: i32 = toI32(this.input[0])
622
+ const alert: i32 = this.handleMessage(type, length)
623
+ if (alert !== 0) {
624
+ return alert
625
+ }
626
+ this.consume(whole)
627
+ const endsFlight: boolean = type === TLS_HANDSHAKE_SERVER_HELLO || type === TLS_HANDSHAKE_FINISHED
628
+ if (endsFlight && this.inputLength > 0) {
629
+ return TLS_ALERT_UNEXPECTED_MESSAGE
630
+ }
631
+ }
632
+ return 0
633
+ }
634
+
635
+ /** Drops the first `count` bytes of `input`, moving the rest to its front. */
636
+ consume(count: i32): void {
637
+ const rest: i32 = this.inputLength - count
638
+ for (let k: i32 = 0; k < rest && count + k < toI32(this.input.length); k++) {
639
+ this.input[k] = this.input[count + k]
640
+ }
641
+ this.inputLength = rest
642
+ }
643
+
644
+ /** Dispatches one whole message, `input[0 .. 4 + length)`, of type `type`, by state. */
645
+ handleMessage(type: i32, length: i32): i32 {
646
+ switch (this.state) {
647
+ case TLS_CLIENT_STATE_WAIT_SERVER_HELLO:
648
+ return type === TLS_HANDSHAKE_SERVER_HELLO
649
+ ? this.handleServerHello(length)
650
+ : TLS_ALERT_UNEXPECTED_MESSAGE
651
+ case TLS_CLIENT_STATE_WAIT_ENCRYPTED_EXTENSIONS:
652
+ return type === TLS_HANDSHAKE_ENCRYPTED_EXTENSIONS
653
+ ? this.handleEncryptedExtensions(length)
654
+ : TLS_ALERT_UNEXPECTED_MESSAGE
655
+ case TLS_CLIENT_STATE_WAIT_CERTIFICATE:
656
+ return type === TLS_HANDSHAKE_CERTIFICATE
657
+ ? this.handleCertificate(length)
658
+ : TLS_ALERT_UNEXPECTED_MESSAGE
659
+ case TLS_CLIENT_STATE_WAIT_CERTIFICATE_VERIFY:
660
+ return type === TLS_HANDSHAKE_CERTIFICATE_VERIFY
661
+ ? this.handleCertificateVerify(length)
662
+ : TLS_ALERT_UNEXPECTED_MESSAGE
663
+ case TLS_CLIENT_STATE_WAIT_FINISHED:
664
+ return type === TLS_HANDSHAKE_FINISHED ? this.handleFinished(length) : TLS_ALERT_UNEXPECTED_MESSAGE
665
+ case TLS_CLIENT_STATE_CONNECTED:
666
+ return type === TLS_HANDSHAKE_NEW_SESSION_TICKET
667
+ ? this.handleTicket(length)
668
+ : TLS_ALERT_UNEXPECTED_MESSAGE
669
+ default:
670
+ return TLS_ALERT_UNEXPECTED_MESSAGE
671
+ }
672
+ }
673
+
674
+ /**
675
+ * The checks a ServerHello and a HelloRetryRequest share, in the order
676
+ * that names a TLS 1.2 server for its version: the structure, then
677
+ * `supported_versions` and the downgrade sentinel (§4.1.3), then the
678
+ * extensions, the echoed session id, null compression and an offered
679
+ * suite. Answers 0 or the alert.
680
+ */
681
+ checkHello(view: TlsServerFlightView): i32 {
682
+ if (view.alert !== 0) {
683
+ return view.alert
684
+ }
685
+ if (!view.hasSupportedVersions) {
686
+ return !view.isRetry && tlsServerHelloDowngrade(view)
687
+ ? TLS_ALERT_ILLEGAL_PARAMETER
688
+ : TLS_ALERT_PROTOCOL_VERSION
689
+ }
690
+ if (view.selectedVersion !== TLS_VERSION_13 || view.legacyVersion !== TLS_LEGACY_VERSION) {
691
+ return TLS_ALERT_ILLEGAL_PARAMETER
692
+ }
693
+ if (view.extensionAlert !== 0) {
694
+ return view.extensionAlert
695
+ }
696
+ if (
697
+ view.sessionIdLength !== TLS_CLIENT_SESSION_ID_SIZE ||
698
+ !timingSafeEqualAt(
699
+ view.data,
700
+ view.sessionIdAt,
701
+ this.sessionId,
702
+ TLS_CLIENT_FROM,
703
+ TLS_CLIENT_SESSION_ID_SIZE
704
+ ) ||
705
+ view.compression !== 0
706
+ ) {
707
+ return TLS_ALERT_ILLEGAL_PARAMETER
708
+ }
709
+ if (this.config.suites.indexOf(view.suite) < 0) {
710
+ return TLS_ALERT_ILLEGAL_PARAMETER
711
+ }
712
+ // §4.1.4: the ServerHello after a HelloRetryRequest keeps its suite.
713
+ if (this.retried && view.suite !== this.suite) {
714
+ return TLS_ALERT_ILLEGAL_PARAMETER
715
+ }
716
+ return 0
717
+ }
718
+
719
+ /** A ServerHello or HelloRetryRequest, `input[0 .. 4 + length)`. */
720
+ handleServerHello(length: i32): i32 {
721
+ const view: TlsServerFlightView = this.view
722
+ tlsReadServerHello(view, this.input, 4, length)
723
+ if (view.isRetry && this.retried) {
724
+ // §4.1.4: a second HelloRetryRequest.
725
+ return TLS_ALERT_UNEXPECTED_MESSAGE
726
+ }
727
+ const checked: i32 = this.checkHello(view)
728
+ if (checked !== 0) {
729
+ return checked
730
+ }
731
+ if (view.isRetry) {
732
+ return this.handleRetry(length)
733
+ }
734
+ if (!view.hasKeyShare) {
735
+ return TLS_ALERT_MISSING_EXTENSION
736
+ }
737
+ if (view.shareGroup !== this.shareGroup) {
738
+ return TLS_ALERT_ILLEGAL_PARAMETER
739
+ }
740
+ const shareSize: i32 = this.shareGroup === TLS_GROUP_X25519 ? TLS_X25519_SHARE_SIZE : TLS_P256_SHARE_SIZE
741
+ if (view.shareLength !== shareSize) {
742
+ return TLS_ALERT_ILLEGAL_PARAMETER
743
+ }
744
+ if (!this.retried) {
745
+ this.startTranscript(view.suite)
746
+ }
747
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
748
+ const alert: i32 = this.exchangeKeys()
749
+ if (alert !== 0) {
750
+ return alert
751
+ }
752
+ const pool: TlsSecretPool = this.secrets()
753
+ const h: i32 = this.hashLength
754
+ this.transcript.hashInto(this.hashes, TLS_CLIENT_FROM)
755
+ tlsDeriveSecretHeld(h, pool.handshake, "c hs traffic", this.hashes, TLS_CLIENT_FROM, pool.clientHandshake)
756
+ tlsDeriveSecretHeld(h, pool.handshake, "s hs traffic", this.hashes, TLS_CLIENT_FROM, pool.serverHandshake)
757
+ this.held =
758
+ this.held |
759
+ (1 << TLS_SECRET_HANDSHAKE) |
760
+ (1 << TLS_SECRET_CLIENT_HANDSHAKE) |
761
+ (1 << TLS_SECRET_SERVER_HANDSHAKE)
762
+ this.state = TLS_CLIENT_STATE_WAIT_ENCRYPTED_EXTENSIONS
763
+ return 0
764
+ }
765
+
766
+ /** Starts the transcript over `suite`'s hash, from the first ClientHello. */
767
+ startTranscript(suite: i32): void {
768
+ this.suite = suite
769
+ this.hashLength = tlsSuiteHashLength(suite)
770
+ this.transcript.reset(this.hashLength)
771
+ this.transcript.update(this.firstHello, TLS_CLIENT_FROM, toI32(this.firstHello.length))
772
+ }
773
+
774
+ /**
775
+ * A HelloRetryRequest (§4.1.4), checked by `checkHello` already: it must
776
+ * name a group the client offered and did not send a share for, or carry
777
+ * a cookie, and changes the hello. The transcript restarts from
778
+ * `message_hash` (§4.4.1), and the second ClientHello carries a share of
779
+ * the new group and echoes the cookie. The key of the group not picked is
780
+ * wiped now.
781
+ */
782
+ handleRetry(length: i32): i32 {
783
+ const view: TlsServerFlightView = this.view
784
+ if (!view.hasKeyShare && !view.hasCookie) {
785
+ return TLS_ALERT_ILLEGAL_PARAMETER
786
+ }
787
+ if (
788
+ view.hasKeyShare &&
789
+ (this.config.groups.indexOf(view.shareGroup) < 0 || view.shareGroup === this.shareGroup)
790
+ ) {
791
+ return TLS_ALERT_ILLEGAL_PARAMETER
792
+ }
793
+ this.retried = true
794
+ this.startTranscript(view.suite)
795
+ this.transcript.restartWithMessageHash()
796
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
797
+ if (view.hasCookie) {
798
+ this.cookie = tlsCopyWindow(view.data, view.cookieAt, view.cookieLength)
799
+ }
800
+ if (view.hasKeyShare) {
801
+ this.shareGroup = view.shareGroup
802
+ }
803
+ this.wipeUnpicked()
804
+ // The first hello is not needed again; the second goes out after it.
805
+ this.firstHello = []
806
+ if (this.writeHello() < 0) {
807
+ return TLS_ALERT_INTERNAL_ERROR
808
+ }
809
+ return 0
810
+ }
811
+
812
+ /** Zeroes both private keys, once the exchange is done or cannot be. */
813
+ wipeKeys(): void {
814
+ exposeWith(this.x25519Key, TLS_CLIENT_FROM, tlsZeroHeld)
815
+ exposeWith(this.p256Key, TLS_CLIENT_FROM, tlsZeroHeld)
816
+ }
817
+
818
+ /** Zeroes the private key of the group the client's share is not of. */
819
+ wipeUnpicked(): void {
820
+ if (this.shareGroup === TLS_GROUP_X25519) {
821
+ exposeWith(this.p256Key, TLS_CLIENT_FROM, tlsZeroHeld)
822
+ } else {
823
+ exposeWith(this.x25519Key, TLS_CLIENT_FROM, tlsZeroHeld)
824
+ }
825
+ }
826
+
827
+ /**
828
+ * The ECDHE exchange with the server's share, `input[view.shareAt ..)`, and
829
+ * the handshake secret from it. Both private keys are wiped on every path,
830
+ * and the shared and early secrets before it returns. An all-zero x25519
831
+ * secret is `illegal_parameter` (RFC 7748 §6.1, RFC 8446 §7.4.2), and so is
832
+ * a secp256r1 share that is not an uncompressed point on the curve
833
+ * (§4.2.8.2); its shared secret is the product's x, which may be zero.
834
+ */
835
+ exchangeKeys(): i32 {
836
+ const view: TlsServerFlightView = this.view
837
+ const peer: u8[] = tlsCopyWindow(view.data, view.shareAt, view.shareLength)
838
+ const isX25519: boolean = this.shareGroup === TLS_GROUP_X25519
839
+ const shared: Secret<u8[]> | null = isX25519 ? x25519(this.x25519Key, peer) : p256Ecdh(this.p256Key, peer)
840
+ this.wipeKeys()
841
+ if (shared === null) {
842
+ return TLS_ALERT_ILLEGAL_PARAMETER
843
+ }
844
+ if (isX25519 && expose(shared, tlsIsZeroSecret)) {
845
+ wipe(shared)
846
+ return TLS_ALERT_ILLEGAL_PARAMETER
847
+ }
848
+ const h: i32 = this.hashLength
849
+ const early: Secret<u8[]> = tlsEarlySecret(h)
850
+ tlsHandshakeSecretHeld(h, early, shared, this.secrets().handshake)
851
+ wipe(early)
852
+ wipe(shared)
853
+ return 0
854
+ }
855
+
856
+ /** EncryptedExtensions (§4.3.1): ALPN, if any, must be one the client offered, and `server_name` acknowledged only if sent. */
857
+ handleEncryptedExtensions(length: i32): i32 {
858
+ const view: TlsServerFlightView = this.view
859
+ tlsReadEncryptedExtensions(view, this.input, 4, length)
860
+ if (view.alert !== 0) {
861
+ return view.alert
862
+ }
863
+ if (view.extensionAlert !== 0) {
864
+ return view.extensionAlert
865
+ }
866
+ if (view.serverNameAcknowledged && !this.sentServerName) {
867
+ return TLS_ALERT_UNSUPPORTED_EXTENSION
868
+ }
869
+ if (view.hasAlpn) {
870
+ const chosen: string = tlsWindowString(view.data, view.alpnAt, view.alpnLength)
871
+ if (toI32(this.config.alpn.length) === 0) {
872
+ // An extension the client never sent (§4.2).
873
+ return TLS_ALERT_UNSUPPORTED_EXTENSION
874
+ }
875
+ if (this.config.alpn.indexOf(chosen) < 0) {
876
+ // RFC 7301 §3.2 leaves the alert open; a protocol never offered is
877
+ // a value the client cannot accept.
878
+ return TLS_ALERT_ILLEGAL_PARAMETER
879
+ }
880
+ this.alpnChosen = chosen
881
+ }
882
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
883
+ this.state = TLS_CLIENT_STATE_WAIT_CERTIFICATE
884
+ return 0
885
+ }
886
+
887
+ /** The server's Certificate (§4.4.2): its request context must be empty. The chain is copied out, and what CertificateVerify signs computed. */
888
+ handleCertificate(length: i32): i32 {
889
+ const view: TlsServerFlightView = this.view
890
+ tlsReadCertificate(view, this.input, 4, length)
891
+ if (view.alert !== 0) {
892
+ return view.alert
893
+ }
894
+ if (view.extensionAlert !== 0) {
895
+ return view.extensionAlert
896
+ }
897
+ if (view.contextLength !== 0) {
898
+ return TLS_ALERT_ILLEGAL_PARAMETER
899
+ }
900
+ this.certificates = tlsCopyCertificates(view)
901
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
902
+ this.content = tlsCertificateVerifyContent(this.transcript.hash())
903
+ this.state = TLS_CLIENT_STATE_WAIT_CERTIFICATE_VERIFY
904
+ return 0
905
+ }
906
+
907
+ /** CertificateVerify (§4.4.3): a scheme the client offered. The signature is kept, unchecked, for the caller; the client then waits on it. */
908
+ handleCertificateVerify(length: i32): i32 {
909
+ const view: TlsServerFlightView = this.view
910
+ tlsReadCertificateVerify(view, this.input, 4, length)
911
+ if (view.alert !== 0) {
912
+ return view.alert
913
+ }
914
+ if (!tlsClientOffersScheme(view.scheme)) {
915
+ return TLS_ALERT_ILLEGAL_PARAMETER
916
+ }
917
+ this.scheme = view.scheme
918
+ this.signature = tlsCopyWindow(view.data, view.signatureAt, view.signatureLength)
919
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
920
+ this.state = TLS_CLIENT_STATE_WAIT_AUTH
921
+ return 0
922
+ }
923
+
924
+ /**
925
+ * The server's Finished (§4.4.4), checked with `timingSafeEqualAt` against the one
926
+ * its handshake secret gives (`decrypt_error` if it differs). Then the
927
+ * master secret, the application and exporter secrets, and the client's
928
+ * Finished at the Handshake level; the handshake secret is wiped.
929
+ */
930
+ handleFinished(length: i32): i32 {
931
+ const h: i32 = this.hashLength
932
+ if (length !== h) {
933
+ return TLS_ALERT_DECODE_ERROR
934
+ }
935
+ const pool: TlsSecretPool = this.secrets()
936
+ this.transcript.hashInto(this.hashes, TLS_CLIENT_FROM)
937
+ const expected: u8[] = tlsFinishedVerifyDataHeld(h, pool.serverHandshake, this.hashes, TLS_CLIENT_FROM)
938
+ const matches: boolean = timingSafeEqualAt(this.input, 4, expected, TLS_CLIENT_FROM, h)
939
+ secureZero(expected)
940
+ if (!matches) {
941
+ return TLS_ALERT_DECRYPT_ERROR
942
+ }
943
+ this.transcript.update(this.input, TLS_CLIENT_FROM, 4 + length)
944
+ this.transcript.hashInto(this.hashes, TLS_CLIENT_FROM)
945
+ // The master secret is derived over the handshake secret, which has no
946
+ // other use left.
947
+ tlsMasterSecretHeld(h, pool.handshake, pool.handshake)
948
+ tlsDeriveSecretHeld(
949
+ h,
950
+ pool.handshake,
951
+ "c ap traffic",
952
+ this.hashes,
953
+ TLS_CLIENT_FROM,
954
+ pool.clientApplication
955
+ )
956
+ tlsDeriveSecretHeld(
957
+ h,
958
+ pool.handshake,
959
+ "s ap traffic",
960
+ this.hashes,
961
+ TLS_CLIENT_FROM,
962
+ pool.serverApplication
963
+ )
964
+ tlsDeriveSecretHeld(h, pool.handshake, "exp master", this.hashes, TLS_CLIENT_FROM, pool.exporter)
965
+ pool.zero(TLS_SECRET_HANDSHAKE)
966
+ const verifyData: u8[] = tlsFinishedVerifyDataHeld(h, pool.clientHandshake, this.hashes, TLS_CLIENT_FROM)
967
+ const at: i32 = this.outputHandshakeLength
968
+ const end: i32 = tlsWriteFinished(this.outputHandshake, at, verifyData, TLS_CLIENT_FROM, h)
969
+ secureZero(verifyData)
970
+ this.sent(TLS_LEVEL_HANDSHAKE, at, end)
971
+ this.held =
972
+ (this.held |
973
+ (1 << TLS_SECRET_CLIENT_APPLICATION) |
974
+ (1 << TLS_SECRET_SERVER_APPLICATION) |
975
+ (1 << TLS_SECRET_EXPORTER)) &
976
+ ~(1 << TLS_SECRET_HANDSHAKE)
977
+ this.state = TLS_CLIENT_STATE_CONNECTED
978
+ return 0
979
+ }
980
+
981
+ /** A NewSessionTicket (§4.6.1) once connected: checked for its structure and dropped, since this client does not resume. */
982
+ handleTicket(length: i32): i32 {
983
+ tlsReadNewSessionTicket(this.view, this.input, 4, length)
984
+ return this.view.alert
985
+ }
986
+
987
+ /** The server's DER certificates, leaf first; empty before its Certificate is read. */
988
+ peerCertificates(): u8[][] {
989
+ return this.certificates
990
+ }
991
+
992
+ /** The CertificateVerify scheme (`TLS_SIGNATURE_*`); 0 before it is read. */
993
+ verifyScheme(): i32 {
994
+ return this.scheme
995
+ }
996
+
997
+ /** What the CertificateVerify signature is over (§4.4.3): 64 spaces, the server's context string, a zero byte and the transcript hash. */
998
+ verifyContent(): u8[] {
999
+ return this.content
1000
+ }
1001
+
1002
+ /** The CertificateVerify signature as sent: DER for ECDSA, raw for RSA-PSS. */
1003
+ verifySignature(): u8[] {
1004
+ return this.signature
1005
+ }
1006
+
1007
+ /** The ALPN protocol the server chose, or empty when it chose none. */
1008
+ alpn(): string {
1009
+ return this.alpnChosen
1010
+ }
1011
+
1012
+ /**
1013
+ * The caller's verdict on the server's chain and signature, at
1014
+ * `TLS_CLIENT_STATE_WAIT_AUTH`: `true` goes on to the server's Finished,
1015
+ * reading what was held meanwhile; `false` is `reject(bad_certificate)`.
1016
+ * Answers 0 or the alert; in any other state the client fails with
1017
+ * `internal_error`.
1018
+ */
1019
+ authenticate(ok: boolean): i32 {
1020
+ if (!ok) {
1021
+ return this.reject(TLS_ALERT_BAD_CERTIFICATE)
1022
+ }
1023
+ if (this.state === TLS_CLIENT_STATE_FAILED) {
1024
+ return this.alert
1025
+ }
1026
+ if (this.state !== TLS_CLIENT_STATE_WAIT_AUTH) {
1027
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
1028
+ }
1029
+ this.state = TLS_CLIENT_STATE_WAIT_FINISHED
1030
+ const alert: i32 = this.drain()
1031
+ if (alert !== 0) {
1032
+ return this.fail(alert)
1033
+ }
1034
+ return 0
1035
+ }
1036
+
1037
+ /**
1038
+ * Refuses the server at `TLS_CLIENT_STATE_WAIT_AUTH` with `alert`, the
1039
+ * caller's reason (`bad_certificate`, `certificate_expired`,
1040
+ * `unknown_ca`, `decrypt_error`…): the client fails with it and answers it.
1041
+ * An alert outside 1 to 255 is taken as `bad_certificate`. In any other
1042
+ * state the client fails with `internal_error`.
1043
+ */
1044
+ reject(alert: i32): i32 {
1045
+ if (this.state === TLS_CLIENT_STATE_FAILED) {
1046
+ return this.alert
1047
+ }
1048
+ if (this.state !== TLS_CLIENT_STATE_WAIT_AUTH) {
1049
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
1050
+ }
1051
+ return this.fail(alert >= 1 && alert <= 255 ? alert : TLS_ALERT_BAD_CERTIFICATE)
1052
+ }
1053
+
1054
+ /** A message the client wrote at `level`, `output[at .. end)`, goes into the transcript once its hash is known and stays on the output. */
1055
+ sent(level: i32, at: i32, end: i32): void {
1056
+ if (level === TLS_LEVEL_INITIAL) {
1057
+ if (this.hashLength !== 0) {
1058
+ this.transcript.update(this.outputInitial, at, end - at)
1059
+ }
1060
+ this.outputInitialLength = end
1061
+ } else {
1062
+ this.transcript.update(this.outputHandshake, at, end - at)
1063
+ this.outputHandshakeLength = end
1064
+ }
1065
+ }
1066
+
1067
+ /** The handshake bytes written at `level` since the last call, in a fresh array, and forgets them. */
1068
+ takeOutput(level: i32): u8[] {
1069
+ const none: u8[] = []
1070
+ switch (level) {
1071
+ case TLS_LEVEL_INITIAL: {
1072
+ const out: u8[] = tlsCopyWindow(this.outputInitial, TLS_CLIENT_FROM, this.outputInitialLength)
1073
+ this.outputInitialLength = 0
1074
+ return out
1075
+ }
1076
+ case TLS_LEVEL_HANDSHAKE: {
1077
+ const out: u8[] = tlsCopyWindow(this.outputHandshake, TLS_CLIENT_FROM, this.outputHandshakeLength)
1078
+ this.outputHandshakeLength = 0
1079
+ return out
1080
+ }
1081
+ default:
1082
+ return none
1083
+ }
1084
+ }
1085
+
1086
+ /** Forgets the handshake bytes written at `level`, which a carrier has read out of the buffer itself. */
1087
+ clearOutput(level: i32): void {
1088
+ if (level === TLS_LEVEL_INITIAL) {
1089
+ this.outputInitialLength = 0
1090
+ } else if (level === TLS_LEVEL_HANDSHAKE) {
1091
+ this.outputHandshakeLength = 0
1092
+ }
1093
+ }
1094
+ }