@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
@@ -7,12 +7,14 @@
7
7
  * import { x509MintSelfSigned, x509CertificateHash, derToPem } from "nish/crypto/x509";
8
8
  *
9
9
  * // Both secrets come from the kernel's CSPRNG; a predictable key or serial is the caller's bug.
10
- * const priv: u8[] = new Array<u8>(32);
11
- * crypto.getRandomValues(priv); // p256PublicKey(priv) === null: draw again (odds 2^-32)
10
+ * const drawn: u8[] = new Array<u8>(32);
11
+ * crypto.getRandomValues(drawn);
12
+ * const priv: Secret<u8[]> = secret(drawn); // nish:secret; p256PublicKey(priv) === null: draw again (odds 2^-32)
12
13
  * const serial: u8[] = new Array<u8>(16);
13
14
  * crypto.getRandomValues(serial);
14
15
  * serial[0] = serial[0] & toU8(0x7f); // positive, so it stays 16 octets
15
16
  * const der: u8[] | null = x509MintSelfSigned(priv, "localhost", toI64(Date.now()), 14, serial);
17
+ * wipe(priv);
16
18
  * // x509CertificateHash(der) is what serverCertificateHashes names;
17
19
  * // derToPem(der, "CERTIFICATE") is the file a TLS stack loads.
18
20
  *
@@ -31,7 +33,8 @@
31
33
  * `null`, never a panic. The reader looks at what this module uses — the
32
34
  * version, serial, algorithms, names as opaque bytes, validity, the subject
33
35
  * public key and the signature — and checks the order of what follows the key
34
- * (`[1]`, `[2]`, `[3]`) without parsing extensions.
36
+ * (`[1]`, `[2]`, `[3]`) without parsing extensions; the mint writes two of its
37
+ * own (below).
35
38
  *
36
39
  * **PEM is RFC 7468's strict grammar**, with standard base64 (`+` and `/`,
37
40
  * padded with `=`), not `base64url`: 64 characters a line and a shorter last
@@ -45,15 +48,26 @@
45
48
  * requirements"): X.509 v3, an ECDSA P-256 key signed with ecdsa-with-SHA256,
46
49
  * and a validity period of at most fourteen days — `notAfter - notBefore`,
47
50
  * which is what Chromium compares, so `days` is 1 to 14. Subject and issuer
