@amritk/nish-x86_64-linux 0.27.2 → 0.29.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 (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
@@ -0,0 +1,239 @@
1
+ /* Nish runtime, the SHA-256 half: the block function of FIPS 180-4 §6.2.2 on
2
+ * the CPU's own SHA-256 instructions. std/crypto/sha256.ts is the hash; this is
3
+ * a faster body for its private `sha256Compress`, which the compiler calls in
4
+ * that function's place when `nish_sha256_hw` says this machine can run it
5
+ * (src/kernels.ts).
6
+ *
7
+ * A translation unit of its own for the reason runtime-aes.c is one: its own
8
+ * measured `.text*` ceiling in tests/runtime-budgets.js, and a program that
9
+ * hashes links no AES and one that encrypts no SHA-256. Section GC means a
10
+ * program that never calls the kernel links none of this file.
11
+ *
12
+ * There are two paths and no portable one. x86-64 runs the SHA extensions
13
+ * (SHA-NI: `sha256rnds2`, `sha256msg1`, `sha256msg2`) with SSSE3's byte shuffle
14
+ * and SSE4.1's word blend, compiled for those extensions alone with
15
+ * `target(...)` so the binary stays a baseline binary; AArch64 runs the ARMv8
16
+ * Cryptography Extension's SHA-256 instructions, likewise with
17
+ * `target("+sha2")`. A machine without them, or a process with
18
+ * `NISH_SHA256=soft`, runs the Nish code in std/crypto/sha256.ts instead: the
19
+ * compiler keeps that code and branches on `nish_sha256_hw` at each call. Every
20
+ * other target, wasm32 included, compiles `nish_sha256_hw` to `return 0` and
21
+ * the kernel to a trap nothing reaches, so a link that names it still resolves.
22
+ *
23
+ * The kernel checks the guard `sha256Compress` runs its body under and returns
24
+ * 0 without touching anything when it fails, so the caller runs the Nish
25
+ * function, which panics with its own message: both paths fail the same way.
26
+ *
27
+ * Constant time: the kernel's branches are on array headers and `at`, which are
28
+ * public, and its loads and stores are at addresses fixed by them; the
29
+ * round-constant table is read whole, in order. tests/ct-asm-runtime.js reads
30
+ * the `clang -O2` assembly of this file for both targets and holds the kernel
31
+ * to that. The one branch on anything else is `nish_sha256_hw`'s, on the CPU's
32
+ * feature bits and `NISH_SHA256` (docs/security/ct-verification.md).
33
+ *
34
+ * Wiping: the kernel keeps no buffer of its own; the state and the message
35
+ * words live in vector registers between the load from `src` and the store to
36
+ * `h`. What sits in those registers, and what the register allocator spills,
37
+ * no store from C can reach (docs/security/crypto-sha256-hw.md).
38
+ *
39
+ * The algorithm is FIPS 180-4 §6.2.2; the instruction sequences are the ones
40
+ * the instructions are defined for, written here from Intel's "Intel SHA
41
+ * Extensions" (S. Gulley et al., 2013) and Arm's description of SHA256H,
42
+ * SHA256H2, SHA256SU0 and SHA256SU1 (Arm ARM, DDI 0487).
43
+ */
44
+ #include <stddef.h>
45
+ #include <stdint.h>
46
+
47
+ #include "nish.h"
48
+
49
+ #if defined(__x86_64__)
50
+ #include <cpuid.h>
51
+ #include <immintrin.h>
52
+ #define NISH_SHA256_X86 1
53
+ #elif defined(__aarch64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
54
+ #include <arm_neon.h>
55
+ #define NISH_SHA256_ARM 1
56
+ #endif
57
+
58
+ #if defined(NISH_SHA256_X86) || defined(NISH_SHA256_ARM)
59
+
60
+ /* Named here rather than through <stdlib.h>, for runtime-aes.c's reason: the
61
+ constant-time check compiles this file for targets whose libc headers the
62
+ host does not have. */
63
+ char *getenv(const char *name);
64
+
65
+ #if defined(__has_attribute) && __has_attribute(no_builtin)
66
+ #define NISH_SHA256_NO_LIBC no_builtin
67
+ #else
68
+ #define NISH_SHA256_NO_LIBC
69
+ #endif
70
+ #if defined(NISH_SHA256_X86)
71
+ #define NISH_SHA256_TARGET __attribute__((target("sha,sse4.1,ssse3"), NISH_SHA256_NO_LIBC))
72
+ #else
73
+ #define NISH_SHA256_TARGET __attribute__((target("+sha2"), NISH_SHA256_NO_LIBC))
74
+ #endif
75
+ #define NISH_SHA256_INLINE static inline __attribute__((always_inline)) NISH_SHA256_TARGET
76
+
77
+ /* K of FIPS 180-4 §4.2.2: the first 32 bits of the fractional parts of the cube
78
+ roots of the first sixty-four primes. */
79
+ static const uint32_t nish_sha256_k[64] __attribute__((aligned(16))) = {
80
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
81
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
82
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
83
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
84
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
85
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
86
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
87
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
88
+ };
89
+
90
+ #if defined(NISH_SHA256_X86)
91
+ /* One block. `sha256rnds2` runs two rounds on the state split as ABEF and CDGH
92
+ (the words high lane to low), taking W + K for the two rounds from its third
93
+ operand's low two lanes, so the eight words are shuffled into that order
94
+ once, on the way in, and back on the way out. */
95
+ NISH_SHA256_INLINE void nish_sha256_block(char *state, const char *in) {
96
+ /* Each of the sixteen bytes' places in a big-endian word read little-endian. */
97
+ const __m128i bswap = _mm_set_epi64x(0x0c0d0e0f08090a0bLL, 0x0405060700010203LL);
98
+ __m128i dcba = _mm_loadu_si128((const __m128i *)state);
99
+ __m128i hgfe = _mm_loadu_si128((const __m128i *)(state + 16));
100
+ __m128i cdab = _mm_shuffle_epi32(dcba, 0xb1);
101
+ __m128i efgh = _mm_shuffle_epi32(hgfe, 0x1b);
102
+ __m128i abef = _mm_alignr_epi8(cdab, efgh, 8);
103
+ __m128i cdgh = _mm_blend_epi16(efgh, cdab, 0xf0);
104
+ const __m128i abef0 = abef;
105
+ const __m128i cdgh0 = cdgh;
106
+ /* W in four-word groups, a ring of the last four: group i is W[4i .. 4i + 4). */
107
+ __m128i w[4];
108
+ for (int i = 0; i < 16; i++) {
109
+ if (i < 4) {
110
+ w[i] = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(in + 16 * i)), bswap);
111
+ } else {
112
+ /* (6.2) for t = 4i .. 4i + 3: msg1 adds σ0 of the next word to W[t - 16],
113
+ the align brings in W[t - 7], and msg2 adds σ1 of W[t - 2], two of
114
+ which it computes itself from the words before. */
115
+ __m128i w7 = _mm_alignr_epi8(w[(i - 1) & 3], w[(i - 2) & 3], 4);
116
+ w[i & 3] = _mm_sha256msg2_epu32(_mm_add_epi32(_mm_sha256msg1_epu32(w[i & 3], w[(i - 3) & 3]), w7),
117
+ w[(i - 1) & 3]);
118
+ }
119
+ __m128i wk = _mm_add_epi32(w[i & 3], _mm_load_si128((const __m128i *)&nish_sha256_k[4 * i]));
120
+ cdgh = _mm_sha256rnds2_epu32(cdgh, abef, wk);
121
+ abef = _mm_sha256rnds2_epu32(abef, cdgh, _mm_shuffle_epi32(wk, 0x0e));
122
+ }
123
+ abef = _mm_add_epi32(abef, abef0);
124
+ cdgh = _mm_add_epi32(cdgh, cdgh0);
125
+ __m128i feba = _mm_shuffle_epi32(abef, 0x1b);
126
+ __m128i dchg = _mm_shuffle_epi32(cdgh, 0xb1);
127
+ _mm_storeu_si128((__m128i *)state, _mm_blend_epi16(feba, dchg, 0xf0));
128
+ _mm_storeu_si128((__m128i *)(state + 16), _mm_alignr_epi8(dchg, feba, 8));
129
+ }
130
+ #else
131
+ /* One block. SHA256H and SHA256H2 run four rounds on ABCD and EFGH together,
132
+ each giving one half of the new state, with W + K for the four in their
133
+ third operand. */
134
+ NISH_SHA256_INLINE void nish_sha256_block(char *state, const char *in) {
135
+ uint32x4_t abcd = vld1q_u32((const uint32_t *)state);
136
+ uint32x4_t efgh = vld1q_u32((const uint32_t *)(state + 16));
137
+ const uint32x4_t abcd0 = abcd;
138
+ const uint32x4_t efgh0 = efgh;
139
+ uint32x4_t w[4];
140
+ for (int i = 0; i < 16; i++) {
141
+ if (i < 4) {
142
+ w[i] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8((const uint8_t *)(in + 16 * i))));
143
+ } else {
144
+ /* (6.2) for t = 4i .. 4i + 3: SU0 adds σ0 to W[t - 16], SU1 adds W[t - 7]
145
+ and σ1 of W[t - 2]. */
146
+ w[i & 3] = vsha256su1q_u32(vsha256su0q_u32(w[i & 3], w[(i - 3) & 3]), w[(i - 2) & 3], w[(i - 1) & 3]);
147
+ }
148
+ uint32x4_t wk = vaddq_u32(w[i & 3], vld1q_u32(&nish_sha256_k[4 * i]));
149
+ uint32x4_t before = abcd;
150
+ abcd = vsha256hq_u32(abcd, efgh, wk);
151
+ efgh = vsha256h2q_u32(efgh, before, wk);
152
+ }
153
+ vst1q_u32((uint32_t *)state, vaddq_u32(abcd, abcd0));
154
+ vst1q_u32((uint32_t *)(state + 16), vaddq_u32(efgh, efgh0));
155
+ }
156
+ #endif
157
+
158
+ /* `sha256Compress`'s own guard, then the block. That guard reads the source's
159
+ length through `toI32`, which saturates at 2^31 - 1, so it refuses a block
160
+ ending past that offset whatever the length: `at > INT32_MAX - 64`. */
161
+ NISH_SHA256_TARGET int32_t nish_sha256_compress(nish_array *h, const nish_array *w, const nish_array *src,
162
+ int32_t at) {
163
+ if (h->len < 8 || w->len < 64 || at < 0 || at > INT32_MAX - 64 || src->len < 64 || (uint64_t)at > src->len - 64)
164
+ return 0;
165
+ nish_sha256_block(h->data, src->data + at);
166
+ return 1;
167
+ }
168
+
169
+ #if defined(NISH_SHA256_X86)
170
+ /* Whether this CPU runs the kernel: CPUID leaf 7's EBX has SHA (bit 29), and
171
+ leaf 1's ECX SSE4.1 (bit 19) and SSSE3 (bit 9). Asked through `cpuid.h`, for
172
+ runtime-aes.c's reason; `__get_cpuid_count` answers 0 when leaf 7 is past
173
+ the CPU's highest. */
174
+ static int32_t nish_sha256_cpu(void) {
175
+ unsigned a, b, c, d;
176
+ if (!__get_cpuid(1, &a, &b, &c, &d)) return 0;
177
+ if ((c & bit_SSE4_1) == 0 || (c & bit_SSSE3) == 0) return 0;
178
+ if (!__get_cpuid_count(7, 0, &a, &b, &c, &d)) return 0;
179
+ return (b & bit_SHA) != 0;
180
+ }
181
+ #elif defined(__linux__)
182
+ /* Linux reports the ARMv8 SHA-256 instructions in the auxiliary vector:
183
+ AT_HWCAP is 16, and its HWCAP_SHA2 bit is 6
184
+ (arch/arm64/include/uapi/asm/hwcap.h). */
185
+ unsigned long getauxval(unsigned long type);
186
+
187
+ static int32_t nish_sha256_cpu(void) {
188
+ return (getauxval(16) & (1ul << 6)) != 0;
189
+ }
190
+ #elif defined(__APPLE__)
191
+ /* macOS names the feature in sysctl (macOS 12 and later). A system that does
192
+ not know the name answers as a CPU without it: the Nish path. */
193
+ int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
194
+
195
+ static int32_t nish_sha256_cpu(void) {
196
+ int32_t has = 0;
197
+ size_t size = sizeof has;
198
+ if (sysctlbyname("hw.optional.arm.FEAT_SHA256", &has, &size, 0, 0) != 0) return 0;
199
+ return has != 0;
200
+ }
201
+ #else
202
+ /* Another AArch64 system: no question asked, so the Nish path. */
203
+ static int32_t nish_sha256_cpu(void) {
204
+ return 0;
205
+ }
206
+ #endif
207
+
208
+ /* The answer: 0 until the first call, then 1 for the kernel or 2 for the Nish
209
+ path, with runtime-aes.c's relaxed atomics for the same reason. */
210
+ static int32_t nish_sha256_answer;
211
+
212
+ int32_t nish_sha256_hw(void) {
213
+ int32_t answer = __atomic_load_n(&nish_sha256_answer, __ATOMIC_RELAXED);
214
+ if (answer == 0) {
215
+ /* `soft` pins the Nish path and never asks the CPU; anything else, or
216
+ nothing, means the best this machine has. */
217
+ const char *pin = getenv("NISH_SHA256");
218
+ int soft = pin != 0 && pin[0] == 's' && pin[1] == 'o' && pin[2] == 'f' && pin[3] == 't' && pin[4] == 0;
219
+ answer = !soft && nish_sha256_cpu() ? 1 : 2;
220
+ __atomic_store_n(&nish_sha256_answer, answer, __ATOMIC_RELAXED);
221
+ }
222
+ return answer == 1;
223
+ }
224
+
225
+ #else
226
+
227
+ /* A target with no SHA-256 instructions this file uses, wasm32 among them: the
228
+ Nish path always, and a kernel that a program whose every call is behind
229
+ `nish_sha256_hw` never reaches. */
230
+ int32_t nish_sha256_hw(void) {
231
+ return 0;
232
+ }
233
+
234
+ int32_t nish_sha256_compress(nish_array *h, const nish_array *w, const nish_array *src, int32_t at) {
235
+ (void)h, (void)w, (void)src, (void)at;
236
+ __builtin_trap();
237
+ }
238
+
239
+ #endif