@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.
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.h +20 -6
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +11 -0
- package/runtime/runtime.c +20 -10
- package/scripts/build.sh +13 -9
- package/std/README.md +9 -8
- package/std/crypto/p256.ts +473 -10
- package/std/json.ts +297 -41
- package/std/net/http3-server.ts +22 -16
- package/std/net/quic-conn-params.ts +86 -16
- package/std/net/quic-listener.ts +489 -213
- package/std/net/quic-packet.ts +226 -2
- package/std/net/quic-stream.ts +10 -4
- package/std/net/quic.ts +484 -120
- package/std/net/tls-tcp.ts +8 -4
- package/std/net/tls.ts +114 -45
- package/std/text.ts +77 -0
package/std/crypto/p256.ts
CHANGED
|
@@ -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 = (
|
|
3963
|
-
|
|
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
|
-
|
|
4030
|
-
|
|
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
|
+
}
|