@amritk/nish-x86_64-linux 0.15.0 → 0.17.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 (57) hide show
  1. package/INSTALL.md +58 -40
  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 +67 -10
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +72 -26
  8. package/runtime/runtime-net.c +72 -19
  9. package/runtime/runtime-os.c +127 -45
  10. package/runtime/runtime-parallel.c +5 -1
  11. package/runtime/runtime-wasm.c +26 -2
  12. package/runtime/runtime.c +86 -14
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +1 -3
  15. package/std/README.md +192 -36
  16. package/std/crypto/aes.ts +104 -21
  17. package/std/crypto/base64url.ts +43 -17
  18. package/std/crypto/chacha20poly1305.ts +52 -31
  19. package/std/crypto/ct.ts +14 -3
  20. package/std/crypto/hkdf.ts +385 -8
  21. package/std/crypto/hmac.ts +325 -9
  22. package/std/crypto/p256.ts +153 -27
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +15 -3
  25. package/std/crypto/sha512.ts +16 -2
  26. package/std/crypto/x25519.ts +90 -37
  27. package/std/crypto/x509.ts +265 -65
  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 +905 -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 +392 -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 +653 -0
  45. package/std/net/quic-packet.ts +1474 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1603 -0
  48. package/std/net/quic.ts +3214 -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 +410 -0
  54. package/std/net/tls.ts +1368 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
@@ -6,10 +6,15 @@
6
6
  *
7
7
  * import { x509MintSelfSigned, x509CertificateHash, derToPem } from "nish/crypto/x509";
8
8
  *
9
+ * // Both secrets come from the kernel's CSPRNG; a predictable key or serial is the caller's bug.
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)
9
13
  * const serial: u8[] = new Array<u8>(16);
10
14
  * crypto.getRandomValues(serial);
11
15
  * serial[0] = serial[0] & toU8(0x7f); // positive, so it stays 16 octets
12
16
  * const der: u8[] | null = x509MintSelfSigned(priv, "localhost", toI64(Date.now()), 14, serial);
17
+ * wipe(priv);
13
18
  * // x509CertificateHash(der) is what serverCertificateHashes names;
14
19
  * // derToPem(der, "CERTIFICATE") is the file a TLS stack loads.
15
20
  *
@@ -28,7 +33,8 @@
28
33
  * `null`, never a panic. The reader looks at what this module uses — the
29
34
  * version, serial, algorithms, names as opaque bytes, validity, the subject
30
35
  * public key and the signature — and checks the order of what follows the key
31
- * (`[1]`, `[2]`, `[3]`) without parsing extensions.
36
+ * (`[1]`, `[2]`, `[3]`) without parsing extensions; the mint writes two of its
37
+ * own (below).
32
38
  *
33
39
  * **PEM is RFC 7468's strict grammar**, with standard base64 (`+` and `/`,
34
40
  * padded with `=`), not `base64url`: 64 characters a line and a shorter last
@@ -42,14 +48,27 @@
42
48
  * requirements"): X.509 v3, an ECDSA P-256 key signed with ecdsa-with-SHA256,
43
49
  * and a validity period of at most fourteen days — `notAfter - notBefore`,
44
50
  * which is what Chromium compares, so `days` is 1 to 14. Subject and issuer
