@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.
Files changed (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
@@ -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 { HmacSha256, hmacSha256 } from "nish/crypto/hmac"
81
- import { sha256 } from "nish/crypto/sha256"
91
+ import { Secret, expose, exposeWith, wipe } from "nish:secret"
92
+ import { SHA256_BLOCK, SHA256_SIZE, 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)
@@ -3791,6 +3819,13 @@ const p256PointCopy = (out: P256ProjectivePoint, p: P256ProjectivePoint): void =
3791
3819
  p256Copy(out.z, p.z)
3792
3820
  }
3793
3821
 
3822
+ /** `p = (0 : 1 : 0)`, the identity, in place: what `new P256ProjectivePoint()` makes. */
3823
+ const p256SetIdentity = (p: P256ProjectivePoint): void => {
3824
+ p256SetZero(p.x)
3825
+ p256FiatSetOne(p.y)
3826
+ p256SetZero(p.z)
3827
+ }
3828
+
3794
3829
  /**
3795
3830
  * `out = p + q`, by Renes, Costello and Batina's Algorithm 4 (complete
3796
3831
  * addition for a = -3), step for step in the paper's order and with its names.
@@ -3929,10 +3964,15 @@ const p256TableStorePoint = (table: u32[], i: i32, p: P256ProjectivePoint): void
3929
3964
  * The window table: entry `i` is `i * p`, for `i` from 0 to 15. Built from
3930
3965
  * `p`, which is public here (G or a received key) and a secret only in that
3931
3966
  * it is about to be multiplied. Entry 0 is the identity and entry 1 is `p`;
3932
- * each later one adds `p` to the one before.
3967
+ * each later one adds `p` to the one before, in `acc`, which the caller owns.
3933
3968
  */
