@amritk/nish-x86_64-linux 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +58 -40
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +67 -10
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +72 -26
- package/runtime/runtime-net.c +72 -19
- package/runtime/runtime-os.c +127 -45
- package/runtime/runtime-parallel.c +5 -1
- package/runtime/runtime-wasm.c +26 -2
- package/runtime/runtime.c +86 -14
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +1 -3
- package/std/README.md +192 -36
- package/std/crypto/aes.ts +104 -21
- package/std/crypto/base64url.ts +43 -17
- package/std/crypto/chacha20poly1305.ts +52 -31
- package/std/crypto/ct.ts +14 -3
- package/std/crypto/hkdf.ts +385 -8
- package/std/crypto/hmac.ts +325 -9
- package/std/crypto/p256.ts +153 -27
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +15 -3
- package/std/crypto/sha512.ts +16 -2
- package/std/crypto/x25519.ts +90 -37
- package/std/crypto/x509.ts +265 -65
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +905 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +392 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +653 -0
- package/std/net/quic-packet.ts +1474 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1603 -0
- package/std/net/quic.ts +3214 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +410 -0
- package/std/net/tls.ts +1368 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
package/std/crypto/hmac.ts
CHANGED
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
* which HMAC does not keep secret; a received tag is compared with
|
|
25
25
|
* `timingSafeEqual`, never `===`.
|
|
26
26
|
*
|
|
27
|
+
* `HmacSha256Scratch` and `HmacSha384Scratch` are the same computation in
|
|
28
|
+
* hashers the caller allocates once: `begin` keys them again in place and
|
|
29
|
+
* `finishInto` writes the tag into the caller's array, so a loop of them
|
|
30
|
+
* stores nothing of its own and an arena scope around it releases all it
|
|
31
|
+
* allocated (see "Caller-owned scratch" below).
|
|
32
|
+
*
|
|
27
33
|
* Written from RFC 2104, not ported from another implementation.
|
|
28
34
|
*/
|
|
29
35
|
import { SHA256_BLOCK, SHA256_SIZE, Sha256, sha256 } from "nish/crypto/sha256"
|
|
@@ -40,25 +46,33 @@ const HMAC_OPAD: i32 = 0x5c
|
|
|
40
46
|
const HMAC_FROM: i32 = 0
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
|
-
* `key XOR pad` over a whole block
|
|
44
|
-
* longer than
|
|
45
|
-
* zero padding of RFC 2104 §2 step (1) would be.
|
|
49
|
+
* `key[off .. off + len) XOR pad` over a whole block, `out`, with the key
|
|
50
|
+
* already no longer than the block: the key's bytes XOR `pad`, then `pad`
|
|
51
|
+
* alone where the zero padding of RFC 2104 §2 step (1) would be.
|
|
46
52
|
*/
|
|
47
|
-
const
|
|
53
|
+
const hmacPadInto = (out: u8[], key: u8[], off: i32, len: i32, pad: i32): void => {
|
|
48
54
|
const padByte: u8 = toU8(pad)
|
|
49
|
-
const out: u8[] = new Array<u8>(blockSize)
|
|
50
55
|
const outLength: i32 = toI32(out.length)
|
|
51
56
|
const keyLength: i32 = toI32(key.length)
|
|
52
57
|
// Bounded by both lengths, so both indices are proved in range.
|
|
53
58
|
let i: i32 = 0
|
|
54
|
-
while (i <
|
|
55
|
-
out[i] = key[i] ^ padByte
|
|
59
|
+
while (i < len && i < outLength && off + i >= 0 && off + i < keyLength) {
|
|
60
|
+
out[i] = key[off + i] ^ padByte
|
|
56
61
|
i += 1
|
|
57
62
|
}
|
|
58
63
|
while (i < outLength) {
|
|
59
64
|
out[i] = padByte
|
|
60
65
|
i += 1
|
|
61
66
|
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `key XOR pad` over a whole block of `blockSize` bytes, in a fresh array, with
|
|
71
|
+
* `key` already no longer than a block (`hmacPadInto`).
|
|
72
|
+
*/
|
|
73
|
+
const hmacPadBlock = (key: u8[], blockSize: i32, pad: i32): u8[] => {
|
|
74
|
+
const out: u8[] = new Array<u8>(blockSize)
|
|
75
|
+
hmacPadInto(out, key, HMAC_FROM, toI32(key.length), pad)
|
|
62
76
|
return out
|
|
63
77
|
}
|
|
64
78
|
|
|
@@ -127,15 +141,31 @@ export class HmacSha384 {
|
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* The HMAC-SHA-256 of all of `data` under `key`, as a fresh 32-byte array.
|
|
146
|
+
*
|
|
147
|
+
* `data` longer than 2^31 - 1 bytes panics, as `sha256` does and for its
|
|
148
|
+
* reason: under `--number-mode f64` the tag would be of a prefix, and a tag
|
|
149
|
+
* that verifies every message sharing that prefix is a forgery. A key that
|
|
150
|
+
* long reaches `sha256` in the constructor and panics there.
|
|
151
|
+
*/
|
|
131
152
|
export const hmacSha256 = (key: u8[], data: u8[]): u8[] => {
|
|
153
|
+
if (data.length > 2147483647) {
|
|
154
|
+
panic("hmacSha256: a message longer than 2^31 - 1 bytes")
|
|
155
|
+
}
|
|
132
156
|
const mac = new HmacSha256(key)
|
|
133
157
|
mac.update(data, HMAC_FROM, toI32(data.length))
|
|
134
158
|
return mac.digest()
|
|
135
159
|
}
|
|
136
160
|
|
|
137
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* The HMAC-SHA-384 of all of `data` under `key`, as a fresh 48-byte array.
|
|
163
|
+
* `data` longer than 2^31 - 1 bytes panics, as in `hmacSha256`.
|
|
164
|
+
*/
|
|
138
165
|
export const hmacSha384 = (key: u8[], data: u8[]): u8[] => {
|
|
166
|
+
if (data.length > 2147483647) {
|
|
167
|
+
panic("hmacSha384: a message longer than 2^31 - 1 bytes")
|
|
168
|
+
}
|
|
139
169
|
const mac = new HmacSha384(key)
|
|
140
170
|
mac.update(data, HMAC_FROM, toI32(data.length))
|
|
141
171
|
return mac.digest()
|
|
@@ -153,3 +183,289 @@ export const hmacSha256Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
|
|
|
153
183
|
/** Whether `tag` is the HMAC-SHA-384 of `data` under `key`, compared as `hmacSha256Verify` does. */
|
|
154
184
|
export const hmacSha384Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
|
|
155
185
|
timingSafeEqual(hmacSha384(key, data), tag)
|
|
186
|
+
|
|
187
|
+
// ---- Caller-owned scratch --------------------------------------------------
|
|
188
|
+
//
|
|
189
|
+
// `HmacSha256` makes its two hashers in its constructor, and the one-shot
|
|
190
|
+
// functions above make an `HmacSha256` and a fresh tag on every call, so all
|
|
191
|
+
// of them store allocations of their own: memory no automatic arena scope and
|
|
192
|
+
// no `using a = arena()` block may release around them (docs/LANGUAGE.md,
|
|
193
|
+
// "`using a = arena()`", NL2424). The scratch below is the same computation in
|
|
194
|
+
// hashers the caller allocates once and keeps: `begin` keys it again by
|
|
195
|
+
// rewriting their state in place, and `finishInto` writes the tag into the
|
|
196
|
+
// caller's array. What it allocates on the way — the two digests a hasher
|
|
197
|
+
// answers — dies with the call that made it, so a caller may run it a
|
|
198
|
+
// thousand times inside one arena scope and leave the arena where it was.
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Makes `to` the hasher `from` is — the same point in the same message, as
|
|
202
|
+
* `Sha256.copy` makes it — in a hasher the caller already holds, so that
|
|
203
|
+
* nothing is allocated. Copying from a hasher nothing has been fed starts `to`
|
|
204
|
+
* again. It is here rather than beside `Sha256` because the scratch below and
|
|
205
|
+
* the TLS transcript are what need it, and HMAC is the module both sit on.
|
|
206
|
+
*/
|
|
207
|
+
export const hmacCopySha256 = (from: Sha256, to: Sha256): void => {
|
|
208
|
+
const fromState: u32[] = from.state
|
|
209
|
+
const toState: u32[] = to.state
|
|
210
|
+
for (let i: i32 = 0; i < toI32(fromState.length) && i < toI32(toState.length); i += 1) {
|
|
211
|
+
toState[i] = fromState[i]
|
|
212
|
+
}
|
|
213
|
+
const fromBlock: u8[] = from.block
|
|
214
|
+
const toBlock: u8[] = to.block
|
|
215
|
+
for (let i: i32 = 0; i < from.fill && i < toI32(fromBlock.length) && i < toI32(toBlock.length); i += 1) {
|
|
216
|
+
toBlock[i] = fromBlock[i]
|
|
217
|
+
}
|
|
218
|
+
to.fill = from.fill
|
|
219
|
+
to.total = from.total
|
|
220
|
+
to.finished = from.finished
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** `hmacCopySha256` for SHA-384: `to` made the hasher `from` is, in place. */
|
|
224
|
+
export const hmacCopySha384 = (from: Sha384, to: Sha384): void => {
|
|
225
|
+
const fromState: u64[] = from.engine.state
|
|
226
|
+
const toState: u64[] = to.engine.state
|
|
227
|
+
for (let i: i32 = 0; i < toI32(fromState.length) && i < toI32(toState.length); i += 1) {
|
|
228
|
+
toState[i] = fromState[i]
|
|
229
|
+
}
|
|
230
|
+
const fromBlock: u8[] = from.engine.block
|
|
231
|
+
const toBlock: u8[] = to.engine.block
|
|
232
|
+
for (
|
|
233
|
+
let i: i32 = 0;
|
|
234
|
+
i < from.engine.filled && i < toI32(fromBlock.length) && i < toI32(toBlock.length);
|
|
235
|
+
i += 1
|
|
236
|
+
) {
|
|
237
|
+
toBlock[i] = fromBlock[i]
|
|
238
|
+
}
|
|
239
|
+
to.engine.filled = from.engine.filled
|
|
240
|
+
to.engine.count = from.engine.count
|
|
241
|
+
to.engine.finished = from.engine.finished
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Zeroes everything `h` holds of the message it was fed: the pending block
|
|
246
|
+
* with `secureZero`, and the hash value and message schedule, which are words
|
|
247
|
+
* `secureZero` does not take, with ordinary stores. Those stand because the
|
|
248
|
+
* hasher stays reachable for the next `begin`. The hasher is left finished, so
|
|
249
|
+
* that feeding it before it is started again panics.
|
|
250
|
+
*/
|
|
251
|
+
const hmacWipeSha256 = (h: Sha256): void => {
|
|
252
|
+
secureZero(h.block)
|
|
253
|
+
const state: u32[] = h.state
|
|
254
|
+
for (let i: i32 = 0; i < toI32(state.length); i += 1) {
|
|
255
|
+
state[i] = toU32(0)
|
|
256
|
+
}
|
|
257
|
+
const schedule: u32[] = h.schedule
|
|
258
|
+
for (let i: i32 = 0; i < toI32(schedule.length); i += 1) {
|
|
259
|
+
schedule[i] = toU32(0)
|
|
260
|
+
}
|
|
261
|
+
h.fill = 0
|
|
262
|
+
h.total = 0
|
|
263
|
+
h.finished = true
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** `hmacWipeSha256` for SHA-384. */
|
|
267
|
+
const hmacWipeSha384 = (h: Sha384): void => {
|
|
268
|
+
secureZero(h.engine.block)
|
|
269
|
+
const state: u64[] = h.engine.state
|
|
270
|
+
for (let i: i32 = 0; i < toI32(state.length); i += 1) {
|
|
271
|
+
state[i] = toU64(0)
|
|
272
|
+
}
|
|
273
|
+
const schedule: u64[] = h.engine.schedule
|
|
274
|
+
for (let i: i32 = 0; i < toI32(schedule.length); i += 1) {
|
|
275
|
+
schedule[i] = toU64(0)
|
|
276
|
+
}
|
|
277
|
+
h.engine.filled = 0
|
|
278
|
+
h.engine.count = toU64(0)
|
|
279
|
+
h.engine.finished = true
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Whether `[off, off + len)` is a window inside an array of `length` elements. */
|
|
283
|
+
const hmacWindowFits = (length: i32, off: i32, len: i32): boolean =>
|
|
284
|
+
off >= 0 && len >= 0 && off <= length - len
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* HMAC-SHA-256 over hashers the caller owns: allocate one, then for each tag
|
|
288
|
+
* `begin` with the key, `update` with the message, and `finishInto` the
|
|
289
|
+
* caller's array. The tag is the one `hmacSha256` answers.
|
|
290
|
+
*
|
|
291
|
+
* const mac = new HmacSha256Scratch(); // once, outside the loop
|
|
292
|
+
* mac.begin(key, 0, key.length);
|
|
293
|
+
* mac.update(message, 0, message.length);
|
|
294
|
+
* mac.finishInto(tag, 0); // 32 bytes at tag[0]
|
|
295
|
+
*
|
|
296
|
+
* The key block is wiped with `secureZero` once both hashers have absorbed
|
|
297
|
+
* it, and so is every digest on the way; `wipe` zeroes the hashers too, for a
|
|
298
|
+
* caller done with the key. A key or tag window outside its array panics, as
|
|
299
|
+
* a message window does in `Sha256.update`.
|
|
300
|
+
*/
|
|
301
|
+
export class HmacSha256Scratch {
|
|
302
|
+
/** H(K XOR ipad, …): keyed by `begin`, fed the message by `update`. */
|
|
303
|
+
inner: Sha256
|
|
304
|
+
/** H(K XOR opad, …): keyed by `begin`, fed the inner digest by `finishInto`. */
|
|
305
|
+
outer: Sha256
|
|
306
|
+
/** A hasher nothing is ever fed: the initial hash value `begin` copies from. */
|
|
307
|
+
fresh: Sha256
|
|
308
|
+
/** One key block XOR a pad, wiped as soon as it is absorbed. */
|
|
309
|
+
pad: u8[]
|
|
310
|
+
/** The SHA-256 of a key longer than a block, which RFC 2104 §2 keys with instead; wiped once used. */
|
|
311
|
+
keyHash: u8[]
|
|
312
|
+
|
|
313
|
+
constructor() {
|
|
314
|
+
this.inner = new Sha256()
|
|
315
|
+
this.outer = new Sha256()
|
|
316
|
+
this.fresh = new Sha256()
|
|
317
|
+
this.pad = new Array<u8>(SHA256_BLOCK)
|
|
318
|
+
this.keyHash = new Array<u8>(SHA256_SIZE)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Keys a new computation with `key[off .. off + len)`, a key longer than 64 bytes by its SHA-256 (RFC 2104 §2). */
|
|
322
|
+
begin(key: u8[], off: i32, len: i32): void {
|
|
323
|
+
if (!hmacWindowFits(toI32(key.length), off, len)) {
|
|
324
|
+
panic("HmacSha256Scratch: the key window is outside its array")
|
|
325
|
+
}
|
|
326
|
+
if (len <= SHA256_BLOCK) {
|
|
327
|
+
this.keyBlocks(key, off, len)
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
hmacCopySha256(this.fresh, this.inner)
|
|
331
|
+
this.inner.update(key, off, len)
|
|
332
|
+
const digest: u8[] = this.inner.digest()
|
|
333
|
+
for (let i: i32 = 0; i < toI32(digest.length) && i < toI32(this.keyHash.length); i += 1) {
|
|
334
|
+
this.keyHash[i] = digest[i]
|
|
335
|
+
}
|
|
336
|
+
secureZero(digest)
|
|
337
|
+
this.keyBlocks(this.keyHash, HMAC_FROM, SHA256_SIZE)
|
|
338
|
+
secureZero(this.keyHash)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Absorbs `key[off .. off + len)`, no longer than a block, XOR ipad into the
|
|
343
|
+
* inner hasher and XOR opad into the outer one, each started again, and
|
|
344
|
+
* wipes the block. A method of its own so that `begin` never keeps the
|
|
345
|
+
* caller's key in a local it reassigns, which would make the key look
|
|
346
|
+
* stored to the arena analysis.
|
|
347
|
+
*/
|
|
348
|
+
keyBlocks(key: u8[], off: i32, len: i32): void {
|
|
349
|
+
hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
|
|
350
|
+
hmacCopySha256(this.fresh, this.inner)
|
|
351
|
+
this.inner.update(this.pad, HMAC_FROM, SHA256_BLOCK)
|
|
352
|
+
hmacPadInto(this.pad, key, off, len, HMAC_OPAD)
|
|
353
|
+
hmacCopySha256(this.fresh, this.outer)
|
|
354
|
+
this.outer.update(this.pad, HMAC_FROM, SHA256_BLOCK)
|
|
355
|
+
secureZero(this.pad)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Absorbs `data[off .. off + len)`; a window outside `data` panics in `Sha256.update`. */
|
|
359
|
+
update(data: u8[], off: i32, len: i32): void {
|
|
360
|
+
this.inner.update(data, off, len)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Writes the 32-byte tag at `out[at]` and ends the computation; a window outside `out` panics. */
|
|
364
|
+
finishInto(out: u8[], at: i32): void {
|
|
365
|
+
if (!hmacWindowFits(toI32(out.length), at, SHA256_SIZE)) {
|
|
366
|
+
panic("HmacSha256Scratch: the tag window is outside its array")
|
|
367
|
+
}
|
|
368
|
+
const innerDigest: u8[] = this.inner.digest()
|
|
369
|
+
this.outer.update(innerDigest, HMAC_FROM, SHA256_SIZE)
|
|
370
|
+
secureZero(innerDigest)
|
|
371
|
+
const tag: u8[] = this.outer.digest()
|
|
372
|
+
for (let i: i32 = 0; i < SHA256_SIZE && i < toI32(tag.length) && at + i < toI32(out.length); i += 1) {
|
|
373
|
+
out[at + i] = tag[i]
|
|
374
|
+
}
|
|
375
|
+
secureZero(tag)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Zeroes both keyed hashers, for a caller done with the key; `begin` starts again. */
|
|
379
|
+
wipe(): void {
|
|
380
|
+
hmacWipeSha256(this.inner)
|
|
381
|
+
hmacWipeSha256(this.outer)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* HMAC-SHA-384 over hashers the caller owns, with `HmacSha256Scratch`'s
|
|
387
|
+
* methods. The block is 128 bytes and the tag 48.
|
|
388
|
+
*/
|
|
389
|
+
export class HmacSha384Scratch {
|
|
390
|
+
/** H(K XOR ipad, …): keyed by `begin`, fed the message by `update`. */
|
|
391
|
+
inner: Sha384
|
|
392
|
+
/** H(K XOR opad, …): keyed by `begin`, fed the inner digest by `finishInto`. */
|
|
393
|
+
outer: Sha384
|
|
394
|
+
/** A hasher nothing is ever fed: the initial hash value `begin` copies from. */
|
|
395
|
+
fresh: Sha384
|
|
396
|
+
/** One key block XOR a pad, wiped as soon as it is absorbed. */
|
|
397
|
+
pad: u8[]
|
|
398
|
+
/** The SHA-384 of a key longer than a block; wiped once used. */
|
|
399
|
+
keyHash: u8[]
|
|
400
|
+
|
|
401
|
+
constructor() {
|
|
402
|
+
this.inner = new Sha384()
|
|
403
|
+
this.outer = new Sha384()
|
|
404
|
+
this.fresh = new Sha384()
|
|
405
|
+
this.pad = new Array<u8>(SHA512_BLOCK)
|
|
406
|
+
this.keyHash = new Array<u8>(SHA384_SIZE)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Keys a new computation with `key[off .. off + len)`, a key longer than 128 bytes by its SHA-384 (RFC 2104 §2). */
|
|
410
|
+
begin(key: u8[], off: i32, len: i32): void {
|
|
411
|
+
if (!hmacWindowFits(toI32(key.length), off, len)) {
|
|
412
|
+
panic("HmacSha384Scratch: the key window is outside its array")
|
|
413
|
+
}
|
|
414
|
+
if (len <= SHA512_BLOCK) {
|
|
415
|
+
this.keyBlocks(key, off, len)
|
|
416
|
+
return
|
|
417
|
+
}
|
|
418
|
+
hmacCopySha384(this.fresh, this.inner)
|
|
419
|
+
this.inner.update(key, off, len)
|
|
420
|
+
const digest: u8[] = this.inner.digest()
|
|
421
|
+
for (let i: i32 = 0; i < toI32(digest.length) && i < toI32(this.keyHash.length); i += 1) {
|
|
422
|
+
this.keyHash[i] = digest[i]
|
|
423
|
+
}
|
|
424
|
+
secureZero(digest)
|
|
425
|
+
this.keyBlocks(this.keyHash, HMAC_FROM, SHA384_SIZE)
|
|
426
|
+
secureZero(this.keyHash)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Absorbs `key[off .. off + len)`, no longer than a block, XOR ipad into the
|
|
431
|
+
* inner hasher and XOR opad into the outer one, each started again, and
|
|
432
|
+
* wipes the block. A method of its own so that `begin` never keeps the
|
|
433
|
+
* caller's key in a local it reassigns, which would make the key look
|
|
434
|
+
* stored to the arena analysis.
|
|
435
|
+
*/
|
|
436
|
+
keyBlocks(key: u8[], off: i32, len: i32): void {
|
|
437
|
+
hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
|
|
438
|
+
hmacCopySha384(this.fresh, this.inner)
|
|
439
|
+
this.inner.update(this.pad, HMAC_FROM, SHA512_BLOCK)
|
|
440
|
+
hmacPadInto(this.pad, key, off, len, HMAC_OPAD)
|
|
441
|
+
hmacCopySha384(this.fresh, this.outer)
|
|
442
|
+
this.outer.update(this.pad, HMAC_FROM, SHA512_BLOCK)
|
|
443
|
+
secureZero(this.pad)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Absorbs `data[off .. off + len)`; a window outside `data` panics in the hasher. */
|
|
447
|
+
update(data: u8[], off: i32, len: i32): void {
|
|
448
|
+
this.inner.update(data, off, len)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** Writes the 48-byte tag at `out[at]` and ends the computation; a window outside `out` panics. */
|
|
452
|
+
finishInto(out: u8[], at: i32): void {
|
|
453
|
+
if (!hmacWindowFits(toI32(out.length), at, SHA384_SIZE)) {
|
|
454
|
+
panic("HmacSha384Scratch: the tag window is outside its array")
|
|
455
|
+
}
|
|
456
|
+
const innerDigest: u8[] = this.inner.digest()
|
|
457
|
+
this.outer.update(innerDigest, HMAC_FROM, SHA384_SIZE)
|
|
458
|
+
secureZero(innerDigest)
|
|
459
|
+
const tag: u8[] = this.outer.digest()
|
|
460
|
+
for (let i: i32 = 0; i < SHA384_SIZE && i < toI32(tag.length) && at + i < toI32(out.length); i += 1) {
|
|
461
|
+
out[at + i] = tag[i]
|
|
462
|
+
}
|
|
463
|
+
secureZero(tag)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Zeroes both keyed hashers, for a caller done with the key; `begin` starts again. */
|
|
467
|
+
wipe(): void {
|
|
468
|
+
hmacWipeSha384(this.inner)
|
|
469
|
+
hmacWipeSha384(this.outer)
|
|
470
|
+
}
|
|
471
|
+
}
|
package/std/crypto/p256.ts
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* import { p256PublicKey, p256SignSha256, p256VerifySha256 } from "nish/crypto/p256";
|
|
6
6
|
*
|
|
7
|
+
* const priv: Secret<u8[]> = secret(readKey()); // nish:secret
|
|
7
8
|
* const pub: u8[] | null = p256PublicKey(priv); // 65 bytes, 0x04 || X || Y
|
|
8
9
|
* const sig: u8[] | null = p256SignSha256(priv, message); // 64 bytes, r || s
|
|
10
|
+
* wipe(priv);
|
|
9
11
|
* const ok: boolean = p256VerifySha256(pub, message, sig);
|
|
10
12
|
*
|
|
11
13
|
* A private key is 32 big-endian bytes holding a scalar in [1, n); a public
|
|
@@ -73,12 +75,21 @@
|
|
|
73
75
|
*
|
|
74
76
|
* Verification multiplies only public values, and uses the same routine.
|
|
75
77
|
*
|
|
78
|
+
* **The private key is a `Secret`** (`nish:secret`, docs/LANGUAGE.md
|
|
79
|
+
* "Secrets"). `p256PublicKey` and `p256Sign` read it only inside `expose` /
|
|
80
|
+
* `exposeWith`, whose function the checker holds to reaching no I/O and no C,
|
|
81
|
+
* and every intermediate the key or the nonce reaches is wiped before the
|
|
82
|
+
* signature is returned (ECC-2, docs/security/crypto-ecc.md). That is why RFC
|
|
83
|
+
* 6979's HMAC is written out over `Sha256` here (`p256Mac`) rather than taken
|
|
84
|
+
* from `nish/crypto/hmac`: its key blocks and hasher state have to be this
|
|
85
|
+
* module's to wipe.
|
|
86
|
+
*
|
|
76
87
|
* Private names carry the `p256` prefix because a `std/` module's private
|
|
77
88
|
* functions share the importing program's flat symbol namespace
|
|
78
89
|
* (`docs/wp26-stdlib.md` §3e).
|
|
79
90
|
*/
|
|
80
|
-
import {
|
|
81
|
-
import { sha256 } from "nish/crypto/sha256"
|
|
91
|
+
import { Secret, expose, exposeWith, wipe } from "nish:secret"
|
|
92
|
+
import { Sha256, sha256 } from "nish/crypto/sha256"
|
|
82
93
|
import { timingSafeEqual } from "nish/crypto/ct"
|
|
83
94
|
|
|
84
95
|
/** The length in bytes of a private key: one scalar, big-endian. */
|
|
@@ -3784,6 +3795,23 @@ class P256PointScratch {
|
|
|
3784
3795
|
}
|
|
3785
3796
|
}
|
|
3786
3797
|
|
|
3798
|
+
/** Zero `p`'s three coordinates, with the store `nish:secret` keeps from the optimiser (ECC-2). */
|
|
3799
|
+
const p256WipePoint = (p: P256ProjectivePoint): void => {
|
|
3800
|
+
wipe(p.x)
|
|
3801
|
+
wipe(p.y)
|
|
3802
|
+
wipe(p.z)
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
/** Zero every temporary a point operation left in `s`; `b` is the curve's public constant. */
|
|
3806
|
+
const p256WipeScratch = (s: P256PointScratch): void => {
|
|
3807
|
+
wipe(s.t0)
|
|
3808
|
+
wipe(s.t1)
|
|
3809
|
+
wipe(s.t2)
|
|
3810
|
+
wipe(s.t3)
|
|
3811
|
+
wipe(s.t4)
|
|
3812
|
+
p256WipePoint(s.sum)
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3787
3815
|
/** `out = p`, coordinate by coordinate. */
|
|
3788
3816
|
const p256PointCopy = (out: P256ProjectivePoint, p: P256ProjectivePoint): void => {
|
|
3789
3817
|
p256Copy(out.x, p.x)
|
|
@@ -3948,17 +3976,18 @@ const p256TableBuild = (table: u32[], p: P256ProjectivePoint, s: P256PointScratc
|
|
|
3948
3976
|
* way, at an offset that is the caller's loop position, never the secret.
|
|
3949
3977
|
*/
|
|
3950
3978
|
const p256TableMove = (out: u32[], table: u32[], at: i32, hit: u32): void => {
|
|
3979
|
+
const p: u32 = toU32(at)
|
|
3951
3980
|
if (toI32(out.length) < 8 || at < 0 || at > toI32(table.length) - 8) {
|
|
3952
3981
|
return
|
|
3953
3982
|
}
|
|
3954
|
-
out[0] = p256FiatCmovznzU32(hit, out[0], table[
|
|
3955
|
-
out[1] = p256FiatCmovznzU32(hit, out[1], table[
|
|
3956
|
-
out[2] = p256FiatCmovznzU32(hit, out[2], table[
|
|
3957
|
-
out[3] = p256FiatCmovznzU32(hit, out[3], table[
|
|
3958
|
-
out[4] = p256FiatCmovznzU32(hit, out[4], table[
|
|
3959
|
-
out[5] = p256FiatCmovznzU32(hit, out[5], table[
|
|
3960
|
-
out[6] = p256FiatCmovznzU32(hit, out[6], table[
|
|
3961
|
-
out[7] = p256FiatCmovznzU32(hit, out[7], table[
|
|
3983
|
+
out[0] = p256FiatCmovznzU32(hit, out[0], table[p])
|
|
3984
|
+
out[1] = p256FiatCmovznzU32(hit, out[1], table[p + 1])
|
|
3985
|
+
out[2] = p256FiatCmovznzU32(hit, out[2], table[p + 2])
|
|
3986
|
+
out[3] = p256FiatCmovznzU32(hit, out[3], table[p + 3])
|
|
3987
|
+
out[4] = p256FiatCmovznzU32(hit, out[4], table[p + 4])
|
|
3988
|
+
out[5] = p256FiatCmovznzU32(hit, out[5], table[p + 5])
|
|
3989
|
+
out[6] = p256FiatCmovznzU32(hit, out[6], table[p + 6])
|
|
3990
|
+
out[7] = p256FiatCmovznzU32(hit, out[7], table[p + 7])
|
|
3962
3991
|
}
|
|
3963
3992
|
|
|
3964
3993
|
/**
|
|
@@ -4014,6 +4043,11 @@ const p256ScalarMult = (
|
|
|
4014
4043
|
p256PointAdd(acc, acc, entry, s)
|
|
4015
4044
|
}
|
|
4016
4045
|
p256PointCopy(out, acc)
|
|
4046
|
+
// The accumulator and the selected entry follow the scalar's digits, and a
|
|
4047
|
+
// signature's scalar is its nonce: both are wiped, and the table with them.
|
|
4048
|
+
wipe(table)
|
|
4049
|
+
p256WipePoint(acc)
|
|
4050
|
+
p256WipePoint(entry)
|
|
4017
4051
|
}
|
|
4018
4052
|
|
|
4019
4053
|
/** The base point G (SEC 2 §2.4.2), in the Montgomery domain with Z = 1. */
|
|
@@ -4133,6 +4167,7 @@ const p256ScalarInRange = (bytes: u8[]): boolean => {
|
|
|
4133
4167
|
p256LimbsFromBytes(a, bytes, 0)
|
|
4134
4168
|
const below: u32 = p256Below(a, p256Order())
|
|
4135
4169
|
const zero: u32 = ctEq(p256FiatNonzero(a), 0) & 1
|
|
4170
|
+
wipe(a) // a private key's limbs, when `bytes` is one
|
|
4136
4171
|
return (below & (zero ^ 1)) === 1
|
|
4137
4172
|
}
|
|
4138
4173
|
|
|
@@ -4155,19 +4190,72 @@ const p256DigestScalar = (out: u32[], digest: u8[]): void => {
|
|
|
4155
4190
|
p256ReduceOnce(out, a, p256Order())
|
|
4156
4191
|
}
|
|
4157
4192
|
|
|
4193
|
+
/** RFC 2104's key block for SHA-256: `k` zero-extended to 64 bytes, each byte XOR `pad`. */
|
|
4194
|
+
const p256PadBlock = (k: u8[], pad: u8): u8[] => {
|
|
4195
|
+
const out: u8[] = new Array<u8>(64)
|
|
4196
|
+
const outLength: i32 = toI32(out.length)
|
|
4197
|
+
const keyLength: i32 = toI32(k.length)
|
|
4198
|
+
for (let i: i32 = 0; i < outLength; i++) {
|
|
4199
|
+
if (i < keyLength) {
|
|
4200
|
+
out[i] = k[i] ^ pad
|
|
4201
|
+
} else {
|
|
4202
|
+
out[i] = pad
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
return out
|
|
4206
|
+
}
|
|
4207
|
+
|
|
4208
|
+
/** Zero what a SHA-256 in progress holds: its chaining value, its pending block and its schedule. */
|
|
4209
|
+
const p256WipeHash = (h: Sha256): void => {
|
|
4210
|
+
wipe(h.state)
|
|
4211
|
+
wipe(h.block)
|
|
4212
|
+
wipe(h.schedule)
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
/**
|
|
4216
|
+
* HMAC-SHA-256 keyed by `k` over `a || b || c || d` (RFC 2104), the one MAC
|
|
4217
|
+
* RFC 6979 runs. It is written out over `Sha256` here rather than taken from
|
|
4218
|
+
* `nish/crypto/hmac` so that every buffer the key reaches — the two key
|
|
4219
|
+
* blocks, both hashers' state, block and schedule, and the inner digest — is
|
|
4220
|
+
* this function's to wipe before it returns (ECC-2). `k` is always 32 bytes,
|
|
4221
|
+
* under SHA-256's 64-byte block, so it is never hashed first.
|
|
4222
|
+
*/
|
|
4223
|
+
const p256Mac = (k: u8[], a: u8[], b: u8[], c: u8[], d: u8[]): u8[] => {
|
|
4224
|
+
const ipad: u8[] = p256PadBlock(k, 0x36)
|
|
4225
|
+
const opad: u8[] = p256PadBlock(k, 0x5c)
|
|
4226
|
+
const inner = new Sha256()
|
|
4227
|
+
inner.update(ipad, P256_FROM, toI32(ipad.length))
|
|
4228
|
+
inner.update(a, P256_FROM, toI32(a.length))
|
|
4229
|
+
inner.update(b, P256_FROM, toI32(b.length))
|
|
4230
|
+
inner.update(c, P256_FROM, toI32(c.length))
|
|
4231
|
+
inner.update(d, P256_FROM, toI32(d.length))
|
|
4232
|
+
const innerDigest: u8[] = inner.digest()
|
|
4233
|
+
const outer = new Sha256()
|
|
4234
|
+
outer.update(opad, P256_FROM, toI32(opad.length))
|
|
4235
|
+
outer.update(innerDigest, P256_FROM, toI32(innerDigest.length))
|
|
4236
|
+
const tag: u8[] = outer.digest()
|
|
4237
|
+
wipe(ipad)
|
|
4238
|
+
wipe(opad)
|
|
4239
|
+
wipe(innerDigest)
|
|
4240
|
+
p256WipeHash(inner)
|
|
4241
|
+
p256WipeHash(outer)
|
|
4242
|
+
return tag
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4158
4245
|
/**
|
|
4159
4246
|
* HMAC_K(V || sep || x || h), the key update of RFC 6979 §3.2 steps d and f
|
|
4160
4247
|
* (with `sep` 0 and then 1) and, with `x` and `h` empty, step h.3.
|
|
4161
4248
|
*/
|
|
4162
4249
|
const p256NonceKey = (k: u8[], v: u8[], sep: u8, x: u8[], h: u8[]): u8[] => {
|
|
4163
|
-
const mac = new HmacSha256(k)
|
|
4164
4250
|
const separator: u8[] = new Array<u8>(1)
|
|
4165
4251
|
separator[0] = sep
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4252
|
+
return p256Mac(k, v, separator, x, h)
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
/** HMAC_K(V), RFC 6979 §3.2 steps e, g and h.2. */
|
|
4256
|
+
const p256NonceValue = (k: u8[], v: u8[]): u8[] => {
|
|
4257
|
+
const empty: u8[] = new Array<u8>(0)
|
|
4258
|
+
return p256Mac(k, v, empty, empty, empty)
|
|
4171
4259
|
}
|
|
4172
4260
|
|
|
4173
4261
|
/**
|
|
@@ -4192,10 +4280,20 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
|
|
|
4192
4280
|
for (let i: i32 = 0; i < 32; i++) {
|
|
4193
4281
|
v[i] = 1
|
|
4194
4282
|
}
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4283
|
+
// Each K and V is wiped as the next replaces it (ECC-2): both are derived
|
|
4284
|
+
// from the private key, and the last V is the nonce.
|
|
4285
|
+
let next: u8[] = p256NonceKey(k, v, 0, priv, h1) // step d
|
|
4286
|
+
wipe(k)
|
|
4287
|
+
k = next
|
|
4288
|
+
next = p256NonceValue(k, v) // step e
|
|
4289
|
+
wipe(v)
|
|
4290
|
+
v = next
|
|
4291
|
+
next = p256NonceKey(k, v, 1, priv, h1) // step f
|
|
4292
|
+
wipe(k)
|
|
4293
|
+
k = next
|
|
4294
|
+
next = p256NonceValue(k, v) // step g
|
|
4295
|
+
wipe(v)
|
|
4296
|
+
v = next
|
|
4199
4297
|
|
|
4200
4298
|
const dM: u32[] = p256Limbs()
|
|
4201
4299
|
p256LimbsFromBytes(dM, priv, 0)
|
|
@@ -4208,7 +4306,9 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
|
|
|
4208
4306
|
const kM: u32[] = p256Limbs()
|
|
4209
4307
|
const s: u32[] = p256Limbs()
|
|
4210
4308
|
while (true) {
|
|
4211
|
-
|
|
4309
|
+
next = p256NonceValue(k, v) // step h.2
|
|
4310
|
+
wipe(v)
|
|
4311
|
+
v = next
|
|
4212
4312
|
if (p256ScalarInRange(v)) {
|
|
4213
4313
|
p256ScalarMult(point, v, g, scratch)
|
|
4214
4314
|
p256AffineX(x, point)
|
|
@@ -4226,11 +4326,23 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
|
|
|
4226
4326
|
const sig: u8[] = new Array<u8>(64)
|
|
4227
4327
|
p256LimbsToBytes(sig, 0, r)
|
|
4228
4328
|
p256LimbsToBytes(sig, 32, s)
|
|
4329
|
+
// ECC-2: the private key's limbs, the nonce and its inverse, RFC
|
|
4330
|
+
// 6979's K and V, the nonce point and the scratch it was built in.
|
|
4331
|
+
wipe(k)
|
|
4332
|
+
wipe(v)
|
|
4333
|
+
wipe(dM)
|
|
4334
|
+
wipe(kM)
|
|
4335
|
+
p256WipePoint(point)
|
|
4336
|
+
p256WipeScratch(scratch)
|
|
4229
4337
|
return sig
|
|
4230
4338
|
}
|
|
4231
4339
|
}
|
|
4232
|
-
|
|
4233
|
-
|
|
4340
|
+
next = p256NonceKey(k, v, 0, empty, empty) // step h.3
|
|
4341
|
+
wipe(k)
|
|
4342
|
+
k = next
|
|
4343
|
+
next = p256NonceValue(k, v)
|
|
4344
|
+
wipe(v)
|
|
4345
|
+
v = next
|
|
4234
4346
|
}
|
|
4235
4347
|
}
|
|
4236
4348
|
|
|
@@ -4239,12 +4351,17 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
|
|
|
4239
4351
|
* encoding. Answers `null` unless `priv` is `P256_SCALAR_SIZE` bytes holding a
|
|
4240
4352
|
* scalar in [1, n) — 0 and n and everything above are not private keys.
|
|
4241
4353
|
*/
|
|
4242
|
-
export const p256PublicKey = (priv: u8[]): u8[] | null =>
|
|
4354
|
+
export const p256PublicKey = (priv: Secret<u8[]>): u8[] | null => expose(priv, p256PublicKeyExposed)
|
|
4355
|
+
|
|
4356
|
+
/** `p256PublicKey` on the plain key, for `expose` to run: the scratch the key's digits steered is wiped. */
|
|
4357
|
+
const p256PublicKeyExposed = (priv: u8[]): u8[] | null => {
|
|
4243
4358
|
if (!p256ScalarInRange(priv)) {
|
|
4244
4359
|
return null
|
|
4245
4360
|
}
|
|
4246
4361
|
const point = new P256ProjectivePoint()
|
|
4247
|
-
|
|
4362
|
+
const scratch = new P256PointScratch()
|
|
4363
|
+
p256ScalarMult(point, priv, p256Generator(), scratch)
|
|
4364
|
+
p256WipeScratch(scratch)
|
|
4248
4365
|
return p256EncodePoint(point)
|
|
4249
4366
|
}
|
|
4250
4367
|
|
|
@@ -4256,7 +4373,11 @@ export const p256PublicKey = (priv: u8[]): u8[] | null => {
|
|
|
4256
4373
|
* not normalised to the lower half. Answers `null` for a private key
|
|
4257
4374
|
* `p256PublicKey` would refuse.
|
|
4258
4375
|
*/
|
|
4259
|
-
export const p256Sign = (priv: u8[]
|
|
4376
|
+
export const p256Sign = (priv: Secret<u8[]>, digest: u8[]): u8[] | null =>
|
|
4377
|
+
exposeWith(priv, digest, p256SignExposed)
|
|
4378
|
+
|
|
4379
|
+
/** `p256Sign` on the plain key, for `exposeWith` to run. */
|
|
4380
|
+
const p256SignExposed = (priv: u8[], digest: u8[]): u8[] | null => {
|
|
4260
4381
|
if (!p256ScalarInRange(priv)) {
|
|
4261
4382
|
return null
|
|
4262
4383
|
}
|
|
@@ -4273,6 +4394,11 @@ export const p256Sign = (priv: u8[], digest: u8[]): u8[] | null => {
|
|
|
4273
4394
|
* that is not a point on the curve, and a sum that is the identity all answer
|
|
4274
4395
|
* `false`. Every value here is public, so the answer may be reached by
|
|
4275
4396
|
* branches; the multiplications are the constant-time ones all the same.
|
|
4397
|
+
*
|
|
4398
|
+
* ECDSA is malleable, and so is this: when `(r, s)` verifies, `(r, n - s)`
|
|
4399
|
+
* verifies too, as FIPS 186-4 and SEC 1 have it. Nothing here asks for the
|
|
4400
|
+
* lower `s`, so a protocol that needs one signature per message and key (a
|
|
4401
|
+
* signature used as an identifier, say) has to refuse the upper half itself.
|
|
4276
4402
|
*/
|
|
4277
4403
|
export const p256Verify = (pub: u8[], digest: u8[], sig: u8[]): boolean => {
|
|
4278
4404
|
if (toI32(sig.length) !== 64) {
|
|
@@ -4330,7 +4456,7 @@ export const p256Verify = (pub: u8[], digest: u8[], sig: u8[]): boolean => {
|
|
|
4330
4456
|
}
|
|
4331
4457
|
|
|
4332
4458
|
/** `p256Sign` of the SHA-256 of `msg`: ECDSA with SHA-256 over the whole message. */
|
|
4333
|
-
export const p256SignSha256 = (priv: u8[]
|
|
4459
|
+
export const p256SignSha256 = (priv: Secret<u8[]>, msg: u8[]): u8[] | null => p256Sign(priv, sha256(msg))
|
|
4334
4460
|
|
|
4335
4461
|
/** `p256Verify` of the SHA-256 of `msg`. */
|
|
4336
4462
|
export const p256VerifySha256 = (pub: u8[], msg: u8[], sig: u8[]): boolean =>
|