@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/hkdf.ts
CHANGED
|
@@ -16,12 +16,66 @@
|
|
|
16
16
|
* The counter `i` is one byte, so `L` is at most 255 × HashLen (§2.3): 8160
|
|
17
17
|
* bytes for SHA-256 and 12240 for SHA-384. A longer `L`, or a negative one,
|
|
18
18
|
* answers `null` rather than panicking, because `L` usually comes from a
|
|
19
|
-
* protocol field; `L = 0` answers an empty array.
|
|
19
|
+
* protocol field; `L = 0` answers an empty array. So does an `info` longer
|
|
20
|
+
* than 2^31 - 1 bytes, which `toI32` would cut to a prefix under
|
|
21
|
+
* `--number-mode f64`, so that two `info`s sharing it gave one key, and a PRK
|
|
22
|
+
* shorter than HashLen, which §2.3 does not allow: an empty one is a key
|
|
23
|
+
* anybody can compute with.
|
|
20
24
|
*
|
|
21
|
-
* HKDF-Expand-Label
|
|
22
|
-
*
|
|
25
|
+
* HKDF-Expand-Label is TLS 1.3's wrapper around `expand` (RFC 8446 §7.1), and
|
|
26
|
+
* QUIC derives its packet keys with it too (RFC 9001 §5.1), so it is here once
|
|
27
|
+
* for both rather than in each:
|
|
28
|
+
*
|
|
29
|
+
* HKDF-Expand-Label(Secret, Label, Context, Length) =
|
|
30
|
+
* HKDF-Expand(Secret, HkdfLabel, Length)
|
|
31
|
+
* struct {
|
|
32
|
+
* uint16 length = Length;
|
|
33
|
+
* opaque label<7..255> = "tls13 " + Label;
|
|
34
|
+
* opaque context<0..255> = Context;
|
|
35
|
+
* } HkdfLabel;
|
|
36
|
+
*
|
|
37
|
+
* const key: u8[] = hkdfExpandLabelSha256(secret, "quic key", [], 16);
|
|
38
|
+
*
|
|
39
|
+
* `label` is given without the `"tls13 "` prefix, which the function adds. Its
|
|
40
|
+
* arguments come from the program rather than from the peer — a label is a
|
|
41
|
+
* literal, a length a key or hash size, a context a transcript hash — so one
|
|
42
|
+
* out of range is a bug in the caller, and it panics rather than answering
|
|
43
|
+
* `null`: a length below 0 or above 255, a label empty or longer than 249
|
|
44
|
+
* bytes (the struct's 7 to 255 less the prefix), a context longer than 255
|
|
45
|
+
* bytes, and a secret shorter than HashLen.
|
|
46
|
+
*
|
|
47
|
+
* **Caller-owned scratch.** The functions above make their HMAC objects and
|
|
48
|
+
* their output fresh on every call, which stores allocations no arena scope
|
|
49
|
+
* may release around them. `hkdfExtractInto`, `hkdfExpandInto` and
|
|
50
|
+
* `hkdfExpandLabelInto` compute the same bytes in an `HkdfScratch` the caller
|
|
51
|
+
* allocates once, over the hash `hashLength` names (32 for SHA-256, 48 for
|
|
52
|
+
* SHA-384), and write them into the caller's array, so that a loop of key
|
|
53
|
+
* derivations inside one `using a = arena()` block leaves the arena where it
|
|
54
|
+
* was. Each wipes the scratch before it returns, its last block and the
|
|
55
|
+
* keyed hashers of the hash it ran over, so no key material outlives the
|
|
56
|
+
* call there:
|
|
57
|
+
*
|
|
58
|
+
* const kdf = new HkdfScratch(); // once
|
|
59
|
+
* hkdfExtractInto(kdf, 32, salt, ikm, prk, 0); // 32 bytes at prk[0]
|
|
60
|
+
* hkdfExpandLabelInto(kdf, 32, prk, "key", context, 0, 0, key, 0, 16);
|
|
61
|
+
*
|
|
62
|
+
* They refuse what the functions above refuse, the same way: `hkdfExpandInto`
|
|
63
|
+
* answers `false` where `hkdfExpandSha256` answers `null`, and
|
|
64
|
+
* `hkdfExpandLabelInto` panics where `hkdfExpandLabelSha256` does. A hash
|
|
65
|
+
* length other than 32 or 48, or an output window outside its array, is the
|
|
66
|
+
* caller's bug and panics.
|
|
67
|
+
*
|
|
68
|
+
* Written from RFC 5869 and RFC 8446 §7.1, not ported from another
|
|
69
|
+
* implementation.
|
|
23
70
|
*/
|
|
24
|
-
import {
|
|
71
|
+
import {
|
|
72
|
+
HmacSha256,
|
|
73
|
+
HmacSha256Scratch,
|
|
74
|
+
HmacSha384,
|
|
75
|
+
HmacSha384Scratch,
|
|
76
|
+
hmacSha256,
|
|
77
|
+
hmacSha384,
|
|
78
|
+
} from "nish/crypto/hmac"
|
|
25
79
|
import { SHA256_SIZE } from "nish/crypto/sha256"
|
|
26
80
|
import { SHA384_SIZE } from "nish/crypto/sha512"
|
|
27
81
|
|
|
@@ -31,6 +85,9 @@ const HKDF_MAX_BLOCKS: i32 = 255
|
|
|
31
85
|
/** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
|
|
32
86
|
const HKDF_FROM: i32 = 0
|
|
33
87
|
|
|
88
|
+
/** What HKDF-Expand-Label puts before every label (RFC 8446 §7.1). */
|
|
89
|
+
const HKDF_LABEL_PREFIX: string = "tls13 "
|
|
90
|
+
|
|
34
91
|
/**
|
|
35
92
|
* The salt `extract` keys HMAC with: `salt` itself, or HashLen zero bytes when
|
|
36
93
|
* it is empty (RFC 5869 §2.2, "if not provided"). HMAC zero-pads a short key to
|
|
@@ -64,10 +121,16 @@ export const hkdfExtractSha384 = (salt: u8[], ikm: u8[]): u8[] => hmacSha384(hkd
|
|
|
64
121
|
/**
|
|
65
122
|
* HKDF-Expand with HMAC-SHA-256 (RFC 5869 §2.3): `length` bytes of output
|
|
66
123
|
* keying material from `prk` and `info`, or `null` when `length` is negative
|
|
67
|
-
* or past 255 × 32
|
|
124
|
+
* or past 255 × 32, when `info` is longer than 2^31 - 1 bytes, or when `prk`
|
|
125
|
+
* is shorter than 32 bytes.
|
|
68
126
|
*/
|
|
69
127
|
export const hkdfExpandSha256 = (prk: u8[], info: u8[], length: i32): u8[] | null => {
|
|
70
|
-
if (
|
|
128
|
+
if (
|
|
129
|
+
length < 0 ||
|
|
130
|
+
length > HKDF_MAX_BLOCKS * SHA256_SIZE ||
|
|
131
|
+
info.length > 2147483647 ||
|
|
132
|
+
toI32(prk.length) < SHA256_SIZE
|
|
133
|
+
) {
|
|
71
134
|
return null
|
|
72
135
|
}
|
|
73
136
|
const out: u8[] = new Array<u8>(length)
|
|
@@ -92,10 +155,16 @@ export const hkdfExpandSha256 = (prk: u8[], info: u8[], length: i32): u8[] | nul
|
|
|
92
155
|
/**
|
|
93
156
|
* HKDF-Expand with HMAC-SHA-384 (RFC 5869 §2.3): `length` bytes of output
|
|
94
157
|
* keying material from `prk` and `info`, or `null` when `length` is negative
|
|
95
|
-
* or past 255 × 48
|
|
158
|
+
* or past 255 × 48, when `info` is longer than 2^31 - 1 bytes, or when `prk`
|
|
159
|
+
* is shorter than 48 bytes.
|
|
96
160
|
*/
|
|
97
161
|
export const hkdfExpandSha384 = (prk: u8[], info: u8[], length: i32): u8[] | null => {
|
|
98
|
-
if (
|
|
162
|
+
if (
|
|
163
|
+
length < 0 ||
|
|
164
|
+
length > HKDF_MAX_BLOCKS * SHA384_SIZE ||
|
|
165
|
+
info.length > 2147483647 ||
|
|
166
|
+
toI32(prk.length) < SHA384_SIZE
|
|
167
|
+
) {
|
|
99
168
|
return null
|
|
100
169
|
}
|
|
101
170
|
const out: u8[] = new Array<u8>(length)
|
|
@@ -116,3 +185,311 @@ export const hkdfExpandSha384 = (prk: u8[], info: u8[], length: i32): u8[] | nul
|
|
|
116
185
|
}
|
|
117
186
|
return out
|
|
118
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The `HkdfLabel` struct of RFC 8446 §7.1, the `info` HKDF-Expand-Label hands
|
|
191
|
+
* to `expand`, or a panic naming `caller` when `length`, `label` or `context`
|
|
192
|
+
* is outside the range the module docs give: 249 is `label<7..255>`'s 255 less
|
|
193
|
+
* the prefix's 6 bytes. The lengths of `label` and `context` are compared as
|
|
194
|
+
* the `number`s they are, before any `toI32`, which would saturate them under
|
|
195
|
+
* `--number-mode f64`; so the bounds are literals rather than `i32` constants.
|
|
196
|
+
*/
|
|
197
|
+
const hkdfLabel = (caller: string, label: string, context: u8[], length: i32): u8[] => {
|
|
198
|
+
if (length < 0 || length > 255) {
|
|
199
|
+
panic(`${caller}: a length of ${length} bytes, outside 0 to 255`)
|
|
200
|
+
}
|
|
201
|
+
if (label.length === 0 || label.length > 249) {
|
|
202
|
+
panic(`${caller}: a label of ${toI32(label.length)} bytes, outside 1 to 249`)
|
|
203
|
+
}
|
|
204
|
+
if (context.length > 255) {
|
|
205
|
+
panic(`${caller}: a context of more than 255 bytes`)
|
|
206
|
+
}
|
|
207
|
+
const full: string = `${HKDF_LABEL_PREFIX}${label}`
|
|
208
|
+
const fullLength: i32 = toI32(full.length)
|
|
209
|
+
const contextLength: i32 = toI32(context.length)
|
|
210
|
+
const out: u8[] = new Array<u8>(4 + fullLength + contextLength)
|
|
211
|
+
out[0] = toU8(length >> 8)
|
|
212
|
+
out[1] = toU8(length & 0xff)
|
|
213
|
+
out[2] = toU8(fullLength)
|
|
214
|
+
for (let k: i32 = 0; k < fullLength; k += 1) {
|
|
215
|
+
out[3 + k] = toU8(toI32(full.charCodeAt(k)))
|
|
216
|
+
}
|
|
217
|
+
out[3 + fullLength] = toU8(contextLength)
|
|
218
|
+
hkdfAppend(out, 4 + fullLength, context)
|
|
219
|
+
return out
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* HKDF-Expand-Label with HMAC-SHA-256 (RFC 8446 §7.1): `length` bytes from
|
|
224
|
+
* `secret`, `"tls13 " + label` and `context`. Panics on an argument out of the
|
|
225
|
+
* range the module docs give, a secret shorter than 32 bytes among them.
|
|
226
|
+
*/
|
|
227
|
+
export const hkdfExpandLabelSha256 = (secret: u8[], label: string, context: u8[], length: i32): u8[] => {
|
|
228
|
+
const okm: u8[] | null = hkdfExpandSha256(
|
|
229
|
+
secret,
|
|
230
|
+
hkdfLabel("hkdfExpandLabelSha256", label, context, length),
|
|
231
|
+
length
|
|
232
|
+
)
|
|
233
|
+
if (okm === null) {
|
|
234
|
+
panic("hkdfExpandLabelSha256: a secret shorter than 32 bytes")
|
|
235
|
+
}
|
|
236
|
+
return okm
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* HKDF-Expand-Label with HMAC-SHA-384 (RFC 8446 §7.1): `length` bytes from
|
|
241
|
+
* `secret`, `"tls13 " + label` and `context`. Panics on an argument out of the
|
|
242
|
+
* range the module docs give, a secret shorter than 48 bytes among them.
|
|
243
|
+
*/
|
|
244
|
+
export const hkdfExpandLabelSha384 = (secret: u8[], label: string, context: u8[], length: i32): u8[] => {
|
|
245
|
+
const okm: u8[] | null = hkdfExpandSha384(
|
|
246
|
+
secret,
|
|
247
|
+
hkdfLabel("hkdfExpandLabelSha384", label, context, length),
|
|
248
|
+
length
|
|
249
|
+
)
|
|
250
|
+
if (okm === null) {
|
|
251
|
+
panic("hkdfExpandLabelSha384: a secret shorter than 48 bytes")
|
|
252
|
+
}
|
|
253
|
+
return okm
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---- Caller-owned scratch ----------------------------------------------------
|
|
257
|
+
|
|
258
|
+
/** The longest `HkdfLabel`: two length bytes, `label<7..255>` and `context<0..255>`, each with its length byte. */
|
|
259
|
+
const HKDF_LABEL_MAX: i32 = 514
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* What the `*Into` functions compute in: an HMAC scratch for each hash, the
|
|
263
|
+
* previous output block T(i - 1), and the `HkdfLabel` HKDF-Expand-Label
|
|
264
|
+
* builds. Allocate one per long-lived owner — a connection slot, say — and
|
|
265
|
+
* hand it to every derivation; it carries nothing from one call to the next.
|
|
266
|
+
*/
|
|
267
|
+
export class HkdfScratch {
|
|
268
|
+
sha256: HmacSha256Scratch
|
|
269
|
+
sha384: HmacSha384Scratch
|
|
270
|
+
/** T(i - 1), HashLen bytes of it; wiped before every function returns. */
|
|
271
|
+
previous: u8[]
|
|
272
|
+
/** The one-byte block counter `i`. */
|
|
273
|
+
counter: u8[]
|
|
274
|
+
/** HKDF-Expand-Label's `info`, built here: public, so it is not wiped. */
|
|
275
|
+
label: u8[]
|
|
276
|
+
|
|
277
|
+
constructor() {
|
|
278
|
+
this.sha256 = new HmacSha256Scratch()
|
|
279
|
+
this.sha384 = new HmacSha384Scratch()
|
|
280
|
+
this.previous = new Array<u8>(SHA384_SIZE)
|
|
281
|
+
this.counter = new Array<u8>(1)
|
|
282
|
+
this.label = new Array<u8>(HKDF_LABEL_MAX)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Zeroes the key material the last derivation left: T(i - 1) and both keyed hashers. */
|
|
286
|
+
wipe(): void {
|
|
287
|
+
secureZero(this.previous)
|
|
288
|
+
this.sha256.wipe()
|
|
289
|
+
this.sha384.wipe()
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Panics unless `hashLength` is 32 or 48, the two hashes HKDF runs over here. */
|
|
294
|
+
const hkdfCheckHash = (caller: string, hashLength: i32): void => {
|
|
295
|
+
if (hashLength !== SHA256_SIZE && hashLength !== SHA384_SIZE) {
|
|
296
|
+
panic(`${caller}: a hash length of ${hashLength}, not 32 or 48`)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Panics unless `out[at .. at + length)` is a window inside `out`. */
|
|
301
|
+
const hkdfCheckOutput = (caller: string, out: u8[], at: i32, length: i32): void => {
|
|
302
|
+
if (at < 0 || length < 0 || at > toI32(out.length) - length) {
|
|
303
|
+
panic(`${caller}: the output window is outside its array`)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Zeroes what the last derivation over `hashLength` left in `s`: T(i - 1) and that hash's keyed hashers. */
|
|
308
|
+
const hkdfWipe = (s: HkdfScratch, hashLength: i32): void => {
|
|
309
|
+
secureZero(s.previous)
|
|
310
|
+
if (hashLength === SHA384_SIZE) {
|
|
311
|
+
s.sha384.wipe()
|
|
312
|
+
} else {
|
|
313
|
+
s.sha256.wipe()
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Keys the scratch's HMAC for `hashLength` with `key[off .. off + len)`. */
|
|
318
|
+
const hkdfMacBegin = (s: HkdfScratch, hashLength: i32, key: u8[], off: i32, len: i32): void => {
|
|
319
|
+
if (hashLength === SHA384_SIZE) {
|
|
320
|
+
s.sha384.begin(key, off, len)
|
|
321
|
+
} else {
|
|
322
|
+
s.sha256.begin(key, off, len)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Feeds `data[off .. off + len)` to the scratch's HMAC for `hashLength`. */
|
|
327
|
+
const hkdfMacUpdate = (s: HkdfScratch, hashLength: i32, data: u8[], off: i32, len: i32): void => {
|
|
328
|
+
if (hashLength === SHA384_SIZE) {
|
|
329
|
+
s.sha384.update(data, off, len)
|
|
330
|
+
} else {
|
|
331
|
+
s.sha256.update(data, off, len)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Writes the HMAC for `hashLength`'s tag at `out[at]`. */
|
|
336
|
+
const hkdfMacFinish = (s: HkdfScratch, hashLength: i32, out: u8[], at: i32): void => {
|
|
337
|
+
if (hashLength === SHA384_SIZE) {
|
|
338
|
+
s.sha384.finishInto(out, at)
|
|
339
|
+
} else {
|
|
340
|
+
s.sha256.finishInto(out, at)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* HKDF-Extract (RFC 5869 §2.2) over the hash `hashLength` names, the PRK's
|
|
346
|
+
* HashLen bytes written at `out[at]`. An empty `salt` keys HMAC with no bytes,
|
|
347
|
+
* which HMAC pads to the block with zeros, exactly as the HashLen zeros §2.2
|
|
348
|
+
* asks for would be. An `ikm` longer than 2^31 - 1 bytes panics, as
|
|
349
|
+
* `hmacSha256` does.
|
|
350
|
+
*/
|
|
351
|
+
export const hkdfExtractInto = (
|
|
352
|
+
s: HkdfScratch,
|
|
353
|
+
hashLength: i32,
|
|
354
|
+
salt: u8[],
|
|
355
|
+
ikm: u8[],
|
|
356
|
+
out: u8[],
|
|
357
|
+
at: i32
|
|
358
|
+
): void => {
|
|
359
|
+
hkdfCheckHash("hkdfExtractInto", hashLength)
|
|
360
|
+
hkdfCheckOutput("hkdfExtractInto", out, at, hashLength)
|
|
361
|
+
if (ikm.length > 2147483647) {
|
|
362
|
+
panic("hkdfExtractInto: input keying material longer than 2^31 - 1 bytes")
|
|
363
|
+
}
|
|
364
|
+
hkdfMacBegin(s, hashLength, salt, HKDF_FROM, toI32(salt.length))
|
|
365
|
+
hkdfMacUpdate(s, hashLength, ikm, HKDF_FROM, toI32(ikm.length))
|
|
366
|
+
hkdfMacFinish(s, hashLength, out, at)
|
|
367
|
+
hkdfWipe(s, hashLength)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The T(1) | T(2) | … loop of RFC 5869 §2.3, `length` bytes of it at
|
|
372
|
+
* `out[at]`, with `info` the first `infoLen` bytes of `info`.
|
|
373
|
+
* The callers have checked every bound.
|
|
374
|
+
*/
|
|
375
|
+
const hkdfExpandWindow = (
|
|
376
|
+
s: HkdfScratch,
|
|
377
|
+
hashLength: i32,
|
|
378
|
+
prk: u8[],
|
|
379
|
+
info: u8[],
|
|
380
|
+
infoLen: i32,
|
|
381
|
+
out: u8[],
|
|
382
|
+
at: i32,
|
|
383
|
+
length: i32
|
|
384
|
+
): void => {
|
|
385
|
+
const counterLength: i32 = 1
|
|
386
|
+
let done: i32 = 0
|
|
387
|
+
let i: i32 = 1
|
|
388
|
+
while (done < length) {
|
|
389
|
+
hkdfMacBegin(s, hashLength, prk, HKDF_FROM, toI32(prk.length))
|
|
390
|
+
if (i > 1) {
|
|
391
|
+
hkdfMacUpdate(s, hashLength, s.previous, HKDF_FROM, hashLength)
|
|
392
|
+
}
|
|
393
|
+
hkdfMacUpdate(s, hashLength, info, HKDF_FROM, infoLen)
|
|
394
|
+
s.counter[0] = toU8(i)
|
|
395
|
+
hkdfMacUpdate(s, hashLength, s.counter, HKDF_FROM, counterLength)
|
|
396
|
+
hkdfMacFinish(s, hashLength, s.previous, HKDF_FROM)
|
|
397
|
+
for (let k: i32 = 0; k < hashLength && done < length && k < toI32(s.previous.length); k += 1) {
|
|
398
|
+
out[at + done] = s.previous[k]
|
|
399
|
+
done += 1
|
|
400
|
+
}
|
|
401
|
+
i += 1
|
|
402
|
+
}
|
|
403
|
+
hkdfWipe(s, hashLength)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* HKDF-Expand (RFC 5869 §2.3) over the hash `hashLength` names: `length`
|
|
408
|
+
* bytes from `prk` and `info`, written at `out[at]`. `false`, writing nothing,
|
|
409
|
+
* where `hkdfExpandSha256` answers `null`: a `length` negative or past
|
|
410
|
+
* 255 × HashLen, an `info` longer than 2^31 - 1 bytes, or a `prk` shorter than
|
|
411
|
+
* HashLen.
|
|
412
|
+
*/
|
|
413
|
+
export const hkdfExpandInto = (
|
|
414
|
+
s: HkdfScratch,
|
|
415
|
+
hashLength: i32,
|
|
416
|
+
prk: u8[],
|
|
417
|
+
info: u8[],
|
|
418
|
+
out: u8[],
|
|
419
|
+
at: i32,
|
|
420
|
+
length: i32
|
|
421
|
+
): boolean => {
|
|
422
|
+
hkdfCheckHash("hkdfExpandInto", hashLength)
|
|
423
|
+
if (
|
|
424
|
+
length < 0 ||
|
|
425
|
+
length > HKDF_MAX_BLOCKS * hashLength ||
|
|
426
|
+
info.length > 2147483647 ||
|
|
427
|
+
toI32(prk.length) < hashLength
|
|
428
|
+
) {
|
|
429
|
+
return false
|
|
430
|
+
}
|
|
431
|
+
hkdfCheckOutput("hkdfExpandInto", out, at, length)
|
|
432
|
+
hkdfExpandWindow(s, hashLength, prk, info, toI32(info.length), out, at, length)
|
|
433
|
+
return true
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* HKDF-Expand-Label (RFC 8446 §7.1) over the hash `hashLength` names: `length`
|
|
438
|
+
* bytes from `secret`, `"tls13 " + label` and the context
|
|
439
|
+
* `context[contextOff .. contextOff + contextLen)`, written at `out[at]`. It
|
|
440
|
+
* panics where `hkdfExpandLabelSha256` does — a length outside 0 to 255, a
|
|
441
|
+
* label empty or past 249 bytes, a context past 255, a secret shorter than
|
|
442
|
+
* HashLen — and on a context window outside its array.
|
|
443
|
+
*/
|
|
444
|
+
export const hkdfExpandLabelInto = (
|
|
445
|
+
s: HkdfScratch,
|
|
446
|
+
hashLength: i32,
|
|
447
|
+
secret: u8[],
|
|
448
|
+
label: string,
|
|
449
|
+
context: u8[],
|
|
450
|
+
contextOff: i32,
|
|
451
|
+
contextLen: i32,
|
|
452
|
+
out: u8[],
|
|
453
|
+
at: i32,
|
|
454
|
+
length: i32
|
|
455
|
+
): void => {
|
|
456
|
+
hkdfCheckHash("hkdfExpandLabelInto", hashLength)
|
|
457
|
+
if (length < 0 || length > 255) {
|
|
458
|
+
panic(`hkdfExpandLabelInto: a length of ${length} bytes, outside 0 to 255`)
|
|
459
|
+
}
|
|
460
|
+
if (label.length === 0 || label.length > 249) {
|
|
461
|
+
panic(`hkdfExpandLabelInto: a label of ${toI32(label.length)} bytes, outside 1 to 249`)
|
|
462
|
+
}
|
|
463
|
+
if (
|
|
464
|
+
contextOff < 0 ||
|
|
465
|
+
contextLen < 0 ||
|
|
466
|
+
contextOff > toI32(context.length) - contextLen ||
|
|
467
|
+
contextLen > 255
|
|
468
|
+
) {
|
|
469
|
+
panic("hkdfExpandLabelInto: the context window is outside its array or longer than 255 bytes")
|
|
470
|
+
}
|
|
471
|
+
if (toI32(secret.length) < hashLength) {
|
|
472
|
+
panic("hkdfExpandLabelInto: a secret shorter than HashLen")
|
|
473
|
+
}
|
|
474
|
+
hkdfCheckOutput("hkdfExpandLabelInto", out, at, length)
|
|
475
|
+
// The `HkdfLabel` struct, as `hkdfLabel` builds it, into the scratch.
|
|
476
|
+
const prefixLength: i32 = toI32(HKDF_LABEL_PREFIX.length)
|
|
477
|
+
const labelLength: i32 = toI32(label.length)
|
|
478
|
+
const fullLength: i32 = prefixLength + labelLength
|
|
479
|
+
const info: u8[] = s.label
|
|
480
|
+
info[0] = toU8(length >> 8)
|
|
481
|
+
info[1] = toU8(length & 0xff)
|
|
482
|
+
info[2] = toU8(fullLength)
|
|
483
|
+
for (let k: i32 = 0; k < prefixLength && 3 + k < toI32(info.length); k += 1) {
|
|
484
|
+
info[3 + k] = toU8(toI32(HKDF_LABEL_PREFIX.charCodeAt(k)))
|
|
485
|
+
}
|
|
486
|
+
for (let k: i32 = 0; k < labelLength && 3 + prefixLength + k < toI32(info.length); k += 1) {
|
|
487
|
+
info[3 + prefixLength + k] = toU8(toI32(label.charCodeAt(k)))
|
|
488
|
+
}
|
|
489
|
+
const contextAt: i32 = 3 + fullLength
|
|
490
|
+
info[contextAt] = toU8(contextLen)
|
|
491
|
+
for (let k: i32 = 0; k < contextLen && contextAt + 1 + k < toI32(info.length); k += 1) {
|
|
492
|
+
info[contextAt + 1 + k] = context[contextOff + k]
|
|
493
|
+
}
|
|
494
|
+
hkdfExpandWindow(s, hashLength, secret, info, contextAt + 1 + contextLen, out, at, length)
|
|
495
|
+
}
|