45
- * are the same single common name, as a UTF8String. It carries **no
46
- * extensions**: the specification requires none, a browser checking a pinned
47
- * hash does no path building for basicConstraints or keyUsage to inform, and
48
- * every extension would be one more field whose absence is part of the
49
- * contract a hash pins. OpenSSL verifies it as its own trust anchor all the
50
- * same (`tests/link/crypto_x509`'s header has the commands). The serial and
51
- * the time are parameters so the golden certificate is deterministic; a caller
52
- * passes `Date.now()` and random bytes.
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,
71
+ * because a call to the operating system would stop it compiling for wasm32.
53
72
  *
54
73
  * **Constant time.** A private key is the one secret that passes through here:
55
74
  * it is base64-decoded from PEM, lifted out of its DER, and handed to
@@ -65,10 +84,18 @@
65
84
  * public key stored beside it compares public values. None of this is under
66
85
  * the disassembly check; it is discipline.
67
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
+ *
68
94
  * Private names carry the `x509` prefix because a `std/` module's private
69
95
  * functions share the importing program's flat symbol namespace
70
96
  * (`docs/wp26-stdlib.md` §3e).
71
97
  */
98
+ import { Secret, secret, wipe } from "nish:secret"
72
99
  import { p256PublicKey, p256SignSha256, p256VerifySha256 } from "nish/crypto/p256"
73
100
  import { sha256 } from "nish/crypto/sha256"
74
101
 
@@ -288,12 +315,29 @@ const x509DerSmallInteger = (der: u8[], e: X509DerElement): i32 => {
288
315
  return toI32(der[e.start])
289
316
  }
290
317
 
318
+ /**
319
+ * Whether `e`'s contents are a DER BIT STRING's, whatever its tag (an IMPLICIT
320
+ * one keeps the grammar): an unused-bits count of 0 to 7, none without an
321
+ * octet to hold them, and those bits zero (X.690 §8.6.2 and §11.2.1).
322
+ */
323
+ const x509DerBitStringSound = (der: u8[], e: X509DerElement): boolean => {
324
+ const n: i32 = e.end - e.start
325
+ if (n < 1) {
326
+ return false
327
+ }
328
+ const unused: i32 = toI32(der[e.start])
329
+ if (unused > 7 || (n === 1 && unused !== 0)) {
330
+ return false
331
+ }
332
+ return (toI32(der[e.end - 1]) & ((1 << unused) - 1)) === 0
333
+ }
334
+
291
335
  /**
292
336
  * The octets of a BIT STRING that has no unused bits, or `null`. Keys and
293
337
  * signatures are whole octets, so the leading unused-bits count must be 0.
294
338
  */
295
339
  const x509DerBitStringOctets = (der: u8[], e: X509DerElement): u8[] | null => {
296
- if (e.tag !== X509_TAG_BIT_STRING || e.end - e.start < 1 || toI32(der[e.start]) !== 0) {
340
+ if (e.tag !== X509_TAG_BIT_STRING || !x509DerBitStringSound(der, e) || toI32(der[e.start]) !== 0) {
297
341
  return null
298
342
  }
299
343
  return x509DerSlice(der, e.start + 1, e.end)
@@ -468,9 +512,11 @@ const x509DerUnsignedInto = (der: u8[], e: X509DerElement, out: u8[], off: i32):
468
512
  /**
469
513
  * `r || s`, 32 octets each, out of an ECDSA-Sig-Value `SEQUENCE { INTEGER r,
470
514
  * INTEGER s }` (RFC 5480 §2.2 by way of RFC 3279 §2.2.3), or `null` unless it
471
- * is exactly one with both integers minimal, positive and below 2^256.
515
+ * is exactly one with both integers minimal, positive and below 2^256. This is
516
+ * the form a TLS `CertificateVerify` or an X.509 signature carries, and
517
+ * `nish/crypto/p256` takes the `r || s` it answers.
472
518
  */
473
- const x509DerSignatureRS = (sig: u8[]): u8[] | null => {
519
+ export const x509DerSignatureRS = (sig: u8[]): u8[] | null => {
474
520
  const total: i32 = toI32(sig.length)
475
521
  const seq: X509DerElement | null = x509DerExpect(sig, 0, total, X509_TAG_SEQUENCE)
476
522
  if (seq === null || seq.end !== total) {
@@ -605,6 +651,48 @@ const x509DerTimeAt = (seconds: i64): u8[] => {
605
651
  return x509DerTlv(utc ? X509_TAG_UTC_TIME : X509_TAG_GENERALIZED_TIME, text)
606
652
  }
607
653
 
654
+ /**
655
+ * Whether `bytes` is well-formed UTF-8 (RFC 3629 §4) with no NUL: no stray
656
+ * continuation octet, no truncated sequence, no overlong form, no surrogate and
657
+ * nothing past U+10FFFF. A string in the language is bytes and need not be
658
+ * UTF-8, and a UTF8String that is not UTF-8 is a malformed certificate; a NUL
659
+ * is refused because a name that reads differently to C is the old
660
+ * `localhost\0.example` confusion.
661
+ */
662
+ const x509IsUtf8Name = (bytes: u8[]): boolean => {
663
+ // Continuation octets still owed, and the range the next one must fall in:
664
+ // 80..BF, narrowed after E0, ED, F0 and F4 (Unicode 15 §3.9, Table 3-7).
665
+ let need: i32 = 0
666
+ let lo: i32 = 0x80
667
+ let hi: i32 = 0xbf
668
+ for (let k: i32 = 0; k < toI32(bytes.length); k++) {
669
+ const b: i32 = toI32(bytes[k])
670
+ if (need > 0) {
671
+ if (b < lo || b > hi) {
672
+ return false
673
+ }
674
+ need = need - 1
675
+ lo = 0x80
676
+ hi = 0xbf
677
+ } else if (b === 0) {
678
+ return false
679
+ } else if (b >= 0xc2 && b <= 0xdf) {
680
+ need = 1
681
+ } else if (b >= 0xe0 && b <= 0xef) {
682
+ need = 2
683
+ lo = b === 0xe0 ? 0xa0 : 0x80
684
+ hi = b === 0xed ? 0x9f : 0xbf
685
+ } else if (b >= 0xf0 && b <= 0xf4) {
686
+ need = 3
687
+ lo = b === 0xf0 ? 0x90 : 0x80
688
+ hi = b === 0xf4 ? 0x8f : 0xbf
689
+ } else if (b >= 0x80) {
690
+ return false
691
+ }
692
+ }
693
+ return need === 0
694
+ }
695
+
608
696
  /** The bytes of `text`, as the language holds a string. */
609
697
  const x509Bytes = (text: string): u8[] => {
610
698
  const out: u8[] = []
@@ -619,8 +707,15 @@ const x509Bytes = (text: string): u8[] => {
619
707
  // PEM (RFC 7468) and standard base64 (RFC 4648 §4)
620
708
  // ---------------------------------------------------------------------------
621
709
 
622
- /** All ones when `lo <= c <= hi`, else zero, without a branch; `c` is a byte or a sextet. */
623
- 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
624
719
 
625
720
  /**
626
721
  * The base64 character for the sextet `v`: `'A' + v`, stepped by the gap to
@@ -628,12 +723,14 @@ const x509RangeMask = (c: i32, lo: i32, hi: i32): i32 => ((lo - 1 - c) & (c - hi
628
723
  * 63 on `/`.
629
724
  */
630
725
  const x509Base64CharOf = (v: i32): i32 =>
631
- 65 +
632
- v +
633
- (x509RangeMask(v, 26, 63) & 6) -
634
- (x509RangeMask(v, 52, 63) & 75) -
635
- (x509RangeMask(v, 62, 63) & 15) +
636
- (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
+ )
637
734
 
638
735
  /** The sextet the byte `c` stands for in base64, or `-1` when it is not in the alphabet. */
639
736
  const x509Base64SextetOf = (c: i32): i32 => {
@@ -642,7 +739,12 @@ const x509Base64SextetOf = (c: i32): i32 => {
642
739
  const digit: i32 = x509RangeMask(c, 48, 57)
643
740
  const plus: i32 = x509RangeMask(c, 43, 43)
644
741
  const slash: i32 = x509RangeMask(c, 47, 47)
645
- 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)
646
748
  return value | ~(upper | lower | digit | plus | slash)
647
749
  }
648
750
 
@@ -790,11 +892,13 @@ export const pemToDer = (pem: string, label: string): u8[][] | null => {
790
892
  if (line !== `-----END ${inside}-----`) {
791
893
  return null
792
894
  }
895
+ // Every block is decoded, whatever its label: a malformed block beside the
896
+ // one asked for is refused, not skipped (docs/security/crypto-x509.md, X509-1).
897
+ const der: u8[] | null = x509Base64Decode(body.join(""))
898
+ if (der === null) {
899
+ return null
900
+ }
793
901
  if (inside === label) {
794
- const der: u8[] | null = x509Base64Decode(body.join(""))
795
- if (der === null) {
796
- return null
797
- }
798
902
  out.push(der)
799
903
  }
800
904
  inside = ""
@@ -824,7 +928,7 @@ export const pemToDer = (pem: string, label: string): u8[][] | null => {
824
928
  * optional `[1]` public key, which must be the key's own. `null` otherwise,
825
929
  * or when the scalar is 0 or not below n.
826
930
  */
827
- 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 => {
828
932
  const seq: X509DerElement | null = x509DerExpect(der, start, end, X509_TAG_SEQUENCE)
829
933
  if (seq === null || seq.end !== end) {
830
934
  return null
@@ -837,7 +941,6 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
837
941
  if (key === null || key.end - key.start !== 32) {
838
942
  return null
839
943
  }
840
- const priv: u8[] = x509DerSlice(der, key.start, key.end)
841
944
  let at: i32 = key.end
842
945
  let curve: boolean = false
843
946
  let stored: u8[] | null = null
@@ -867,12 +970,13 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
867
970
  if (at !== seq.end || (needCurve && !curve)) {
868
971
  return null
869
972
  }
870
- // 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))
871
977
  const pub: u8[] | null = p256PublicKey(priv)
872
- if (pub === null) {
873
- return null
874
- }
875
- if (stored !== null && !x509BytesEqual(stored, pub)) {
978
+ if (pub === null || (stored !== null && !x509BytesEqual(stored, pub))) {
979
+ wipe(priv)
876
980
  return null
877
981
  }
878
982
  return priv
@@ -882,10 +986,11 @@ const x509Sec1Key = (der: u8[], start: i32, end: i32, needCurve: boolean): u8[]
882
986
  * The scalar of a PKCS#8 PrivateKeyInfo (RFC 5208 §5) or OneAsymmetricKey
883
987
  * (RFC 5958 §2): version 0 or 1, the algorithm id-ecPublicKey with the
884
988
  * prime256v1 curve, and an ECPrivateKey inside the OCTET STRING, followed by
885
- * the optional `[0]` attributes and — in version 1 — `[1]` public key, which
886
- * are skipped.
989
+ * the optional `[0]` attributes, which are skipped, and — in version 1 — the
990
+ * optional `[1]` public key, which must be a BIT STRING of the key's own point
991
+ * (X509-8 in docs/security/crypto-x509.md).
887
992
  */
888
- const x509Pkcs8Key = (der: u8[]): u8[] | null => {
993
+ const x509Pkcs8Key = (der: u8[]): Secret<u8[]> | null => {
889
994
  const total: i32 = toI32(der.length)
890
995
  const seq: X509DerElement | null = x509DerExpect(der, 0, total, X509_TAG_SEQUENCE)
891
996
  if (seq === null || seq.end !== total) {
@@ -915,44 +1020,88 @@ const x509Pkcs8Key = (der: u8[]): u8[] | null => {
915
1020
  }
916
1021
  at = attributes.end
917
1022
  }
1023
+ let stored: u8[] | null = null
918
1024
  if (v === 1 && x509DerPeek(der, at, seq.end) === 0x81) {
919
- const pub: X509DerElement | null = x509DerRead(der, at, seq.end)
920
- if (pub === null) {
1025
+ const outer: X509DerElement | null = x509DerRead(der, at, seq.end)
1026
+ if (outer === null) {
921
1027
  return null
922
1028
  }
923
- at = pub.end
1029
+ // `[1] IMPLICIT BIT STRING`: the 0x81 replaces the BIT STRING's own tag,
1030
+ // so its contents are the unused-bits octet and then the point.
1031
+ const bits: X509DerElement = new X509DerElement(X509_TAG_BIT_STRING, outer.at, outer.start, outer.end)
1032
+ stored = x509DerBitStringOctets(der, bits)
1033
+ if (stored === null) {
1034
+ return null
1035
+ }
1036
+ at = outer.end
924
1037
  }
925
1038
  if (at !== seq.end) {
926
1039
  return null
927
1040
  }
928
- return x509Sec1Key(der, key.start, key.end, false)
1041
+ const priv: Secret<u8[]> | null = x509Sec1Key(der, key.start, key.end, false)
1042
+ if (priv === null || stored === null) {
1043
+ return priv
1044
+ }
1045
+ // The same rule as the SEC1 key's own `[1]`: the stored point is the key's.
1046
+ const pub: u8[] | null = p256PublicKey(priv)
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
+ }
929
1062
  }
930
1063
 
931
1064
  /**
932
- * The 32-byte P-256 private key in `pem`: one `EC PRIVATE KEY` block (SEC1,
933
- * RFC 5915, which must name its curve) or one `PRIVATE KEY` block (unencrypted
934
- * PKCS#8, RFC 5208 or RFC 5958), and not both. `null` for no such block or
935
- * more than one, for any curve but P-256, for a scalar of 0 or n or more, for
936
- * a stored public key that is not the key's own, and for anything malformed.
937
- * 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).
938
1074
  */
939
- export const x509ParseP256PrivateKey = (pem: string): u8[] | null => {
1075
+ export const x509ParseP256PrivateKey = (pem: string): Secret<u8[]> | null => {
940
1076
  const sec1: u8[][] | null = pemToDer(pem, "EC PRIVATE KEY")
941
1077
  const pkcs8: u8[][] | null = pemToDer(pem, "PRIVATE KEY")
1078
+ let key: Secret<u8[]> | null = null
942
1079
  if (sec1 !== null && pkcs8 === null && toI32(sec1.length) === 1) {
943
1080
  const der: u8[] = sec1[0]
944
- return x509Sec1Key(der, 0, toI32(der.length), true)
945
- }
946
- if (pkcs8 !== null && sec1 === null && toI32(pkcs8.length) === 1) {
947
- return x509Pkcs8Key(pkcs8[0])
948
- }
949
- 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
950
1089
  }
951
1090
 
952
1091
  // ---------------------------------------------------------------------------
953
1092
  // Certificates (RFC 5280 §4.1)
954
1093
  // ---------------------------------------------------------------------------
955
1094
 
1095
+ /**
1096
+ * Whether a `[3]` extensions wrapper holds one non-empty SEQUENCE and nothing
1097
+ * after it (RFC 5280 §4.1's `SIZE (1..MAX)`). What an extension says is not
1098
+ * read.
1099
+ */
1100
+ const x509DerExtensionsSound = (der: u8[], e: X509DerElement): boolean => {
1101
+ const seq: X509DerElement | null = x509DerExpect(der, e.start, e.end, X509_TAG_SEQUENCE)
1102
+ return seq !== null && seq.end === e.end && seq.end > seq.start
1103
+ }
1104
+
956
1105
  /**
957
1106
  * `der` read as one Certificate (RFC 5280 §4.1), or `null` unless it is
958
1107
  * exactly one in DER: `SEQUENCE { tbsCertificate, signatureAlgorithm,
@@ -961,8 +1110,16 @@ export const x509ParseP256PrivateKey = (pem: string): u8[] | null => {
961
1110
  * to the outer one byte for byte (§4.1.1.2), issuer and subject Names, a
962
1111
  * Validity of two valid times, a SubjectPublicKeyInfo whose key is whole
963
1112
  * octets, then only the optional `[1]` and `[2]` unique identifiers (v2 and
964
- * v3) and `[3]` extensions (v3), in that order. Any algorithm and key type is
965
- * read; `p256` and `ecdsaSha256` say whether they are this module's.
1113
+ * v3) and `[3]` extensions (v3), in that order, each well formed on the
1114
+ * outside. Any algorithm and key type is read; `p256` and `ecdsaSha256` say
1115
+ * whether they are this module's.
1116
+ *
1117
+ * **A parser, not a validator.** It checks that the bytes are one certificate
1118
+ * and nothing else. It does not check the signature (`x509VerifySignature`
1119
+ * does), that `notBefore` is not after `notAfter` or that either is near any
1120
+ * clock, and it does not read extensions — basicConstraints, keyUsage,
1121
+ * subjectAltName and the critical flag RFC 5280 §4.2 obliges a verifier to
1122
+ * honour are neither checked nor exposed.
966
1123
  */
967
1124
  export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
968
1125
  const total: i32 = toI32(der.length)
@@ -1051,6 +1208,7 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
1051
1208
  at = spki.end
1052
1209
  // [1] issuerUniqueID and [2] subjectUniqueID are IMPLICIT BIT STRINGs (so
1053
1210
  // primitive, 0x81 and 0x82) from v2; [3] extensions is explicit (0xa3), v3.
1211
+ // Each is held to its outer shape; what it says is not read.
1054
1212
  const trailing: i32[] = [0x81, 0x82, 0xa3]
1055
1213
  for (let i: i32 = 0; i < 3; i++) {
1056
1214
  if (x509DerPeek(der, at, tbs.end) === trailing[i]) {
@@ -1058,6 +1216,9 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
1058
1216
  if (e === null || version < (i === 2 ? 2 : 1)) {
1059
1217
  return null
1060
1218
  }
1219
+ if (i === 2 ? !x509DerExtensionsSound(der, e) : !x509DerBitStringSound(der, e)) {
1220
+ return null
1221
+ }
1061
1222
  at = e.end
1062
1223
  }
1063
1224
  }
@@ -1086,9 +1247,17 @@ export const x509ParseCertificate = (der: u8[]): X509Certificate | null => {
1086
1247
  }
1087
1248
 
1088
1249
  /**
1089
- * Every `CERTIFICATE` block in `pem`, in order — a server's chain, leaf first
1090
- * as TLS sends it — or `null` when there is none, or when any block is not
1091
- * PEM `pemToDer` accepts or a certificate `x509ParseCertificate` accepts.
1250
+ * Every `CERTIFICATE` block in `pem`, in the order they appear — for a
1251
+ * server's chain file, leaf first as TLS sends it — or `null` when there is
1252
+ * none, or when any block is not PEM `pemToDer` accepts or a certificate
1253
+ * `x509ParseCertificate` accepts.
1254
+ *
1255
+ * **It reads a chain; it does not validate one.** Nothing relates one
1256
+ * certificate to the next: not the order, not that each is issued by the one
1257
+ * after it (issuer against subject, or signature), not validity against a
1258
+ * clock, basicConstraints, keyUsage, name constraints or a trust anchor. RFC
1259
+ * 5280 §6's path validation is not in this module, and a chain it answers is
1260
+ * no evidence that anyone vouches for the leaf.
1092
1261
  */
1093
1262
  export const x509ParseChain = (pem: string): X509Certificate[] | null => {
1094
1263
  const blocks: u8[][] | null = pemToDer(pem, "CERTIFICATE")
@@ -1110,8 +1279,11 @@ export const x509ParseChain = (pem: string): X509Certificate[] | null => {
1110
1279
  * Whether `cert`'s signature is a valid ecdsa-with-SHA256 signature of its TBS
1111
1280
  * bytes under `issuer`'s P-256 key — a self-signed certificate passes itself
1112
1281
  * as both. `false` for any other algorithm or key, and for a signature that is
1113
- * not a DER ECDSA-Sig-Value. Only the signature: names, times and extensions
1114
- * are the caller's policy.
1282
+ * not a DER ECDSA-Sig-Value. Only the signature: that `cert.issuer` is
1283
+ * `issuer.subject` and that the times hold are fields the caller compares, and
1284
+ * whether `issuer` may issue at all (basicConstraints, keyUsage) is in
1285
+ * extensions this module does not read, so a `true` here is not path
1286
+ * validation.
1115
1287
  */
1116
1288
  export const x509VerifySignature = (cert: X509Certificate, issuer: X509Certificate): boolean => {
1117
1289
  if (!cert.ecdsaSha256 || !issuer.p256) {
@@ -1124,20 +1296,47 @@ export const x509VerifySignature = (cert: X509Certificate, issuer: X509Certifica
1124
1296
  return p256VerifySha256(issuer.publicKey, cert.tbs, rs)
1125
1297
  }
1126
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
+
1127
1324
  /**
1128
1325
  * A self-signed X.509 v3 certificate, in DER, for the P-256 key `priv`: subject
1129
1326
  * and issuer `CN=commonName`, valid from `notBeforeMs` (milliseconds since
1130
1327
  * 1970, rounded down to the second) for exactly `days` days, serial number
1131
- * `serial`, and signed with ecdsa-with-SHA256 — see the module comment for why
1132
- * 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
1133
1331
  * `X509_MAX_DAYS`, `notBeforeMs` is negative or the validity would end after
1134
- * 9999, `commonName` is empty or longer than 64 bytes, `priv` is not a P-256
1135
- * private key, or `serial` — big-endian, leading zeros ignored — is zero or
1332
+ * 9999, `commonName` is empty, longer than 64 bytes, not UTF-8 or holds a NUL
1333
+ * (it is written as a UTF8String), `priv` is not a P-256 private key, or
1334
+ * `serial` — big-endian, leading zeros ignored — is zero or
1136
1335
  * needs more than `X509_MAX_SERIAL` octets as a positive INTEGER (clear the top
1137
1336
  * bit of 20 random bytes, or use fewer).
1138
1337
  */
1139
1338
  export const x509MintSelfSigned = (
1140
- priv: u8[],
1339
+ priv: Secret<u8[]>,
1141
1340
  commonName: string,
1142
1341
  notBeforeMs: i64,
1143
1342
  days: i32,
@@ -1154,7 +1353,7 @@ export const x509MintSelfSigned = (
1154
1353
  }
1155
1354
  const name: u8[] = x509Bytes(commonName)
1156
1355
  const nameLength: i32 = toI32(name.length)
1157
- if (nameLength < 1 || nameLength > X509_MAX_COMMON_NAME) {
1356
+ if (nameLength < 1 || nameLength > X509_MAX_COMMON_NAME || !x509IsUtf8Name(name)) {
1158
1357
  return null
1159
1358
  }
1160
1359
  const serialDer: u8[] = x509DerUnsignedInteger(serial)
@@ -1195,6 +1394,7 @@ export const x509MintSelfSigned = (
1195
1394
  x509DerWrap(X509_TAG_SEQUENCE, [x509DerTimeAt(start), x509DerTimeAt(end)]),
1196
1395
  distinguished,
1197
1396
  spki,
1397
+ x509MintExtensions(),
1198
1398
  ])
1199
1399
  const rs: u8[] | null = p256SignSha256(priv, tbs)
1200
1400
  if (rs === null) {