@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 +43 -34
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +50 -13
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +43 -24
- package/runtime/runtime-net.c +57 -10
- package/runtime/runtime-os.c +62 -20
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +12 -0
- package/runtime/runtime.c +66 -18
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +14 -12
- package/std/README.md +168 -36
- package/std/crypto/aes.ts +51 -8
- package/std/crypto/base64url.ts +25 -12
- package/std/crypto/chacha20poly1305.ts +43 -29
- package/std/crypto/ct.ts +6 -1
- package/std/crypto/hkdf.ts +364 -3
- package/std/crypto/hmac.ts +307 -7
- package/std/crypto/p256.ts +618 -34
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +3 -2
- package/std/crypto/x25519.ts +88 -36
- package/std/crypto/x509.ts +130 -47
- package/std/json.ts +297 -41
- 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 +911 -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 +462 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +929 -0
- package/std/net/quic-packet.ts +1698 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1609 -0
- package/std/net/quic.ts +3578 -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 +414 -0
- package/std/net/tls.ts +1437 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
- package/std/text.ts +77 -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
|
|
|
@@ -169,3 +183,289 @@ export const hmacSha256Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
|
|
|
169
183
|
/** Whether `tag` is the HMAC-SHA-384 of `data` under `key`, compared as `hmacSha256Verify` does. */
|
|
170
184
|
export const hmacSha384Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
|
|
171
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
|
+
}
|