@amritk/nish-x86_64-linux 0.18.0 → 0.21.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 (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
@@ -64,7 +64,14 @@
64
64
  * `exposeWith`, and the clamped copy and every ladder element are wiped before
65
65
  * it returns (ECC-2, docs/security/crypto-ecc.md).
66
66
  *
67
- * Private names carry the `f25519` / `x25519` prefix because a `std/` module's
67
+ * **The field is shared with Ed25519.** `nish/crypto/ed25519` works over the
68
+ * same GF(p), so the `f25519` functions are exported for it to import rather
69
+ * than written twice; they are the module's internals, with the bounds each
70
+ * one states, not an API for programs. Ed25519's square root needs
71
+ * `z^(2^252 - 3)`, which shares its first 250 bits with the inversion's
72
+ * exponent, so that part of the chain is `f25519Pow250`, which both call.
73
+ *
74
+ * Names carry the `f25519` / `x25519` prefix because a `std/` module's
68
75
  * private functions share the importing program's flat symbol namespace
69
76
  * (`docs/wp26-stdlib.md` §3e).
70
77
  */
@@ -84,9 +91,9 @@ const X25519_A24: i64 = 121665
84
91
  * because signed arithmetic is checked for an overflow, and a check is a
85
92
  * branch on a secret limb. Each is one instruction once LLVM inlines it.
86
93
  */
87
- const f25519Add64 = (a: i64, b: i64): i64 => toI64(toU64(a) + toU64(b))
88
- const f25519Sub64 = (a: i64, b: i64): i64 => toI64(toU64(a) - toU64(b))
89
- const f25519Mul64 = (a: i64, b: i64): i64 => toI64(toU64(a) * toU64(b))
94
+ export const f25519Add64 = (a: i64, b: i64): i64 => toI64(toU64(a) + toU64(b))
95
+ export const f25519Sub64 = (a: i64, b: i64): i64 => toI64(toU64(a) - toU64(b))
96
+ export const f25519Mul64 = (a: i64, b: i64): i64 => toI64(toU64(a) * toU64(b))
90
97
 
91
98
  /** The width of limb `i`: 26 bits for an even limb, 25 for an odd one. */
92
99
  const f25519Width = (i: i32): i64 => {
@@ -98,20 +105,20 @@ const f25519Width = (i: i32): i64 => {
98
105
  const f25519LimbMask = (i: i32): i64 => f25519Sub64(toI64(1) << f25519Width(i), toI64(1))
99
106
 
100
107
  /** A fresh field element, zero. */
101
- const f25519Zero = (): i64[] => {
108
+ export const f25519Zero = (): i64[] => {
102
109
  const f: i64[] = new Array<i64>(10)
103
110
  return f
104
111
  }
105
112
 
106
113
  /** A fresh field element holding the small constant `n`. */
107
- const f25519Small = (n: i64): i64[] => {
114
+ export const f25519Small = (n: i64): i64[] => {
108
115
  const f: i64[] = f25519Zero()
109
116
  f[0] = n
110
117
  return f
111
118
  }
112
119
 
113
120
  /** `out = f`, limb by limb. */
114
- const f25519Copy = (out: i64[], f: i64[]): void => {
121
+ export const f25519Copy = (out: i64[], f: i64[]): void => {
115
122
  if (toI32(out.length) < 10 || toI32(f.length) < 10) {
116
123
  return
117
124
  }
@@ -152,7 +159,7 @@ const f25519CarryChain = (h: i64[]): void => {
152
159
  * `h & (2^width - 1)`, which is the matching non-negative remainder for a
153
160
  * negative limb too, without shifting a negative number left.
154
161
  */
155
- const f25519Carry = (h: i64[]): void => {
162
+ export const f25519Carry = (h: i64[]): void => {
156
163
  if (toI32(h.length) < 10) {
157
164
  return
158
165
  }
@@ -169,7 +176,7 @@ const f25519Carry = (h: i64[]): void => {
169
176
  * `out = f + g`, not carried. With both reduced (limbs below 2^26 in
170
177
  * magnitude) the sum's limbs are below 2^27, which `f25519Mul` accepts.
171
178
  */
172
- const f25519Add = (out: i64[], f: i64[], g: i64[]): void => {
179
+ export const f25519Add = (out: i64[], f: i64[], g: i64[]): void => {
173
180
  if (toI32(out.length) < 10 || toI32(f.length) < 10 || toI32(g.length) < 10) {
174
181
  return
175
182
  }
@@ -183,7 +190,7 @@ const f25519Add = (out: i64[], f: i64[], g: i64[]): void => {
183
190
  * both reduced the difference's limbs are below 2^27 in magnitude, which
184
191
  * `f25519Mul` accepts.
185
192
  */
186
- const f25519Sub = (out: i64[], f: i64[], g: i64[]): void => {
193
+ export const f25519Sub = (out: i64[], f: i64[], g: i64[]): void => {
187
194
  if (toI32(out.length) < 10 || toI32(f.length) < 10 || toI32(g.length) < 10) {
188
195
  return
189
196
  }
@@ -208,7 +215,7 @@ const f25519Sub = (out: i64[], f: i64[], g: i64[]): void => {
208
215
  * sums added again, say — breaks this bound, which is why every caller carries
209
216
  * or multiplies before it adds a second time.
210
217
  */
211
- const f25519Mul = (out: i64[], f: i64[], g: i64[]): void => {
218
+ export const f25519Mul = (out: i64[], f: i64[], g: i64[]): void => {
212
219
  if (toI32(f.length) < 10 || toI32(g.length) < 10) {
213
220
  return
214
221
  }
@@ -240,12 +247,12 @@ const f25519Mul = (out: i64[], f: i64[], g: i64[]): void => {
240
247
  }
241
248
 
242
249
  /** `out = f * f`, reduced; the same bounds as `f25519Mul`. */
243
- const f25519Square = (out: i64[], f: i64[]): void => {
250
+ export const f25519Square = (out: i64[], f: i64[]): void => {
244
251
  f25519Mul(out, f, f)
245
252
  }
246
253
 
247
254
  /** `out = f` squared `n` times in a row (`n` at least 1), reduced. */
248
- const f25519SquareTimes = (out: i64[], f: i64[], n: i32): void => {
255
+ export const f25519SquareTimes = (out: i64[], f: i64[], n: i32): void => {
249
256
  f25519Square(out, f)
250
257
  for (let i: i32 = 1; i < n; i++) {
251
258
  f25519Square(out, out)
@@ -268,25 +275,20 @@ const f25519MulA24 = (out: i64[], f: i64[]): void => {
268
275
  }
269
276
 
270
277
  /**
271
- * `out = z^(p - 2)`, which is `1 / z` by Fermat's little theorem, and zero for
272
- * a zero `z` — what RFC 7748 §5 asks of the ladder's last step.
273
- *
274
- * p - 2 = 2^255 - 21 = (2^250 - 1) * 2^5 + 11, so the exponent is 250 ones
275
- * followed by `01011`. The chain builds `z^(2^n - 1)` for n = 2, 4, 5, 10, 20,
276
- * 40, 50, 100, 200 and 250 from the rule
277
- * `z^(2^(a+b) - 1) = (z^(2^a - 1))^(2^b) * z^(2^b - 1)`, then squares five
278
- * times and multiplies by `z^11`: 254 squarings and 11 multiplications, the
279
- * same for every `z`.
278
+ * `out = z^(2^250 - 1)` and `z11 = z^11`: the part of the exponent chain that
279
+ * `f25519Invert` and Ed25519's square root (`ed25519Pow22523`,
280
+ * `nish/crypto/ed25519`) share. The chain builds `z^(2^n - 1)` for n = 2, 4,
281
+ * 5, 10, 20, 40, 50, 100, 200 and 250 from the rule
282
+ * `z^(2^(a+b) - 1) = (z^(2^a - 1))^(2^b) * z^(2^b - 1)`: 251 squarings and 11
283
+ * multiplications, the same for every `z`. `out` and `z11` may not be `z`.
280
284
  */
281
- const f25519Invert = (out: i64[], z: i64[]): void => {
285
+ export const f25519Pow250 = (out: i64[], z11: i64[], z: i64[]): void => {
282
286
  const z2: i64[] = f25519Zero()
283
- const z11: i64[] = f25519Zero()
284
287
  const e2: i64[] = f25519Zero()
285
288
  const e5: i64[] = f25519Zero()
286
289
  const e10: i64[] = f25519Zero()
287
290
  const e50: i64[] = f25519Zero()
288
291
  const t: i64[] = f25519Zero()
289
- const acc: i64[] = f25519Zero()
290
292
 
291
293
  f25519Square(z2, z)
292
294
  f25519Mul(e2, z2, z) // z^3 = z^(2^2 - 1)
@@ -300,17 +302,33 @@ const f25519Invert = (out: i64[], z: i64[]): void => {
300
302
  f25519SquareTimes(t, e5, 5)
301
303
  f25519Mul(e10, t, e5) // 2^10 - 1
302
304
  f25519SquareTimes(t, e10, 10)
303
- f25519Mul(acc, t, e10) // 2^20 - 1
304
- f25519SquareTimes(t, acc, 20)
305
- f25519Mul(acc, t, acc) // 2^40 - 1
306
- f25519SquareTimes(t, acc, 10)
305
+ f25519Mul(out, t, e10) // 2^20 - 1
306
+ f25519SquareTimes(t, out, 20)
307
+ f25519Mul(out, t, out) // 2^40 - 1
308
+ f25519SquareTimes(t, out, 10)
307
309
  f25519Mul(e50, t, e10) // 2^50 - 1
308
310
  f25519SquareTimes(t, e50, 50)
309
- f25519Mul(acc, t, e50) // 2^100 - 1
310
- f25519SquareTimes(t, acc, 100)
311
- f25519Mul(acc, t, acc) // 2^200 - 1
312
- f25519SquareTimes(t, acc, 50)
313
- f25519Mul(acc, t, e50) // 2^250 - 1
311
+ f25519Mul(out, t, e50) // 2^100 - 1
312
+ f25519SquareTimes(t, out, 100)
313
+ f25519Mul(out, t, out) // 2^200 - 1
314
+ f25519SquareTimes(t, out, 50)
315
+ f25519Mul(out, t, e50) // 2^250 - 1
316
+ }
317
+
318
+ /**
319
+ * `out = z^(p - 2)`, which is `1 / z` by Fermat's little theorem, and zero for
320
+ * a zero `z` — what RFC 7748 §5 asks of the ladder's last step.
321
+ *
322
+ * p - 2 = 2^255 - 21 = (2^250 - 1) * 2^5 + 11, so the exponent is 250 ones
323
+ * followed by `01011`: `f25519Pow250`, then five squarings and a
324
+ * multiplication by `z^11` — 256 squarings and 12 multiplications, the same
325
+ * for every `z`.
326
+ */
327
+ export const f25519Invert = (out: i64[], z: i64[]): void => {
328
+ const z11: i64[] = f25519Zero()
329
+ const acc: i64[] = f25519Zero()
330
+ const t: i64[] = f25519Zero()
331
+ f25519Pow250(acc, z11, z)
314
332
  f25519SquareTimes(t, acc, 5)
315
333
  f25519Mul(out, t, z11) // 2^255 - 21
316
334
  }
@@ -320,7 +338,7 @@ const f25519Invert = (out: i64[], z: i64[]): void => {
320
338
  * same instructions either way: the mask is all ones or all zeros, and the
321
339
  * swap is the masked xor of the two.
322
340
  */
323
- const f25519Swap = (f: i64[], g: i64[], bit: i64): void => {
341
+ export const f25519Swap = (f: i64[], g: i64[], bit: i64): void => {
324
342
  if (toI32(f.length) < 10 || toI32(g.length) < 10) {
325
343
  return
326
344
  }
@@ -341,7 +359,7 @@ const f25519Swap = (f: i64[], g: i64[], bit: i64): void => {
341
359
  * `[p, 2^255)` is neither refused nor adjusted: §5 has it processed as its
342
360
  * residue, and the arithmetic does that by itself.
343
361
  */
344
- const f25519Decode = (bytes: u8[]): i64[] => {
362
+ export const f25519Decode = (bytes: u8[]): i64[] => {
345
363
  const f: i64[] = new Array<i64>(10)
346
364
  if (toI32(bytes.length) < 32) {
347
365
  return f
@@ -382,7 +400,7 @@ const f25519Decode = (bytes: u8[]): i64[] => {
382
400
  * when the value is at least p — so the element adds `19 * q` and drops bit
383
401
  * 255, which is subtracting `p * q`.
384
402
  */
385
- const f25519Encode = (f: i64[]): u8[] => {
403
+ export const f25519Encode = (f: i64[]): u8[] => {
386
404
  const h: i64[] = new Array<i64>(10)
387
405
  f25519Copy(h, f)
388
406
  f25519Carry(h)
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `nish/crypto/x509-random` — the private key and the serial number
3
+ * `nish/crypto/x509`'s `x509MintSelfSigned` needs, drawn from the operating
4
+ * system's CSPRNG, and the mint that draws its own serial.
5
+ *
6
+ * import { Secret, wipe } from "nish:secret";
7
+ * import { x509DrawP256Key, x509MintSelfSignedDrawn } from "nish/crypto/x509-random";
8
+ *
9
+ * const priv: Secret<u8[]> = x509DrawP256Key();
10
+ * const der: u8[] | null = x509MintSelfSignedDrawn(priv, "localhost", toI64(Date.now()), 14);
11
+ * // ... present `der` and sign the handshake with `priv` ...
12
+ * wipe(priv);
13
+ *
14
+ * **Native only.** Every function here reaches `crypto.getRandomValues`, so a
15
+ * program that imports this module does not compile for wasm32, which has no
16
+ * entropy to reach. That is why it is a module of its own: `nish/crypto/x509`
17
+ * takes the key and the serial as arguments and imports nothing from here, so
18
+ * a wasm32 program can still parse, verify and mint with bytes it was handed
19
+ * (X509-6, docs/security/crypto-x509.md).
20
+ */
21
+ import { Secret, secret, wipe } from "nish:secret"
22
+ import { P256_SCALAR_SIZE, p256PublicKey } from "nish/crypto/p256"
23
+ import { X509_MAX_SERIAL, x509MintSelfSigned } from "nish/crypto/x509"
24
+
25
+ /**
26
+ * A fresh P-256 private key: `P256_SCALAR_SIZE` bytes from
27
+ * `crypto.getRandomValues`, drawn again until they hold a scalar in [1, n),
28
+ * which `p256PublicKey` is the test of. Rejection rather than reduction mod n,
29
+ * so every key is equally likely; a draw is refused with odds below 2^-32. A
30
+ * refused draw is wiped before the next, and the caller wipes the one
31
+ * returned.
32
+ */
33
+ export const x509DrawP256Key = (): Secret<u8[]> => {
34
+ while (true) {
35
+ const drawn: u8[] = new Array<u8>(P256_SCALAR_SIZE)
36
+ crypto.getRandomValues(drawn)
37
+ const key: Secret<u8[]> = secret(drawn)
38
+ if (p256PublicKey(key) !== null) {
39
+ return key
40
+ }
41
+ wipe(key)
42
+ }
43
+ }
44
+
45
+ /**
46
+ * A fresh serial number for `x509MintSelfSigned`: `X509_MAX_SERIAL` (20) bytes
47
+ * from `crypto.getRandomValues` with the first one's top two bits set to `01`,
48
+ * so the INTEGER is positive, never zero, and always twenty octets long — 158
49
+ * random bits, past the 64 the CA/Browser Forum asks of a serial. A serial is
50
+ * public, written into the certificate, so it is a plain array.
51
+ */
52
+ export const x509DrawSerial = (): u8[] => {
53
+ const serial: u8[] = new Array<u8>(X509_MAX_SERIAL)
54
+ crypto.getRandomValues(serial)
55
+ serial[0] = (serial[0] & toU8(0x3f)) | toU8(0x40)
56
+ return serial
57
+ }
58
+
59
+ /**
60
+ * `x509MintSelfSigned(priv, commonName, notBeforeMs, days, x509DrawSerial())`:
61
+ * the self-signed certificate with a serial drawn here, `null` for everything
62
+ * the mint answers `null` for. The key stays the caller's, since the server
63
+ * that presents the certificate signs its handshakes with it; draw one with
64
+ * `x509DrawP256Key`.
65
+ */
66
+ export const x509MintSelfSignedDrawn = (
67
+ priv: Secret<u8[]>,
68
+ commonName: string,
69
+ notBeforeMs: i64,
70
+ days: i32
71
+ ): u8[] | null => x509MintSelfSigned(priv, commonName, notBeforeMs, days, x509DrawSerial())
@@ -5,15 +5,13 @@
5
5
  * pins by its SHA-256.
6
6
  *
7
7
  * import { x509MintSelfSigned, x509CertificateHash, derToPem } from "nish/crypto/x509";
8
+ * // Native only: a program that imports this does not compile for wasm32, which has no entropy.
9
+ * import { x509DrawP256Key, x509DrawSerial } from "nish/crypto/x509-random";
8
10
  *
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)
13
- * const serial: u8[] = new Array<u8>(16);
14
- * crypto.getRandomValues(serial);
15
- * serial[0] = serial[0] & toU8(0x7f); // positive, so it stays 16 octets
16
- * const der: u8[] | null = x509MintSelfSigned(priv, "localhost", toI64(Date.now()), 14, serial);
11
+ * // Both come from the kernel's CSPRNG; a predictable key or serial is the caller's bug.
12
+ * // A wasm32 program is handed them instead, and passes them in as they are passed here.
13
+ * const priv: Secret<u8[]> = x509DrawP256Key();
14
+ * const der: u8[] | null = x509MintSelfSigned(priv, "localhost", toI64(Date.now()), 14, x509DrawSerial());
17
15
  * wipe(priv);
18
16
  * // x509CertificateHash(der) is what serverCertificateHashes names;
19
17
  * // derToPem(der, "CERTIFICATE") is the file a TLS stack loads.
@@ -68,14 +66,16 @@
68
66
  * parameters so the golden certificate is deterministic; a caller passes
69
67
  * `Date.now()` and, for the key and the serial, bytes from
70
68
  * `crypto.getRandomValues`. This module does not draw them itself,
71
- * because a call to the operating system would stop it compiling for wasm32.
69
+ * because a call to the operating system would stop it compiling for wasm32;
70
+ * `nish/crypto/x509-random`, which this module does not import, draws both
71
+ * for a native program (X509-6).
72
72
  *
73
73
  * **Constant time.** A private key is the one secret that passes through here:
74
74
  * it is base64-decoded from PEM, lifted out of its DER, and handed to
75
- * `nish/crypto/p256`. Base64 maps a character to its sextet and back by
76
- * arithmetic on range masks, as `nish/crypto/base64url` does, and folds a bad
77
- * character into one word read after the whole text, so neither a branch nor a
78
- * table index depends on the key's bytes. PEM's framing is found by comparing
75
+ * `nish/crypto/p256`. Base64 is `nish/crypto/base64url`'s `base64StdPadDecode`,
76
+ * which maps a character to its sextet by arithmetic on range masks and folds a
77
+ * bad character into one word read after the whole text, so neither a branch
78
+ * nor a table index depends on the key's bytes. PEM's framing is found by comparing
79
79
  * the start of each line with the fixed `-----BEGIN ` and `-----END `, which no
80
80
  * base64 character matches, so those comparisons end on a line's first byte
81
81
  * whatever the key is. The DER reader branches on tags and
@@ -96,6 +96,7 @@
96
96
  * (`docs/wp26-stdlib.md` §3e).
97
97
  */
98
98
  import { Secret, secret, wipe } from "nish:secret"
99
+ import { base64StdPadDecode, base64StdPadEncode } from "nish/crypto/base64url"
99
100
  import { p256PublicKey, p256SignSha256, p256VerifySha256 } from "nish/crypto/p256"
100
101
  import { sha256 } from "nish/crypto/sha256"
101
102
 
@@ -704,124 +705,9 @@ const x509Bytes = (text: string): u8[] => {
704
705
  }
705
706
 
706
707
  // ---------------------------------------------------------------------------
707
- // PEM (RFC 7468) and standard base64 (RFC 4648 §4)
708
+ // PEM (RFC 7468), over `nish/crypto/base64url`'s padded base64
708
709
  // ---------------------------------------------------------------------------
709
710
 
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
719
-
720
- /**
721
- * The base64 character for the sextet `v`: `'A' + v`, stepped by the gap to
722
- * each later range it has reached — 26 lands on `a`, 52 on `0`, 62 on `+` and
723
- * 63 on `/`.
724
- */
725
- const x509Base64CharOf = (v: i32): i32 =>
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
- )
734
-
735
- /** The sextet the byte `c` stands for in base64, or `-1` when it is not in the alphabet. */
736
- const x509Base64SextetOf = (c: i32): i32 => {
737
- const upper: i32 = x509RangeMask(c, 65, 90)
738
- const lower: i32 = x509RangeMask(c, 97, 122)
739
- const digit: i32 = x509RangeMask(c, 48, 57)
740
- const plus: i32 = x509RangeMask(c, 43, 43)
741
- const slash: i32 = x509RangeMask(c, 47, 47)
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)
748
- return value | ~(upper | lower | digit | plus | slash)
749
- }
750
-
751
- /** `data` in padded base64, as one line. */
752
- const x509Base64Encode = (data: u8[]): string => {
753
- const parts: string[] = []
754
- let acc: i32 = 0
755
- let bits: i32 = 0
756
- for (let k: i32 = 0; k < toI32(data.length); k++) {
757
- acc = ((acc << 8) | toI32(data[k])) & 0xfff
758
- bits = bits + 8
759
- while (bits >= 6) {
760
- bits = bits - 6
761
- parts.push(String.fromCharCode(x509Base64CharOf((acc >> bits) & 63)))
762
- }
763
- }
764
- if (bits > 0) {
765
- parts.push(String.fromCharCode(x509Base64CharOf((acc << (6 - bits)) & 63)))
766
- }
767
- // One `=` for a final group of two bytes, two for one byte.
768
- const tail: i32 = toI32(data.length) % 3
769
- if (tail > 0) {
770
- parts.push(tail === 1 ? "==" : "=")
771
- }
772
- return parts.join("")
773
- }
774
-
775
- /**
776
- * The bytes of padded base64 `text`, or `null` unless it is the one canonical
777
- * spelling of them: a whole number of four-character groups, `=` only as the
778
- * last one or two characters, every other character in the alphabet, and the
779
- * bits the padding leaves over all zero (RFC 4648 §3.5). Where the padding is
780
- * depends on the length alone; the characters themselves are read by masks and
781
- * judged once, after the last.
782
- */
783
- const x509Base64Decode = (text: string): u8[] | null => {
784
- const n: i32 = toI32(text.length)
785
- if (n === 0 || (n & 3) !== 0) {
786
- return null
787
- }
788
- let pad: i32 = 0
789
- if (toI32(text.charCodeAt(n - 1)) === 61) {
790
- pad = toI32(text.charCodeAt(n - 2)) === 61 ? 2 : 1
791
- }
792
- const chars: i32 = n - pad
793
- const out: u8[] = new Array<u8>((n >> 2) * 3 - pad)
794
- const outLen: i32 = toI32(out.length)
795
- let bad: i32 = 0
796
- let acc: i32 = 0
797
- let bits: i32 = 0
798
- let j: i32 = 0
799
- for (let k: i32 = 0; k < toI32(text.length); k++) {
800
- // The padding is past `chars`, and is not data; which positions those are
801
- // is a fact about the length.
802
- if (k < chars) {
803
- const v: i32 = x509Base64SextetOf(toI32(text.charCodeAt(k)))
804
- bad = bad | (v >> 31)
805
- acc = ((acc << 6) | (v & 63)) & 0xfff
806
- bits = bits + 6
807
- if (bits >= 8) {
808
- bits = bits - 8
809
- // Always true — `out` was sized from the same length — and written so
810
- // the store keeps no bounds check.
811
- if (j >= 0 && j < outLen) {
812
- out[j] = toU8((acc >> bits) & 0xff)
813
- }
814
- j = j + 1
815
- }
816
- }
817
- }
818
- bad = bad | (acc & ((1 << bits) - 1))
819
- if (bad !== 0) {
820
- return null
821
- }
822
- return out
823
- }
824
-
825
711
  /** Whether `text` starts with `prefix`. */
826
712
  const x509StartsWith = (text: string, prefix: string): boolean => {
827
713
  const n: i32 = toI32(prefix.length)
@@ -841,7 +727,7 @@ const x509EndsWith = (text: string, suffix: string): boolean => {
841
727
  * ending in a line feed.
842
728
  */
843
729
  export const derToPem = (der: u8[], label: string): string => {
844
- const body: string = x509Base64Encode(der)
730
+ const body: string = base64StdPadEncode(der)
845
731
  const n: i32 = toI32(body.length)
846
732
  const lines: string[] = [`-----BEGIN ${label}-----`]
847
733
  for (let at: i32 = 0; at < n; at = at + 64) {
@@ -894,7 +780,10 @@ export const pemToDer = (pem: string, label: string): u8[][] | null => {
894
780
  }
895
781
  // Every block is decoded, whatever its label: a malformed block beside the
896
782
  // one asked for is refused, not skipped (docs/security/crypto-x509.md, X509-1).
897
- const der: u8[] | null = x509Base64Decode(body.join(""))
783
+ // An empty body is refused: a block with no DER is malformed, though
784
+ // the empty text is base64 for no bytes.
785
+ const text: string = body.join("")
786
+ const der: u8[] | null = text.length === 0 ? null : base64StdPadDecode(text)
898
787
  if (der === null) {
899
788
  return null
900
789
  }