@amritk/nish-x86_64-linux 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +43 -34
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +50 -13
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +43 -24
- package/runtime/runtime-net.c +57 -10
- package/runtime/runtime-os.c +62 -20
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +12 -0
- package/runtime/runtime.c +66 -18
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +14 -12
- package/std/README.md +168 -36
- package/std/crypto/aes.ts +51 -8
- package/std/crypto/base64url.ts +25 -12
- package/std/crypto/chacha20poly1305.ts +43 -29
- package/std/crypto/ct.ts +6 -1
- package/std/crypto/hkdf.ts +364 -3
- package/std/crypto/hmac.ts +307 -7
- package/std/crypto/p256.ts +618 -34
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +3 -2
- package/std/crypto/x25519.ts +88 -36
- package/std/crypto/x509.ts +130 -47
- package/std/json.ts +297 -41
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +911 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +462 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +929 -0
- package/std/net/quic-packet.ts +1698 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1609 -0
- package/std/net/quic.ts +3578 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +414 -0
- package/std/net/tls.ts +1437 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
- package/std/text.ts +77 -0
package/std/crypto/p256.ts
CHANGED
|
@@ -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 {
|
|
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 = (
|
|
3935
|
-
|
|
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[
|
|
3955
|
-
out[1] = p256FiatCmovznzU32(hit, out[1], table[
|
|
3956
|
-
out[2] = p256FiatCmovznzU32(hit, out[2], table[
|
|
3957
|
-
out[3] = p256FiatCmovznzU32(hit, out[3], table[
|
|
3958
|
-
out[4] = p256FiatCmovznzU32(hit, out[4], table[
|
|
3959
|
-
out[5] = p256FiatCmovznzU32(hit, out[5], table[
|
|
3960
|
-
out[6] = p256FiatCmovznzU32(hit, out[6], table[
|
|
3961
|
-
out[7] = p256FiatCmovznzU32(hit, out[7], table[
|
|
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
|
-
|
|
4001
|
-
|
|
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
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
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
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4233
|
-
|
|
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
|
-
|
|
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[]
|
|
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[]
|
|
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
|
+
}
|