@amritk/nish-x86_64-linux 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nish +0 -0
- package/docs/AI.md +24 -4
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/host.d.ts +8 -0
- package/runtime/core/host.mjs +50 -2
- package/runtime/nish.d.ts +100 -0
- package/runtime/nish.h +34 -0
- package/runtime/nish.mjs +21 -0
- package/runtime/node.mjs +57 -6
- package/runtime/runtime-net.c +44 -3
- package/runtime/runtime-os.c +115 -12
- package/scripts/build.sh +13 -3
- package/std/README.md +25 -8
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +132 -20
- package/std/compress/deflate.ts +638 -248
- package/std/compress/gzip.ts +20 -6
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +7 -2
- package/std/net/http1.ts +9 -9
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/std/net/tls/codec.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nish/net/tls/codec` — the TLS 1.3 handshake messages a server
|
|
3
|
-
*
|
|
2
|
+
* `nish/net/tls/codec` — the TLS 1.3 handshake messages a server and a
|
|
3
|
+
* client read and write (RFC 8446 §4), as bytes.
|
|
4
4
|
*
|
|
5
5
|
* One reader and eight writers. `tlsParseClientHello` reads a ClientHello body
|
|
6
6
|
* into a `TlsClientHello`, checking its structure: every length against the
|
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
* offset — run over an empty array, it measures — and a `tlsEncode*` one
|
|
26
26
|
* that answers the message in a fresh array.
|
|
27
27
|
*
|
|
28
|
+
* The client's side is the mirror: `tlsWriteClientHello` and
|
|
29
|
+
* `tlsEncodeClientHello` write the hello `nish/net/tls/client` sends, and five
|
|
30
|
+
* readers — `tlsReadServerHello` (a HelloRetryRequest too),
|
|
31
|
+
* `tlsReadEncryptedExtensions`, `tlsReadCertificate`,
|
|
32
|
+
* `tlsReadCertificateVerify` and `tlsReadNewSessionTicket` — read the
|
|
33
|
+
* server's flight into a `TlsServerFlightView` made once, as windows, with
|
|
34
|
+
* the same checks and the same alerts in its `alert` field. A server's bytes
|
|
35
|
+
* are the peer's just as a client's are, so these readers never panic either
|
|
36
|
+
* (the `tls-server-flight` fuzz target).
|
|
37
|
+
*
|
|
28
38
|
* The extensions read are `server_name` (RFC 6066 §3), `supported_groups`,
|
|
29
39
|
* `signature_algorithms`, `supported_versions` and `key_share` (RFC 8446
|
|
30
40
|
* §4.2), `application_layer_protocol_negotiation` (RFC 7301 §3.1) and
|
|
@@ -67,6 +77,8 @@ export const TLS_EXT_ALPN: i32 = 16
|
|
|
67
77
|
export const TLS_EXT_SUPPORTED_VERSIONS: i32 = 43
|
|
68
78
|
export const TLS_EXT_KEY_SHARE: i32 = 51
|
|
69
79
|
export const TLS_EXT_QUIC_TRANSPORT_PARAMETERS: i32 = 57
|
|
80
|
+
/** `cookie` (§4.2.2): what a HelloRetryRequest may carry and the client's second ClientHello echoes. */
|
|
81
|
+
export const TLS_EXT_COOKIE: i32 = 44
|
|
70
82
|
|
|
71
83
|
// ---- Versions, groups and signature schemes -------------------------------
|
|
72
84
|
|
|
@@ -82,6 +94,16 @@ export const TLS_X25519_SHARE_SIZE: i32 = 32
|
|
|
82
94
|
export const TLS_SIGNATURE_ECDSA_SECP256R1_SHA256: i32 = 0x0403
|
|
83
95
|
/** `rsa_pss_rsae_sha256` (§4.2.3), what RFC 8448's server signs with. */
|
|
84
96
|
export const TLS_SIGNATURE_RSA_PSS_RSAE_SHA256: i32 = 0x0804
|
|
97
|
+
/** `ecdsa_secp384r1_sha384` (§4.2.3), offered by the client. */
|
|
98
|
+
export const TLS_SIGNATURE_ECDSA_SECP384R1_SHA384: i32 = 0x0503
|
|
99
|
+
/** `rsa_pss_rsae_sha384` (§4.2.3), offered by the client. */
|
|
100
|
+
export const TLS_SIGNATURE_RSA_PSS_RSAE_SHA384: i32 = 0x0805
|
|
101
|
+
/** `rsa_pss_rsae_sha512` (§4.2.3), offered by the client. */
|
|
102
|
+
export const TLS_SIGNATURE_RSA_PSS_RSAE_SHA512: i32 = 0x0806
|
|
103
|
+
/** secp256r1 (§4.2.7), the second group the client offers. */
|
|
104
|
+
export const TLS_GROUP_SECP256R1: i32 = 0x0017
|
|
105
|
+
/** The length of a secp256r1 key share: an uncompressed point, 0x04 then X and Y (§4.2.8.2). */
|
|
106
|
+
export const TLS_P256_SHARE_SIZE: i32 = 65
|
|
85
107
|
|
|
86
108
|
// ---- Alert descriptions (RFC 8446 §6) --------------------------------------
|
|
87
109
|
|
|
@@ -89,6 +111,8 @@ export const TLS_SIGNATURE_RSA_PSS_RSAE_SHA256: i32 = 0x0804
|
|
|
89
111
|
export const TLS_ALERT_UNEXPECTED_MESSAGE: i32 = 10
|
|
90
112
|
/** No acceptable set of parameters: no common suite, group or signature scheme. */
|
|
91
113
|
export const TLS_ALERT_HANDSHAKE_FAILURE: i32 = 40
|
|
114
|
+
/** The client's verdict on a server certificate it does not accept, which `TlsClient.authenticate(false)` sends by default. */
|
|
115
|
+
export const TLS_ALERT_BAD_CERTIFICATE: i32 = 42
|
|
92
116
|
/** A field well formed but wrong: a duplicate, a bad share, a second retry without the share. */
|
|
93
117
|
export const TLS_ALERT_ILLEGAL_PARAMETER: i32 = 47
|
|
94
118
|
/** A message that does not parse: a length past its bytes, a vector out of its bounds, trailing bytes. */
|
|
@@ -103,6 +127,8 @@ export const TLS_ALERT_INTERNAL_ERROR: i32 = 80
|
|
|
103
127
|
export const TLS_ALERT_MISSING_EXTENSION: i32 = 109
|
|
104
128
|
/** ALPN offered no protocol the server accepts (RFC 7301 §3.2, RFC 9001 §8.1). */
|
|
105
129
|
export const TLS_ALERT_NO_APPLICATION_PROTOCOL: i32 = 120
|
|
130
|
+
/** An extension in a server message that the client never offered (§4.2). */
|
|
131
|
+
export const TLS_ALERT_UNSUPPORTED_EXTENSION: i32 = 110
|
|
106
132
|
|
|
107
133
|
/** `psk_ke` (§4.2.9): resumption from the PSK alone, without forward secrecy, which the server never selects. */
|
|
108
134
|
export const TLS_PSK_KE: i32 = 0
|
|
@@ -821,7 +847,7 @@ export class TlsClientHello {
|
|
|
821
847
|
}
|
|
822
848
|
|
|
823
849
|
/** A copy of `data[at .. at + length)`. */
|
|
824
|
-
const tlsCopyWindow = (data: u8[], at: i32, length: i32): u8[] => {
|
|
850
|
+
export const tlsCopyWindow = (data: u8[], at: i32, length: i32): u8[] => {
|
|
825
851
|
const out: u8[] = new Array<u8>(length < 0 ? TLS_CODEC_FROM : length)
|
|
826
852
|
for (let k: i32 = 0; k < toI32(out.length) && at + k >= 0 && at + k < toI32(data.length); k++) {
|
|
827
853
|
out[k] = data[at + k]
|
|
@@ -1486,3 +1512,559 @@ export const tlsCertificateVerifyContent = (transcriptHash: u8[]): u8[] => {
|
|
|
1486
1512
|
tlsWriteCertificateVerifyContent(out, TLS_CODEC_FROM, transcriptHash, TLS_CODEC_FROM, length)
|
|
1487
1513
|
return out
|
|
1488
1514
|
}
|
|
1515
|
+
|
|
1516
|
+
// ---------------------------------------------------------------------------
|
|
1517
|
+
// The client's half: the ClientHello it writes and the server flight it reads.
|
|
1518
|
+
// ---------------------------------------------------------------------------
|
|
1519
|
+
|
|
1520
|
+
/** The `signature_algorithms` the client offers (§4.2.3), in its order of preference. */
|
|
1521
|
+
const tlsPutClientSchemes = (out: u8[], at: i32): i32 => {
|
|
1522
|
+
let p: i32 = tlsPutU16(out, at, TLS_SIGNATURE_ECDSA_SECP256R1_SHA256)
|
|
1523
|
+
p = tlsPutU16(out, p, TLS_SIGNATURE_ECDSA_SECP384R1_SHA384)
|
|
1524
|
+
p = tlsPutU16(out, p, TLS_SIGNATURE_RSA_PSS_RSAE_SHA256)
|
|
1525
|
+
p = tlsPutU16(out, p, TLS_SIGNATURE_RSA_PSS_RSAE_SHA384)
|
|
1526
|
+
return tlsPutU16(out, p, TLS_SIGNATURE_RSA_PSS_RSAE_SHA512)
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/** Whether `scheme` is one the client's `signature_algorithms` offers, the only ones a server's CertificateVerify may use (§4.4.3). */
|
|
1530
|
+
export const tlsClientOffersScheme = (scheme: i32): boolean =>
|
|
1531
|
+
scheme === TLS_SIGNATURE_ECDSA_SECP256R1_SHA256 ||
|
|
1532
|
+
scheme === TLS_SIGNATURE_ECDSA_SECP384R1_SHA384 ||
|
|
1533
|
+
scheme === TLS_SIGNATURE_RSA_PSS_RSAE_SHA256 ||
|
|
1534
|
+
scheme === TLS_SIGNATURE_RSA_PSS_RSAE_SHA384 ||
|
|
1535
|
+
scheme === TLS_SIGNATURE_RSA_PSS_RSAE_SHA512
|
|
1536
|
+
|
|
1537
|
+
/**
|
|
1538
|
+
* Writes a ClientHello (§4.1.2) at `out[at]` and answers where it ends:
|
|
1539
|
+
* `random`, the `legacy_session_id` `sessionId`, the cipher suites `suites`,
|
|
1540
|
+
* null compression, then the extensions `server_name` (a `host_name`, left
|
|
1541
|
+
* out when `serverName` is empty), `supported_groups` (`groups`),
|
|
1542
|
+
* `signature_algorithms`, ALPN (left out when `alpn` is empty),
|
|
1543
|
+
* `supported_versions` (TLS 1.3 alone), a `key_share` with the one entry
|
|
1544
|
+
* `shareGroup`/`sharePublic`, and `cookie` when `cookie` is not empty (the
|
|
1545
|
+
* echo of a HelloRetryRequest's, §4.2.2).
|
|
1546
|
+
*/
|
|
1547
|
+
export const tlsWriteClientHello = (
|
|
1548
|
+
out: u8[],
|
|
1549
|
+
at: i32,
|
|
1550
|
+
random: u8[],
|
|
1551
|
+
sessionId: u8[],
|
|
1552
|
+
suites: i32[],
|
|
1553
|
+
groups: i32[],
|
|
1554
|
+
shareGroup: i32,
|
|
1555
|
+
sharePublic: u8[],
|
|
1556
|
+
serverName: string,
|
|
1557
|
+
alpn: string[],
|
|
1558
|
+
cookie: u8[]
|
|
1559
|
+
): i32 => {
|
|
1560
|
+
let p: i32 = tlsPutU16(out, at + 4, TLS_LEGACY_VERSION)
|
|
1561
|
+
p = tlsPutBytes(out, p, random)
|
|
1562
|
+
p = tlsPutU8(out, p, toI32(sessionId.length))
|
|
1563
|
+
p = tlsPutBytes(out, p, sessionId)
|
|
1564
|
+
const suiteList: i32 = p + 2
|
|
1565
|
+
p = suiteList
|
|
1566
|
+
for (const suite of suites) {
|
|
1567
|
+
p = tlsPutU16(out, p, suite)
|
|
1568
|
+
}
|
|
1569
|
+
tlsPutLength(out, suiteList, p, 2)
|
|
1570
|
+
p = tlsPutU8(out, tlsPutU8(out, p, 1), 0)
|
|
1571
|
+
const extensions: i32 = p + 2
|
|
1572
|
+
p = extensions
|
|
1573
|
+
if (toI32(serverName.length) > 0) {
|
|
1574
|
+
p = tlsPutU16(out, p, TLS_EXT_SERVER_NAME)
|
|
1575
|
+
const body: i32 = p + 2
|
|
1576
|
+
const list: i32 = body + 2
|
|
1577
|
+
const name: i32 = tlsPutU8(out, list, 0) + 2
|
|
1578
|
+
p = tlsPutAscii(out, name, serverName)
|
|
1579
|
+
tlsPutLength(out, name, p, 2)
|
|
1580
|
+
tlsPutLength(out, list, p, 2)
|
|
1581
|
+
tlsPutLength(out, body, p, 2)
|
|
1582
|
+
}
|
|
1583
|
+
p = tlsPutU16(out, p, TLS_EXT_SUPPORTED_GROUPS)
|
|
1584
|
+
const groupsBody: i32 = p + 2
|
|
1585
|
+
const groupList: i32 = groupsBody + 2
|
|
1586
|
+
p = groupList
|
|
1587
|
+
for (const group of groups) {
|
|
1588
|
+
p = tlsPutU16(out, p, group)
|
|
1589
|
+
}
|
|
1590
|
+
tlsPutLength(out, groupList, p, 2)
|
|
1591
|
+
tlsPutLength(out, groupsBody, p, 2)
|
|
1592
|
+
p = tlsPutU16(out, p, TLS_EXT_SIGNATURE_ALGORITHMS)
|
|
1593
|
+
const schemesBody: i32 = p + 2
|
|
1594
|
+
const schemeList: i32 = schemesBody + 2
|
|
1595
|
+
p = tlsPutClientSchemes(out, schemeList)
|
|
1596
|
+
tlsPutLength(out, schemeList, p, 2)
|
|
1597
|
+
tlsPutLength(out, schemesBody, p, 2)
|
|
1598
|
+
if (toI32(alpn.length) > 0) {
|
|
1599
|
+
p = tlsPutU16(out, p, TLS_EXT_ALPN)
|
|
1600
|
+
const body: i32 = p + 2
|
|
1601
|
+
const list: i32 = body + 2
|
|
1602
|
+
p = list
|
|
1603
|
+
for (const protocol of alpn) {
|
|
1604
|
+
const name: i32 = p + 1
|
|
1605
|
+
p = tlsPutAscii(out, name, protocol)
|
|
1606
|
+
tlsPutLength(out, name, p, 1)
|
|
1607
|
+
}
|
|
1608
|
+
tlsPutLength(out, list, p, 2)
|
|
1609
|
+
tlsPutLength(out, body, p, 2)
|
|
1610
|
+
}
|
|
1611
|
+
p = tlsPutU16(out, tlsPutU16(out, p, TLS_EXT_SUPPORTED_VERSIONS), 3)
|
|
1612
|
+
p = tlsPutU16(out, tlsPutU8(out, p, 2), TLS_VERSION_13)
|
|
1613
|
+
p = tlsPutU16(out, p, TLS_EXT_KEY_SHARE)
|
|
1614
|
+
const shareBody: i32 = p + 2
|
|
1615
|
+
const shareList: i32 = shareBody + 2
|
|
1616
|
+
const key: i32 = tlsPutU16(out, shareList, shareGroup) + 2
|
|
1617
|
+
p = tlsPutBytes(out, key, sharePublic)
|
|
1618
|
+
tlsPutLength(out, key, p, 2)
|
|
1619
|
+
tlsPutLength(out, shareList, p, 2)
|
|
1620
|
+
tlsPutLength(out, shareBody, p, 2)
|
|
1621
|
+
if (toI32(cookie.length) > 0) {
|
|
1622
|
+
p = tlsPutU16(out, p, TLS_EXT_COOKIE)
|
|
1623
|
+
const body: i32 = p + 2
|
|
1624
|
+
const value: i32 = body + 2
|
|
1625
|
+
p = tlsPutBytes(out, value, cookie)
|
|
1626
|
+
tlsPutLength(out, value, p, 2)
|
|
1627
|
+
tlsPutLength(out, body, p, 2)
|
|
1628
|
+
}
|
|
1629
|
+
tlsPutLength(out, extensions, p, 2)
|
|
1630
|
+
tlsPutHeader(out, at, TLS_HANDSHAKE_CLIENT_HELLO, p)
|
|
1631
|
+
return p
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/** A ClientHello (§4.1.2) in a fresh array (`tlsWriteClientHello`). */
|
|
1635
|
+
export const tlsEncodeClientHello = (
|
|
1636
|
+
random: u8[],
|
|
1637
|
+
sessionId: u8[],
|
|
1638
|
+
suites: i32[],
|
|
1639
|
+
groups: i32[],
|
|
1640
|
+
shareGroup: i32,
|
|
1641
|
+
sharePublic: u8[],
|
|
1642
|
+
serverName: string,
|
|
1643
|
+
alpn: string[],
|
|
1644
|
+
cookie: u8[]
|
|
1645
|
+
): u8[] => {
|
|
1646
|
+
const none: u8[] = []
|
|
1647
|
+
const out: u8[] = new Array<u8>(
|
|
1648
|
+
tlsWriteClientHello(
|
|
1649
|
+
none,
|
|
1650
|
+
TLS_CODEC_FROM,
|
|
1651
|
+
random,
|
|
1652
|
+
sessionId,
|
|
1653
|
+
suites,
|
|
1654
|
+
groups,
|
|
1655
|
+
shareGroup,
|
|
1656
|
+
sharePublic,
|
|
1657
|
+
serverName,
|
|
1658
|
+
alpn,
|
|
1659
|
+
cookie
|
|
1660
|
+
)
|
|
1661
|
+
)
|
|
1662
|
+
tlsWriteClientHello(
|
|
1663
|
+
out,
|
|
1664
|
+
TLS_CODEC_FROM,
|
|
1665
|
+
random,
|
|
1666
|
+
sessionId,
|
|
1667
|
+
suites,
|
|
1668
|
+
groups,
|
|
1669
|
+
shareGroup,
|
|
1670
|
+
sharePublic,
|
|
1671
|
+
serverName,
|
|
1672
|
+
alpn,
|
|
1673
|
+
cookie
|
|
1674
|
+
)
|
|
1675
|
+
return out
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* One message of the server's flight as a `tlsRead*` function below read it:
|
|
1680
|
+
* a ServerHello or HelloRetryRequest, EncryptedExtensions, Certificate,
|
|
1681
|
+
* CertificateVerify or NewSessionTicket. Like `TlsClientHelloView` its fields
|
|
1682
|
+
* are windows into `data` and numbers, so a read allocates nothing; each read
|
|
1683
|
+
* forgets the last message first.
|
|
1684
|
+
*
|
|
1685
|
+
* Two verdicts, kept apart so the client can rank them. `alert` is the
|
|
1686
|
+
* structure's: `decode_error` for a length past its bytes, a vector out of
|
|
1687
|
+
* its bounds or bytes left over. `extensionAlert` is the extensions': an
|
|
1688
|
+
* extension sent twice, or one the client offered but this message may not
|
|
1689
|
+
* carry, is `illegal_parameter`; one the client never offers is
|
|
1690
|
+
* `unsupported_extension` (§4.2). The client reads the structure first, then
|
|
1691
|
+
* the version and the downgrade sentinel, then the extensions, so that a
|
|
1692
|
+
* TLS 1.2 ServerHello is refused for its version, not for the extensions a
|
|
1693
|
+
* TLS 1.2 server sends.
|
|
1694
|
+
*/
|
|
1695
|
+
export class TlsServerFlightView {
|
|
1696
|
+
/** The buffer the windows point into: the one the last message was read from. */
|
|
1697
|
+
data: u8[]
|
|
1698
|
+
/** One bit per extension type read, for the duplicate check (§4.2). */
|
|
1699
|
+
extensionSeen: u32[]
|
|
1700
|
+
/** The reader each read runs, kept so that a read allocates none. */
|
|
1701
|
+
reader: TlsReader
|
|
1702
|
+
/** 0 when the message parsed, else `decode_error`. */
|
|
1703
|
+
alert: i32 = 0
|
|
1704
|
+
/** 0 when the extensions are ones this message may carry, else `illegal_parameter` or `unsupported_extension`. */
|
|
1705
|
+
extensionAlert: i32 = 0
|
|
1706
|
+
legacyVersion: i32 = 0
|
|
1707
|
+
randomAt: i32 = 0
|
|
1708
|
+
sessionIdAt: i32 = 0
|
|
1709
|
+
sessionIdLength: i32 = 0
|
|
1710
|
+
suite: i32 = 0
|
|
1711
|
+
compression: i32 = 0
|
|
1712
|
+
selectedVersion: i32 = 0
|
|
1713
|
+
/** `key_share`: a ServerHello's entry, or a HelloRetryRequest's `selected_group` with an empty share. */
|
|
1714
|
+
shareGroup: i32 = 0
|
|
1715
|
+
shareAt: i32 = 0
|
|
1716
|
+
shareLength: i32 = 0
|
|
1717
|
+
/** A HelloRetryRequest's `cookie`, which the second ClientHello echoes (§4.2.2). */
|
|
1718
|
+
cookieAt: i32 = 0
|
|
1719
|
+
cookieLength: i32 = 0
|
|
1720
|
+
/** EncryptedExtensions' ALPN: the one protocol the server chose (RFC 7301 §3.1). */
|
|
1721
|
+
alpnAt: i32 = 0
|
|
1722
|
+
alpnLength: i32 = 0
|
|
1723
|
+
/** Certificate's `certificate_request_context`, which must be empty for a server's (§4.4.2). */
|
|
1724
|
+
contextLength: i32 = 0
|
|
1725
|
+
/** Certificate's `certificate_list`, every entry in it, and how many there are. */
|
|
1726
|
+
certificatesAt: i32 = 0
|
|
1727
|
+
certificatesLength: i32 = 0
|
|
1728
|
+
certificateCount: i32 = 0
|
|
1729
|
+
/** CertificateVerify's scheme and signature (§4.4.3). */
|
|
1730
|
+
scheme: i32 = 0
|
|
1731
|
+
signatureAt: i32 = 0
|
|
1732
|
+
signatureLength: i32 = 0
|
|
1733
|
+
/** A ServerHello whose `random` is the HelloRetryRequest value (§4.1.3). */
|
|
1734
|
+
isRetry: boolean = false
|
|
1735
|
+
hasSupportedVersions: boolean = false
|
|
1736
|
+
hasKeyShare: boolean = false
|
|
1737
|
+
hasCookie: boolean = false
|
|
1738
|
+
hasAlpn: boolean = false
|
|
1739
|
+
/** EncryptedExtensions' empty `server_name`, the acknowledgement of RFC 6066 §3. */
|
|
1740
|
+
serverNameAcknowledged: boolean = false
|
|
1741
|
+
|
|
1742
|
+
constructor() {
|
|
1743
|
+
this.data = []
|
|
1744
|
+
this.extensionSeen = new Array<u32>(TLS_U16_BITMAP_WORDS)
|
|
1745
|
+
this.reader = new TlsReader(this.data, TLS_CODEC_FROM, TLS_CODEC_FROM)
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/** Forgets the last message: every window empty, every flag down, the bitmap clear. */
|
|
1749
|
+
clear(): void {
|
|
1750
|
+
this.alert = 0
|
|
1751
|
+
this.extensionAlert = 0
|
|
1752
|
+
this.isRetry = false
|
|
1753
|
+
this.legacyVersion = 0
|
|
1754
|
+
this.randomAt = 0
|
|
1755
|
+
this.sessionIdAt = 0
|
|
1756
|
+
this.sessionIdLength = 0
|
|
1757
|
+
this.suite = 0
|
|
1758
|
+
this.compression = 0
|
|
1759
|
+
this.hasSupportedVersions = false
|
|
1760
|
+
this.selectedVersion = 0
|
|
1761
|
+
this.hasKeyShare = false
|
|
1762
|
+
this.shareGroup = 0
|
|
1763
|
+
this.shareAt = 0
|
|
1764
|
+
this.shareLength = 0
|
|
1765
|
+
this.hasCookie = false
|
|
1766
|
+
this.cookieAt = 0
|
|
1767
|
+
this.cookieLength = 0
|
|
1768
|
+
this.hasAlpn = false
|
|
1769
|
+
this.alpnAt = 0
|
|
1770
|
+
this.alpnLength = 0
|
|
1771
|
+
this.serverNameAcknowledged = false
|
|
1772
|
+
this.contextLength = 0
|
|
1773
|
+
this.certificatesAt = 0
|
|
1774
|
+
this.certificatesLength = 0
|
|
1775
|
+
this.certificateCount = 0
|
|
1776
|
+
this.scheme = 0
|
|
1777
|
+
this.signatureAt = 0
|
|
1778
|
+
this.signatureLength = 0
|
|
1779
|
+
for (let k: i32 = 0; k < toI32(this.extensionSeen.length); k++) {
|
|
1780
|
+
this.extensionSeen[k] = toU32(0)
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
/** Records `alert` as `view`'s extension verdict unless an earlier one is there. */
|
|
1786
|
+
const tlsFlightRefuseExtension = (view: TlsServerFlightView, alert: i32): void => {
|
|
1787
|
+
if (view.extensionAlert === 0) {
|
|
1788
|
+
view.extensionAlert = alert
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
/** Whether the client's hello ever offers extension `type`: what separates `illegal_parameter` from `unsupported_extension` (§4.2). */
|
|
1793
|
+
const tlsClientOffersExtension = (type: i32): boolean =>
|
|
1794
|
+
type === TLS_EXT_SERVER_NAME ||
|
|
1795
|
+
type === TLS_EXT_SUPPORTED_GROUPS ||
|
|
1796
|
+
type === TLS_EXT_SIGNATURE_ALGORITHMS ||
|
|
1797
|
+
type === TLS_EXT_ALPN ||
|
|
1798
|
+
type === TLS_EXT_SUPPORTED_VERSIONS ||
|
|
1799
|
+
type === TLS_EXT_KEY_SHARE ||
|
|
1800
|
+
type === TLS_EXT_COOKIE
|
|
1801
|
+
|
|
1802
|
+
/** Opens `view` on `data[off .. off + len)` and answers its reader, or `null` for a window outside `data` (`decode_error`). */
|
|
1803
|
+
const tlsFlightOpen = (view: TlsServerFlightView, data: u8[], off: i32, len: i32): TlsReader | null => {
|
|
1804
|
+
view.clear()
|
|
1805
|
+
view.data = data
|
|
1806
|
+
if (off < 0 || len < 0 || off > toI32(data.length) - len) {
|
|
1807
|
+
view.alert = TLS_ALERT_DECODE_ERROR
|
|
1808
|
+
return null
|
|
1809
|
+
}
|
|
1810
|
+
const r: TlsReader = view.reader
|
|
1811
|
+
r.data = data
|
|
1812
|
+
r.at = off
|
|
1813
|
+
r.end = off + len
|
|
1814
|
+
r.failed = false
|
|
1815
|
+
return r
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/** Closes a read: a failed reader, or bytes left before `end`, is `decode_error`. */
|
|
1819
|
+
const tlsFlightClose = (view: TlsServerFlightView, r: TlsReader, end: i32): void => {
|
|
1820
|
+
if (r.failed || r.at !== end) {
|
|
1821
|
+
view.alert = TLS_ALERT_DECODE_ERROR
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* Reads one ServerHello or HelloRetryRequest extension body, `r.at .. end`,
|
|
1827
|
+
* of type `type`, and judges whether it may be there: `key_share` and
|
|
1828
|
+
* `supported_versions` in either, `cookie` in a HelloRetryRequest alone
|
|
1829
|
+
* (§4.2's table).
|
|
1830
|
+
*/
|
|
1831
|
+
const tlsReadServerHelloExtension = (r: TlsReader, type: i32, end: i32, view: TlsServerFlightView): void => {
|
|
1832
|
+
if (type === TLS_EXT_SUPPORTED_VERSIONS) {
|
|
1833
|
+
view.hasSupportedVersions = true
|
|
1834
|
+
view.selectedVersion = tlsReadU16(r)
|
|
1835
|
+
} else if (type === TLS_EXT_KEY_SHARE) {
|
|
1836
|
+
view.hasKeyShare = true
|
|
1837
|
+
view.shareGroup = tlsReadU16(r)
|
|
1838
|
+
if (!view.isRetry) {
|
|
1839
|
+
const keyEnd: i32 = tlsReadVector(r, 2, 1, 65535)
|
|
1840
|
+
view.shareAt = r.at
|
|
1841
|
+
view.shareLength = keyEnd - r.at
|
|
1842
|
+
tlsReadSkipTo(r, keyEnd)
|
|
1843
|
+
}
|
|
1844
|
+
} else if (type === TLS_EXT_COOKIE && view.isRetry) {
|
|
1845
|
+
const cookieEnd: i32 = tlsReadVector(r, 2, 1, 65535)
|
|
1846
|
+
view.hasCookie = true
|
|
1847
|
+
view.cookieAt = r.at
|
|
1848
|
+
view.cookieLength = cookieEnd - r.at
|
|
1849
|
+
tlsReadSkipTo(r, cookieEnd)
|
|
1850
|
+
} else {
|
|
1851
|
+
tlsFlightRefuseExtension(
|
|
1852
|
+
view,
|
|
1853
|
+
tlsClientOffersExtension(type) ? TLS_ALERT_ILLEGAL_PARAMETER : TLS_ALERT_UNSUPPORTED_EXTENSION
|
|
1854
|
+
)
|
|
1855
|
+
tlsReadSkipTo(r, end)
|
|
1856
|
+
}
|
|
1857
|
+
if (r.at !== end) {
|
|
1858
|
+
r.failed = true
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/** Reads one EncryptedExtensions body (§4.3.1): ALPN, an empty `server_name` and `supported_groups` may be there. */
|
|
1863
|
+
const tlsReadEncryptedExtension = (r: TlsReader, type: i32, end: i32, view: TlsServerFlightView): void => {
|
|
1864
|
+
if (type === TLS_EXT_ALPN) {
|
|
1865
|
+
// RFC 7301 §3.1: the server's list holds exactly one protocol.
|
|
1866
|
+
const listEnd: i32 = tlsReadVector(r, 2, 2, 65535)
|
|
1867
|
+
const nameEnd: i32 = tlsReadVector(r, 1, 1, 255)
|
|
1868
|
+
view.hasAlpn = true
|
|
1869
|
+
view.alpnAt = r.at
|
|
1870
|
+
view.alpnLength = nameEnd - r.at
|
|
1871
|
+
tlsReadSkipTo(r, nameEnd)
|
|
1872
|
+
if (!r.failed && nameEnd !== listEnd) {
|
|
1873
|
+
tlsFlightRefuseExtension(view, TLS_ALERT_ILLEGAL_PARAMETER)
|
|
1874
|
+
tlsReadSkipTo(r, listEnd)
|
|
1875
|
+
}
|
|
1876
|
+
} else if (type === TLS_EXT_SERVER_NAME) {
|
|
1877
|
+
// RFC 6066 §3: the acknowledgement's body is empty; the check below
|
|
1878
|
+
// refuses one with bytes in it.
|
|
1879
|
+
view.serverNameAcknowledged = true
|
|
1880
|
+
} else if (type === TLS_EXT_SUPPORTED_GROUPS) {
|
|
1881
|
+
// §4.2.7: the server's own preference, which the client may note and
|
|
1882
|
+
// this one does not; only its syntax is checked.
|
|
1883
|
+
tlsReadU16Vector(r, 2, 2, 65535)
|
|
1884
|
+
} else {
|
|
1885
|
+
tlsFlightRefuseExtension(
|
|
1886
|
+
view,
|
|
1887
|
+
tlsClientOffersExtension(type) ? TLS_ALERT_ILLEGAL_PARAMETER : TLS_ALERT_UNSUPPORTED_EXTENSION
|
|
1888
|
+
)
|
|
1889
|
+
tlsReadSkipTo(r, end)
|
|
1890
|
+
}
|
|
1891
|
+
if (r.at !== end) {
|
|
1892
|
+
r.failed = true
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
/** Reads an extensions block, each body by `hello`'s reader: true for a ServerHello or HelloRetryRequest, false for EncryptedExtensions. */
|
|
1897
|
+
const tlsReadServerExtensions = (r: TlsReader, view: TlsServerFlightView, hello: boolean): void => {
|
|
1898
|
+
const extensionsEnd: i32 = tlsReadVector(r, 2, 0, 65535)
|
|
1899
|
+
const messageEnd: i32 = tlsReadList(r, extensionsEnd)
|
|
1900
|
+
while (!r.failed && r.at < extensionsEnd) {
|
|
1901
|
+
const type: i32 = tlsReadU16(r)
|
|
1902
|
+
const bodyEnd: i32 = tlsReadVector(r, 2, 0, 65535)
|
|
1903
|
+
if (r.failed) {
|
|
1904
|
+
break
|
|
1905
|
+
}
|
|
1906
|
+
if (tlsMarkSeen(view.extensionSeen, type)) {
|
|
1907
|
+
tlsFlightRefuseExtension(view, TLS_ALERT_ILLEGAL_PARAMETER)
|
|
1908
|
+
}
|
|
1909
|
+
const outerEnd: i32 = tlsReadList(r, bodyEnd)
|
|
1910
|
+
if (hello) {
|
|
1911
|
+
tlsReadServerHelloExtension(r, type, bodyEnd, view)
|
|
1912
|
+
} else {
|
|
1913
|
+
tlsReadEncryptedExtension(r, type, bodyEnd, view)
|
|
1914
|
+
}
|
|
1915
|
+
r.end = outerEnd
|
|
1916
|
+
}
|
|
1917
|
+
r.end = messageEnd
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
/** Whether the bytes at `data[at]` are the ones the hex constant `text` spells. */
|
|
1921
|
+
const tlsWindowIsHex = (data: u8[], at: i32, text: string): boolean => {
|
|
1922
|
+
const length: i32 = toI32(text.length) >> 1
|
|
1923
|
+
if (at < 0 || at > toI32(data.length) - length) {
|
|
1924
|
+
return false
|
|
1925
|
+
}
|
|
1926
|
+
for (let k: i32 = 0; k < length; k++) {
|
|
1927
|
+
const hi: i32 = tlsHexNibble(toI32(text.charCodeAt(2 * k)))
|
|
1928
|
+
const lo: i32 = tlsHexNibble(toI32(text.charCodeAt(2 * k + 1)))
|
|
1929
|
+
if (toI32(data[at + k]) !== ((hi << 4) | lo)) {
|
|
1930
|
+
return false
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return true
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* Reads the ServerHello body `data[off .. off + len)` — without its handshake
|
|
1938
|
+
* header — into `view`, which tells a HelloRetryRequest from it by its
|
|
1939
|
+
* `random` (§4.1.3). An absent extensions block parses: it is how a TLS 1.2
|
|
1940
|
+
* or older server answers, which the client refuses for its version.
|
|
1941
|
+
*/
|
|
1942
|
+
export const tlsReadServerHello = (view: TlsServerFlightView, data: u8[], off: i32, len: i32): void => {
|
|
1943
|
+
const r: TlsReader | null = tlsFlightOpen(view, data, off, len)
|
|
1944
|
+
if (r === null) {
|
|
1945
|
+
return
|
|
1946
|
+
}
|
|
1947
|
+
view.legacyVersion = tlsReadU16(r)
|
|
1948
|
+
view.randomAt = r.at
|
|
1949
|
+
view.isRetry = tlsWindowIsHex(data, r.at, TLS_HELLO_RETRY_RANDOM) && r.end - r.at >= TLS_RANDOM_SIZE
|
|
1950
|
+
tlsReadSkipTo(r, r.at + TLS_RANDOM_SIZE)
|
|
1951
|
+
const sessionEnd: i32 = tlsReadVector(r, 1, 0, 32)
|
|
1952
|
+
view.sessionIdAt = r.at
|
|
1953
|
+
view.sessionIdLength = sessionEnd - r.at
|
|
1954
|
+
tlsReadSkipTo(r, sessionEnd)
|
|
1955
|
+
view.suite = tlsReadU16(r)
|
|
1956
|
+
view.compression = tlsReadU8(r)
|
|
1957
|
+
if (!r.failed && r.at < off + len) {
|
|
1958
|
+
tlsReadServerExtensions(r, view, true)
|
|
1959
|
+
}
|
|
1960
|
+
tlsFlightClose(view, r, off + len)
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
/** The 8 bytes RFC 8446 §4.1.3 has a TLS 1.3 server put at the end of its random when it negotiates TLS 1.2 or below. */
|
|
1964
|
+
const TLS_DOWNGRADE_12: string = "444f574e47524401"
|
|
1965
|
+
const TLS_DOWNGRADE_11: string = "444f574e47524400"
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* Whether the ServerHello in `view` carries a downgrade sentinel in the last
|
|
1969
|
+
* 8 bytes of its random (§4.1.3), which a TLS 1.3 client must refuse with
|
|
1970
|
+
* `illegal_parameter`.
|
|
1971
|
+
*/
|
|
1972
|
+
export const tlsServerHelloDowngrade = (view: TlsServerFlightView): boolean => {
|
|
1973
|
+
const at: i32 = view.randomAt + TLS_RANDOM_SIZE - 8
|
|
1974
|
+
return tlsWindowIsHex(view.data, at, TLS_DOWNGRADE_12) || tlsWindowIsHex(view.data, at, TLS_DOWNGRADE_11)
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
/** Reads an EncryptedExtensions body (§4.3.1) into `view`. */
|
|
1978
|
+
export const tlsReadEncryptedExtensions = (
|
|
1979
|
+
view: TlsServerFlightView,
|
|
1980
|
+
data: u8[],
|
|
1981
|
+
off: i32,
|
|
1982
|
+
len: i32
|
|
1983
|
+
): void => {
|
|
1984
|
+
const r: TlsReader | null = tlsFlightOpen(view, data, off, len)
|
|
1985
|
+
if (r === null) {
|
|
1986
|
+
return
|
|
1987
|
+
}
|
|
1988
|
+
tlsReadServerExtensions(r, view, false)
|
|
1989
|
+
tlsFlightClose(view, r, off + len)
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Reads a server's Certificate body (§4.4.2) into `view`: the request
|
|
1994
|
+
* context's length, which the client requires to be 0, and the list of
|
|
1995
|
+
* entries. An empty list is `decode_error`, as §4.4.2.4 has a client answer
|
|
1996
|
+
* it; an entry with extensions is `unsupported_extension`, because the
|
|
1997
|
+
* client offers none that a CertificateEntry may carry.
|
|
1998
|
+
*/
|
|
1999
|
+
export const tlsReadCertificate = (view: TlsServerFlightView, data: u8[], off: i32, len: i32): void => {
|
|
2000
|
+
const r: TlsReader | null = tlsFlightOpen(view, data, off, len)
|
|
2001
|
+
if (r === null) {
|
|
2002
|
+
return
|
|
2003
|
+
}
|
|
2004
|
+
const contextEnd: i32 = tlsReadVector(r, 1, 0, 255)
|
|
2005
|
+
view.contextLength = contextEnd - r.at
|
|
2006
|
+
tlsReadSkipTo(r, contextEnd)
|
|
2007
|
+
const listEnd: i32 = tlsReadVector(r, 3, 1, 16777215)
|
|
2008
|
+
view.certificatesAt = r.at
|
|
2009
|
+
view.certificatesLength = listEnd - r.at
|
|
2010
|
+
const messageEnd: i32 = tlsReadList(r, listEnd)
|
|
2011
|
+
while (!r.failed && r.at < listEnd) {
|
|
2012
|
+
tlsReadSkipTo(r, tlsReadVector(r, 3, 1, 16777215))
|
|
2013
|
+
const extensionsEnd: i32 = tlsReadVector(r, 2, 0, 65535)
|
|
2014
|
+
if (extensionsEnd !== r.at) {
|
|
2015
|
+
tlsFlightRefuseExtension(view, TLS_ALERT_UNSUPPORTED_EXTENSION)
|
|
2016
|
+
}
|
|
2017
|
+
tlsReadSkipTo(r, extensionsEnd)
|
|
2018
|
+
view.certificateCount = view.certificateCount + 1
|
|
2019
|
+
}
|
|
2020
|
+
r.end = messageEnd
|
|
2021
|
+
tlsFlightClose(view, r, off + len)
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/** Reads a CertificateVerify body (§4.4.3) into `view`: the scheme and the signature. */
|
|
2025
|
+
export const tlsReadCertificateVerify = (view: TlsServerFlightView, data: u8[], off: i32, len: i32): void => {
|
|
2026
|
+
const r: TlsReader | null = tlsFlightOpen(view, data, off, len)
|
|
2027
|
+
if (r === null) {
|
|
2028
|
+
return
|
|
2029
|
+
}
|
|
2030
|
+
view.scheme = tlsReadU16(r)
|
|
2031
|
+
const signatureEnd: i32 = tlsReadVector(r, 2, 1, 65535)
|
|
2032
|
+
view.signatureAt = r.at
|
|
2033
|
+
view.signatureLength = signatureEnd - r.at
|
|
2034
|
+
tlsReadSkipTo(r, signatureEnd)
|
|
2035
|
+
tlsFlightClose(view, r, off + len)
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
/**
|
|
2039
|
+
* Checks a NewSessionTicket body's structure (§4.6.1) into `view`'s `alert`:
|
|
2040
|
+
* the client keeps no tickets, so it reads one only to discard it, but a
|
|
2041
|
+
* malformed one is still `decode_error`.
|
|
2042
|
+
*/
|
|
2043
|
+
export const tlsReadNewSessionTicket = (view: TlsServerFlightView, data: u8[], off: i32, len: i32): void => {
|
|
2044
|
+
const r: TlsReader | null = tlsFlightOpen(view, data, off, len)
|
|
2045
|
+
if (r === null) {
|
|
2046
|
+
return
|
|
2047
|
+
}
|
|
2048
|
+
tlsReadSkipTo(r, r.at + 8)
|
|
2049
|
+
tlsReadSkipTo(r, tlsReadVector(r, 1, 0, 255))
|
|
2050
|
+
tlsReadSkipTo(r, tlsReadVector(r, 2, 1, 65535))
|
|
2051
|
+
tlsReadSkipTo(r, tlsReadVector(r, 2, 0, 65534))
|
|
2052
|
+
tlsFlightClose(view, r, off + len)
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/** The DER certificates of the Certificate `tlsReadCertificate` last read into `view`, leaf first, each a fresh array. */
|
|
2056
|
+
export const tlsCopyCertificates = (view: TlsServerFlightView): u8[][] => {
|
|
2057
|
+
const chain: u8[][] = []
|
|
2058
|
+
const r: TlsReader = new TlsReader(
|
|
2059
|
+
view.data,
|
|
2060
|
+
view.certificatesAt,
|
|
2061
|
+
view.certificatesAt + view.certificatesLength
|
|
2062
|
+
)
|
|
2063
|
+
while (view.alert === 0 && !r.failed && r.at < r.end) {
|
|
2064
|
+
const certificateEnd: i32 = tlsReadVector(r, 3, 1, 16777215)
|
|
2065
|
+
chain.push(tlsCopyWindow(view.data, r.at, certificateEnd - r.at))
|
|
2066
|
+
tlsReadSkipTo(r, certificateEnd)
|
|
2067
|
+
tlsReadSkipTo(r, tlsReadVector(r, 2, 0, 65535))
|
|
2068
|
+
}
|
|
2069
|
+
return chain
|
|
2070
|
+
}
|