3934
- const p256TableBuild = (table: u32[], p: P256ProjectivePoint, s: P256PointScratch): void => {
3935
- const acc = new P256ProjectivePoint()
3969
+ const p256TableBuild = (
3970
+ table: u32[],
3971
+ p: P256ProjectivePoint,
3972
+ s: P256PointScratch,
3973
+ acc: P256ProjectivePoint
3974
+ ): void => {
3975
+ p256SetIdentity(acc)
3936
3976
  p256TableStorePoint(table, 0, acc)
3937
3977
  p256PointCopy(acc, p)
3938
3978
  p256TableStorePoint(table, 1, acc)
@@ -3948,17 +3988,18 @@ const p256TableBuild = (table: u32[], p: P256ProjectivePoint, s: P256PointScratc
3948
3988
  * way, at an offset that is the caller's loop position, never the secret.
3949
3989
  */
3950
3990
  const p256TableMove = (out: u32[], table: u32[], at: i32, hit: u32): void => {
3991
+ const p: u32 = toU32(at)
3951
3992
  if (toI32(out.length) < 8 || at < 0 || at > toI32(table.length) - 8) {
3952
3993
  return
3953
3994
  }
3954
- out[0] = p256FiatCmovznzU32(hit, out[0], table[at])
3955
- out[1] = p256FiatCmovznzU32(hit, out[1], table[at + 1])
3956
- out[2] = p256FiatCmovznzU32(hit, out[2], table[at + 2])
3957
- out[3] = p256FiatCmovznzU32(hit, out[3], table[at + 3])
3958
- out[4] = p256FiatCmovznzU32(hit, out[4], table[at + 4])
3959
- out[5] = p256FiatCmovznzU32(hit, out[5], table[at + 5])
3960
- out[6] = p256FiatCmovznzU32(hit, out[6], table[at + 6])
3961
- out[7] = p256FiatCmovznzU32(hit, out[7], table[at + 7])
3995
+ out[0] = p256FiatCmovznzU32(hit, out[0], table[p])
3996
+ out[1] = p256FiatCmovznzU32(hit, out[1], table[p + 1])
3997
+ out[2] = p256FiatCmovznzU32(hit, out[2], table[p + 2])
3998
+ out[3] = p256FiatCmovznzU32(hit, out[3], table[p + 3])
3999
+ out[4] = p256FiatCmovznzU32(hit, out[4], table[p + 4])
4000
+ out[5] = p256FiatCmovznzU32(hit, out[5], table[p + 5])
4001
+ out[6] = p256FiatCmovznzU32(hit, out[6], table[p + 6])
4002
+ out[7] = p256FiatCmovznzU32(hit, out[7], table[p + 7])
3962
4003
  }
3963
4004
 
3964
4005
  /**
@@ -3992,15 +4033,31 @@ const p256ScalarMult = (
3992
4033
  k: u8[],
3993
4034
  p: P256ProjectivePoint,
3994
4035
  s: P256PointScratch
4036
+ ): void => {
4037
+ p256ScalarMultWith(out, k, p, s, new Array<u32>(384), new P256ProjectivePoint(), new P256ProjectivePoint())
4038
+ }
4039
+
4040
+ /**
4041
+ * `p256ScalarMult` in the caller's table (384 limbs) and points, so that it
4042
+ * allocates nothing: what `P256SignScratch` signs with. The accumulator, the
4043
+ * selected entry and the table follow the scalar's digits, and a signature's
4044
+ * scalar is its nonce, so all three are wiped before it returns.
4045
+ */
4046
+ const p256ScalarMultWith = (
4047
+ out: P256ProjectivePoint,
4048
+ k: u8[],
4049
+ p: P256ProjectivePoint,
4050
+ s: P256PointScratch,
4051
+ table: u32[],
4052
+ acc: P256ProjectivePoint,
4053
+ entry: P256ProjectivePoint
3995
4054
  ): void => {
3996
4055
  const kLength: i32 = toI32(k.length)
3997
4056
  if (kLength < 32) {
3998
4057
  return
3999
4058
  }
4000
- const table: u32[] = new Array<u32>(384)
4001
- p256TableBuild(table, p, s)
4002
- const acc = new P256ProjectivePoint()
4003
- const entry = new P256ProjectivePoint()
4059
+ p256TableBuild(table, p, s, acc)
4060
+ p256SetIdentity(acc)
4004
4061
  for (let i: i32 = 0; i < 64; i++) {
4005
4062
  // Digit i is the high nibble of byte i / 2 when i is even, the low when odd.
4006
4063
  const at: i32 = i >> 1
@@ -4014,6 +4071,9 @@ const p256ScalarMult = (
4014
4071
  p256PointAdd(acc, acc, entry, s)
4015
4072
  }
4016
4073
  p256PointCopy(out, acc)
4074
+ wipe(table)
4075
+ p256WipePoint(acc)
4076
+ p256WipePoint(entry)
4017
4077
  }
4018
4078
 
4019
4079
  /** The base point G (SEC 2 §2.4.2), in the Montgomery domain with Z = 1. */
@@ -4133,6 +4193,7 @@ const p256ScalarInRange = (bytes: u8[]): boolean => {
4133
4193
  p256LimbsFromBytes(a, bytes, 0)
4134
4194
  const below: u32 = p256Below(a, p256Order())
4135
4195
  const zero: u32 = ctEq(p256FiatNonzero(a), 0) & 1
4196
+ wipe(a) // a private key's limbs, when `bytes` is one
4136
4197
  return (below & (zero ^ 1)) === 1
4137
4198
  }
4138
4199
 
@@ -4155,19 +4216,72 @@ const p256DigestScalar = (out: u32[], digest: u8[]): void => {
4155
4216
  p256ReduceOnce(out, a, p256Order())
4156
4217
  }
4157
4218
 
4219
+ /** RFC 2104's key block for SHA-256: `k` zero-extended to 64 bytes, each byte XOR `pad`. */
4220
+ const p256PadBlock = (k: u8[], pad: u8): u8[] => {
4221
+ const out: u8[] = new Array<u8>(64)
4222
+ const outLength: i32 = toI32(out.length)
4223
+ const keyLength: i32 = toI32(k.length)
4224
+ for (let i: i32 = 0; i < outLength; i++) {
4225
+ if (i < keyLength) {
4226
+ out[i] = k[i] ^ pad
4227
+ } else {
4228
+ out[i] = pad
4229
+ }
4230
+ }
4231
+ return out
4232
+ }
4233
+
4234
+ /** Zero what a SHA-256 in progress holds: its chaining value, its pending block and its schedule. */
4235
+ const p256WipeHash = (h: Sha256): void => {
4236
+ wipe(h.state)
4237
+ wipe(h.block)
4238
+ wipe(h.schedule)
4239
+ }
4240
+
4241
+ /**
4242
+ * HMAC-SHA-256 keyed by `k` over `a || b || c || d` (RFC 2104), the one MAC
4243
+ * RFC 6979 runs. It is written out over `Sha256` here rather than taken from
4244
+ * `nish/crypto/hmac` so that every buffer the key reaches — the two key
4245
+ * blocks, both hashers' state, block and schedule, and the inner digest — is
4246
+ * this function's to wipe before it returns (ECC-2). `k` is always 32 bytes,
4247
+ * under SHA-256's 64-byte block, so it is never hashed first.
4248
+ */
4249
+ const p256Mac = (k: u8[], a: u8[], b: u8[], c: u8[], d: u8[]): u8[] => {
4250
+ const ipad: u8[] = p256PadBlock(k, 0x36)
4251
+ const opad: u8[] = p256PadBlock(k, 0x5c)
4252
+ const inner = new Sha256()
4253
+ inner.update(ipad, P256_FROM, toI32(ipad.length))
4254
+ inner.update(a, P256_FROM, toI32(a.length))
4255
+ inner.update(b, P256_FROM, toI32(b.length))
4256
+ inner.update(c, P256_FROM, toI32(c.length))
4257
+ inner.update(d, P256_FROM, toI32(d.length))
4258
+ const innerDigest: u8[] = inner.digest()
4259
+ const outer = new Sha256()
4260
+ outer.update(opad, P256_FROM, toI32(opad.length))
4261
+ outer.update(innerDigest, P256_FROM, toI32(innerDigest.length))
4262
+ const tag: u8[] = outer.digest()
4263
+ wipe(ipad)
4264
+ wipe(opad)
4265
+ wipe(innerDigest)
4266
+ p256WipeHash(inner)
4267
+ p256WipeHash(outer)
4268
+ return tag
4269
+ }
4270
+
4158
4271
  /**
4159
4272
  * HMAC_K(V || sep || x || h), the key update of RFC 6979 §3.2 steps d and f
4160
4273
  * (with `sep` 0 and then 1) and, with `x` and `h` empty, step h.3.
4161
4274
  */
4162
4275
  const p256NonceKey = (k: u8[], v: u8[], sep: u8, x: u8[], h: u8[]): u8[] => {
4163
- const mac = new HmacSha256(k)
4164
4276
  const separator: u8[] = new Array<u8>(1)
4165
4277
  separator[0] = sep
4166
- mac.update(v, P256_FROM, toI32(v.length))
4167
- mac.update(separator, P256_FROM, toI32(separator.length))
4168
- mac.update(x, P256_FROM, toI32(x.length))
4169
- mac.update(h, P256_FROM, toI32(h.length))
4170
- return mac.digest()
4278
+ return p256Mac(k, v, separator, x, h)
4279
+ }
4280
+
4281
+ /** HMAC_K(V), RFC 6979 §3.2 steps e, g and h.2. */
4282
+ const p256NonceValue = (k: u8[], v: u8[]): u8[] => {
4283
+ const empty: u8[] = new Array<u8>(0)
4284
+ return p256Mac(k, v, empty, empty, empty)
4171
4285
  }
4172
4286
 
4173
4287
  /**
@@ -4192,10 +4306,20 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
4192
4306
  for (let i: i32 = 0; i < 32; i++) {
4193
4307
  v[i] = 1
4194
4308
  }
4195
- k = p256NonceKey(k, v, 0, priv, h1) // step d
4196
- v = hmacSha256(k, v) // step e
4197
- k = p256NonceKey(k, v, 1, priv, h1) // step f
4198
- v = hmacSha256(k, v) // step g
4309
+ // Each K and V is wiped as the next replaces it (ECC-2): both are derived
4310
+ // from the private key, and the last V is the nonce.
4311
+ let next: u8[] = p256NonceKey(k, v, 0, priv, h1) // step d
4312
+ wipe(k)
4313
+ k = next
4314
+ next = p256NonceValue(k, v) // step e
4315
+ wipe(v)
4316
+ v = next
4317
+ next = p256NonceKey(k, v, 1, priv, h1) // step f
4318
+ wipe(k)
4319
+ k = next
4320
+ next = p256NonceValue(k, v) // step g
4321
+ wipe(v)
4322
+ v = next
4199
4323
 
4200
4324
  const dM: u32[] = p256Limbs()
4201
4325
  p256LimbsFromBytes(dM, priv, 0)
@@ -4208,7 +4332,9 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
4208
4332
  const kM: u32[] = p256Limbs()
4209
4333
  const s: u32[] = p256Limbs()
4210
4334
  while (true) {
4211
- v = hmacSha256(k, v) // step h.2
4335
+ next = p256NonceValue(k, v) // step h.2
4336
+ wipe(v)
4337
+ v = next
4212
4338
  if (p256ScalarInRange(v)) {
4213
4339
  p256ScalarMult(point, v, g, scratch)
4214
4340
  p256AffineX(x, point)
@@ -4226,11 +4352,23 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
4226
4352
  const sig: u8[] = new Array<u8>(64)
4227
4353
  p256LimbsToBytes(sig, 0, r)
4228
4354
  p256LimbsToBytes(sig, 32, s)
4355
+ // ECC-2: the private key's limbs, the nonce and its inverse, RFC
4356
+ // 6979's K and V, the nonce point and the scratch it was built in.
4357
+ wipe(k)
4358
+ wipe(v)
4359
+ wipe(dM)
4360
+ wipe(kM)
4361
+ p256WipePoint(point)
4362
+ p256WipeScratch(scratch)
4229
4363
  return sig
4230
4364
  }
4231
4365
  }
4232
- k = p256NonceKey(k, v, 0, empty, empty) // step h.3
4233
- v = hmacSha256(k, v)
4366
+ next = p256NonceKey(k, v, 0, empty, empty) // step h.3
4367
+ wipe(k)
4368
+ k = next
4369
+ next = p256NonceValue(k, v)
4370
+ wipe(v)
4371
+ v = next
4234
4372
  }
4235
4373
  }
4236
4374
 
@@ -4239,12 +4377,17 @@ const p256SignScalar = (priv: u8[], z: u32[]): u8[] => {
4239
4377
  * encoding. Answers `null` unless `priv` is `P256_SCALAR_SIZE` bytes holding a
4240
4378
  * scalar in [1, n) — 0 and n and everything above are not private keys.
4241
4379
  */
4242
- export const p256PublicKey = (priv: u8[]): u8[] | null => {
4380
+ export const p256PublicKey = (priv: Secret<u8[]>): u8[] | null => expose(priv, p256PublicKeyExposed)
4381
+
4382
+ /** `p256PublicKey` on the plain key, for `expose` to run: the scratch the key's digits steered is wiped. */
4383
+ const p256PublicKeyExposed = (priv: u8[]): u8[] | null => {
4243
4384
  if (!p256ScalarInRange(priv)) {
4244
4385
  return null
4245
4386
  }
4246
4387
  const point = new P256ProjectivePoint()
4247
- p256ScalarMult(point, priv, p256Generator(), new P256PointScratch())
4388
+ const scratch = new P256PointScratch()
4389
+ p256ScalarMult(point, priv, p256Generator(), scratch)
4390
+ p256WipeScratch(scratch)
4248
4391
  return p256EncodePoint(point)
4249
4392
  }
4250
4393
 
@@ -4256,7 +4399,11 @@ export const p256PublicKey = (priv: u8[]): u8[] | null => {
4256
4399
  * not normalised to the lower half. Answers `null` for a private key
4257
4400
  * `p256PublicKey` would refuse.
4258
4401
  */
4259
- export const p256Sign = (priv: u8[], digest: u8[]): u8[] | null => {
4402
+ export const p256Sign = (priv: Secret<u8[]>, digest: u8[]): u8[] | null =>
4403
+ exposeWith(priv, digest, p256SignExposed)
4404
+
4405
+ /** `p256Sign` on the plain key, for `exposeWith` to run. */
4406
+ const p256SignExposed = (priv: u8[], digest: u8[]): u8[] | null => {
4260
4407
  if (!p256ScalarInRange(priv)) {
4261
4408
  return null
4262
4409
  }
@@ -4335,8 +4482,445 @@ export const p256Verify = (pub: u8[], digest: u8[], sig: u8[]): boolean => {
4335
4482
  }
4336
4483
 
4337
4484
  /** `p256Sign` of the SHA-256 of `msg`: ECDSA with SHA-256 over the whole message. */
4338
- export const p256SignSha256 = (priv: u8[], msg: u8[]): u8[] | null => p256Sign(priv, sha256(msg))
4485
+ export const p256SignSha256 = (priv: Secret<u8[]>, msg: u8[]): u8[] | null => p256Sign(priv, sha256(msg))
4339
4486
 
4340
4487
  /** `p256Verify` of the SHA-256 of `msg`. */
4341
4488
  export const p256VerifySha256 = (pub: u8[], msg: u8[], sig: u8[]): boolean =>
4342
4489
  p256Verify(pub, sha256(msg), sig)
4490
+
4491
+ // ---- Signing over caller-owned scratch --------------------------------------
4492
+ //
4493
+ // `p256SignSha256` makes its points, its point scratch and RFC 6979's hashers
4494
+ // with `new`, which stores fresh arrays into their fields, so it stores
4495
+ // allocations of its own: no automatic arena scope and no `using a = arena()`
4496
+ // block may release around it (docs/LANGUAGE.md, "`using a = arena()`",
4497
+ // NL2424), and every signature keeps what it made. `P256SignScratch` holds all
4498
+ // of that, made once, and `p256SignSha256Into` signs in it and writes `r || s`
4499
+ // into the caller's array, inside an arena block of its own: a signature keeps
4500
+ // nothing, and it is byte for byte `p256SignSha256`'s, because RFC 6979 is
4501
+ // deterministic.
4502
+ //
4503
+ // The private key is still read only inside `expose` / `exposeWith`, and a
4504
+ // function `exposeWith` runs may not write the argument it is handed (NL2450),
4505
+ // so the three steps that read the key — the range check, RFC 6979 §3.2 steps
4506
+ // d and f, and `s = k^-1 (z + r d)` — compute on fresh arrays, wipe them, and
4507
+ // answer a value the caller copies into the scratch. That is why the hashing
4508
+ // below is SHA-256 written out over plain arrays (FIPS 180-4) rather than
4509
+ // `Sha256` or `HmacSha256Scratch`: a hasher is an object whose state a step
4510
+ // inside `expose` would have to write. Everything else — the message hash,
4511
+ // steps e, g and h, the nonce's multiple of G and its inverse — runs on the
4512
+ // scratch outside `expose`, on values derived from the key but not the key.
4513
+
4514
+ /** The shift of a word's top byte, typed for `--number-mode f64`. */
4515
+ const P256_WORD_TOP: i32 = 24
4516
+
4517
+ /** RFC 6979 §3.2 steps d and f hash `V || sep || x || h1`: 32 + 1 + 32 + 32 bytes. */
4518
+ const P256_NONCE_MESSAGE: i32 = 97
4519
+
4520
+ /** ROTR^n(x) of FIPS 180-4 §3.2, `0 < n < 32`. */
4521
+ const p256Rotr = (x: u32, n: u32): u32 => (x >>> n) | (x << (32 - n))
4522
+
4523
+ /** The big-endian word at `buf[at .. at + 4)`; the caller has checked the window. */
4524
+ const p256LoadWord = (buf: u8[], at: i32): u32 =>
4525
+ (toU32(buf[at]) << 24) | (toU32(buf[at + 1]) << 16) | (toU32(buf[at + 2]) << 8) | toU32(buf[at + 3])
4526
+
4527
+ /**
4528
+ * FIPS 180-4 §6.2.2 steps 1 to 4 for the block at `src[at .. at + 64)`, folded
4529
+ * into the hash value `h` (8 words) with the schedule `w` (64 words) and the
4530
+ * round constants `kt` (§4.2.2, 64 words), all the caller's.
4531
+ */
4532
+ const p256ShaCompress = (h: u32[], w: u32[], kt: u32[], src: u8[], at: i32): void => {
4533
+ if (
4534
+ toI32(h.length) >= 8 &&
4535
+ toI32(w.length) >= 64 &&
4536
+ toI32(kt.length) >= 64 &&
4537
+ at >= 0 &&
4538
+ at <= toI32(src.length) - SHA256_BLOCK
4539
+ ) {
4540
+ for (let t: i32 = 0; t < 16; t++) {
4541
+ w[t] = p256LoadWord(src, at + 4 * t)
4542
+ }
4543
+ for (let t: i32 = 16; t < 64; t++) {
4544
+ const w2: u32 = w[t - 2]
4545
+ const w15: u32 = w[t - 15]
4546
+ const sigma1: u32 = p256Rotr(w2, 17) ^ p256Rotr(w2, 19) ^ (w2 >>> 10)
4547
+ const sigma0: u32 = p256Rotr(w15, 7) ^ p256Rotr(w15, 18) ^ (w15 >>> 3)
4548
+ w[t] = sigma1 + w[t - 7] + sigma0 + w[t - 16]
4549
+ }
4550
+ let a: u32 = h[0]
4551
+ let b: u32 = h[1]
4552
+ let c: u32 = h[2]
4553
+ let d: u32 = h[3]
4554
+ let e: u32 = h[4]
4555
+ let f: u32 = h[5]
4556
+ let g: u32 = h[6]
4557
+ let hh: u32 = h[7]
4558
+ for (let t: i32 = 0; t < 64; t++) {
4559
+ const bigSigma1: u32 = p256Rotr(e, 6) ^ p256Rotr(e, 11) ^ p256Rotr(e, 25)
4560
+ const choose: u32 = (e & f) ^ (~e & g)
4561
+ const t1: u32 = hh + bigSigma1 + choose + kt[t] + w[t]
4562
+ const bigSigma0: u32 = p256Rotr(a, 2) ^ p256Rotr(a, 13) ^ p256Rotr(a, 22)
4563
+ const majority: u32 = (a & b) ^ (a & c) ^ (b & c)
4564
+ hh = g
4565
+ g = f
4566
+ f = e
4567
+ e = d + t1
4568
+ d = c
4569
+ c = b
4570
+ b = a
4571
+ a = t1 + bigSigma0 + majority
4572
+ }
4573
+ h[0] += a
4574
+ h[1] += b
4575
+ h[2] += c
4576
+ h[3] += d
4577
+ h[4] += e
4578
+ h[5] += f
4579
+ h[6] += g
4580
+ h[7] += hh
4581
+ }
4582
+ }
4583
+
4584
+ /**
4585
+ * The SHA-256 of `data[off .. off + len)` into `out[at .. at + 32)`, on the
4586
+ * caller's hash value `h`, schedule `w` and `tail` (128 bytes, where the last
4587
+ * part block is padded as §5.1.1 says). Whole blocks are compressed where
4588
+ * they lie, so the message is never copied. `out` may be `data`: the digest is
4589
+ * written once every byte has been read. The caller has checked both windows.
4590
+ */
4591
+ const p256ShaInto = (
4592
+ out: u8[],
4593
+ at: i32,
4594
+ data: u8[],
4595
+ off: i32,
4596
+ len: i32,
4597
+ kt: u32[],
4598
+ h: u32[],
4599
+ w: u32[],
4600
+ tail: u8[]
4601
+ ): void => {
4602
+ p256SetLimbs(
4603
+ h,
4604
+ 0x6a09e667,
4605
+ 0xbb67ae85,
4606
+ 0x3c6ef372,
4607
+ 0xa54ff53a,
4608
+ 0x510e527f,
4609
+ 0x9b05688c,
4610
+ 0x1f83d9ab,
4611
+ 0x5be0cd19
4612
+ )
4613
+ let from: i32 = off
4614
+ while (len - (from - off) >= SHA256_BLOCK) {
4615
+ p256ShaCompress(h, w, kt, data, from)
4616
+ from = from + SHA256_BLOCK
4617
+ }
4618
+ const rest: i32 = len - (from - off)
4619
+ const tailLength: i32 = toI32(tail.length)
4620
+ const dataLength: i32 = toI32(data.length)
4621
+ for (let i: i32 = 0; i < tailLength; i++) {
4622
+ tail[i] = 0
4623
+ }
4624
+ for (let i: i32 = 0; i < rest && i < tailLength && from + i >= 0 && from + i < dataLength; i++) {
4625
+ tail[i] = data[from + i]
4626
+ }
4627
+ if (rest >= 0 && rest < tailLength) {
4628
+ tail[rest] = 0x80
4629
+ }
4630
+ const end: i32 = rest < SHA256_BLOCK - 8 ? SHA256_BLOCK : 2 * SHA256_BLOCK
4631
+ const bits: u64 = toU64(len) << 3
4632
+ for (let i: i32 = 0; i < 8; i++) {
4633
+ const j: i32 = end - 1 - i
4634
+ if (j >= 0 && j < tailLength) {
4635
+ tail[j] = toU8(bits >>> toU64(8 * i))
4636
+ }
4637
+ }
4638
+ p256ShaCompress(h, w, kt, tail, 0)
4639
+ if (end > SHA256_BLOCK) {
4640
+ p256ShaCompress(h, w, kt, tail, SHA256_BLOCK)
4641
+ }
4642
+ const outLength: i32 = toI32(out.length)
4643
+ for (let i: i32 = 0; i < 8 && i < toI32(h.length); i++) {
4644
+ const word: u32 = h[i]
4645
+ for (let b: i32 = 0; b < 4; b++) {
4646
+ const j: i32 = at + 4 * i + b
4647
+ if (j >= 0 && j < outLength) {
4648
+ out[j] = toU8(word >>> toU32(P256_WORD_TOP - 8 * b))
4649
+ }
4650
+ }
4651
+ }
4652
+ }
4653
+
4654
+ /**
4655
+ * HMAC-SHA-256 (RFC 2104) keyed by the 32-byte `key` over `message[0 .. len)`,
4656
+ * into `out[0 .. 32)`: the inner hash of `K XOR ipad || message` in `block`,
4657
+ * its digest put where the outer hash of `K XOR opad || inner` reads it. `out`
4658
+ * may be `key` or `message`, which are read first. Every work array the key
4659
+ * reached — `block`, `h`, `w` and `tail` — is wiped before it returns (ECC-2).
4660
+ */
4661
+ const p256HmacInto = (
4662
+ out: u8[],
4663
+ key: u8[],
4664
+ message: u8[],
4665
+ len: i32,
4666
+ kt: u32[],
4667
+ h: u32[],
4668
+ w: u32[],
4669
+ tail: u8[],
4670
+ block: u8[]
4671
+ ): void => {
4672
+ const blockLength: i32 = toI32(block.length)
4673
+ const keyLength: i32 = toI32(key.length)
4674
+ const messageLength: i32 = toI32(message.length)
4675
+ if (blockLength < SHA256_BLOCK + len || keyLength > SHA256_BLOCK || len > messageLength) {
4676
+ panic("p256HmacInto: a key, message or block outside its bounds")
4677
+ }
4678
+ for (let i: i32 = 0; i < SHA256_BLOCK && i < blockLength; i++) {
4679
+ block[i] = i < keyLength ? key[i] ^ 0x36 : 0x36
4680
+ }
4681
+ for (let i: i32 = 0; i < len && i < messageLength && SHA256_BLOCK + i < blockLength; i++) {
4682
+ block[SHA256_BLOCK + i] = message[i]
4683
+ }
4684
+ p256ShaInto(block, SHA256_BLOCK, block, P256_FROM, SHA256_BLOCK + len, kt, h, w, tail)
4685
+ for (let i: i32 = 0; i < SHA256_BLOCK && i < blockLength; i++) {
4686
+ block[i] = i < keyLength ? key[i] ^ 0x5c : 0x5c
4687
+ }
4688
+ p256ShaInto(out, P256_FROM, block, P256_FROM, SHA256_BLOCK + SHA256_SIZE, kt, h, w, tail)
4689
+ wipe(block)
4690
+ wipe(h)
4691
+ wipe(w)
4692
+ wipe(tail)
4693
+ }
4694
+
4695
+ /** `out[0 .. n) = from[0 .. n)`. */
4696
+ const p256CopyBytes = (out: u8[], from: u8[], n: i32): void => {
4697
+ for (let i: i32 = 0; i < n && i < toI32(out.length) && i < toI32(from.length); i++) {
4698
+ out[i] = from[i]
4699
+ }
4700
+ }
4701
+
4702
+ /** Whether `[at, at + len)` is a window inside an array of `length` elements. */
4703
+ const p256WindowFits = (length: i32, at: i32, len: i32): boolean => at >= 0 && len >= 0 && at <= length - len
4704
+
4705
+ /**
4706
+ * Everything `p256SignSha256Into` signs in, made once and reused: RFC 6979's
4707
+ * `K` and `V`, the hashing's work arrays and round constants, the nonce's
4708
+ * multiple of G with its window table and point scratch, and the scalars of
4709
+ * the signature. One per signer, not per connection: signing is synchronous,
4710
+ * and every secret it held is wiped before `p256SignSha256Into` returns.
4711
+ *
4712
+ * const scratch = new P256SignScratch(); // once, outside the loop
4713
+ * const sig: u8[] = new Array<u8>(P256_SIGNATURE_SIZE);
4714
+ * const ok: boolean = p256SignSha256Into(scratch, priv, msg, 0, msg.length, sig, 0);
4715
+ */
4716
+ export class P256SignScratch {
4717
+ /** FIPS 180-4 §4.2.2's round constants. */
4718
+ kt: u32[]
4719
+ /** A SHA-256 hash value, schedule and padded tail. */
4720
+ hash: u32[]
4721
+ schedule: u32[]
4722
+ tail: u8[]
4723
+ /** An HMAC key block and the message after it. */
4724
+ block: u8[]
4725
+ /** `V || sep`, where every key update's message starts (RFC 6979 §3.2 steps d, f and h.3). */
4726
+ message: u8[]
4727
+ /** RFC 6979's `K` and `V`; the last `V` is the nonce. */
4728
+ k: u8[]
4729
+ v: u8[]
4730
+ /** The message's digest, then int2octets of it mod n: RFC 6979's `h1`. */
4731
+ h1: u8[]
4732
+ /** The digest mod n, `r`, the nonce point's affine x, and `k^-1` in the Montgomery domain. */
4733
+ z: u32[]
4734
+ r: u32[]
4735
+ x: u32[]
4736
+ kM: u32[]
4737
+ /** The order n and R^2 mod n. */
4738
+ n: u32[]
4739
+ r2: u32[]
4740
+ /** G, then the nonce's multiple of it, its accumulator and table entry, its table and point scratch. */
4741
+ g: P256ProjectivePoint
4742
+ point: P256ProjectivePoint
4743
+ acc: P256ProjectivePoint
4744
+ entry: P256ProjectivePoint
4745
+ table: u32[]
4746
+ points: P256PointScratch
4747
+
4748
+ constructor() {
4749
+ this.kt = [
4750
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
4751
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
4752
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
4753
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
4754
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
4755
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
4756
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
4757
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
4758
+ ]
4759
+ this.hash = p256Limbs()
4760
+ this.schedule = new Array<u32>(64)
4761
+ this.tail = new Array<u8>(2 * SHA256_BLOCK)
4762
+ this.block = new Array<u8>(SHA256_BLOCK + P256_NONCE_MESSAGE)
4763
+ this.message = new Array<u8>(SHA256_SIZE + 1)
4764
+ this.k = new Array<u8>(SHA256_SIZE)
4765
+ this.v = new Array<u8>(SHA256_SIZE)
4766
+ this.h1 = new Array<u8>(SHA256_SIZE)
4767
+ this.z = p256Limbs()
4768
+ this.r = p256Limbs()
4769
+ this.x = p256Limbs()
4770
+ this.kM = p256Limbs()
4771
+ this.n = p256Order()
4772
+ this.r2 = p256ScalarR2()
4773
+ this.g = p256Generator()
4774
+ this.point = new P256ProjectivePoint()
4775
+ this.acc = new P256ProjectivePoint()
4776
+ this.entry = new P256ProjectivePoint()
4777
+ this.table = new Array<u32>(384)
4778
+ this.points = new P256PointScratch()
4779
+ }
4780
+ }
4781
+
4782
+ /** `out = HMAC_K(message[0 .. len))` with the scratch's `K` and work arrays. */
4783
+ const p256SignMac = (sc: P256SignScratch, out: u8[], message: u8[], len: i32): void =>
4784
+ p256HmacInto(out, sc.k, message, len, sc.kt, sc.hash, sc.schedule, sc.tail, sc.block)
4785
+
4786
+ /**
4787
+ * HMAC_K(V || sep || x || h1) for RFC 6979 §3.2 steps d and f, on the plain
4788
+ * key `x`, for `exposeWith` to run: `V || sep` is the scratch's `message`,
4789
+ * which is only read, as the rest of the scratch is; the message and the
4790
+ * hashing's work arrays are fresh and wiped, and the new `K` is the answer.
4791
+ */
4792
+ const p256NonceKeyExposed = (priv: u8[], sc: P256SignScratch): u8[] => {
4793
+ const message: u8[] = new Array<u8>(P256_NONCE_MESSAGE)
4794
+ p256CopyBytes(message, sc.message, SHA256_SIZE + 1)
4795
+ for (let i: i32 = 0; i < SHA256_SIZE && i < toI32(priv.length) && i < toI32(sc.h1.length); i++) {
4796
+ const j: i32 = SHA256_SIZE + 1 + i
4797
+ const l: i32 = j + SHA256_SIZE
4798
+ if (j >= 0 && l >= 0 && j < toI32(message.length) && l < toI32(message.length)) {
4799
+ message[j] = priv[i]
4800
+ message[l] = sc.h1[i]
4801
+ }
4802
+ }
4803
+ const key: u8[] = new Array<u8>(SHA256_SIZE)
4804
+ p256HmacInto(
4805
+ key,
4806
+ sc.k,
4807
+ message,
4808
+ P256_NONCE_MESSAGE,
4809
+ sc.kt,
4810
+ p256Limbs(),
4811
+ new Array<u32>(64),
4812
+ new Array<u8>(2 * SHA256_BLOCK),
4813
+ new Array<u8>(SHA256_BLOCK + P256_NONCE_MESSAGE)
4814
+ )
4815
+ wipe(message)
4816
+ return key
4817
+ }
4818
+
4819
+ /**
4820
+ * `s = k^-1 (z + r d) mod n` on the plain key `d`, for `exposeWith` to run,
4821
+ * with `p256SignScalar`'s arithmetic: `d` taken into the Montgomery domain,
4822
+ * multiplied by the plain `r`, `z` added, and the sum multiplied by `k^-1`,
4823
+ * which is in the Montgomery domain, so `s` comes out plain. The key's limbs
4824
+ * are wiped; `s` is half of the signature, and public.
4825
+ */
4826
+ const p256SignSExposed = (priv: u8[], sc: P256SignScratch): u32[] => {
4827
+ const dM: u32[] = p256Limbs()
4828
+ p256LimbsFromBytes(dM, priv, 0)
4829
+ p256FiatScalarMul(dM, dM, sc.r2)
4830
+ const s: u32[] = p256Limbs()
4831
+ p256FiatScalarMul(s, sc.r, dM)
4832
+ p256FiatScalarAdd(s, s, sc.z)
4833
+ p256FiatScalarMul(s, s, sc.kM)
4834
+ wipe(dM)
4835
+ return s
4836
+ }
4837
+
4838
+ /** `sc.message = V || sep`, the start of every key update's message (RFC 6979 §3.2 steps d, f and h.3). */
4839
+ const p256NonceSeparated = (sc: P256SignScratch, sep: u8): void => {
4840
+ p256CopyBytes(sc.message, sc.v, SHA256_SIZE)
4841
+ sc.message[SHA256_SIZE] = sep
4842
+ }
4843
+
4844
+ /** Steps d and f: `K` replaced by what the exposed step answers, which is wiped once copied. */
4845
+ const p256SignNonceKey = (sc: P256SignScratch, priv: Secret<u8[]>, sep: u8): void => {
4846
+ p256NonceSeparated(sc, sep)
4847
+ const next: u8[] = exposeWith(priv, sc, p256NonceKeyExposed)
4848
+ p256CopyBytes(sc.k, next, SHA256_SIZE)
4849
+ wipe(next)
4850
+ }
4851
+
4852
+ /** Zero every secret the scratch held: `K`, `V`, `k^-1`, the nonce point and its scratch (ECC-2). */
4853
+ const p256WipeSignScratch = (sc: P256SignScratch): void => {
4854
+ wipe(sc.k)
4855
+ wipe(sc.v)
4856
+ wipe(sc.message)
4857
+ wipe(sc.kM)
4858
+ p256WipePoint(sc.point)
4859
+ p256WipeScratch(sc.points)
4860
+ }
4861
+
4862
+ /**
4863
+ * `p256SignSha256` of `msg[off .. off + len)` in `scratch`, written as `r || s`
4864
+ * at `out[at .. at + 64)`: the same 64 bytes `p256SignSha256` answers for the
4865
+ * same key and message. Answers `false`, writing nothing, for a private key
4866
+ * `p256PublicKey` would refuse. A message or signature window outside its
4867
+ * array panics before anything is read.
4868
+ *
4869
+ * It runs inside an arena block of its own and stores nothing, so a signature
4870
+ * leaves the arena where it was; every secret the scratch held — RFC 6979's
4871
+ * `K` and `V`, the nonce's inverse, its point and the point scratch — is wiped
4872
+ * before it returns, as are the fresh arrays the key's own steps worked in.
4873
+ */
4874
+ export const p256SignSha256Into = (
4875
+ sc: P256SignScratch,
4876
+ priv: Secret<u8[]>,
4877
+ msg: u8[],
4878
+ off: i32,
4879
+ len: i32,
4880
+ out: u8[],
4881
+ at: i32
4882
+ ): boolean => {
4883
+ if (!p256WindowFits(toI32(msg.length), off, len)) {
4884
+ panic("p256SignSha256Into: the message window is outside its array")
4885
+ }
4886
+ if (!p256WindowFits(toI32(out.length), at, P256_SIGNATURE_SIZE)) {
4887
+ panic("p256SignSha256Into: the signature window is outside its array")
4888
+ }
4889
+ using _scope = arena()
4890
+ if (!expose(priv, p256ScalarInRange)) {
4891
+ return false
4892
+ }
4893
+ p256ShaInto(sc.h1, P256_FROM, msg, off, len, sc.kt, sc.hash, sc.schedule, sc.tail)
4894
+ p256DigestScalar(sc.z, sc.h1)
4895
+ p256LimbsToBytes(sc.h1, 0, sc.z)
4896
+ for (let i: i32 = 0; i < SHA256_SIZE && i < toI32(sc.v.length) && i < toI32(sc.k.length); i++) {
4897
+ sc.v[i] = 1
4898
+ sc.k[i] = 0
4899
+ }
4900
+ p256SignNonceKey(sc, priv, 0) // step d
4901
+ p256SignMac(sc, sc.v, sc.v, SHA256_SIZE) // step e
4902
+ p256SignNonceKey(sc, priv, 1) // step f
4903
+ p256SignMac(sc, sc.v, sc.v, SHA256_SIZE) // step g
4904
+ while (true) {
4905
+ p256SignMac(sc, sc.v, sc.v, SHA256_SIZE) // step h.2
4906
+ if (p256ScalarInRange(sc.v)) {
4907
+ p256ScalarMultWith(sc.point, sc.v, sc.g, sc.points, sc.table, sc.acc, sc.entry)
4908
+ p256AffineX(sc.x, sc.point)
4909
+ p256ReduceOnce(sc.r, sc.x, sc.n)
4910
+ p256LimbsFromBytes(sc.kM, sc.v, 0)
4911
+ p256FiatScalarMul(sc.kM, sc.kM, sc.r2)
4912
+ p256ScalarInvert(sc.kM, sc.kM)
4913
+ const s: u32[] = exposeWith(priv, sc, p256SignSExposed)
4914
+ if (p256FiatNonzero(sc.r) !== 0 && p256FiatNonzero(s) !== 0) {
4915
+ p256LimbsToBytes(out, at, sc.r)
4916
+ p256LimbsToBytes(out, at + 32, s)
4917
+ p256WipeSignScratch(sc)
4918
+ return true
4919
+ }
4920
+ }
4921
+ // step h.3: K = HMAC_K(V || 0x00), then V = HMAC_K(V).
4922
+ p256NonceSeparated(sc, 0)
4923
+ p256SignMac(sc, sc.k, sc.message, SHA256_SIZE + 1)
4924
+ p256SignMac(sc, sc.v, sc.v, SHA256_SIZE)
4925
+ }
4926
+ }