48
- * are the same single common name, as a UTF8String. It carries **no
49
- * extensions**: the specification requires none, a browser checking a pinned
50
- * hash does no path building for basicConstraints or keyUsage to inform, and
51
- * every extension would be one more field whose absence is part of the
52
- * contract a hash pins. OpenSSL verifies it as its own trust anchor all the
53
- * same (`tests/link/crypto_x509`'s header has the commands). The key, the
54
- * serial and the time are parameters so the golden certificate is
55
- * deterministic; a caller passes `Date.now()` and, for the key and the serial,
56
- * bytes from `crypto.getRandomValues`. This module does not draw them itself,
51
+ * are the same single common name, as a UTF8String. It carries **two
52
+ * extensions, both critical and both constant**: basicConstraints with cA
53
+ * FALSE and keyUsage with digitalSignature alone. The W3C text requires no
54
+ * extension, but Chromium does: the verifier behind `serverCertificateHashes`,
55
+ * quiche's `WebTransportFingerprintProofVerifier`, parses the certificate
56
+ * with `CertificateView::ParseSingleCertificate` before it compares the hash,
57
+ * and that answers null when `[3] extensions` is absent
58
+ * (`quiche/quic/core/crypto/certificate_view.cc:395`, at the quiche revision
59
+ * 62826931 Chromium 141 pins), so the handshake fails with
60
+ * CERTIFICATE_VERIFY_FAILED. It also holds every extension to `SEQUENCE {
61
+ * OID, BOOLEAN OPTIONAL, OCTET STRING }` (`:413`–`:432`) and reads
62
+ * subjectAltName only when one is present (`:434`), so no name is needed
63
+ * there. The two chosen say what the certificate is, an end entity whose key
64
+ * only signs handshakes, so a verifier that does build a path need not fall
65
+ * back on its defaults; OpenSSL verifies it as its own trust anchor
66
+ * (`tests/link/crypto_x509`'s header has the commands; X509-9 in
67
+ * docs/security/crypto-x509.md). The key, the serial and the time are
68
+ * parameters so the golden certificate is deterministic; a caller passes
69
+ * `Date.now()` and, for the key and the serial, bytes from
70
+ * `crypto.getRandomValues`. This module does not draw them itself,
57
71
  * because a call to the operating system would stop it compiling for wasm32.
58
72
  *
59
73
  * **Constant time.** A private key is the one secret that passes through here:
@@ -70,10 +84,18 @@
70
84
  * public key stored beside it compares public values. None of this is under
71
85
  * the disassembly check; it is discipline.
72
86
  *
87
+ * **The private key is a `Secret`** (`nish:secret`, docs/LANGUAGE.md
88
+ * "Secrets"). `x509ParseP256PrivateKey` answers one and `x509MintSelfSigned`
89
+ * takes one; the scalar is copied out of its DER once, into the `Secret`, and
90
+ * every block decoded from the PEM is wiped before the parse returns (X509-7,
91
+ * docs/security/crypto-x509.md). The PEM text is the caller's string, which
92
+ * nothing in the language can wipe.
93
+ *
73
94
  * Private names carry the `x509` prefix because a `std/` module's private
74
95
  * functions share the importing program's flat symbol namespace
75
96
  * (`docs/wp26-stdlib.md` §3e).
76
97
  */
98
+ import { Secret, secret, wipe } from "nish:secret"
77
99
  import { p256PublicKey, p256SignSha256, p256VerifySha256 } from "nish/crypto/p256"
78
100
  import { sha256 } from "nish/crypto/sha256"
79
101
 
@@ -685,8 +707,15 @@ const x509Bytes = (text: string): u8[] => {
685
707
  // PEM (RFC 7468) and standard base64 (RFC 4648 §4)
686
708
  // ---------------------------------------------------------------------------
687
709
 
688
- /** All ones when `lo <= c <= hi`, else zero, without a branch; `c` is a byte or a sextet. */
689
- const x509RangeMask = (c: i32, lo: i32, hi: i32): i32 => ((lo - 1 - c) & (c - hi - 1)) >> 31
710
+ /**
711
+ * All ones when `lo <= c <= hi`, else zero, without a branch; `c` is a byte or
712
+ * a sextet. The subtractions are `u32`'s, which wrap by definition, rather
713
+ * than checked `i32` ones, whose check would be a branch on a byte of what may
714
+ * be a private key's PEM; the sign bit `>> 31` reads is the same.
715
+ * `x509Base64CharOf` and `x509Base64SextetOf` sum in `u32` for the same reason.
716
+ */
717
+ const x509RangeMask = (c: i32, lo: i32, hi: i32): i32 =>
718
+ toI32((toU32(lo) - 1 - toU32(c)) & (toU32(c) - toU32(hi) - 1)) >> 31
690
719
 
691
720
  /**
692
721
  * The base64 character for the sextet `v`: `'A' + v`, stepped by the gap to
@@ -694,12 +723,14 @@ const x509RangeMask = (c: i32, lo: i32, hi: i32): i32 => ((lo - 1 - c) & (c - hi
694
723
  * 63 on `/`.
695
724
  */
696
725
  const x509Base64CharOf = (v: i32): i32 =>
697
- 65 +
698
- v +
699
- (x509RangeMask(v, 26, 63) & 6) -
700
- (x509RangeMask(v, 52, 63) & 75) -
701
- (x509RangeMask(v, 62, 63) & 15) +
702
- (x509RangeMask(v, 63, 63) & 3)
726
+ toI32(
727
+ 65 +
728
+ toU32(v) +
729
+ toU32(x509RangeMask(v, 26, 63) & 6) -
730
+ toU32(x509RangeMask(v, 52, 63) & 75) -
731
+ toU32(x509RangeMask(v, 62, 63) & 15) +
732
+ toU32(x509RangeMask(v, 63, 63) & 3)
733
+ )
703
734
 
704
735
  /** The sextet the byte `c` stands for in base64, or `-1` when it is not in the alphabet. */
705
736
  const x509Base64SextetOf = (c: i32): i32 => {
@@ -708,7 +739,12 @@ const x509Base64SextetOf = (c: i32): i32 => {
708
739
  const digit: i32 = x509RangeMask(c, 48, 57)
709
740
  const plus: i32 = x509RangeMask(c, 43, 43)
710
741
  const slash: i32 = x509RangeMask(c, 47, 47)
711
- const value: i32 = (upper & (c - 65)) | (lower & (c - 71)) | (digit & (c + 4)) | (plus & 62) | (slash & 63)
742
+ const value: i32 =
743
+ (upper & toI32(toU32(c) - 65)) |
744
+ (lower & toI32(toU32(c) - 71)) |
745
+ (digit & toI32(toU32(c) + 4)) |
746
+ (plus & 62) |
747
+ (slash & 63)
712
748
  return value | ~(upper | lower | digit | plus | slash)
713
749
  }
714
750
 
@@ -892,7 +928,7 @@ export const pemToDer = (pem: string, label: string): u8[][] | null => {
892
928
  * optional `[1]` public key, which must be the key's own. `null` otherwise,
893
929
  * or when the scalar is 0 or not below n.
894
930
  */
895
- const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[] | null => {
931
+ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): Secret<u8[]> | null => {
896
932
  const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
897
933
  if (seq === null || seq.end !== end) {
898
934
  return null
@@ -905,7 +941,6 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
905
941
  if (key === null || key.end - key.start !== 32) {
906
942
  return null
907
943
  }
908
- const priv: u8[] = x509DerSlice(der, key.start, key.end)
909
944
  let at: i32 = key.end
910
945
  let curve: boolean = false
911
946
  let stored: u8[] | null = null
@@ -935,12 +970,13 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
935
970
  if (at !== seq.end || (needCurve && !curve)) {
936
971
  return null
937
972
  }
938
- // Refuses 0 and n and above, and gives the point to hold `stored` against.
973
+ // The scalar is copied out of the DER only once the structure is known to
974
+ // be sound, straight into the `Secret` that owns it (X509-7). The check
975
+ // refuses 0 and n and above, and gives the point to hold `stored` against.
976
+ const priv: Secret<u8[]> = secret(x509DerSlice(der, key.start, key.end))
939
977
  const pub: u8[] | null = p256PublicKey(priv)
940
- if (pub === null) {
941
- return null
942
- }
943
- if (stored !== null && !x509BytesEqual(stored, pub)) {
978
+ if (pub === null || (stored !== null && !x509BytesEqual(stored, pub))) {
979
+ wipe(priv)
944
980
  return null
945
981
  }
946
982
  return priv
@@ -954,7 +990,7 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
954
990
  * optional `[1]` public key, which must be a BIT STRING of the key's own point
955
991
  * (X509-8 in docs/security/crypto-x509.md).
956
992
  */
957
- const x509Pkcs8Key = (der: u8[]): u8[] | null => {
993
+ const x509Pkcs8Key = (der: u8[]): Secret<u8[]> | null => {
958
994
  const total: i32 = toI32(der.length)
959
995
  const seq: X509DerElement | null = x509DerExpect(der, 0, total, X509_TAG_SEQUENCE)
960
996
  if (seq === null || seq.end !== total) {
@@ -1002,34 +1038,54 @@ const x509Pkcs8Key = (der: u8[]): u8[] | null => {
1002
1038
  if (at !== seq.end) {
1003
1039
  return null
1004
1040
  }
1005
- const priv: u8[] | null = x509Sec1Key(der, key.start, key.end, false)
1041
+ const priv: Secret<u8[]> | null = x509Sec1Key(der, key.start, key.end, false)
1006
1042
  if (priv === null || stored === null) {
1007
1043
  return priv
1008
1044
  }
1009
1045
  // The same rule as the SEC1 key's own `[1]`: the stored point is the key's.
1010
1046
  const pub: u8[] | null = p256PublicKey(priv)
1011
- return pub !== null && x509BytesEqual(stored, pub) ? priv : null
1047
+ if (pub === null || !x509BytesEqual(stored, pub)) {
1048
+ wipe(priv)
1049
+ return null
1050
+ }
1051
+ return priv
1052
+ }
1053
+
1054
+ /** Zero every block `pemToDer` decoded: a private key's DER is key material, refused or not. */
1055
+ const x509WipeBlocks = (blocks: u8[][] | null): void => {
1056
+ if (blocks === null) {
1057
+ return
1058
+ }
1059
+ for (const block of blocks) {
1060
+ wipe(block)
1061
+ }
1012
1062
  }
1013
1063
 
1014
1064
  /**
1015
- * The 32-byte P-256 private key in `pem`: one `EC PRIVATE KEY` block (SEC1,
1016
- * RFC 5915, which must name its curve) or one `PRIVATE KEY` block (unencrypted
1017
- * PKCS#8, RFC 5208 or RFC 5958), and not both. `null` for no such block or
1018
- * more than one, for any curve but P-256, for a scalar of 0 or n or more, for
1019
- * a stored public key that is not the key's own, and for anything malformed.
1020
- * An `ENCRYPTED PRIVATE KEY` is a different label, and answers `null`.
1065
+ * The 32-byte P-256 private key in `pem`, as the `Secret` it is: one `EC
1066
+ * PRIVATE KEY` block (SEC1, RFC 5915, which must name its curve) or one
1067
+ * `PRIVATE KEY` block (unencrypted PKCS#8, RFC 5208 or RFC 5958), and not both.
1068
+ * `null` for no such block or more than one, for any curve but P-256, for a
1069
+ * scalar of 0 or n or more, for a stored public key that is not the key's own,
1070
+ * and for anything malformed. An `ENCRYPTED PRIVATE KEY` is a different label,
1071
+ * and answers `null`. Every block decoded from `pem` is wiped before this
1072
+ * returns, whichever answer it gives; `pem` itself is the caller's string,
1073
+ * which nothing can wipe (X509-7 in docs/security/crypto-x509.md).
1021
1074
  */
1022
- export const x509ParseP256PrivateKey = (pem: string): u8[] | null => {
1075
+ export const x509ParseP256PrivateKey = (pem: string): Secret<u8[]> | null => {
1023
1076
  const sec1: u8[][] | null = pemToDer(pem, "EC PRIVATE KEY")
1024
1077
  const pkcs8: u8[][] | null = pemToDer(pem, "PRIVATE KEY")
1078
+ let key: Secret<u8[]> | null = null
1025
1079
  if (sec1 !== null && pkcs8 === null && toI32(sec1.length) === 1) {
1026
1080
  const der: u8[] = sec1[0]
1027
- return x509Sec1Key(der, 0, toI32(der.length), true)
1028
- }
1029
- if (pkcs8 !== null && sec1 === null && toI32(pkcs8.length) === 1) {
1030
- return x509Pkcs8Key(pkcs8[0])
1031
- }
1032
- return null
1081
+ key = x509Sec1Key(der, 0, toI32(der.length), true)
1082
+ } else if (pkcs8 !== null && sec1 === null && toI32(pkcs8.length) === 1) {
1083
+ key = x509Pkcs8Key(pkcs8[0])
1084
+ }
1085
+ // X509-7: the decoded DER holds the scalar too, whichever block it was.
1086
+ x509WipeBlocks(sec1)
1087
+ x509WipeBlocks(pkcs8)
1088
+ return key
1033
1089
  }
1034
1090
 
1035
1091
  // ---------------------------------------------------------------------------
@@ -1240,12 +1296,38 @@ export const x509VerifySignature = (cert: X509Certificate, issuer: X509Certifica
1240
1296
  return p256VerifySha256(issuer.publicKey, cert.tbs, rs)
1241
1297
  }
1242
1298
 
1299
+ /**
1300
+ * The minted certificate's `[3] extensions`, whole, in DER. They do not depend
1301
+ * on the key, the name or the time, so they are one constant block rather
1302
+ * than a tree of `x509DerWrap` calls (`tests/link/crypto_x509` pins each
1303
+ * octet, and `x509ParseCertificate` reads the result back):
1304
+ *
1305
+ * a3 20 [3] EXPLICIT, 32 octets
1306
+ * 30 1e Extensions SEQUENCE, 30 octets
1307
+ * 30 0c Extension (RFC 5280 §4.2.1.9)
1308
+ * 06 03 55 1d 13 basicConstraints, 2.5.29.19
1309
+ * 01 01 ff critical TRUE
1310
+ * 04 02 30 00 OCTET STRING { SEQUENCE {} }: cA FALSE is the
1311
+ * DEFAULT, so DER leaves it out (X.690 §11.5)
1312
+ * 30 0e Extension (RFC 5280 §4.2.1.3)
1313
+ * 06 03 55 1d 0f keyUsage, 2.5.29.15
1314
+ * 01 01 ff critical TRUE
1315
+ * 04 04 03 02 07 80 OCTET STRING { BIT STRING, 7 unused bits,
1316
+ * bit 0 digitalSignature }: DER drops a named
1317
+ * bit list's trailing zero bits (X.690 §11.2.2)
1318
+ */
1319
+ const x509MintExtensions = (): u8[] => [
1320
+ 0xa3, 0x20, 0x30, 0x1e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
1321
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
1322
+ ]
1323
+
1243
1324
  /**
1244
1325
  * A self-signed X.509 v3 certificate, in DER, for the P-256 key `priv`: subject
1245
1326
  * and issuer `CN=commonName`, valid from `notBeforeMs` (milliseconds since
1246
1327
  * 1970, rounded down to the second) for exactly `days` days, serial number
1247
- * `serial`, and signed with ecdsa-with-SHA256 — see the module comment for why
1248
- * it has no extensions. `null` when `days` is below 1 or above
1328
+ * `serial`, carrying `x509MintExtensions`' basicConstraints and keyUsage, and
1329
+ * signed with ecdsa-with-SHA256 — see the module comment for why Chromium
1330
+ * needs an extension at all. `null` when `days` is below 1 or above
1249
1331
  * `X509_MAX_DAYS`, `notBeforeMs` is negative or the validity would end after
1250
1332
  * 9999, `commonName` is empty, longer than 64 bytes, not UTF-8 or holds a NUL
1251
1333
  * (it is written as a UTF8String), `priv` is not a P-256 private key, or
@@ -1254,7 +1336,7 @@ export const x509VerifySignature = (cert: X509Certificate, issuer: X509Certifica
1254
1336
  * bit of 20 random bytes, or use fewer).
1255
1337
  */
1256
1338
  export const x509MintSelfSigned = (
1257
- priv: u8[],
1339
+ priv: Secret<u8[]>,
1258
1340
  commonName: string,
1259
1341
  notBeforeMs: i64,
1260
1342
  days: i32,
@@ -1312,6 +1394,7 @@ export const x509MintSelfSigned = (
1312
1394
  x509DerWrap(X509_TAG_SEQUENCE, [x509DerTimeAt(start), x509DerTimeAt(end)]),
1313
1395
  distinguished,
1314
1396
  spki,
1397
+ x509MintExtensions(),
1315
1398
  ])
1316
1399
  const rs: u8[] | null = p256SignSha256(priv, tbs)
1317
1400
  if (rs === null) {