@amritk/nish-x86_64-linux 0.23.1 → 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.
Files changed (51) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +50 -23
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/arrays.d.ts +0 -2
  6. package/runtime/core/arrays.mjs +0 -2
  7. package/runtime/core/host.d.ts +8 -0
  8. package/runtime/core/host.mjs +50 -2
  9. package/runtime/core/numbers.mjs +2 -3
  10. package/runtime/nish.d.ts +100 -4
  11. package/runtime/nish.h +34 -0
  12. package/runtime/nish.mjs +23 -5
  13. package/runtime/node.mjs +57 -6
  14. package/runtime/runtime-net.c +45 -4
  15. package/runtime/runtime-os.c +115 -12
  16. package/runtime/runtime-term.c +2 -2
  17. package/runtime/runtime.c +1 -1
  18. package/scripts/build.sh +13 -3
  19. package/std/README.md +44 -7
  20. package/std/cli-args.ts +1216 -0
  21. package/std/cli-flags.ts +585 -0
  22. package/std/cli.ts +308 -19
  23. package/std/compress/checksum.ts +290 -0
  24. package/std/compress/deflate.ts +2790 -0
  25. package/std/compress/gzip.ts +761 -0
  26. package/std/compress/zlib.ts +168 -0
  27. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  28. package/std/crypto/p256.ts +63 -1
  29. package/std/crypto/p384.ts +6044 -0
  30. package/std/crypto/pki-roots.ts +322 -0
  31. package/std/crypto/pki.ts +1207 -0
  32. package/std/crypto/rsa.ts +663 -0
  33. package/std/crypto/sha256.ts +2 -3
  34. package/std/crypto/x25519.ts +3 -4
  35. package/std/crypto/x509.ts +1054 -58
  36. package/std/net/dns-config.ts +734 -0
  37. package/std/net/dns-message.ts +517 -0
  38. package/std/net/dns-resolver.ts +1015 -0
  39. package/std/net/dns.ts +478 -0
  40. package/std/net/http-compress.ts +393 -0
  41. package/std/net/http1-client.ts +1227 -0
  42. package/std/net/http1-server.ts +101 -1
  43. package/std/net/http1.ts +9 -9
  44. package/std/net/tls/client.ts +1094 -0
  45. package/std/net/tls/codec.ts +585 -3
  46. package/std/net/tls/record-client.ts +1192 -0
  47. package/std/net/tls/verify.ts +221 -0
  48. package/std/net/tls-tcp.ts +642 -11
  49. package/std/net/tls.ts +2 -2
  50. package/std/net/url.ts +559 -4
  51. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `nish/net/tls/verify` — the TLS client's one authentication seam: whether
