@amritk/nish-x86_64-linux 0.14.0 → 0.16.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 +15 -6
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +91 -0
- package/runtime/nish.h +97 -5
- package/runtime/nish.mjs +23 -0
- package/runtime/runtime-host.c +29 -2
- package/runtime/runtime-net.c +534 -0
- package/runtime/runtime-os.c +77 -37
- package/runtime/runtime-parallel.c +5 -1
- package/runtime/runtime-wasm.c +25 -2
- package/runtime/runtime.c +40 -6
- package/runtime/shim.mjs +15 -0
- package/scripts/build.sh +8 -7
- package/std/README.md +88 -23
- package/std/crypto/LICENSE-bearssl +21 -0
- package/std/crypto/LICENSE-fiat-crypto +21 -0
- package/std/crypto/aes.ts +939 -0
- package/std/crypto/base64url.ts +18 -5
- package/std/crypto/chacha20poly1305.ts +705 -0
- package/std/crypto/ct.ts +8 -2
- package/std/crypto/hkdf.ts +21 -5
- package/std/crypto/hmac.ts +18 -2
- package/std/crypto/p256.ts +4342 -0
- package/std/crypto/sha256.ts +12 -1
- package/std/crypto/sha512.ts +16 -2
- package/std/crypto/x25519.ts +26 -10
- package/std/crypto/x509.ts +1331 -0
package/runtime/runtime-os.c
CHANGED
|
@@ -52,6 +52,14 @@ extern char **environ;
|
|
|
52
52
|
|
|
53
53
|
#include "nish.h"
|
|
54
54
|
|
|
55
|
+
/* Darwin's <fcntl.h> declares `O_NOFOLLOW` only at its full C level, and the
|
|
56
|
+
strict level the two macros above select is the one that binds the right
|
|
57
|
+
`realpath`, so the flag is spelled here with Darwin's own value
|
|
58
|
+
(<sys/fcntl.h>: 0x00000100). Linux and WASI declare it at POSIX 2008. */
|
|
59
|
+
#if defined(__APPLE__) && !defined(O_NOFOLLOW)
|
|
60
|
+
#define O_NOFOLLOW 0x00000100
|
|
61
|
+
#endif
|
|
62
|
+
|
|
55
63
|
/* The same spelling runtime.c uses for its own cold paths: a function that
|
|
56
64
|
reports and exits is never on a path worth optimising for. */
|
|
57
65
|
#define NISH_COLD __attribute__((noreturn, cold, noinline))
|
|
@@ -63,6 +71,12 @@ static NISH_COLD void nish_io_fail(const char *what, const nish_str *path) {
|
|
|
63
71
|
_exit(1);
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
/* The longest string or array a read hands back: 2^31 - 1, the same limit
|
|
75
|
+
runtime.c's `NISH_ARRAY_MAX` puts on a push. Under --number-mode i32
|
|
76
|
+
`length` is an `i32`, so one byte more reads back negative and bounds-check
|
|
77
|
+
elimination trusts it (docs/security/runtime.md, RT-1). */
|
|
78
|
+
#define NISH_LENGTH_MAX 2147483647
|
|
79
|
+
|
|
66
80
|
/* `readFileSyncOrNull(path)`: null rather than a message, so a program can
|
|
67
81
|
turn a missing file into its own diagnostic and carry on.
|
|
68
82
|
*
|
|
@@ -84,11 +98,16 @@ static NISH_COLD void nish_io_fail(const char *what, const nish_str *path) {
|
|
|
84
98
|
* rather than `stat` on the path, so the answer is about the file that was
|
|
85
99
|
* opened and not about whatever the name means a moment later. */
|
|
86
100
|
nish_str *nish_read_file_or_null(const nish_str *path) {
|
|
87
|
-
int fd = open(path
|
|
101
|
+
int fd = open(nish_cpath(path), O_RDONLY);
|
|
88
102
|
if (fd < 0) return 0;
|
|
89
103
|
struct stat st;
|
|
90
104
|
off_t len = fstat(fd, &st) == 0 && !S_ISDIR(st.st_mode) ? lseek(fd, 0, SEEK_END) : -1;
|
|
91
|
-
|
|
105
|
+
/* A file longer than a length can say is refused before anything is
|
|
106
|
+
allocated, as an unreadable one is: null here, null from
|
|
107
|
+
`readFileBytesSync`, and `cannot read` from `readFileSync`. The bytes read
|
|
108
|
+
below are never more than `len`, so a file that grows meanwhile cannot
|
|
109
|
+
pass the limit either. */
|
|
110
|
+
if (len < 0 || len > NISH_LENGTH_MAX) {
|
|
92
111
|
close(fd);
|
|
93
112
|
return 0;
|
|
94
113
|
}
|
|
@@ -123,8 +142,14 @@ nish_str *nish_read_file(const nish_str *path) {
|
|
|
123
142
|
return s;
|
|
124
143
|
}
|
|
125
144
|
|
|
145
|
+
/* `O_NOFOLLOW` (docs/security/runtime.md, RT-4): a symbolic link as the last
|
|
146
|
+
component of the path is refused, as an unwritable file is, rather than
|
|
147
|
+
followed to whatever file it names. `nish file.ts` writes `file.ll` beside
|
|
148
|
+
its source, so a link planted at that name in a checkout would otherwise
|
|
149
|
+
truncate any file the user can write. A link among the directories on the
|
|
150
|
+
way is still followed. */
|
|
126
151
|
static void nish_put_file(const nish_str *path, const nish_str *data, int flags) {
|
|
127
|
-
int fd = open(path
|
|
152
|
+
int fd = open(nish_cpath(path), O_WRONLY | O_CREAT | O_NOFOLLOW | flags, 0644);
|
|
128
153
|
if (fd < 0) nish_io_fail("write ", path);
|
|
129
154
|
for (uint64_t done = 0; done < data->len;) {
|
|
130
155
|
ssize_t n = write(fd, data->data + done, data->len - done);
|
|
@@ -149,14 +174,14 @@ void nish_append_file(const nish_str *path, const nish_str *data) { nish_put_fil
|
|
|
149
174
|
answer to that question. */
|
|
150
175
|
_Bool nish_is_dir(const nish_str *path) {
|
|
151
176
|
struct stat st;
|
|
152
|
-
return stat(path
|
|
177
|
+
return stat(nish_cpath(path), &st) == 0 && S_ISDIR(st.st_mode);
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
/* One directory, not recursive. The retry is the `stat` above rather than
|
|
156
181
|
`errno == EEXIST` so that a plain file at the path answers false, which is
|
|
157
182
|
what the promise "a directory is there afterwards" means. */
|
|
158
183
|
_Bool nish_mkdir(const nish_str *path) {
|
|
159
|
-
return mkdir(path
|
|
184
|
+
return mkdir(nish_cpath(path), 0777) == 0 || nish_is_dir(path);
|
|
160
185
|
}
|
|
161
186
|
|
|
162
187
|
/* `posix_spawnp` is one libc call where fork/execvp/waitpid would be three,
|
|
@@ -168,7 +193,7 @@ _Bool nish_mkdir(const nish_str *path) {
|
|
|
168
193
|
two arguments answered differently, so the argv vector, the wait and the
|
|
169
194
|
signal convention are written once; it is `static`, so a program that spawns
|
|
170
195
|
nothing still loses all three to `--gc-sections`. */
|
|
171
|
-
static int32_t nish_spawn_impl(const nish_array *argv, const
|
|
196
|
+
static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, const nish_str *err) {
|
|
172
197
|
#ifdef __wasi__
|
|
173
198
|
(void)argv;
|
|
174
199
|
(void)out;
|
|
@@ -181,18 +206,27 @@ static int32_t nish_spawn_impl(const nish_array *argv, const char *out, const ch
|
|
|
181
206
|
path out of here has anything to free. */
|
|
182
207
|
char **v = nish_alloc_struct((argv->len + 1) * sizeof *v);
|
|
183
208
|
uint64_t i = 0;
|
|
184
|
-
|
|
209
|
+
/* An argument holding a NUL is refused rather than cut short at it, which
|
|
210
|
+
would run a different command line from the one the program built: it is
|
|
211
|
+
the one whose C string is not its own bytes. */
|
|
212
|
+
for (; i < argv->len; i++) {
|
|
213
|
+
v[i] = (char *)nish_cpath(s[i]);
|
|
214
|
+
if (v[i] != s[i]->data) return -1;
|
|
215
|
+
}
|
|
185
216
|
v[i] = 0;
|
|
186
217
|
posix_spawn_file_actions_t fa;
|
|
187
218
|
posix_spawn_file_actions_t *fap = 0;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
219
|
+
const nish_str *to[2] = { out, err };
|
|
220
|
+
for (int k = 0; k < 2; k++) {
|
|
221
|
+
if (!to[k]) continue;
|
|
222
|
+
if (!fap) {
|
|
223
|
+
if (posix_spawn_file_actions_init(&fa)) return -1;
|
|
224
|
+
fap = &fa;
|
|
225
|
+
}
|
|
191
226
|
/* The child opens the file, not this process: a redirect that this process
|
|
192
227
|
performed would have to be undone afterwards, and a failed open would
|
|
193
228
|
leave its own stdout pointing at the file. */
|
|
194
|
-
|
|
195
|
-
if (err) posix_spawn_file_actions_addopen(fap, 2, err, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
229
|
+
posix_spawn_file_actions_addopen(fap, k + 1, nish_cpath(to[k]), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644);
|
|
196
230
|
}
|
|
197
231
|
pid_t pid;
|
|
198
232
|
int status;
|
|
@@ -210,14 +244,15 @@ int32_t nish_spawn(const nish_array *argv) { return nish_spawn_impl(argv, 0, 0);
|
|
|
210
244
|
to a file, which is what comparing a program's output against a golden needs
|
|
211
245
|
— `nish_spawn` answers a status and the output is gone. An **empty** path
|
|
212
246
|
leaves that stream inherited, so one call can capture stdout and let stderr
|
|
213
|
-
through to the terminal. Each file is created or truncated at 0644
|
|
214
|
-
`writeFileSync` does
|
|
247
|
+
through to the terminal. Each file is created or truncated at 0644 and a
|
|
248
|
+
symbolic link at its name is refused, as `writeFileSync` does; the child
|
|
249
|
+
then fails to start, and the answer is -1.
|
|
215
250
|
|
|
216
251
|
Two streams must not name one path: each would be opened separately, with
|
|
217
252
|
its own offset, and the two would overwrite each other rather than
|
|
218
253
|
interleave. Capturing them apart and concatenating is the way to merge. */
|
|
219
254
|
int32_t nish_spawn_to(const nish_array *argv, const nish_str *out, const nish_str *err) {
|
|
220
|
-
return nish_spawn_impl(argv, out->len ? out
|
|
255
|
+
return nish_spawn_impl(argv, out->len ? out : 0, err->len ? err : 0);
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
/* `readdirSync(path)`: the listing a driver needs to discover its own inputs.
|
|
@@ -240,7 +275,7 @@ nish_array *nish_readdir(const nish_str *path) {
|
|
|
240
275
|
(void)path;
|
|
241
276
|
return 0;
|
|
242
277
|
#else
|
|
243
|
-
DIR *d = opendir(path
|
|
278
|
+
DIR *d = opendir(nish_cpath(path));
|
|
244
279
|
if (!d) return 0;
|
|
245
280
|
nish_array *a = nish_alloc_struct(sizeof *a);
|
|
246
281
|
*a = (nish_array){ 0, 0, 0 };
|
|
@@ -252,15 +287,30 @@ nish_array *nish_readdir(const nish_str *path) {
|
|
|
252
287
|
((nish_str **)a->data)[a->len++] = nish_str_new(n, strlen(n));
|
|
253
288
|
}
|
|
254
289
|
closedir(d);
|
|
255
|
-
/*
|
|
256
|
-
a
|
|
290
|
+
/* Heapsort over pointers, in place. It was an insertion sort, on the theory
|
|
291
|
+
that a directory is short, but whoever fills the directory decides that:
|
|
292
|
+
100,000 names took 2.5 billion compares and 12.5 s, where this takes 3.0
|
|
293
|
+
million and 0.07 s (docs/security/runtime.md, RT-5). It needs no scratch
|
|
294
|
+
memory, and unlike `qsort` costs no comparator symbol or unwind entry. */
|
|
257
295
|
nish_str **v = (nish_str **)a->data;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
296
|
+
/* Each pass sifts one name down the max-heap `v[0, end)`: first every
|
|
297
|
+
parent from the middle up, which builds the heap, then the name the
|
|
298
|
+
largest is swapped out for, which shrinks the heap by one. */
|
|
299
|
+
uint64_t i = a->len / 2, end = a->len;
|
|
300
|
+
while (end > 1) {
|
|
301
|
+
nish_str *s;
|
|
302
|
+
if (i) {
|
|
303
|
+
s = v[--i];
|
|
304
|
+
} else {
|
|
305
|
+
s = v[--end];
|
|
306
|
+
v[end] = v[0];
|
|
307
|
+
}
|
|
308
|
+
uint64_t j = i, c;
|
|
309
|
+
while ((c = 2 * j + 1) < end) {
|
|
310
|
+
if (c + 1 < end && strcmp(v[c]->data, v[c + 1]->data) < 0) c++;
|
|
311
|
+
if (strcmp(s->data, v[c]->data) >= 0) break;
|
|
312
|
+
v[j] = v[c];
|
|
313
|
+
j = c;
|
|
264
314
|
}
|
|
265
315
|
v[j] = s;
|
|
266
316
|
}
|
|
@@ -290,13 +340,8 @@ int64_t nish_monotonic_nanos(void) {
|
|
|
290
340
|
string with the lifetime every other one has. NULL for an unset variable,
|
|
291
341
|
which is the language's `string | null`. Contract: nish.h. */
|
|
292
342
|
nish_str *nish_getenv(const nish_str *name) {
|
|
293
|
-
const char *v = getenv(name
|
|
294
|
-
|
|
295
|
-
uint64_t len = strlen(v);
|
|
296
|
-
nish_str *s = nish_alloc_struct(8 + len + 1);
|
|
297
|
-
s->len = len;
|
|
298
|
-
memcpy(s->data, v, len + 1);
|
|
299
|
-
return s;
|
|
343
|
+
const char *v = getenv(nish_cpath(name));
|
|
344
|
+
return v ? nish_str_new(v, strlen(v)) : 0;
|
|
300
345
|
}
|
|
301
346
|
|
|
302
347
|
/* ---- Symlinks (WP19 §5a item 4): `realpathSync(path)`, the one path
|
|
@@ -321,12 +366,7 @@ nish_str *nish_getenv(const nish_str *name) {
|
|
|
321
366
|
it resolves. Contract: nish.h. */
|
|
322
367
|
nish_str *nish_realpath(const nish_str *path) {
|
|
323
368
|
char buf[PATH_MAX];
|
|
324
|
-
|
|
325
|
-
uint64_t len = strlen(buf);
|
|
326
|
-
nish_str *s = nish_alloc_struct(8 + len + 1);
|
|
327
|
-
s->len = len;
|
|
328
|
-
memcpy(s->data, buf, len + 1);
|
|
329
|
-
return s;
|
|
369
|
+
return realpath(nish_cpath(path), buf) ? nish_str_new(buf, strlen(buf)) : 0;
|
|
330
370
|
}
|
|
331
371
|
|
|
332
372
|
/* ---- What machine this is (WP14 §7a): `process.platform` and `process.arch`,
|
|
@@ -109,7 +109,11 @@ void nish_parallel_range(nish_par_body body, void *ctx, int64_t len, int64_t gra
|
|
|
109
109
|
if (grain < 1) grain = 1;
|
|
110
110
|
/* Never more threads than there is work for at the requested granularity,
|
|
111
111
|
* and never more than the machine has. */
|
|
112
|
-
|
|
112
|
+
/* Rounded up without `len + grain - 1`, which overflows -- undefined
|
|
113
|
+
behaviour, and in practice a negative count that ran the whole range on
|
|
114
|
+
one thread -- once `len` is within `grain` of 2^63
|
|
115
|
+
(docs/security/runtime.md, RT-8). */
|
|
116
|
+
int64_t chunks = len / grain + (len % grain != 0);
|
|
113
117
|
int64_t n = nish_cpu_count();
|
|
114
118
|
if (n > NISH_PAR_MAX) n = NISH_PAR_MAX;
|
|
115
119
|
if (chunks < n) n = chunks;
|
package/runtime/runtime-wasm.c
CHANGED
|
@@ -40,7 +40,14 @@ _Static_assert(offsetof(struct nish_arena, chunks) == 24, "arena layout is ABI")
|
|
|
40
40
|
extern unsigned char __heap_base;
|
|
41
41
|
#define NISH_PAGE ((uint64_t)65536)
|
|
42
42
|
|
|
43
|
-
/* Slow path of the inlined allocator (`size` is already 8-byte rounded).
|
|
43
|
+
/* Slow path of the inlined allocator (`size` is already 8-byte rounded).
|
|
44
|
+
*
|
|
45
|
+
* `need` has to fit the 32-bit address space, and is checked before anything
|
|
46
|
+
* is grown (docs/security/runtime.md, RT-7). Without that, an `off + size` that
|
|
47
|
+
* wrapped 2^64 looked small and moved the offset backwards, and a page count
|
|
48
|
+
* past 2^32 truncated to the `uintptr_t` `memory.grow` takes: a request of
|
|
49
|
+
* 2^48 bytes grew nothing, claimed 2^48 bytes of capacity, and the next block
|
|
50
|
+
* landed at `buf + off` reduced mod 2^32, on top of live data. */
|
|
44
51
|
void *nish_arena_grow(uint64_t size) {
|
|
45
52
|
if (!nish_arena.buf) {
|
|
46
53
|
uintptr_t base = ((uintptr_t)&__heap_base + 7) & ~(uintptr_t)7;
|
|
@@ -48,6 +55,7 @@ void *nish_arena_grow(uint64_t size) {
|
|
|
48
55
|
nish_arena.cap = (uint64_t)__builtin_wasm_memory_size(0) * NISH_PAGE - base;
|
|
49
56
|
}
|
|
50
57
|
uint64_t need = nish_arena.off + size;
|
|
58
|
+
if (need < size || need > (uintptr_t)-1 - (uintptr_t)nish_arena.buf) __builtin_trap();
|
|
51
59
|
if (need > nish_arena.cap) {
|
|
52
60
|
uint64_t pages = (need - nish_arena.cap + NISH_PAGE - 1) / NISH_PAGE;
|
|
53
61
|
if (__builtin_wasm_memory_grow(0, (uintptr_t)pages) == (uintptr_t)-1) __builtin_trap();
|
|
@@ -58,9 +66,12 @@ void *nish_arena_grow(uint64_t size) {
|
|
|
58
66
|
return p;
|
|
59
67
|
}
|
|
60
68
|
|
|
69
|
+
/* `size <= cap - off` rather than `off + size <= cap`, which a size near 2^64
|
|
70
|
+
wraps into a yes; a size that rounding wraps to 0 is trapped the same way. */
|
|
61
71
|
void *nish_alloc_struct(uint64_t size) {
|
|
72
|
+
if (size > (uint64_t)-8) __builtin_trap();
|
|
62
73
|
size = (size + 7) & ~(uint64_t)7;
|
|
63
|
-
if (nish_arena.buf &&
|
|
74
|
+
if (nish_arena.buf && size <= nish_arena.cap - nish_arena.off) {
|
|
64
75
|
void *p = nish_arena.buf + nish_arena.off;
|
|
65
76
|
nish_arena.off += size;
|
|
66
77
|
return p;
|
|
@@ -77,7 +88,15 @@ void nish_arena_release(uint64_t mark) { nish_arena.off = mark ? mark - (uintptr
|
|
|
77
88
|
/* ---- Arrays: %struct.nish_array = type { i64, i64, i8* } */
|
|
78
89
|
typedef struct nish_array { uint64_t len; uint64_t cap; char *data; } nish_array;
|
|
79
90
|
|
|
91
|
+
/* The length limit runtime.c's `nish_array_grow` and `nish_alloc_array` keep:
|
|
92
|
+
2^31 - 1 elements, so an i32-mode `length` is never negative. Past it the
|
|
93
|
+
wasm runtime traps, as it does for every other failure. */
|
|
94
|
+
#define NISH_ARRAY_MAX 2147483647u
|
|
95
|
+
|
|
80
96
|
nish_array *nish_alloc_array(uint64_t elem_size, uint64_t len) {
|
|
97
|
+
/* With `len` in range, `len * elem_size` cannot wrap for any element
|
|
98
|
+
smaller than 8 GiB, and a product past 4 GiB traps in the grow. */
|
|
99
|
+
if (len > NISH_ARRAY_MAX) __builtin_trap();
|
|
81
100
|
nish_array *a = (nish_array *)nish_alloc_struct(sizeof *a);
|
|
82
101
|
a->len = a->cap = len;
|
|
83
102
|
a->data = (char *)nish_alloc_struct(len * elem_size);
|
|
@@ -86,6 +105,10 @@ nish_array *nish_alloc_array(uint64_t elem_size, uint64_t len) {
|
|
|
86
105
|
|
|
87
106
|
void nish_array_grow(nish_array *a, uint64_t elem_size) {
|
|
88
107
|
uint64_t cap = a->cap ? a->cap * 2 : 4;
|
|
108
|
+
if (cap > NISH_ARRAY_MAX) {
|
|
109
|
+
if (a->len >= NISH_ARRAY_MAX) __builtin_trap();
|
|
110
|
+
cap = NISH_ARRAY_MAX;
|
|
111
|
+
}
|
|
89
112
|
char *data = (char *)nish_alloc_struct(cap * elem_size);
|
|
90
113
|
if (a->len) __builtin_memcpy(data, a->data, (uintptr_t)(a->len * elem_size)); /* memory.copy (-mbulk-memory) */
|
|
91
114
|
a->data = data;
|
package/runtime/runtime.c
CHANGED
|
@@ -83,7 +83,13 @@ static NISH_COLD void nish_die(const char *msg) {
|
|
|
83
83
|
(void)!write(2, msg, strlen(msg));
|
|
84
84
|
_exit(1);
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
/* A function rather than a macro around `nish_die`: seven call sites each
|
|
87
|
+
loading the message's address cost more than one more symbol does. */
|
|
88
|
+
static NISH_COLD void nish_oom(void) { nish_die("nish: out of memory\n"); }
|
|
89
|
+
|
|
90
|
+
/* The most elements an array and the most bytes a string may hold: see
|
|
91
|
+
`nish_array_grow` and `nish_str_concat`. */
|
|
92
|
+
#define NISH_ARRAY_MAX 2147483647u
|
|
87
93
|
|
|
88
94
|
/* Slow path (size 8-byte rounded): push a chunk (>= 64 KB), bump from it. */
|
|
89
95
|
void *nish_arena_grow(uint64_t size) {
|
|
@@ -196,9 +202,16 @@ nish_str *nish_str_new(const char *bytes, uint64_t len) {
|
|
|
196
202
|
return s;
|
|
197
203
|
}
|
|
198
204
|
|
|
205
|
+
/* A string never passes 2^31 - 1 bytes, for the reason an array never passes
|
|
206
|
+
that many elements (`NISH_ARRAY_MAX`, below): under --number-mode i32 a
|
|
207
|
+
longer one's `length` reads back negative and bounds-check elimination
|
|
208
|
+
trusts it (docs/security/runtime.md, RT-2). `a + b` past it fails as an
|
|
209
|
+
allocation does. */
|
|
199
210
|
nish_str *nish_str_concat(const nish_str *a, const nish_str *b) {
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
uint64_t len = a->len + b->len;
|
|
212
|
+
if (len > NISH_ARRAY_MAX) nish_oom();
|
|
213
|
+
nish_str *s = nish_alloc_struct(8 + len + 1);
|
|
214
|
+
s->len = len;
|
|
202
215
|
memcpy(s->data, a->data, a->len);
|
|
203
216
|
memcpy(s->data + a->len, b->data, b->len);
|
|
204
217
|
s->data[s->len] = 0;
|
|
@@ -1106,8 +1119,12 @@ double nish_random(void) {
|
|
|
1106
1119
|
/* ---- Arrays: %struct.nish_array = type { i64, i64, i8* }, cold paths */
|
|
1107
1120
|
typedef struct nish_array { uint64_t len; uint64_t cap; char *data; } nish_array;
|
|
1108
1121
|
|
|
1109
|
-
/* Host entry (WP8): `len` uninitialised elements, len == cap
|
|
1122
|
+
/* Host entry (WP8): `len` uninitialised elements, len == cap. A `len` past
|
|
1123
|
+
`NISH_ARRAY_MAX` fails as an allocation does, which also keeps
|
|
1124
|
+
`len * elem_size` from wrapping into a short block behind a long header
|
|
1125
|
+
(docs/security/runtime.md, RT-7). */
|
|
1110
1126
|
nish_array *nish_alloc_array(uint64_t elem_size, uint64_t len) {
|
|
1127
|
+
if (len > NISH_ARRAY_MAX) nish_oom();
|
|
1111
1128
|
nish_array *a = nish_alloc_struct(sizeof *a);
|
|
1112
1129
|
*a = (nish_array){ len, len, nish_alloc_struct(len * elem_size) };
|
|
1113
1130
|
return a;
|
|
@@ -1160,9 +1177,26 @@ double nish_parse_number(const nish_str *s, int32_t mode) {
|
|
|
1160
1177
|
* `sizeof` one element, which for a record element type (WP15 section 2a) is
|
|
1161
1178
|
* the whole struct, so this relocates the elements themselves rather than a
|
|
1162
1179
|
* block of pointers to them. Compiled code may therefore hold no pointer into
|
|
1163
|
-
* `data` across a push, and the checker refuses the programs that would.
|
|
1164
|
-
|
|
1180
|
+
* `data` across a push, and the checker refuses the programs that would.
|
|
1181
|
+
*
|
|
1182
|
+
* A push never takes an array past 2^31 - 1 elements (docs/security/codegen.md,
|
|
1183
|
+
* K1-6). Under --number-mode i32 `length` is an `i32`, and one element more
|
|
1184
|
+
* read back as a negative or a short length everywhere, a hash or a tag
|
|
1185
|
+
* compare included. This file cannot tell which mode compiled its caller, so
|
|
1186
|
+
* the limit holds in both, and the push past it fails as an allocation does,
|
|
1187
|
+
* through `nish_oom`. `new Array(n)` checks its own `n` in the IR.
|
|
1188
|
+
*
|
|
1189
|
+
* `noinline`: under LTO the grown body was inlined into every push site, and
|
|
1190
|
+
* the extra compare moved register allocation in the hot loops around them
|
|
1191
|
+
* (+0.2% to +2% instructions over bench/instructions.json's map rows). Out of
|
|
1192
|
+
* line, every one of those rows runs fewer instructions than it did before, and
|
|
1193
|
+
* the call costs nothing on a path that runs once per doubling. */
|
|
1194
|
+
__attribute__((noinline)) void nish_array_grow(nish_array *a, uint64_t elem_size) {
|
|
1165
1195
|
uint64_t cap = a->cap ? a->cap * 2 : 4;
|
|
1196
|
+
if (cap > NISH_ARRAY_MAX) {
|
|
1197
|
+
if (a->len >= NISH_ARRAY_MAX) nish_oom();
|
|
1198
|
+
cap = NISH_ARRAY_MAX;
|
|
1199
|
+
}
|
|
1166
1200
|
char *data = nish_alloc_struct(cap * elem_size);
|
|
1167
1201
|
if (a->len) memcpy(data, a->data, a->len * elem_size);
|
|
1168
1202
|
a->data = data;
|
package/runtime/shim.mjs
CHANGED
|
@@ -740,6 +740,21 @@ export function readSignal() {
|
|
|
740
740
|
return signalFd();
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
+
/**
|
|
744
|
+
* The `nish:net` functions (WP34 N5) have no faithful reading under Node
|
|
745
|
+
* either, for `signalFd`'s reason: Node's sockets are asynchronous only, and a
|
|
746
|
+
* socket becomes readable, writable or connected only to the event loop, which
|
|
747
|
+
* a program that owns its loop and spins or blocks in it never returns to. So
|
|
748
|
+
* each throws, naming itself, rather than answer -11 forever; `nish.mjs`
|
|
749
|
+
* installs one per name.
|
|
750
|
+
* docs/wp33-round-trip.md is the note that asks for one stated answer.
|
|
751
|
+
*/
|
|
752
|
+
export function noNetReading(name) {
|
|
753
|
+
throw new Error(
|
|
754
|
+
`\`${name}\` has no synchronous reading under Node: a socket is ready only to the event loop, which a program that owns its loop never returns to (docs/wp33-round-trip.md)`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
743
758
|
/** `process.argv`: index 0 is the program (the script here, the executable natively), then the arguments. */
|
|
744
759
|
export function argv() {
|
|
745
760
|
return process.argv.slice(1);
|
package/scripts/build.sh
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
#
|
|
4
4
|
# scripts/build.sh <module.ll> [more .ll/.c files...] -o <out> [--profile debug|speed|size|wasm]
|
|
5
5
|
#
|
|
6
|
-
# The C runtime is
|
|
6
|
+
# The C runtime is five translation units and is named as one: an input
|
|
7
7
|
# <dir>/runtime.c also compiles <dir>/runtime-os.c, the half that wraps the
|
|
8
8
|
# system calls (files, directories, subprocesses, the environment, the clock),
|
|
9
9
|
# <dir>/runtime-parallel.c, the half that divides a range of work across
|
|
10
|
-
# threads,
|
|
11
|
-
# signals
|
|
10
|
+
# threads, <dir>/runtime-host.c, the wall clock, entropy, file times and
|
|
11
|
+
# signals, and <dir>/runtime-net.c, the sockets of `nish:net`. Each of those
|
|
12
|
+
# files says why they are compiled and measured apart.
|
|
12
13
|
#
|
|
13
14
|
# Profiles:
|
|
14
15
|
# debug clang defaults: no optimisation, symbols kept. The "before" number.
|
|
@@ -84,9 +85,9 @@ done
|
|
|
84
85
|
[ ${#inputs[@]} -gt 0 ] || { echo "error: no input files" >&2; exit 2; }
|
|
85
86
|
[ -n "$out" ] || { echo "error: -o <out> is required" >&2; exit 2; }
|
|
86
87
|
|
|
87
|
-
# The runtime is
|
|
88
|
-
# <dir>/runtime.c gets <dir>/runtime-os.c, <dir>/runtime-parallel.c
|
|
89
|
-
# <dir>/runtime-host.c compiled beside it. They were one file until the operating-system half was split out
|
|
88
|
+
# The runtime is five translation units, and a caller names one: whoever passes
|
|
89
|
+
# <dir>/runtime.c gets <dir>/runtime-os.c, <dir>/runtime-parallel.c,
|
|
90
|
+
# <dir>/runtime-host.c and <dir>/runtime-net.c compiled beside it. They were one file until the operating-system half was split out
|
|
90
91
|
# for its own size budget, and the parallel half followed for the same reason
|
|
91
92
|
# (each file's header comment says why), and a link line is where those splits
|
|
92
93
|
# would otherwise leak: `nish --link` builds its command line in
|
|
@@ -98,7 +99,7 @@ done
|
|
|
98
99
|
for i in ${inputs[@]+"${inputs[@]}"}; do
|
|
99
100
|
case "$i" in
|
|
100
101
|
*/runtime.c|runtime.c)
|
|
101
|
-
for half in runtime-os.c runtime-parallel.c runtime-host.c; do
|
|
102
|
+
for half in runtime-os.c runtime-parallel.c runtime-host.c runtime-net.c; do
|
|
102
103
|
side="${i%runtime.c}$half"
|
|
103
104
|
have=0
|
|
104
105
|
for j in "${inputs[@]}"; do
|
package/std/README.md
CHANGED
|
@@ -19,22 +19,46 @@ whatever program imports it, and subject to the same rules as `examples/` or
|
|
|
19
19
|
## `nish/crypto` — the primitives under TLS 1.3
|
|
20
20
|
|
|
21
21
|
The first lanes of [WP34](../docs/wp34-hosting-cs.md) §5: K1's hashes, MACs and
|
|
22
|
-
key derivation, and K4's key exchange,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
its
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
key derivation, K2's and K3's AEADs, K4's key exchange, K5's signatures and
|
|
23
|
+
K6's certificates, in
|
|
24
|
+
pure Nish (decision S1), each module imported by its own specifier. Every one
|
|
25
|
+
is written from its specification, with two exceptions that keep their
|
|
26
|
+
upstream notice: `crypto/aes.ts`'s `ghashMul32` is adapted from BearSSL, and
|
|
27
|
+
`crypto/p256.ts`'s field and scalar arithmetic is ported from fiat-crypto
|
|
28
|
+
([`THIRD_PARTY_NOTICES.md`](../THIRD_PARTY_NOTICES.md)). Each reproduces its
|
|
29
|
+
specification's published vectors in its `tests/link/crypto_*` programs. The
|
|
30
|
+
performance gate compiles every module with no diagnostics under both
|
|
31
|
+
`--number-mode i32` and `f64`, and the hashes, HMAC, HKDF, X25519,
|
|
32
|
+
ChaCha20-Poly1305, AES-GCM, P-256 and X.509 also run their vectors in `f64`
|
|
33
|
+
(`crypto_*_f64`).
|
|
34
|
+
|
|
35
|
+
Every module here was security-audited (#363); the records are in
|
|
36
|
+
[`docs/security/`](../docs/security/README.md), and how to report a
|
|
37
|
+
vulnerability is in [`SECURITY.md`](../SECURITY.md). Two limits hold across the
|
|
38
|
+
modules. **Lengths stop at 2^31 − 1.** No array built by `push` or `new Array`,
|
|
39
|
+
and no array or string the runtime makes (a file read, a concatenation, a
|
|
40
|
+
template), can pass 2^31 − 1 elements or bytes, and the one-shot functions
|
|
41
|
+
below refuse a longer input as each row says. A string built by `join` still
|
|
42
|
+
can, and under `--number-mode i32` its `length` is then wrong, which nothing in
|
|
43
|
+
`std` can tell (CG-3, open): a program built in that mode must not hand these
|
|
44
|
+
functions anything built from such a string. **Secrets are not wiped.** Private keys,
|
|
45
|
+
nonces and intermediate state stay in arena memory after a call returns, until
|
|
46
|
+
that memory is reused, because the language has no store the optimiser is
|
|
47
|
+
barred from removing (ECC-2, X509-7, open).
|
|
28
48
|
|
|
29
49
|
| Module | What it is | Reproduces |
|
|
30
50
|
| --- | --- | --- |
|
|
31
|
-
| [`crypto/sha256.ts`](./crypto/sha256.ts) | `sha256(data)`, and `Sha256`, a streaming hasher: `update(buf, off, len)` over a window of a `u8[]`, `copy()` for the hash of a prefix while the original keeps going, and `digest()`, a fresh 32-byte array. `SHA256_SIZE` and `SHA256_BLOCK` | FIPS 180-4 §6.2 |
|
|
32
|
-
| [`crypto/sha512.ts`](./crypto/sha512.ts) | SHA-512 and SHA-384 on one compression function: `sha512` and `sha384`, and the streaming `Sha512` and `Sha384` with `Sha256`'s three methods; digests of 64 and 48 bytes. `SHA512_SIZE`, `SHA384_SIZE` and `SHA512_BLOCK` | FIPS 180-4 §6.4, §6.5 |
|
|
33
|
-
| [`crypto/hmac.ts`](./crypto/hmac.ts) | `hmacSha256` and `hmacSha384`, the streaming `HmacSha256` and `HmacSha384` (keyed in the constructor, then `update` and `digest`), and `hmacSha256Verify` / `hmacSha384Verify`, which compare a received tag with `timingSafeEqual
|
|
34
|
-
| [`crypto/hkdf.ts`](./crypto/hkdf.ts) | `hkdfExtractSha256` / `hkdfExtractSha384` (an empty salt is HashLen zeros) and `hkdfExpandSha256` / `hkdfExpandSha384`, which answer `null` for a length below zero or above 255 × HashLen. TLS 1.3's HKDF-Expand-Label is not here; it belongs with TLS | RFC 5869 §2, Appendix A |
|
|
35
|
-
| [`crypto/ct.ts`](./crypto/ct.ts) | `timingSafeEqual(a, b)`, which reads every byte whatever it holds, and `timingSafeEqualAt(a, aOff, b, bOff, len)` over two windows, which answers `false` for a window outside its array. Two lengths that differ answer `false` at once, because a length is public | — |
|
|
36
|
-
| [`crypto/base64url.ts`](./crypto/base64url.ts) | `base64urlEncode(data)` and `base64urlDecode(text)`, unpadded. Decoding is strict, so every byte string has one spelling: a `=`, a character outside the alphabet, a length of 1 mod 4 or nonzero unused low bits answer `null` | RFC 4648 §5, §10 |
|
|
37
|
-
| [`crypto/x25519.ts`](./crypto/x25519.ts) | `x25519(scalar, u)` and `x25519Base(scalar)`, on ten 25.5-bit limbs in `i64`. Either answers `null` unless its arguments are `X25519_SIZE` (32) bytes; the scalar is clamped on a copy | RFC 7748 §5.2, §6.1 |
|
|
51
|
+
| [`crypto/sha256.ts`](./crypto/sha256.ts) | `sha256(data)`, and `Sha256`, a streaming hasher: `update(buf, off, len)` over a window of a `u8[]`, `copy()` for the hash of a prefix while the original keeps going, and `digest()`, a fresh 32-byte array. `SHA256_SIZE` and `SHA256_BLOCK`. `sha256` panics on an array longer than 2^31 − 1 bytes | FIPS 180-4 §6.2 |
|
|
52
|
+
| [`crypto/sha512.ts`](./crypto/sha512.ts) | SHA-512 and SHA-384 on one compression function: `sha512` and `sha384`, and the streaming `Sha512` and `Sha384` with `Sha256`'s three methods; digests of 64 and 48 bytes. `SHA512_SIZE`, `SHA384_SIZE` and `SHA512_BLOCK`. The one-shot `sha512` and `sha384` panic on an array longer than 2^31 − 1 bytes | FIPS 180-4 §6.4, §6.5 |
|
|
53
|
+
| [`crypto/hmac.ts`](./crypto/hmac.ts) | `hmacSha256` and `hmacSha384`, the streaming `HmacSha256` and `HmacSha384` (keyed in the constructor, then `update` and `digest`), and `hmacSha256Verify` / `hmacSha384Verify`, which compare a received tag with `timingSafeEqual`. The one-shot functions panic on a message longer than 2^31 − 1 bytes | RFC 2104, RFC 4231 §4 |
|
|
54
|
+
| [`crypto/hkdf.ts`](./crypto/hkdf.ts) | `hkdfExtractSha256` / `hkdfExtractSha384` (an empty salt is HashLen zeros) and `hkdfExpandSha256` / `hkdfExpandSha384`, which answer `null` for a length below zero or above 255 × HashLen, for an `info` longer than 2^31 − 1 bytes, and for a PRK shorter than HashLen. TLS 1.3's HKDF-Expand-Label is not here; it belongs with TLS | RFC 5869 §2, Appendix A |
|
|
55
|
+
| [`crypto/ct.ts`](./crypto/ct.ts) | `timingSafeEqual(a, b)`, which reads every byte whatever it holds, and `timingSafeEqualAt(a, aOff, b, bOff, len)` over two windows, which answers `false` for a window outside its array. Two lengths that differ answer `false` at once, because a length is public, and so does an array longer than 2^31 − 1 bytes | — |
|
|
56
|
+
| [`crypto/base64url.ts`](./crypto/base64url.ts) | `base64urlEncode(data)` and `base64urlDecode(text)`, unpadded. Decoding is strict, so every byte string has one spelling: a `=`, a character outside the alphabet, a length of 1 mod 4 or nonzero unused low bits answer `null`, as does a text longer than 2^31 − 1 bytes; `base64urlEncode` panics on such an array | RFC 4648 §5, §10 |
|
|
57
|
+
| [`crypto/x25519.ts`](./crypto/x25519.ts) | `x25519(scalar, u)` and `x25519Base(scalar)`, on ten 25.5-bit limbs in `i64`. Either answers `null` unless its arguments are `X25519_SIZE` (32) bytes; the scalar is clamped on a copy | RFC 7748 §5.2, §6.1, Wycheproof `x25519_test` |
|
|
58
|
+
| [`crypto/chacha20poly1305.ts`](./crypto/chacha20poly1305.ts) | `chacha20Poly1305Seal(key, nonce, aad, plaintext)`, which answers the ciphertext followed by the 16-byte tag, and `chacha20Poly1305Open`, which checks the whole tag before it decrypts anything and answers `null` for a message that does not authenticate. Beneath them `chacha20Block`, `chacha20`, `chacha20QuarterRound`, `poly1305` and `poly1305KeyGen`, and `chacha20HeaderMask`, QUIC's 5-byte header-protection mask. Every input of the wrong length answers `null` | RFC 8439 §2, RFC 9001 §5.4.4, Wycheproof `chacha20_poly1305` (all 325 cases) |
|
|
59
|
+
| [`crypto/aes.ts`](./crypto/aes.ts) | AES-128 and AES-256, bitsliced four blocks at a time with the Boyar–Peralta S-box circuit, so no table is read: `aesKey(key)` expands a 16- or 32-byte key into an `AesKey`, then `aesEncryptBlock`, `aesGcmSeal` / `aesGcmOpen` (tag last, checked in full before anything is decrypted; any non-empty IV) and `aesHeaderMask`. AES-192 is out of scope, and a wrong length answers `null`, as does an `AesKey` not made by `aesKey`; one whose H is zero opens nothing | FIPS 197, SP 800-38D, RFC 9001 §5.4.3, Wycheproof `aes_gcm` (all 316 cases; the 103 AES-192 ones check that the key is refused) |
|
|
60
|
+
| [`crypto/p256.ts`](./crypto/p256.ts) | ECDSA over P-256 with RFC 6979's deterministic nonces: `p256PublicKey(priv)` (65-byte uncompressed SEC1), `p256Sign` / `p256Verify` over a digest and `p256SignSha256` / `p256VerifySha256` over a message; a signature is `r` and `s`, 32 big-endian bytes each, not DER (`crypto/x509.ts`'s `x509DerSignatureRS` converts one). A malformed key, a point off the curve or an `r` or `s` out of range answers `null` or `false`, never a panic. ECDSA is malleable, and `p256Verify` accepts a high `s`: when `(r, s)` verifies, so does `(r, n − s)` | RFC 6979 A.2.5, Wycheproof `ecdsa_secp256r1_sha256`, `_sha256_p1363`, `_sha512` and `_sha512_p1363` |
|
|
61
|
+
| [`crypto/x509.ts`](./crypto/x509.ts) | DER, PEM and X.509 over P-256: `pemToDer` / `derToPem` (RFC 7468, padded standard base64, strict labels), `x509ParseP256PrivateKey` (SEC1 or PKCS#8), `x509ParseCertificate` / `x509ParseChain` into an `X509Certificate`, `x509VerifySignature` (ecdsa-with-SHA256 under an issuer's key), `x509DerSignatureRS` (a DER ECDSA-Sig-Value as the `r || s` `crypto/p256.ts` takes), `x509MintSelfSigned`, the 1-to-14-day self-signed P-256 certificate WebTransport's `serverCertificateHashes` accepts, and `x509CertificateHash`, its SHA-256. DER is read strictly — non-minimal lengths and integers, indefinite lengths and trailing bytes answer `null`. `x509ParseCertificate` and `x509ParseChain` parse and do not validate: there is no path validation, no extension is read or exposed, and no clock is consulted. `pemToDer` refuses a file in which any block, of any label, is malformed. `x509MintSelfSigned` refuses a common name that is not UTF-8 or holds a NUL, and its key and serial must come from `crypto.getRandomValues`, which the module cannot call itself without refusing wasm32 (X509-6) | X.690, RFC 5280, RFC 7468, RFC 5915, RFC 5208 / 5958; certificates checked against OpenSSL |
|
|
38
62
|
|
|
39
63
|
Three rules hold across the modules:
|
|
40
64
|
|
|
@@ -45,16 +69,57 @@ Three rules hold across the modules:
|
|
|
45
69
|
that is itself spent, so any `update` or `digest` on it panics. Either way,
|
|
46
70
|
copy *before* `digest` when the computation has to go on.
|
|
47
71
|
- **An all-zero X25519 result is returned, not refused.** It is what a
|
|
48
|
-
low-order `u` gives, and RFC 7748 §6.1 leaves the check to the protocol
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
low-order `u` gives, and RFC 7748 §6.1 leaves the check to the protocol.
|
|
73
|
+
Nothing in this tree makes that check yet (TLS 1.3, WP34 T1, is planned to),
|
|
74
|
+
so every caller doing a key exchange must refuse an all-zero answer itself,
|
|
75
|
+
with `timingSafeEqual` against 32 zero bytes, since the answer is a secret.
|
|
76
|
+
- **Constant time by construction, and verified where the check reaches.** No
|
|
77
|
+
module branches on, or indexes by, a secret: comparisons OR the differences
|
|
78
|
+
into one word and test it once, the ladder swaps with a mask and always runs
|
|
79
|
+
255 steps, AES reads no table, P-256's window reads all sixteen entries and
|
|
80
|
+
keeps one by mask, and base64url maps characters by arithmetic on range masks
|
|
81
|
+
rather than a table. Every branch is on a length, a loop counter or a bit
|
|
82
|
+
position. WP34 N6 checks that the machine code kept that shape:
|
|
83
|
+
`tests/ct-asm.js` disassembles golden fixtures on x86-64 and aarch64 and
|
|
84
|
+
refuses a branch, a call, a secret-addressed load, or a divide or square
|
|
85
|
+
root of a secret (whose latency follows its operands) in them. It reads what
|
|
86
|
+
`clang -O2` emits for each target's baseline CPU, and only the functions a
|
|
87
|
+
fixture names: the fixtures are copies of each module's straight-line cores,
|
|
88
|
+
so the module functions themselves, and every loop, rest on review.
|
|
89
|
+
[`docs/security/ct-verification.md`](../docs/security/ct-verification.md)
|
|
90
|
+
lists every secret-handling function no fixture reads. What it holds, per
|
|
91
|
+
module, is what that module's header says:
|
|
92
|
+
- `tests/cases/ct_asm_mac` pins the two loop shapes the others are built
|
|
93
|
+
from, a full tag compare and a masked table read; `crypto/hmac.ts`'s
|
|
94
|
+
verifiers compare in the first shape.
|
|
95
|
+
- `tests/cases/ct_asm_k1_ct`: copies of `timingSafeEqual`'s loop at 32 and
|
|
96
|
+
48 bytes and `timingSafeEqualAt`'s at a 16-byte window.
|
|
97
|
+
- `tests/cases/ct_asm_k1_base64url`: the two character maps verbatim, and
|
|
98
|
+
one encode group and one decode group. The length-driven loops, the
|
|
99
|
+
decoder's store guard and the encoder's string building remain discipline.
|
|
100
|
+
- `tests/cases/ct_asm_x25519`: X25519's field multiply, square, add,
|
|
101
|
+
subtract and multiply by a24, its conditional swap, and one step of the
|
|
102
|
+
Montgomery ladder, the check following each call into the field functions.
|
|
103
|
+
The 255-step loop that drives the ladder, the inversion and the encodings
|
|
104
|
+
remain discipline.
|
|
105
|
+
- `tests/cases/ct_asm_chacha20poly1305`: Poly1305's key clamp, one block,
|
|
106
|
+
the final reduction with `s`, and the tag compare. The ChaCha20 rounds and
|
|
107
|
+
the loops that drive both halves remain discipline.
|
|
108
|
+
- `tests/cases/ct_asm_aes`: one full bitsliced round, one GHASH multiply and
|
|
109
|
+
the tag compare. Packing, the key schedule and the loops over blocks
|
|
110
|
+
remain discipline.
|
|
111
|
+
- `tests/cases/ct_asm_p256`: fiat's field multiply, square, add and
|
|
112
|
+
subtract, its scalar multiply and its conditional move; the sixteen-entry
|
|
113
|
+
table read by a secret digit; the complete doubling and addition; and one
|
|
114
|
+
window step of the scalar multiplication (four doublings, the table read
|
|
115
|
+
and an addition), the check following each call into the field functions.
|
|
116
|
+
The 64-window loop, the table build, the inversions, the encodings and
|
|
117
|
+
RFC 6979's nonce derivation remain discipline.
|
|
118
|
+
|
|
119
|
+
The SHA-2 hashes and HKDF are additions, rotations and xors that branch
|
|
120
|
+
only on lengths, and are not in a fixture either; nor is `crypto/x509.ts`,
|
|
121
|
+
whose one secret, a private key, is base64-decoded by range masks and
|
|
122
|
+
otherwise only copied and handed to `crypto/p256.ts`.
|
|
58
123
|
|
|
59
124
|
## How a program imports it
|
|
60
125
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
18
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
19
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file).
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|