@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.
- package/INSTALL.md +16 -8
- package/bin/nish +0 -0
- package/docs/AI.md +118 -26
- package/package.json +1 -1
- package/runtime/core/host.d.ts +2 -1
- package/runtime/core/host.mjs +4 -0
- package/runtime/core.d.ts +47 -0
- package/runtime/core.mjs +67 -0
- package/runtime/nish.d.ts +176 -0
- package/runtime/nish.h +120 -6
- package/runtime/nish.mjs +24 -4
- package/runtime/runtime-checksum.c +270 -0
- package/runtime/runtime-net.c +386 -5
- package/runtime/runtime-os.c +59 -8
- package/runtime/runtime-parallel.c +81 -5
- package/runtime/runtime-sandbox.c +479 -0
- package/runtime/runtime-sha256.c +239 -0
- package/runtime/runtime-shm.c +541 -0
- package/runtime/runtime.c +827 -791
- package/scripts/build.sh +79 -28
- package/std/README.md +25 -13
- package/std/collections.ts +5 -5
- package/std/compress/checksum.ts +88 -75
- package/std/compress/deflate.ts +224 -0
- package/std/compress/gzip.ts +52 -0
- package/std/crypto/bcrypt.ts +816 -280
- package/std/crypto/hmac.ts +305 -2
- package/std/crypto/jwt.ts +1661 -0
- package/std/json-equal.ts +335 -0
- package/std/json-format.ts +1070 -0
- package/std/json-number.ts +428 -0
- package/std/json-pointer.ts +245 -0
- package/std/json-typed.ts +34 -0
- package/std/json-value.ts +510 -17
- package/std/json.ts +35 -6
- package/std/net/handler.ts +1348 -0
- package/std/net/http-client.ts +97 -16
- package/std/net/http-compress.ts +301 -16
- package/std/net/http1-client.ts +104 -19
- package/std/net/http1-server.ts +127 -24
- package/std/net/http1.ts +24 -4
- package/std/net/outbound.ts +409 -0
- package/std/net/postgres.ts +405 -0
- package/std/net/prefork.ts +344 -20
- package/std/net/websocket.ts +295 -14
- package/std/regex.ts +613 -52
- package/std/secret.ts +1 -1
- package/std/sync/ring.ts +587 -0
- package/std/sync/shm.ts +284 -0
- package/std/term-io.ts +35 -8
- package/std/term-rate.ts +13 -1
- package/std/threads.ts +35 -3
- package/std/yaml-json.ts +841 -0
- /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
|