@amritk/nish-x86_64-linux 0.17.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.
@@ -89,7 +89,7 @@
89
89
  * (`docs/wp26-stdlib.md` §3e).
90
90
  */
91
91
  import { Secret, expose, exposeWith, wipe } from "nish:secret"
92
- import { Sha256, sha256 } from "nish/crypto/sha256"
92
+ import { SHA256_BLOCK, SHA256_SIZE, Sha256, sha256 } from "nish/crypto/sha256"
93
93
  import { timingSafeEqual } from "nish/crypto/ct"
94
94
 
95
95
  /** The length in bytes of a private key: one scalar, big-endian. */
@@ -3819,6 +3819,13 @@ const p256PointCopy = (out: P256ProjectivePoint, p: P256ProjectivePoint): void =
3819
3819
  p256Copy(out.z, p.z)
3820
3820
  }
3821
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
+
3822
3829
  /**
3823
3830
  * `out = p + q`, by Renes, Costello and Batina's Algorithm 4 (complete
3824
3831
  * addition for a = -3), step for step in the paper's order and with its names.
@@ -3957,10 +3964,15 @@ const p256TableStorePoint = (table: u32[], i: i32, p: P256ProjectivePoint): void
3957
3964
  * The window table: entry `i` is `i * p`, for `i` from 0 to 15. Built from
3958
3965
  * `p`, which is public here (G or a received key) and a secret only in that
3959
3966
  * it is about to be multiplied. Entry 0 is the identity and entry 1 is `p`;
3960
- * 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.
3961
3968
  */
3962
- const p256TableBuild = (table: u32[], p: P256ProjectivePoint, s: P256PointScratch): void => {
3963
- const acc = new P256ProjectivePoint()
3969
+ const p256TableBuild = (
3970
+ table: u32[],
3971
+ p: P256ProjectivePoint,
3972
+ s: P256PointScratch,
3973
+ acc: P256ProjectivePoint
3974
+ ): void => {
3975
+ p256SetIdentity(acc)
3964
3976
  p256TableStorePoint(table, 0, acc)
3965
3977
  p256PointCopy(acc, p)
3966
3978
  p256TableStorePoint(table, 1, acc)
@@ -4021,15 +4033,31 @@ const p256ScalarMult = (
4021
4033
  k: u8[],
4022
4034
  p: P256ProjectivePoint,
4023
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
4024
4054
  ): void => {
4025
4055
  const kLength: i32 = toI32(k.length)
4026
4056
  if (kLength < 32) {
4027
4057
  return
4028
4058
  }
4029
- const table: u32[] = new Array<u32>(384)
4030
- p256TableBuild(table, p, s)
4031
- const acc = new P256ProjectivePoint()
4032
- const entry = new P256ProjectivePoint()
4059
+ p256TableBuild(table, p, s, acc)
4060
+ p256SetIdentity(acc)
4033
4061
  for (let i: i32 = 0; i < 64; i++) {
4034
4062
  // Digit i is the high nibble of byte i / 2 when i is even, the low when odd.
4035
4063
  const at: i32 = i >> 1
@@ -4043,8 +4071,6 @@ const p256ScalarMult = (
4043
4071
  p256PointAdd(acc, acc, entry, s)
4044
4072
  }
4045
4073
  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
4074
  wipe(table)
4049
4075
  p256WipePoint(acc)
4050
4076
  p256WipePoint(entry)
@@ -4461,3 +4487,440 @@ export const p256SignSha256 = (priv: Secret<u8[]>, msg: u8[]): u8[] | null => p2
4461
4487
  /** `p256Verify` of the SHA-256 of `msg`. */
4462
4488
  export const p256VerifySha256 = (pub: u8[], msg: u8[], sig: u8[]): boolean =>
4463
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
+ }