@amritk/nish-aarch64-linux 0.13.0 → 0.15.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/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +156 -0
- package/runtime/nish.h +89 -0
- package/runtime/nish.mjs +58 -0
- package/runtime/runtime-host.c +178 -0
- package/runtime/runtime-net.c +528 -0
- package/runtime/runtime-os.c +15 -0
- package/runtime/shim.mjs +137 -0
- package/scripts/build.sh +9 -7
- package/std/README.md +80 -0
- package/std/crypto/LICENSE-bearssl +21 -0
- package/std/crypto/LICENSE-fiat-crypto +21 -0
- package/std/crypto/aes.ts +899 -0
- package/std/crypto/base64url.ts +145 -0
- package/std/crypto/chacha20poly1305.ts +698 -0
- package/std/crypto/ct.ts +64 -0
- package/std/crypto/hkdf.ts +118 -0
- package/std/crypto/hmac.ts +155 -0
- package/std/crypto/p256.ts +4337 -0
- package/std/crypto/sha256.ts +444 -0
- package/std/crypto/sha512.ts +510 -0
- package/std/crypto/x25519.ts +509 -0
- package/std/crypto/x509.ts +1214 -0
package/runtime/shim.mjs
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* The rewrite rules that call these helpers are listed in docs/wp13-differential.md.
|
|
43
43
|
*/
|
|
44
44
|
import child_process from "node:child_process";
|
|
45
|
+
import { webcrypto } from "node:crypto";
|
|
45
46
|
import fs from "node:fs";
|
|
46
47
|
import os from "node:os";
|
|
47
48
|
|
|
@@ -91,6 +92,37 @@ export function bitsToF64(b) {
|
|
|
91
92
|
return BITS.getFloat64(0);
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
/**
|
|
96
|
+
* `ctSelect` / `ctEq` (WP34 N6). A `u32` is a `number` here and a `u64` a
|
|
97
|
+
* BigInt, so the operands' kind picks the width, and a mix of the two — which
|
|
98
|
+
* the native checker refuses, and which a `u64` written as a bare literal is
|
|
99
|
+
* under an unrewritten run — throws the `TypeError` BigInt arithmetic throws
|
|
100
|
+
* rather than comparing a number with a BigInt and answering zero. JavaScript's
|
|
101
|
+
* `&` reads a `number` as a signed 32-bit integer, so each answer is put back in
|
|
102
|
+
* range with `>>> 0` or `asUintN(64, ...)`. These branch: only the native
|
|
103
|
+
* lowering promises constant time.
|
|
104
|
+
*/
|
|
105
|
+
function ctWide(name, first, second, third) {
|
|
106
|
+
const wide = typeof first === "bigint";
|
|
107
|
+
if ((typeof second === "bigint") !== wide || (typeof third === "bigint") !== wide) {
|
|
108
|
+
throw new TypeError(`${name}: cannot mix a u64 (BigInt) with a u32 (number)`);
|
|
109
|
+
}
|
|
110
|
+
return wide;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const U64_ONES = (1n << 64n) - 1n;
|
|
114
|
+
|
|
115
|
+
export function ctSelect(mask, a, b) {
|
|
116
|
+
if (ctWide("ctSelect", mask, a, b)) return wrapU64((a & mask) | (b & ~mask));
|
|
117
|
+
return ((a & mask) | (b & ~mask)) >>> 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function ctEq(a, b) {
|
|
121
|
+
// `ctEq` has two operands, so the second one stands in for the third.
|
|
122
|
+
if (ctWide("ctEq", a, b, b)) return wrapU64(a ^ b) === 0n ? U64_ONES : 0n;
|
|
123
|
+
return (a ^ b) === 0 ? 0xffffffff : 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
/** Wrap a BigInt to the i64 range: every i64 `+ - * /` and unary minus goes through here. */
|
|
95
127
|
export function wrapI64(x) {
|
|
96
128
|
return BigInt.asIntN(64, x);
|
|
@@ -401,6 +433,28 @@ export function updIdx(a, i, f) {
|
|
|
401
433
|
return v;
|
|
402
434
|
}
|
|
403
435
|
|
|
436
|
+
/**
|
|
437
|
+
* `dst.set(src, offset)` (WP34 N2): `TypedArray.prototype.set`'s copy on the
|
|
438
|
+
* plain array a `u8[]` is here. The source is copied first, so a self-copy or
|
|
439
|
+
* an overlapping one reads what was there before, as `memmove` does natively;
|
|
440
|
+
* a range past the end fails with the native panic and its words, where a
|
|
441
|
+
* typed array would throw a `RangeError` for the same offsets.
|
|
442
|
+
*/
|
|
443
|
+
export function arraySet(dst, src, offset) {
|
|
444
|
+
// `ToIntegerOrInfinity`: NaN is 0, as `llvm.fptosi.sat` makes it natively.
|
|
445
|
+
// `Math.trunc` rather than `toIndex`, which converts a bigint: an `i64` or
|
|
446
|
+
// `u64` offset is a bigint here, and it throws the `TypeError` the typed
|
|
447
|
+
// array and `Array.prototype.fill` throw for one, instead of being rounded
|
|
448
|
+
// to the nearest double past 2^53 (docs/RUN_UNDER_NODE.md).
|
|
449
|
+
const at = offset === undefined ? 0 : Math.trunc(offset) || 0;
|
|
450
|
+
const end = at + src.length;
|
|
451
|
+
if (!(at >= 0 && end <= dst.length)) panicSlice(at, end, dst.length);
|
|
452
|
+
// Two plain arrays overlap only when they are one array, which is the one
|
|
453
|
+
// case that must read the source before writing it.
|
|
454
|
+
const from = src === dst ? src.slice() : src;
|
|
455
|
+
for (let i = 0; i < from.length; i++) dst[at + i] = from[i];
|
|
456
|
+
}
|
|
457
|
+
|
|
404
458
|
/** `new Array<T>(n)`: `n` zero-filled elements (`0`, `0n`, or `false`). */
|
|
405
459
|
export function newArray(n, zero) {
|
|
406
460
|
return new Array(toIndex(n)).fill(zero);
|
|
@@ -434,6 +488,15 @@ export function readFileSyncOrNull(path) {
|
|
|
434
488
|
}
|
|
435
489
|
}
|
|
436
490
|
|
|
491
|
+
/** `readFileBytesSync(path)` (WP34 N2): the bytes as a plain array of numbers, or null. */
|
|
492
|
+
export function readFileBytesSync(path) {
|
|
493
|
+
try {
|
|
494
|
+
return Array.from(fs.readFileSync(path));
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
437
500
|
export function writeFileSync(path, data) {
|
|
438
501
|
try {
|
|
439
502
|
fs.writeFileSync(path, data, "utf8");
|
|
@@ -618,6 +681,80 @@ export function monotonicNanos() {
|
|
|
618
681
|
return process.hrtime.bigint();
|
|
619
682
|
}
|
|
620
683
|
|
|
684
|
+
// ---- The host (WP34 N3) ------------------------------------------------------
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* `statMtimeSync(path)`: Node's `mtimeMs` for the path, or NaN when it cannot
|
|
688
|
+
* be stat'd, which is the native answer too. `mtimeMs` is the same arithmetic
|
|
689
|
+
* `runtime-host.c` does, so the two print the same digits, fraction and all.
|
|
690
|
+
*/
|
|
691
|
+
export function statMtimeSync(path) {
|
|
692
|
+
const st = fs.statSync(path, { throwIfNoEntry: false });
|
|
693
|
+
if (st === undefined) {
|
|
694
|
+
return Number.NaN;
|
|
695
|
+
}
|
|
696
|
+
return st.mtimeMs;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Node's own fill, taken before `runtime/nish.mjs` puts the one below in its
|
|
701
|
+
* place on the same object: `webcrypto` is the global `crypto`.
|
|
702
|
+
*/
|
|
703
|
+
const webRandom = webcrypto.getRandomValues.bind(webcrypto);
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* `crypto.getRandomValues(bytes)` for the plain array a `u8[]` is here. Node's
|
|
707
|
+
* own takes only a typed array, so the bytes are drawn into one and copied
|
|
708
|
+
* across. More than 65,536 fails with the native panic and its words, where
|
|
709
|
+
* Node would throw a `QuotaExceededError`: the exit status is 1 either way. A
|
|
710
|
+
* typed array goes straight through.
|
|
711
|
+
*/
|
|
712
|
+
export function getRandomValues(bytes) {
|
|
713
|
+
if (!Array.isArray(bytes)) {
|
|
714
|
+
return webRandom(bytes);
|
|
715
|
+
}
|
|
716
|
+
if (bytes.length > 65536) {
|
|
717
|
+
panic(`crypto.getRandomValues: ${bytes.length} bytes asked for, and one call fills at most 65536`);
|
|
718
|
+
}
|
|
719
|
+
const drawn = webRandom(new Uint8Array(bytes.length));
|
|
720
|
+
for (let i = 0; i < drawn.length; i++) {
|
|
721
|
+
bytes[i] = drawn[i];
|
|
722
|
+
}
|
|
723
|
+
return bytes;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* `signalFd()` and `readSignal(fd)` have no faithful reading under Node, and
|
|
728
|
+
* these say so rather than answer something else. Node delivers a signal to
|
|
729
|
+
* its event loop (`process.on("SIGTERM")`), and a blocking read keeps the loop
|
|
730
|
+
* from ever running, so no synchronous function here can learn that one
|
|
731
|
+
* arrived. docs/wp33-round-trip.md §3.5 has the row and the translation.
|
|
732
|
+
*/
|
|
733
|
+
export function signalFd() {
|
|
734
|
+
throw new Error(
|
|
735
|
+
"signalFd has no synchronous reading under Node: a signal reaches the event loop, which a blocking readSignal never returns to (docs/wp33-round-trip.md)"
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export function readSignal() {
|
|
740
|
+
return signalFd();
|
|
741
|
+
}
|
|
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
|
+
|
|
621
758
|
/** `process.argv`: index 0 is the program (the script here, the executable natively), then the arguments. */
|
|
622
759
|
export function argv() {
|
|
623
760
|
return process.argv.slice(1);
|
package/scripts/build.sh
CHANGED
|
@@ -3,11 +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
|
-
#
|
|
10
|
-
# threads.
|
|
9
|
+
# <dir>/runtime-parallel.c, the half that divides a range of work across
|
|
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.
|
|
11
13
|
#
|
|
12
14
|
# Profiles:
|
|
13
15
|
# debug clang defaults: no optimisation, symbols kept. The "before" number.
|
|
@@ -83,9 +85,9 @@ done
|
|
|
83
85
|
[ ${#inputs[@]} -gt 0 ] || { echo "error: no input files" >&2; exit 2; }
|
|
84
86
|
[ -n "$out" ] || { echo "error: -o <out> is required" >&2; exit 2; }
|
|
85
87
|
|
|
86
|
-
# The runtime is
|
|
87
|
-
# <dir>/runtime.c gets <dir>/runtime-os.c
|
|
88
|
-
# 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
|
|
89
91
|
# for its own size budget, and the parallel half followed for the same reason
|
|
90
92
|
# (each file's header comment says why), and a link line is where those splits
|
|
91
93
|
# would otherwise leak: `nish --link` builds its command line in
|
|
@@ -97,7 +99,7 @@ done
|
|
|
97
99
|
for i in ${inputs[@]+"${inputs[@]}"}; do
|
|
98
100
|
case "$i" in
|
|
99
101
|
*/runtime.c|runtime.c)
|
|
100
|
-
for half in runtime-os.c runtime-parallel.c; do
|
|
102
|
+
for half in runtime-os.c runtime-parallel.c runtime-host.c runtime-net.c; do
|
|
101
103
|
side="${i%runtime.c}$half"
|
|
102
104
|
have=0
|
|
103
105
|
for j in "${inputs[@]}"; do
|
package/std/README.md
CHANGED
|
@@ -16,6 +16,86 @@ whatever program imports it, and subject to the same rules as `examples/` or
|
|
|
16
16
|
| [`map.ts`](./map.ts) | `reserve(m, n)` and `getOrInsert(m, k, v)` for the global `Map`. Their bodies are the meaning, and what runs under Node: `reserve` does nothing, and `getOrInsert` is a `get`, and a `set` of `v` when the key was missing. Natively the compiler lowers every call in place — `reserve` to the table's `reserveSlots`, which grows the buckets once so that `n` entries fit without a rebuild, and `getOrInsert` to one `probe` and a `valueAt` or an `insertAt` through its answer — so, like `collections.ts`, it writes no `.ll` of its own ([`docs/wp32-map.md`](../docs/wp32-map.md) §9.2, [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#map-and-set)) |
|
|
17
17
|
| [`threads.ts`](./threads.ts) | `parallelMapInto(src, dst, f)` and `parallelReduce(src, f, identity)`: a function over every element of an array, on as many threads as the length is worth. Its bodies are the sequential meaning, which is what runs under Node; the compiler recognises the two templates by module and name, lowers the one loop in each onto `nish_parallel_range`, holds the function to the rules that make that safe, and compiles an importing program with `--threads` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#data-parallelism-nishthreads)). `tests/link/par_*` are its programs |
|
|
18
18
|
|
|
19
|
+
## `nish/crypto` — the primitives under TLS 1.3
|
|
20
|
+
|
|
21
|
+
The first lanes of [WP34](../docs/wp34-hosting-cs.md) §5: K1's hashes, MACs and
|
|
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
|
+
| Module | What it is | Reproduces |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| [`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 |
|
|
38
|
+
| [`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 |
|
|
39
|
+
| [`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` | RFC 2104, RFC 4231 §4 |
|
|
40
|
+
| [`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 |
|
|
41
|
+
| [`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 | — |
|
|
42
|
+
| [`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 |
|
|
43
|
+
| [`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 |
|
|
44
|
+
| [`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 |
|
|
45
|
+
| [`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` | FIPS 197, SP 800-38D, RFC 9001 §5.4.3, Wycheproof `aes_gcm` |
|
|
46
|
+
| [`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. A malformed key, a point off the curve or an `r` or `s` out of range answers `null` or `false`, never a panic | RFC 6979 A.2.5, Wycheproof `ecdsa_secp256r1_sha256` |
|
|
47
|
+
| [`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), `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` | X.690, RFC 5280, RFC 7468, RFC 5915, RFC 5208 / 5958; certificates checked against OpenSSL |
|
|
48
|
+
|
|
49
|
+
Three rules hold across the modules:
|
|
50
|
+
|
|
51
|
+
- **A digest ends the computation.** After `digest()` on a hasher or an HMAC, a
|
|
52
|
+
further `update` or `digest` panics rather than answering a hash over the
|
|
53
|
+
padding, and so does a window outside its buffer. `Sha256.copy()` on a
|
|
54
|
+
digested hasher panics too; `Sha512.copy()` and `Sha384.copy()` answer a copy
|
|
55
|
+
that is itself spent, so any `update` or `digest` on it panics. Either way,
|
|
56
|
+
copy *before* `digest` when the computation has to go on.
|
|
57
|
+
- **An all-zero X25519 result is returned, not refused.** It is what a
|
|
58
|
+
low-order `u` gives, and RFC 7748 §6.1 leaves the check to the protocol; TLS
|
|
59
|
+
1.3 (WP34 T1) makes it. A key exchange outside TLS has to make it itself.
|
|
60
|
+
- **Constant time by construction, and verified where the check reaches.** No
|
|
61
|
+
module branches on, or indexes by, a secret: comparisons OR the differences
|
|
62
|
+
into one word and test it once, the ladder swaps with a mask and always runs
|
|
63
|
+
255 steps, AES reads no table, P-256's window reads all sixteen entries and
|
|
64
|
+
keeps one by mask, and base64url maps characters by arithmetic on range masks
|
|
65
|
+
rather than a table. Every branch is on a length, a loop counter or a bit
|
|
66
|
+
position. WP34 N6 checks that the machine code kept that shape:
|
|
67
|
+
`tests/ct-asm.js` disassembles golden fixtures on x86-64 and aarch64 and
|
|
68
|
+
refuses a branch, a call or a secret-addressed load in them. What it holds,
|
|
69
|
+
per module, is what that module's header says:
|
|
70
|
+
- `tests/cases/ct_asm_mac` pins the two loop shapes the others are built
|
|
71
|
+
from, a full tag compare and a masked table read. `crypto/ct.ts` and
|
|
72
|
+
`crypto/hmac.ts`'s verifiers compare in the first shape and
|
|
73
|
+
`crypto/base64url.ts` selects by mask, but none of those K1 modules is
|
|
74
|
+
itself in a fixture, so they remain discipline.
|
|
75
|
+
- `tests/cases/ct_asm_x25519`: X25519's field multiply, square, add,
|
|
76
|
+
subtract and multiply by a24, its conditional swap, and one step of the
|
|
77
|
+
Montgomery ladder, the check following each call into the field functions.
|
|
78
|
+
The 255-step loop that drives the ladder, the inversion and the encodings
|
|
79
|
+
remain discipline.
|
|
80
|
+
- `tests/cases/ct_asm_chacha20poly1305`: Poly1305's key clamp, one block,
|
|
81
|
+
the final reduction with `s`, and the tag compare. The ChaCha20 rounds and
|
|
82
|
+
the loops that drive both halves remain discipline.
|
|
83
|
+
- `tests/cases/ct_asm_aes`: one full bitsliced round, one GHASH multiply and
|
|
84
|
+
the tag compare. Packing, the key schedule and the loops over blocks
|
|
85
|
+
remain discipline.
|
|
86
|
+
- `tests/cases/ct_asm_p256`: fiat's field multiply, square, add and
|
|
87
|
+
subtract, its scalar multiply and its conditional move; the sixteen-entry
|
|
88
|
+
table read by a secret digit; the complete doubling and addition; and one
|
|
89
|
+
window step of the scalar multiplication (four doublings, the table read
|
|
90
|
+
and an addition), the check following each call into the field functions.
|
|
91
|
+
The 64-window loop, the table build, the inversions, the encodings and
|
|
92
|
+
RFC 6979's nonce derivation remain discipline.
|
|
93
|
+
|
|
94
|
+
The SHA-2 hashes and HKDF are additions, rotations and xors that branch
|
|
95
|
+
only on lengths, and are not in a fixture either; nor is `crypto/x509.ts`,
|
|
96
|
+
whose one secret, a private key, is base64-decoded by range masks and
|
|
97
|
+
otherwise only copied and handed to `crypto/p256.ts`.
|
|
98
|
+
|
|
19
99
|
## How a program imports it
|
|
20
100
|
|
|
21
101
|
By its package specifier:
|
|
@@ -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.
|