@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.
- package/INSTALL.md +58 -28
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +3 -2
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +99 -5
- package/runtime/nish.h +289 -19
- package/runtime/nish.mjs +39 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +8 -7
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +214 -16
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +235 -11
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +5 -14
- package/runtime/runtime.c +257 -17
- package/runtime/shim.mjs +6 -915
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +437 -28
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +108 -58
- package/std/base64.ts +116 -0
- package/std/bytes.ts +419 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +382 -54
- package/std/crypto/p256.ts +932 -61
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +20 -131
- package/std/json-tokenizer.ts +4237 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1182 -0
- package/std/json.ts +1202 -34
- package/std/math.ts +608 -0
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +501 -62
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +261 -43
- package/std/net/http3.ts +187 -28
- package/std/net/postgres.ts +1875 -0
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +108 -11
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/threads.ts +438 -14
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
package/std/crypto/x25519.ts
CHANGED
|
@@ -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
|
-
*
|
|
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^(
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
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
|
|
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(
|
|
304
|
-
f25519SquareTimes(t,
|
|
305
|
-
f25519Mul(
|
|
306
|
-
f25519SquareTimes(t,
|
|
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(
|
|
310
|
-
f25519SquareTimes(t,
|
|
311
|
-
f25519Mul(
|
|
312
|
-
f25519SquareTimes(t,
|
|
313
|
-
f25519Mul(
|
|
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())
|
package/std/crypto/x509.ts
CHANGED
|
@@ -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
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* const
|
|
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
|
|
76
|
-
* arithmetic on range masks
|
|
77
|
-
* character into one word read after the whole text, so neither a branch
|
|
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)
|
|
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 =
|
|
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
|
-
|
|
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
|
}
|