@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/aes.ts
CHANGED
|
@@ -57,6 +57,31 @@
|
|
|
57
57
|
* the loops over blocks — is the discipline above and not a verified
|
|
58
58
|
* property.
|
|
59
59
|
*
|
|
60
|
+
* **The seams.** Four private functions do nearly all of the cipher's work,
|
|
61
|
+
* each called from exactly one place: `aesScheduleInto` (the key schedule),
|
|
62
|
+
* `aesBlockInto` (one block), `aesCtrInto` (GCTR over a run of bytes) and
|
|
63
|
+
* `ghashBlocksInto` (GHASH over a run of bytes). Each takes the arrays its
|
|
64
|
+
* caller passes, writes into them and allocates nothing the caller needs
|
|
65
|
+
* back, and its one caller checks every precondition it names first. That is
|
|
66
|
+
* what lets the compiler, natively, put each of those four calls behind a
|
|
67
|
+
* branch on the runtime's `nish_aes_hw` (`src/kernels.ts`, lowered in
|
|
68
|
+
* `src/emit.ts`): on a CPU with AES-NI and PCLMULQDQ, or the ARMv8 AES and
|
|
69
|
+
* PMULL instructions, the call goes to a kernel in `runtime/runtime-aes.c`
|
|
70
|
+
* that does no bounds checks of its own; on any other CPU, under
|
|
71
|
+
* `NISH_AES=soft`, on wasm32 and under Node, the body here runs (WP38 S5,
|
|
72
|
+
* `docs/wp38-simd.md`). The bodies are the bitsliced code, and keep their
|
|
73
|
+
* own defensive checks. The kernel keeps its own layout in a key's
|
|
74
|
+
* `roundKeys`, which a process only ever reads under the one answer
|
|
75
|
+
* `nish_aes_hw` gives it.
|
|
76
|
+
*
|
|
77
|
+
* What runs outside the seams, and so on the bitsliced code whichever way
|
|
78
|
+
* they go: `ghashLengths`, the last GHASH block of every tag, and so the end
|
|
79
|
+
* of J0's derivation for an IV that is not 12 bytes, multiplies with
|
|
80
|
+
* `ghashMultiply` on the state a seam left; and `aesGcmTag` folds E(K, J0)
|
|
81
|
+
* into it. A kernel therefore leaves the GHASH state in this module's own
|
|
82
|
+
* form, two big-endian words, and `tests/link/crypto_aes_agree` holds the two
|
|
83
|
+
* paths to the same bytes on the IVs that take that road.
|
|
84
|
+
*
|
|
60
85
|
* **What is refused.** A key of any length but 16 or 32 bytes (AES-192 is out
|
|
61
86
|
* of scope), a block or a sample that is not 16 bytes, an empty IV (SP 800-38D
|
|
62
87
|
* §5.2.1.1 asks for at least one bit), a sealed input shorter than its tag, a
|
|
@@ -70,6 +95,8 @@
|
|
|
70
95
|
* (`docs/wp26-stdlib.md` §3e).
|
|
71
96
|
*/
|
|
72
97
|
|
|
98
|
+
import { wipe } from "nish:secret"
|
|
99
|
+
|
|
73
100
|
/** The AES block, in bytes. */
|
|
74
101
|
export const AES_BLOCK: i32 = 16
|
|
75
102
|
/** GCM's tag, in bytes: this module makes and accepts only the full 128 bits. */
|
|
@@ -86,27 +113,53 @@ const AES_BATCH: i32 = 64
|
|
|
86
113
|
* because the language has no private ones, and a key built or edited by hand
|
|
87
114
|
* is not trusted: one whose shape is not what `aesKey` makes answers `null`
|
|
88
115
|
* everywhere (`aesKeyUsable`), and one whose H is zero opens nothing
|
|
89
|
-
* (`aesGcmOpen`).
|
|
116
|
+
* (`aesGcmOpen`). On the runtime kernels' path GHASH takes H and its powers
|
|
117
|
+
* from `roundKeys`, while `ghashLengths` and a non-12-byte IV's J0 take it
|
|
118
|
+
* from `h`; `aesKey` writes both from one schedule, so they agree. A key
|
|
119
|
+
* edited so that they disagree is outside the contract, and what it seals
|
|
120
|
+
* then depends on which path the process runs.
|
|
121
|
+
*
|
|
122
|
+
* Both are secret. A holder done with a key hands it to `aesKeyWipe`, whose
|
|
123
|
+
* volatile stores zero all of `roundKeys` (the kernels' tail of H's powers
|
|
124
|
+
* included) and `h`, and stand however dead the key is about to be.
|
|
90
125
|
*/
|
|
91
126
|
export class AesKey {
|
|
92
127
|
/** Nr: 10 for AES-128, 14 for AES-256. */
|
|
93
128
|
rounds: i32 = 0
|
|
94
129
|
/**
|
|
95
130
|
* Round key `i` in the bitsliced layout, replicated into all four block
|
|
96
|
-
* lanes, as eight words from `8 * i`: `8 * (rounds + 1)` words in all.
|
|
131
|
+
* lanes, as eight words from `8 * i`: `8 * (rounds + 1)` words in all. In a
|
|
132
|
+
* native process that runs the runtime's AES kernels (the seams, above),
|
|
133
|
+
* the same words hold the kernels' layout instead, `runtime/runtime-aes.c`'s.
|
|
97
134
|
*/
|
|
98
135
|
roundKeys: u64[]
|
|
99
|
-
/**
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
136
|
+
/**
|
|
137
|
+
* H as a big-endian 128-bit number, two words: `h[0]` the high half and
|
|
138
|
+
* `h[1]` the low. An array rather than two scalar fields, so that
|
|
139
|
+
* `aesKeyWipe`'s volatile wipe reaches it.
|
|
140
|
+
*/
|
|
141
|
+
h: u64[]
|
|
103
142
|
|
|
104
143
|
constructor(rounds: i32, roundKeys: u64[]) {
|
|
105
144
|
this.rounds = rounds
|
|
106
145
|
this.roundKeys = roundKeys
|
|
146
|
+
this.h = new Array<u64>(2)
|
|
107
147
|
}
|
|
108
148
|
}
|
|
109
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Zeroes everything secret `key` holds — every word of `roundKeys`, the
|
|
152
|
+
* runtime kernels' powers of H after the schedule included, and `h` — with
|
|
153
|
+
* `wipe`'s volatile stores, which no optimiser removes however dead the key
|
|
154
|
+
* is about to be. `rounds` is not secret and stays. A wiped key seals under
|
|
155
|
+
* an all-zero schedule and, its H being zero, opens nothing (`aesGcmOpen`);
|
|
156
|
+
* `aesKeyInto` installs a new key into it.
|
|
157
|
+
*/
|
|
158
|
+
export const aesKeyWipe = (key: AesKey): void => {
|
|
159
|
+
wipe(key.roundKeys)
|
|
160
|
+
wipe(key.h)
|
|
161
|
+
}
|
|
162
|
+
|
|
110
163
|
/** A 16-bit pattern repeated into each of a word's four 16-bit lanes. */
|
|
111
164
|
const aesLanes = (pattern: u64): u64 => {
|
|
112
165
|
const two: u64 = pattern | (pattern << toU64(16))
|
|
@@ -439,17 +492,19 @@ export const aesBitslicedRound = (q: u64[], rk: u64[], at: i32): void => {
|
|
|
439
492
|
aesAddRoundKey(q, rk, at)
|
|
440
493
|
}
|
|
441
494
|
|
|
442
|
-
/**
|
|
443
|
-
|
|
444
|
-
|
|
495
|
+
/**
|
|
496
|
+
* The cipher (FIPS 197 §5.1) on the four blocks of `buf[0 .. 64)`, in place,
|
|
497
|
+
* under the bitsliced round keys `rk`. `q` is scratch.
|
|
498
|
+
*/
|
|
499
|
+
const aesEncryptBatch = (rk: u64[], rounds: i32, buf: u8[], q: u64[]): void => {
|
|
445
500
|
aesPack(q, buf)
|
|
446
501
|
aesAddRoundKey(q, rk, 0)
|
|
447
|
-
for (let round: i32 = 1; round <
|
|
502
|
+
for (let round: i32 = 1; round < rounds; round++) {
|
|
448
503
|
aesBitslicedRound(q, rk, 8 * round)
|
|
449
504
|
}
|
|
450
505
|
aesSbox(q)
|
|
451
506
|
aesShiftRows(q)
|
|
452
|
-
aesAddRoundKey(q, rk, 8 *
|
|
507
|
+
aesAddRoundKey(q, rk, 8 * rounds)
|
|
453
508
|
aesUnpack(buf, q)
|
|
454
509
|
}
|
|
455
510
|
|
|
@@ -496,12 +551,17 @@ const aesStore32 = (out: u8[], at: i32, w: u32): void => {
|
|
|
496
551
|
}
|
|
497
552
|
|
|
498
553
|
/**
|
|
499
|
-
* KeyExpansion (FIPS 197 §5.2) for Nk = 4 or 8
|
|
500
|
-
* bitsliced and replicated into the four block lanes.
|
|
501
|
-
* big-endian, byte 0 of the key in the high byte of
|
|
502
|
-
* writes them. Branches only on the word number.
|
|
554
|
+
* The key-schedule seam: KeyExpansion (FIPS 197 §5.2) for Nk = 4 or 8 into
|
|
555
|
+
* `rk`, every round key bitsliced and replicated into the four block lanes.
|
|
556
|
+
* `w` holds the words big-endian, byte 0 of the key in the high byte of
|
|
557
|
+
* `w[0]`, as the standard writes them. Branches only on the word number.
|
|
558
|
+
*
|
|
559
|
+
* Its one caller, `aesKeyInto`, has checked that `key` is 16 or 32 bytes,
|
|
560
|
+
* that `rounds` is 10 or 14 to match, and that `rk` holds the
|
|
561
|
+
* `8 * (rounds + 1)` words this writes; the compiler may replace that call
|
|
562
|
+
* with a runtime kernel, which writes its own layout into the same words.
|
|
503
563
|
*/
|
|
504
|
-
const
|
|
564
|
+
const aesScheduleInto = (key: u8[], rounds: i32, rk: u64[]): void => {
|
|
505
565
|
const nk: i32 = rounds - 6
|
|
506
566
|
const total: i32 = (rounds + 1) * 4
|
|
507
567
|
const w: u32[] = new Array<u32>(total)
|
|
@@ -528,8 +588,8 @@ const aesExpand = (key: u8[], rounds: i32): u64[] => {
|
|
|
528
588
|
}
|
|
529
589
|
}
|
|
530
590
|
// Each round key, written into all four blocks of a batch and packed.
|
|
531
|
-
const rk: u64[] = new Array<u64>((rounds + 1) * 8)
|
|
532
591
|
const buf: u8[] = new Array<u8>(AES_BATCH)
|
|
592
|
+
const rkLength: i32 = toI32(rk.length)
|
|
533
593
|
for (let round: i32 = 0; round <= rounds; round++) {
|
|
534
594
|
for (let k: i32 = 0; k < 4; k++) {
|
|
535
595
|
for (let j: i32 = 0; j < 4; j++) {
|
|
@@ -538,10 +598,16 @@ const aesExpand = (key: u8[], rounds: i32): u64[] => {
|
|
|
538
598
|
}
|
|
539
599
|
aesPack(q, buf)
|
|
540
600
|
for (let b: i32 = 0; b < 8; b++) {
|
|
541
|
-
|
|
601
|
+
const at: i32 = 8 * round + b
|
|
602
|
+
if (at < rkLength) {
|
|
603
|
+
rk[at] = q[b]
|
|
604
|
+
}
|
|
542
605
|
}
|
|
543
606
|
}
|
|
544
|
-
|
|
607
|
+
// The key's words, the last round key's bits and its bytes, all secret.
|
|
608
|
+
wipe(w)
|
|
609
|
+
wipe(q)
|
|
610
|
+
wipe(buf)
|
|
545
611
|
}
|
|
546
612
|
|
|
547
613
|
/**
|
|
@@ -583,27 +649,51 @@ const aesCopyPrefix = (dst: u8[], src: u8[], count: i32): void => {
|
|
|
583
649
|
}
|
|
584
650
|
|
|
585
651
|
/**
|
|
586
|
-
* Whether `key` has the shape `aesKey` makes: 10 or 14 rounds
|
|
587
|
-
* `(rounds + 1) * 8` round-key words the cipher reads. A key
|
|
588
|
-
* could only have been built or edited by hand, and would
|
|
589
|
-
*
|
|
590
|
-
* branch is on the shape, which is not secret.
|
|
652
|
+
* Whether `key` has the shape `aesKey` makes: 10 or 14 rounds, the
|
|
653
|
+
* `(rounds + 1) * 8` round-key words the cipher reads and a two-word H. A key
|
|
654
|
+
* of another shape could only have been built or edited by hand, and would
|
|
655
|
+
* index past its arrays and panic, so every entry point answers `null` for it
|
|
656
|
+
* instead. The branch is on the shape, which is not secret.
|
|
591
657
|
*/
|
|
592
658
|
const aesKeyUsable = (key: AesKey): boolean => {
|
|
593
659
|
const rounds: i32 = key.rounds
|
|
594
660
|
const words: i32 = (rounds + 1) * 8
|
|
595
|
-
return (
|
|
661
|
+
return (
|
|
662
|
+
(rounds === 10 || rounds === 14) && toI32(key.roundKeys.length) === words && toI32(key.h.length) === 2
|
|
663
|
+
)
|
|
596
664
|
}
|
|
597
665
|
|
|
598
666
|
/**
|
|
599
|
-
* The
|
|
600
|
-
*
|
|
667
|
+
* The single-block seam: `dst[0 .. 16) = E(K, src[0 .. 16))` under the round
|
|
668
|
+
* keys `rk`. The block goes through the first lane of a batch whose other
|
|
669
|
+
* three are zeros and not read, and the batch and the bitsliced state are
|
|
670
|
+
* wiped before they are released: the block may be H or E(K, J0).
|
|
671
|
+
*
|
|
672
|
+
* Its one caller, `aesEncryptOne`, has checked that `rk` is a schedule of
|
|
673
|
+
* `rounds` rounds and that `src` and `dst` each hold 16 bytes; the compiler
|
|
674
|
+
* may replace that call with a runtime kernel.
|
|
601
675
|
*/
|
|
602
|
-
const
|
|
676
|
+
const aesBlockInto = (rk: u64[], rounds: i32, src: u8[], dst: u8[]): void => {
|
|
603
677
|
const buf: u8[] = new Array<u8>(AES_BATCH)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
678
|
+
const q: u64[] = new Array<u64>(8)
|
|
679
|
+
aesCopyPrefix(buf, src, AES_BLOCK)
|
|
680
|
+
aesEncryptBatch(rk, rounds, buf, q)
|
|
681
|
+
aesCopyPrefix(dst, buf, AES_BLOCK)
|
|
682
|
+
wipe(buf)
|
|
683
|
+
wipe(q)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* The first 16 bytes of `src` encrypted into the first 16 of `dst`: the one
|
|
688
|
+
* call of `aesBlockInto`, so the checks its contract needs are made here. A
|
|
689
|
+
* key of the wrong shape, or a block shorter than 16 bytes, leaves `dst` as it
|
|
690
|
+
* was; no caller passes one.
|
|
691
|
+
*/
|
|
692
|
+
const aesEncryptOne = (key: AesKey, src: u8[], dst: u8[]): void => {
|
|
693
|
+
if (!aesKeyUsable(key) || toI32(src.length) < AES_BLOCK || toI32(dst.length) < AES_BLOCK) {
|
|
694
|
+
return
|
|
695
|
+
}
|
|
696
|
+
aesBlockInto(key.roundKeys, key.rounds, src, dst)
|
|
607
697
|
}
|
|
608
698
|
|
|
609
699
|
/**
|
|
@@ -617,10 +707,10 @@ export const aesKey = (key: u8[]): AesKey | null => {
|
|
|
617
707
|
return null
|
|
618
708
|
}
|
|
619
709
|
const rounds: i32 = length === 16 ? 10 : 14
|
|
620
|
-
const expanded: AesKey = new AesKey(rounds,
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
710
|
+
const expanded: AesKey = new AesKey(rounds, new Array<u64>((rounds + 1) * 8))
|
|
711
|
+
if (!aesKeyInto(key, expanded)) {
|
|
712
|
+
return null
|
|
713
|
+
}
|
|
624
714
|
return expanded
|
|
625
715
|
}
|
|
626
716
|
|
|
@@ -630,15 +720,23 @@ export const aesKey = (key: u8[]): AesKey | null => {
|
|
|
630
720
|
* 0^128) for GCM, written into `out`, whose `roundKeys` must already be the
|
|
631
721
|
* `8 * (rounds + 1)` words that key length needs (88 or 120). It answers
|
|
632
722
|
* whether it did; a key of another length, or a schedule array of the wrong
|
|
633
|
-
* size for it, answers `false` and leaves
|
|
723
|
+
* size for it (or an `h` that is not two words), answers `false` and leaves
|
|
724
|
+
* `out` as it was.
|
|
634
725
|
*
|
|
635
726
|
* `aesKey` makes a fresh `AesKey` and stores its schedule into it, which no
|
|
636
727
|
* arena scope may be put around; this stores numbers only, so a caller that
|
|
637
728
|
* keeps one `AesKey` per connection installs every key inside a `using a =
|
|
638
729
|
* arena()` block and leaves the arena where it was. The schedule and H are
|
|
639
|
-
* secret: the caller
|
|
640
|
-
*
|
|
641
|
-
*
|
|
730
|
+
* secret: the caller hands `out` to `aesKeyWipe` when it is done with the
|
|
731
|
+
* key. The schedule is written straight into `out.roundKeys`, and the
|
|
732
|
+
* working copies the bitsliced schedule and the block H passes through are
|
|
733
|
+
* wiped with `wipe`'s volatile stores before they are released. On a CPU
|
|
734
|
+
* that runs the runtime's kernels (the seams, in the module docs) those
|
|
735
|
+
* copies are the kernels' own, in `runtime/runtime-aes.c`, and what they
|
|
736
|
+
* leave in vector registers no wipe here reaches.
|
|
737
|
+
*
|
|
738
|
+
* `aesKey` makes its key through this, so this is the one call of
|
|
739
|
+
* `aesScheduleInto`, and the checks its contract needs are made here.
|
|
642
740
|
*/
|
|
643
741
|
export const aesKeyInto = (key: u8[], out: AesKey): boolean => {
|
|
644
742
|
const length: i32 = toI32(key.length)
|
|
@@ -647,22 +745,17 @@ export const aesKeyInto = (key: u8[], out: AesKey): boolean => {
|
|
|
647
745
|
}
|
|
648
746
|
const rounds: i32 = length === 16 ? 10 : 14
|
|
649
747
|
const roundKeys: u64[] = out.roundKeys
|
|
650
|
-
const
|
|
651
|
-
if (toI32(roundKeys.length) !==
|
|
748
|
+
const hash: u64[] = out.h
|
|
749
|
+
if (toI32(roundKeys.length) !== (rounds + 1) * 8 || toI32(hash.length) !== 2) {
|
|
652
750
|
return false
|
|
653
751
|
}
|
|
654
|
-
|
|
655
|
-
for (let i: i32 = 0; i < toI32(roundKeys.length) && i < toI32(expanded.length); i++) {
|
|
656
|
-
roundKeys[i] = expanded[i]
|
|
657
|
-
expanded[i] = 0
|
|
658
|
-
}
|
|
752
|
+
aesScheduleInto(key, rounds, roundKeys)
|
|
659
753
|
out.rounds = rounds
|
|
660
|
-
const
|
|
661
|
-
out
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
}
|
|
754
|
+
const h: u8[] = new Array<u8>(AES_BLOCK)
|
|
755
|
+
aesEncryptOne(out, new Array<u8>(AES_BLOCK), h)
|
|
756
|
+
hash[0] = aesLoad64(h, 0, 8)
|
|
757
|
+
hash[1] = aesLoad64(h, 8, 16)
|
|
758
|
+
wipe(h)
|
|
666
759
|
return true
|
|
667
760
|
}
|
|
668
761
|
|
|
@@ -672,7 +765,7 @@ export const aesEncryptBlock = (key: AesKey, block: u8[]): u8[] | null => {
|
|
|
672
765
|
return null
|
|
673
766
|
}
|
|
674
767
|
const out: u8[] = new Array<u8>(AES_BLOCK)
|
|
675
|
-
|
|
768
|
+
aesEncryptOne(key, block, out)
|
|
676
769
|
return out
|
|
677
770
|
}
|
|
678
771
|
|
|
@@ -685,8 +778,10 @@ export const aesHeaderMask = (key: AesKey, sample: u8[]): u8[] | null => {
|
|
|
685
778
|
if (!aesKeyUsable(key) || toI32(sample.length) !== AES_BLOCK) {
|
|
686
779
|
return null
|
|
687
780
|
}
|
|
781
|
+
const block: u8[] = new Array<u8>(AES_BLOCK)
|
|
782
|
+
aesEncryptOne(key, sample, block)
|
|
688
783
|
const mask: u8[] = new Array<u8>(5)
|
|
689
|
-
aesCopyPrefix(mask,
|
|
784
|
+
aesCopyPrefix(mask, block, 5)
|
|
690
785
|
return mask
|
|
691
786
|
}
|
|
692
787
|
|
|
@@ -798,10 +893,17 @@ export const ghashMultiply = (y: u64[], hHi: u64, hLo: u64): void => {
|
|
|
798
893
|
}
|
|
799
894
|
|
|
800
895
|
/**
|
|
801
|
-
*
|
|
802
|
-
* the last block padded with zeros
|
|
896
|
+
* The GHASH seam: absorbs `data[0 .. length)` into the GHASH state `y` under
|
|
897
|
+
* H = `hHi:hLo`, 16 bytes at a time, the last block padded with zeros
|
|
898
|
+
* (SP 800-38D §6.4, §7.1 step 5). `_rk` is the key's round keys, which this
|
|
899
|
+
* body does not read: a runtime kernel may keep powers of H after its own
|
|
900
|
+
* schedule there.
|
|
901
|
+
*
|
|
902
|
+
* Its one caller, `ghashUpdate`, has checked that `y` holds two words, that
|
|
903
|
+
* `0 <= length <= data.length` and that `_rk` is a whole schedule; the
|
|
904
|
+
* compiler may replace that call with a runtime kernel.
|
|
803
905
|
*/
|
|
804
|
-
const
|
|
906
|
+
const ghashBlocksInto = (y: u64[], hHi: u64, hLo: u64, _rk: u64[], data: u8[], length: i32): void => {
|
|
805
907
|
if (toI32(y.length) < 2) {
|
|
806
908
|
return
|
|
807
909
|
}
|
|
@@ -812,21 +914,35 @@ const ghashUpdate = (y: u64[], key: AesKey, data: u8[], length: i32): void => {
|
|
|
812
914
|
const at: i32 = block * 16
|
|
813
915
|
y[0] = y[0] ^ aesLoad64(data, at, length)
|
|
814
916
|
y[1] = y[1] ^ aesLoad64(data, at + 8, length)
|
|
815
|
-
ghashMultiply(y,
|
|
917
|
+
ghashMultiply(y, hHi, hLo)
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Absorbs `data[0 .. length)` into the GHASH state `y` under `key`'s H: the
|
|
923
|
+
* one call of `ghashBlocksInto`, so the checks its contract needs are made
|
|
924
|
+
* here. A state, length or key that fails them leaves `y` as it was; no
|
|
925
|
+
* caller passes one.
|
|
926
|
+
*/
|
|
927
|
+
const ghashUpdate = (y: u64[], key: AesKey, data: u8[], length: i32): void => {
|
|
928
|
+
if (toI32(y.length) < 2 || length < 0 || length > toI32(data.length) || !aesKeyUsable(key)) {
|
|
929
|
+
return
|
|
816
930
|
}
|
|
931
|
+
ghashBlocksInto(y, key.h[0], key.h[1], key.roundKeys, data, length)
|
|
817
932
|
}
|
|
818
933
|
|
|
819
934
|
/** The last GHASH block: the AAD's and the ciphertext's lengths in bits, 64 bits each. */
|
|
820
935
|
const ghashLengths = (y: u64[], key: AesKey, aadLength: i32, textLength: i32): void => {
|
|
821
936
|
y[0] = y[0] ^ (toU64(aadLength) << toU64(3))
|
|
822
937
|
y[1] = y[1] ^ (toU64(textLength) << toU64(3))
|
|
823
|
-
ghashMultiply(y, key.
|
|
938
|
+
ghashMultiply(y, key.h[0], key.h[1])
|
|
824
939
|
}
|
|
825
940
|
|
|
826
941
|
/**
|
|
827
942
|
* The pre-counter block J0 (SP 800-38D §7.1 step 2): a 12-byte IV followed by
|
|
828
943
|
* the 32-bit counter 1, or for any other length GHASH of the IV padded to a
|
|
829
|
-
* block and followed by its length in bits.
|
|
944
|
+
* block and followed by its length in bits. That GHASH, with the IV, says
|
|
945
|
+
* something of H, so the GHASH state is wiped here and the caller wipes J0.
|
|
830
946
|
*/
|
|
831
947
|
const aesGcmJ0 = (key: AesKey, iv: u8[]): u8[] => {
|
|
832
948
|
const length: i32 = toI32(iv.length)
|
|
@@ -841,6 +957,8 @@ const aesGcmJ0 = (key: AesKey, iv: u8[]): u8[] => {
|
|
|
841
957
|
ghashLengths(y, key, 0, length)
|
|
842
958
|
aesStore64(j0, 0, y[0])
|
|
843
959
|
aesStore64(j0, 8, y[1])
|
|
960
|
+
// GHASH of the IV under H: with the IV, it says something of H.
|
|
961
|
+
wipe(y)
|
|
844
962
|
return j0
|
|
845
963
|
}
|
|
846
964
|
|
|
@@ -856,12 +974,20 @@ const aesGcmLengthFits = (length: i32): boolean =>
|
|
|
856
974
|
(toI64(length) + toI64(15)) >> toI64(4) <= (toI64(1) << toI64(32)) - toI64(2)
|
|
857
975
|
|
|
858
976
|
/**
|
|
859
|
-
* GCTR (SP 800-38D §6.5) from inc32(J0): `dst[i] = src[i] ⊕
|
|
860
|
-
* `i` below `length`, four blocks per pass of the cipher
|
|
861
|
-
*
|
|
862
|
-
* inc32 does, while the first
|
|
977
|
+
* The GCTR seam (SP 800-38D §6.5) from inc32(J0): `dst[i] = src[i] ⊕
|
|
978
|
+
* keystream[i]` for `i` below `length`, four blocks per pass of the cipher,
|
|
979
|
+
* under the round keys `rk`. The counter is the last four bytes of J0 as a
|
|
980
|
+
* big-endian `u32` and wraps modulo 2^32, as inc32 does, while the first
|
|
981
|
+
* twelve bytes stay as they are. `dst` may be `src`: each byte is read before
|
|
982
|
+
* it is written. The batch of keystream and the bitsliced state are wiped
|
|
983
|
+
* before it returns.
|
|
984
|
+
*
|
|
985
|
+
* Its one caller, `aesGcmCtr`, has checked that `rk` is a schedule of
|
|
986
|
+
* `rounds` rounds, that `j0` is 16 bytes and that `0 <= length` and `length`
|
|
987
|
+
* is within both `src` and `dst`; the compiler may replace that call with a
|
|
988
|
+
* runtime kernel.
|
|
863
989
|
*/
|
|
864
|
-
const
|
|
990
|
+
const aesCtrInto = (rk: u64[], rounds: i32, j0: u8[], src: u8[], dst: u8[], length: i32): void => {
|
|
865
991
|
if (toI32(j0.length) < 16) {
|
|
866
992
|
return
|
|
867
993
|
}
|
|
@@ -870,6 +996,7 @@ const aesGcmCtr = (key: AesKey, j0: u8[], src: u8[], dst: u8[], length: i32): vo
|
|
|
870
996
|
const q: u64[] = new Array<u64>(8)
|
|
871
997
|
const srcLength: i32 = toI32(src.length)
|
|
872
998
|
const dstLength: i32 = toI32(dst.length)
|
|
999
|
+
const bufLength: i32 = toI32(buf.length)
|
|
873
1000
|
// Counted in batches, like `ghashUpdate`'s blocks, so `base` never passes `length`.
|
|
874
1001
|
const batches: i32 = (length >> 6) + ((length & 63) !== 0 ? 1 : 0)
|
|
875
1002
|
for (let batch: i32 = 0; batch < batches; batch++) {
|
|
@@ -880,29 +1007,59 @@ const aesGcmCtr = (key: AesKey, j0: u8[], src: u8[], dst: u8[], length: i32): vo
|
|
|
880
1007
|
}
|
|
881
1008
|
aesStore32(buf, 16 * k + 12, counter + toU32((base >> 4) + k + 1))
|
|
882
1009
|
}
|
|
883
|
-
aesEncryptBatch(
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1010
|
+
aesEncryptBatch(rk, rounds, buf, q)
|
|
1011
|
+
// Every bound is in the loop's condition, where LLVM folds them into one
|
|
1012
|
+
// trip count and vectorises the XOR; the same tests inside the body, once
|
|
1013
|
+
// `aesGcmCtr`'s checks were inlined above them, became three compares a
|
|
1014
|
+
// byte and cost seal and open 6%. The body's test is only for the bounds
|
|
1015
|
+
// prover, and is always true.
|
|
1016
|
+
for (let at: i32 = base; at < length && at < srcLength && at < dstLength && at - base < bufLength; at++) {
|
|
1017
|
+
const j: i32 = at - base
|
|
1018
|
+
if (at >= 0 && j >= 0 && j < bufLength) {
|
|
887
1019
|
dst[at] = src[at] ^ buf[j]
|
|
888
1020
|
}
|
|
889
1021
|
}
|
|
890
1022
|
}
|
|
1023
|
+
// The last batch's keystream and its bitsliced state, both secret.
|
|
1024
|
+
wipe(buf)
|
|
1025
|
+
wipe(q)
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* GCTR from inc32(J0) over `src[0 .. length)` into `dst` under `key`: the one
|
|
1030
|
+
* call of `aesCtrInto`, so the checks its contract needs are made here. A
|
|
1031
|
+
* key, J0 or length that fails them leaves `dst` as it was; no caller passes
|
|
1032
|
+
* one.
|
|
1033
|
+
*/
|
|
1034
|
+
const aesGcmCtr = (key: AesKey, j0: u8[], src: u8[], dst: u8[], length: i32): void => {
|
|
1035
|
+
if (
|
|
1036
|
+
!aesKeyUsable(key) ||
|
|
1037
|
+
toI32(j0.length) !== AES_BLOCK ||
|
|
1038
|
+
length < 0 ||
|
|
1039
|
+
length > toI32(src.length) ||
|
|
1040
|
+
length > toI32(dst.length)
|
|
1041
|
+
) {
|
|
1042
|
+
return
|
|
1043
|
+
}
|
|
1044
|
+
aesCtrInto(key.roundKeys, key.rounds, j0, src, dst, length)
|
|
891
1045
|
}
|
|
892
1046
|
|
|
893
1047
|
/**
|
|
894
1048
|
* E(K, J0) ⊕ S, GCM's tag (SP 800-38D §7.1 step 6), into `y` (zero, two
|
|
895
1049
|
* words) as two big-endian words: S is the GHASH of the AAD and the
|
|
896
|
-
* ciphertext `text[0 .. textLength)`.
|
|
1050
|
+
* ciphertext `text[0 .. textLength)`. E(K, J0)'s block is wiped once it is
|
|
1051
|
+
* folded in.
|
|
897
1052
|
*/
|
|
898
1053
|
const aesGcmTag = (y: u64[], key: AesKey, j0: u8[], aad: u8[], text: u8[], textLength: i32): void => {
|
|
899
1054
|
const aadLength: i32 = toI32(aad.length)
|
|
900
1055
|
ghashUpdate(y, key, aad, aadLength)
|
|
901
1056
|
ghashUpdate(y, key, text, textLength)
|
|
902
1057
|
ghashLengths(y, key, aadLength, textLength)
|
|
903
|
-
const mask: u8[] =
|
|
1058
|
+
const mask: u8[] = new Array<u8>(AES_BLOCK)
|
|
1059
|
+
aesEncryptOne(key, j0, mask)
|
|
904
1060
|
y[0] = y[0] ^ aesLoad64(mask, 0, 16)
|
|
905
1061
|
y[1] = y[1] ^ aesLoad64(mask, 8, 16)
|
|
1062
|
+
wipe(mask)
|
|
906
1063
|
}
|
|
907
1064
|
|
|
908
1065
|
/**
|
|
@@ -937,6 +1094,7 @@ export const aesGcmSeal = (key: AesKey, iv: u8[], aad: u8[], plaintext: u8[]): u
|
|
|
937
1094
|
aesGcmTag(tag, key, j0, aad, out, length)
|
|
938
1095
|
aesStore64(out, length, tag[0])
|
|
939
1096
|
aesStore64(out, length + 8, tag[1])
|
|
1097
|
+
wipe(j0)
|
|
940
1098
|
return out
|
|
941
1099
|
}
|
|
942
1100
|
|
|
@@ -972,11 +1130,15 @@ export const aesGcmOpen = (key: AesKey, iv: u8[], aad: u8[], sealed: u8[]): u8[]
|
|
|
972
1130
|
// authenticate any ciphertext and AAD. Only a key built or edited by hand
|
|
973
1131
|
// has it (for one from `aesKey` the chance is 2^-128), and such a key opens
|
|
974
1132
|
// nothing. H is secret, so this is folded into the mask, not branched on.
|
|
975
|
-
const hashKeySet: u64 = ~ctEq(key.
|
|
976
|
-
|
|
977
|
-
|
|
1133
|
+
const hashKeySet: u64 = ~ctEq(key.h[0] | key.h[1], toU64(0))
|
|
1134
|
+
wipe(tag)
|
|
1135
|
+
let out: u8[] | null = null
|
|
1136
|
+
if ((same & hashKeySet) !== toU64(0)) {
|
|
1137
|
+
const opened: u8[] = new Array<u8>(length)
|
|
1138
|
+
aesGcmCtr(key, j0, sealed, opened, length)
|
|
1139
|
+
out = opened
|
|
978
1140
|
}
|
|
979
|
-
|
|
980
|
-
|
|
1141
|
+
// One wipe both paths reach, so the -O2 count sees it go missing.
|
|
1142
|
+
wipe(j0)
|
|
981
1143
|
return out
|
|
982
1144
|
}
|
package/std/crypto/base64url.ts
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* text already says how many bytes the last group holds (RFC 4648 §3.2 lets a
|
|
7
7
|
* specification that knows its lengths drop the padding, and RFC 7515 §2 does).
|
|
8
8
|
*
|
|
9
|
+
* The module also holds RFC 4648 §4 standard base64 — `+` and `/`, padded with
|
|
10
|
+
* `=` — as `base64StdPadEncode` and `base64StdPadDecode`, for PEM
|
|
11
|
+
* (`nish/crypto/x509`) and SCRAM (`nish/net/postgres`). It is as strict and as
|
|
12
|
+
* constant-time as the URL alphabet, by the same masks, and it refuses a `=`
|
|
13
|
+
* anywhere but the last one or two characters of a text whose length is a
|
|
14
|
+
* multiple of four. They are not `nish/base64`'s `base64Encode`, which encodes
|
|
15
|
+
* only, indexes its alphabet and is for pixels; the `StdPad` names keep the two
|
|
16
|
+
* apart in the program's one namespace.
|
|
17
|
+
*
|
|
9
18
|
* **Decoding is strict, so that each byte string has exactly one spelling.**
|
|
10
19
|
* `base64urlDecode` answers `null` for a `=` anywhere, for any character outside
|
|
11
20
|
* the alphabet (whitespace included), for a length of 1 mod 4 — six bits, not a
|
|
@@ -169,3 +178,119 @@ export const base64urlDecode = (text: string): u8[] | null => {
|
|
|
169
178
|
}
|
|
170
179
|
return out
|
|
171
180
|
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Standard base64 (RFC 4648 §4): `+` and `/`, padded with `=`
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The standard-alphabet character for the sextet `v`: the same steps as
|
|
188
|
+
* `base64urlCharOf` until 62, which lands on `+` and 63 on `/`.
|
|
189
|
+
*/
|
|
190
|
+
const base64StdPadCharOf = (v: i32): i32 =>
|
|
191
|
+
toI32(
|
|
192
|
+
65 +
|
|
193
|
+
toU32(v) +
|
|
194
|
+
toU32(base64urlRangeMask(v, 26, 63) & 6) -
|
|
195
|
+
toU32(base64urlRangeMask(v, 52, 63) & 75) -
|
|
196
|
+
toU32(base64urlRangeMask(v, 62, 63) & 15) +
|
|
197
|
+
toU32(base64urlRangeMask(v, 63, 63) & 3)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
/** The sextet the byte `c` stands for in standard base64, or `-1` when it is not in the alphabet. */
|
|
201
|
+
const base64StdPadSextetOf = (c: i32): i32 => {
|
|
202
|
+
const upper: i32 = base64urlRangeMask(c, 65, 90)
|
|
203
|
+
const lower: i32 = base64urlRangeMask(c, 97, 122)
|
|
204
|
+
const digit: i32 = base64urlRangeMask(c, 48, 57)
|
|
205
|
+
const plus: i32 = base64urlRangeMask(c, 43, 43)
|
|
206
|
+
const slash: i32 = base64urlRangeMask(c, 47, 47)
|
|
207
|
+
const value: i32 =
|
|
208
|
+
(upper & toI32(toU32(c) - 65)) |
|
|
209
|
+
(lower & toI32(toU32(c) - 71)) |
|
|
210
|
+
(digit & toI32(toU32(c) + 4)) |
|
|
211
|
+
(plus & 62) |
|
|
212
|
+
(slash & 63)
|
|
213
|
+
return value | ~(upper | lower | digit | plus | slash)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* `data` in padded standard base64, as one line: PEM's body, and SCRAM's
|
|
218
|
+
* nonces, salts and proofs (RFC 5802 §5.1). One `=` closes a final group of
|
|
219
|
+
* two bytes, two a group of one. An array longer than 2^31 - 1 bytes panics.
|
|
220
|
+
*/
|
|
221
|
+
export const base64StdPadEncode = (data: u8[]): string => {
|
|
222
|
+
if (data.length > 2147483647) {
|
|
223
|
+
panic("base64StdPadEncode: more than 2^31 - 1 bytes")
|
|
224
|
+
}
|
|
225
|
+
const parts: string[] = []
|
|
226
|
+
let acc: i32 = 0
|
|
227
|
+
let bits: u32 = 0
|
|
228
|
+
for (let k: i32 = 0; k < toI32(data.length); k++) {
|
|
229
|
+
acc = ((acc << 8) | toI32(data[k])) & 0xfff
|
|
230
|
+
bits += 8
|
|
231
|
+
while (bits >= 6) {
|
|
232
|
+
bits -= 6
|
|
233
|
+
parts.push(String.fromCharCode(base64StdPadCharOf((acc >> toI32(bits)) & 63)))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (bits > 0) {
|
|
237
|
+
parts.push(String.fromCharCode(base64StdPadCharOf((acc << toI32(6 - bits)) & 63)))
|
|
238
|
+
}
|
|
239
|
+
const tail: i32 = toI32(data.length) % 3
|
|
240
|
+
if (tail > 0) {
|
|
241
|
+
parts.push(tail === 1 ? "==" : "=")
|
|
242
|
+
}
|
|
243
|
+
return parts.join("")
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The bytes of padded standard base64 `text`, or `null` unless it is the one
|
|
248
|
+
* canonical spelling of them: a whole number of four-character groups, `=`
|
|
249
|
+
* only as the last one or two characters, every other character in the
|
|
250
|
+
* alphabet, and the bits the padding leaves over all zero (RFC 4648 §3.5).
|
|
251
|
+
* The empty text is the spelling of no bytes (RFC 4648 §10). Where the padding
|
|
252
|
+
* is depends on the length alone; the characters themselves are read by masks
|
|
253
|
+
* and judged once, after the last.
|
|
254
|
+
*/
|
|
255
|
+
export const base64StdPadDecode = (text: string): u8[] | null => {
|
|
256
|
+
if (text.length > 2147483647) {
|
|
257
|
+
return null
|
|
258
|
+
}
|
|
259
|
+
const n: i32 = toI32(text.length)
|
|
260
|
+
if ((n & 3) !== 0) {
|
|
261
|
+
return null
|
|
262
|
+
}
|
|
263
|
+
let pad: i32 = 0
|
|
264
|
+
if (n > 0 && toI32(text.charCodeAt(n - 1)) === 61) {
|
|
265
|
+
pad = toI32(text.charCodeAt(n - 2)) === 61 ? 2 : 1
|
|
266
|
+
}
|
|
267
|
+
const chars: i32 = n - pad
|
|
268
|
+
const out: u8[] = new Array<u8>((n >> 2) * 3 - pad)
|
|
269
|
+
const outLen: i32 = toI32(out.length)
|
|
270
|
+
let bad: i32 = 0
|
|
271
|
+
let acc: i32 = 0
|
|
272
|
+
let bits: i32 = 0
|
|
273
|
+
let j: i32 = 0
|
|
274
|
+
// The padding is past `chars` and is not data; which positions those are is
|
|
275
|
+
// a fact about the length. A `=` before them is outside the alphabet.
|
|
276
|
+
for (let k: i32 = 0; k < chars && k < toI32(text.length); k++) {
|
|
277
|
+
const v: i32 = base64StdPadSextetOf(toI32(text.charCodeAt(k)))
|
|
278
|
+
bad = bad | (v >> 31)
|
|
279
|
+
acc = ((acc << 6) | (v & 63)) & 0xfff
|
|
280
|
+
bits += 6
|
|
281
|
+
if (bits >= 8) {
|
|
282
|
+
bits -= 8
|
|
283
|
+
// Always true — `out` was sized from the same length — and written so
|
|
284
|
+
// the store keeps no bounds check.
|
|
285
|
+
if (j >= 0 && j < outLen) {
|
|
286
|
+
out[j] = toU8((acc >> bits) & 255)
|
|
287
|
+
}
|
|
288
|
+
j++
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
bad = bad | (acc & ((1 << bits) - 1))
|
|
292
|
+
if (bad !== 0) {
|
|
293
|
+
return null
|
|
294
|
+
}
|
|
295
|
+
return out
|
|
296
|
+
}
|