3
+ * the server a `TlsRecordClient` reached is the one its `serverName` says,
4
+ * decided at `TLS_RECORD_AUTH` and handed to the connection as its verdict.
5
+ *
6
+ * const trust: TlsClientTrust = tlsClientSystemTrust();
7
+ * // when conn.interest() says TLS_RECORD_AUTH:
8
+ * const alert: i32 = tlsClientAuthenticate(conn, trust, toI64(Date.now()));
9
+ *
10
+ * **Two checks, both always made.** The CertificateVerify first: its scheme
11
+ * must be one this client offered and fit the leaf's key — ECDSA P-256 with
12
+ * SHA-256 on a P-256 key, ECDSA P-384 with SHA-384 on a P-384 key, RSA-PSS
13
+ * (rsae, salt as long as the hash) on an RSA key of at least 2048 bits — and
14
+ * the signature must verify over `TlsClient.verifyContent()` under that key
15
+ * (RFC 8446 §4.4.3). Then the chain, from the leaf the server sent to a root
16
+ * in `trust`, for the name the caller dialled. A server with no certificate
17
+ * or a leaf that does not parse is `bad_certificate`, as is a leaf whose key
18
+ * is of any other kind (Ed25519, a short RSA modulus); a signature that does
19
+ * not verify, or a scheme that does not fit the key, is `decrypt_error`.
20
+ * The signature goes first so a forged CertificateVerify reads as what it is
21
+ * whatever the chain would have said.
22
+ *
23
+ * **The chain is `nish/crypto/pki`'s.** `pkiVerifyServer` builds a path from
24
+ * the leaf to a root of `trust`'s store and checks every certificate on it —
25
+ * signatures, validity at the instant given, basicConstraints, keyUsage,
26
+ * serverAuth, name constraints — and that the leaf's subjectAltName names
27
+ * the host dialled. Its refusal becomes the alert: `certificate_expired` for
28
+ * a certificate outside its validity, `unknown_ca` when no path reaches a
29
+ * root of the store, and `bad_certificate` for every other reason. There is
30
+ * no switch that skips the step and none is to be added: a client that is to
31
+ * talk to a server it cannot authenticate is given a store holding its
32
+ * issuing root, never a way around the check (`docs/security/tls.md`, "The
33
+ * client carrier and verify").
34
+ *
35
+ * The verdict is the record client's to send: `tlsClientAuthenticate` calls
36
+ * `authenticate(true)` or `reject(alert)` on it, so the refusal goes out as
37
+ * a fatal alert under the handshake keys. Written from RFC 8446 §4.2.3 and
38
+ * §4.4.3, not ported from another implementation.
39
+ */
40
+ import { p256VerifySha256 } from "nish/crypto/p256"
41
+ import { p384VerifySha384 } from "nish/crypto/p384"
42
+ import {
43
+ PKI_ERR_EXPIRED,
44
+ PKI_ERR_NOT_YET_VALID,
45
+ PKI_ERR_UNTRUSTED,
46
+ PKI_OK,
47
+ PkiResult,
48
+ PkiTrustStore,
49
+ pkiTrustStore,
50
+ pkiVerifyServer,
51
+ } from "nish/crypto/pki"
52
+ import { PkiRoots, pkiLoadSystemRoots } from "nish/crypto/pki-roots"
53
+ import {
54
+ RSA_HASH_SHA256,
55
+ RSA_HASH_SHA384,
56
+ RSA_HASH_SHA512,
57
+ RsaPublicKey,
58
+ rsaParsePublicKey,
59
+ rsaVerifyPss,
60
+ } from "nish/crypto/rsa"
61
+ import { SHA256_SIZE } from "nish/crypto/sha256"
62
+ import { SHA384_SIZE, SHA512_SIZE } from "nish/crypto/sha512"
63
+ import {
64
+ X509Certificate,
65
+ X509_KEY_P256,
66
+ X509_KEY_P384,
67
+ X509_KEY_RSA,
68
+ x509DerSignatureRSWidth,
69
+ x509ParseCertificate,
70
+ } from "nish/crypto/x509"
71
+ import {
72
+ TLS_ALERT_BAD_CERTIFICATE,
73
+ TLS_ALERT_DECRYPT_ERROR,
74
+ TLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
75
+ TLS_SIGNATURE_ECDSA_SECP384R1_SHA384,
76
+ TLS_SIGNATURE_RSA_PSS_RSAE_SHA256,
77
+ TLS_SIGNATURE_RSA_PSS_RSAE_SHA384,
78
+ TLS_SIGNATURE_RSA_PSS_RSAE_SHA512,
79
+ } from "nish/net/tls/codec"
80
+ import { TlsClient } from "nish/net/tls/client"
81
+ import { TlsRecordClient } from "nish/net/tls/record-client"
82
+
83
+ /** `certificate_expired` (RFC 8446 §6.2): the leaf or a certificate above it is outside its validity. */
84
+ export const TLS_ALERT_CERTIFICATE_EXPIRED: i32 = 45
85
+
86
+ /** `unknown_ca` (RFC 8446 §6.2): no path from the chain reaches a root the client trusts. */
87
+ export const TLS_ALERT_UNKNOWN_CA: i32 = 48
88
+
89
+ /** The smallest RSA modulus a CertificateVerify is checked under, in bits: the Web PKI's floor. */
90
+ export const TLS_VERIFY_MIN_RSA_BITS: i32 = 2048
91
+
92
+ /** An ECDSA signature's `r` and `s` width on P-256, in bytes. */
93
+ const TLS_VERIFY_P256_WIDTH: i32 = 32
94
+
95
+ /** An ECDSA signature's `r` and `s` width on P-384, in bytes. */
96
+ const TLS_VERIFY_P384_WIDTH: i32 = 48
97
+
98
+ /**
99
+ * The roots a client trusts, indexed once and shared by every connection
100
+ * made with them.
101
+ */
102
+ export class TlsClientTrust {
103
+ /** The roots, as `pkiVerifyServer` looks them up. */
104
+ store: PkiTrustStore
105
+
106
+ constructor(roots: X509Certificate[]) {
107
+ this.store = pkiTrustStore(roots)
108
+ }
109
+ }
110
+
111
+ /** The trust of `roots`, as `nish/crypto/pki-roots` read them. */
112
+ export const tlsClientTrust = (roots: PkiRoots): TlsClientTrust => new TlsClientTrust(roots.roots)
113
+
114
+ /** The trust of the system's roots (`pkiLoadSystemRoots`): what a client trusts unless it is given a store. */
115
+ export const tlsClientSystemTrust = (): TlsClientTrust => tlsClientTrust(pkiLoadSystemRoots())
116
+
117
+ /**
118
+ * Whether `signature`, as a CertificateVerify carries it under `scheme`,
119
+ * verifies over `content` under `leaf`'s key. `false` for a scheme that
120
+ * does not fit the key's kind and for every scheme this client does not
121
+ * offer.
122
+ */
123
+ export const tlsVerifyCertificateVerify = (
124
+ leaf: X509Certificate,
125
+ scheme: i32,
126
+ content: u8[],
127
+ signature: u8[]
128
+ ): boolean => {
129
+ if (scheme === TLS_SIGNATURE_ECDSA_SECP256R1_SHA256) {
130
+ if (leaf.keyType !== X509_KEY_P256) {
131
+ return false
132
+ }
133
+ const rs: u8[] | null = x509DerSignatureRSWidth(signature, TLS_VERIFY_P256_WIDTH)
134
+ return rs !== null && p256VerifySha256(leaf.publicKey, content, rs)
135
+ }
136
+ if (scheme === TLS_SIGNATURE_ECDSA_SECP384R1_SHA384) {
137
+ if (leaf.keyType !== X509_KEY_P384) {
138
+ return false
139
+ }
140
+ const rs: u8[] | null = x509DerSignatureRSWidth(signature, TLS_VERIFY_P384_WIDTH)
141
+ return rs !== null && p384VerifySha384(leaf.publicKey, content, rs)
142
+ }
143
+ if (leaf.keyType !== X509_KEY_RSA) {
144
+ return false
145
+ }
146
+ const key: RsaPublicKey | null = rsaParsePublicKey(leaf.publicKey)
147
+ if (key === null) {
148
+ return false
149
+ }
150
+ // RFC 8446 §4.2.3: the salt is as long as the digest.
151
+ switch (scheme) {
152
+ case TLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
153
+ return rsaVerifyPss(key, RSA_HASH_SHA256, SHA256_SIZE, content, signature)
154
+ case TLS_SIGNATURE_RSA_PSS_RSAE_SHA384:
155
+ return rsaVerifyPss(key, RSA_HASH_SHA384, SHA384_SIZE, content, signature)
156
+ case TLS_SIGNATURE_RSA_PSS_RSAE_SHA512:
157
+ return rsaVerifyPss(key, RSA_HASH_SHA512, SHA512_SIZE, content, signature)
158
+ default:
159
+ return false
160
+ }
161
+ }
162
+
163
+ /** Whether `leaf`'s key is one a CertificateVerify is checked under: P-256, P-384, or RSA of at least `TLS_VERIFY_MIN_RSA_BITS`. */
164
+ const tlsVerifyKeyUsable = (leaf: X509Certificate): boolean => {
165
+ if (leaf.keyType === X509_KEY_P256 || leaf.keyType === X509_KEY_P384) {
166
+ return true
167
+ }
168
+ if (leaf.keyType !== X509_KEY_RSA) {
169
+ return false
170
+ }
171
+ const key: RsaPublicKey | null = rsaParsePublicKey(leaf.publicKey)
172
+ return key !== null && key.bits >= TLS_VERIFY_MIN_RSA_BITS
173
+ }
174
+
175
+ /**
176
+ * The chain step: 0 when `chain` (DER, leaf first) leads to a root in
177
+ * `trust` and names `serverName` at `nowMs`, else the alert —
178
+ * `certificate_expired`, `unknown_ca` or `bad_certificate`.
179
+ */
180
+ export const tlsClientChain = (chain: u8[][], serverName: string, nowMs: i64, trust: TlsClientTrust): i32 => {
181
+ const result: PkiResult = pkiVerifyServer(chain, serverName, nowMs, trust.store)
182
+ if (result.code === PKI_OK) {
183
+ return 0
184
+ }
185
+ if (result.code === PKI_ERR_EXPIRED || result.code === PKI_ERR_NOT_YET_VALID) {
186
+ return TLS_ALERT_CERTIFICATE_EXPIRED
187
+ }
188
+ return result.code === PKI_ERR_UNTRUSTED ? TLS_ALERT_UNKNOWN_CA : TLS_ALERT_BAD_CERTIFICATE
189
+ }
190
+
191
+ /**
192
+ * The verdict on `tls`'s server at `TLS_CLIENT_STATE_WAIT_AUTH`: 0 when the
193
+ * CertificateVerify verifies under the leaf's key and the chain leads from
194
+ * that leaf to a root in `trust` for the configured server name at `nowMs`
195
+ * (milliseconds since 1970-01-01 UTC), else the alert to refuse with.
196
+ */
197
+ export const tlsClientVerdict = (tls: TlsClient, trust: TlsClientTrust, nowMs: i64): i32 => {
198
+ const chain: u8[][] = tls.peerCertificates()
199
+ if (toI32(chain.length) === 0) {
200
+ return TLS_ALERT_BAD_CERTIFICATE
201
+ }
202
+ const leaf: X509Certificate | null = x509ParseCertificate(chain[0])
203
+ if (leaf === null || !tlsVerifyKeyUsable(leaf)) {
204
+ return TLS_ALERT_BAD_CERTIFICATE
205
+ }
206
+ if (!tlsVerifyCertificateVerify(leaf, tls.verifyScheme(), tls.verifyContent(), tls.verifySignature())) {
207
+ return TLS_ALERT_DECRYPT_ERROR
208
+ }
209
+ return tlsClientChain(chain, tls.config.serverName, nowMs, trust)
210
+ }
211
+
212
+ /**
213
+ * Authenticates `client`'s server, while its interest says
214
+ * `TLS_RECORD_AUTH`: `tlsClientVerdict`, then `client.authenticate(true)` on
215
+ * 0 or `client.reject(alert)` otherwise, which seals the alert. Answers 0
216
+ * or the alert the connection failed with.
217
+ */
218
+ export const tlsClientAuthenticate = (client: TlsRecordClient, trust: TlsClientTrust, nowMs: i64): i32 => {
219
+ const alert: i32 = tlsClientVerdict(client.tls, trust, nowMs)
220
+ return alert === 0 ? client.authenticate(true) : client.reject(alert)
221
+ }