@noble/curves 0.5.1 → 0.6.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/README.md +49 -8
- package/lib/_shortw_utils.d.ts +11 -26
- package/lib/abstract/bls.d.ts +51 -35
- package/lib/abstract/bls.js +77 -139
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -81
- package/lib/abstract/edwards.js +225 -420
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +80 -51
- package/lib/abstract/montgomery.js +3 -4
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -34
- package/lib/abstract/utils.js +23 -63
- package/lib/abstract/weierstrass.d.ts +56 -79
- package/lib/abstract/weierstrass.js +509 -641
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +75 -65
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +87 -84
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +88 -84
- package/lib/esm/abstract/bls.js +77 -139
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +223 -418
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +77 -50
- package/lib/esm/abstract/montgomery.js +4 -7
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -59
- package/lib/esm/abstract/weierstrass.js +508 -640
- package/lib/esm/bls12-381.js +86 -76
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +85 -83
- package/lib/esm/ed448.js +86 -83
- package/lib/esm/jubjub.js +6 -5
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +118 -157
- package/lib/esm/stark.js +104 -39
- package/lib/jubjub.d.ts +3 -2
- package/lib/jubjub.js +6 -5
- package/lib/p192.d.ts +22 -52
- package/lib/p224.d.ts +22 -52
- package/lib/p256.d.ts +25 -52
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +25 -52
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +25 -52
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +26 -42
- package/lib/secp256k1.js +118 -157
- package/lib/stark.d.ts +36 -21
- package/lib/stark.js +107 -39
- package/package.json +14 -9
|
@@ -1,69 +1,17 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
3
|
-
// TODO: sync vs async naming
|
|
4
|
-
// TODO: default randomBytes
|
|
5
|
-
// Differences from @noble/secp256k1 1.7:
|
|
6
|
-
// 1. Different double() formula (but same addition)
|
|
7
|
-
// 2. Different sqrt() function
|
|
8
|
-
// 3. truncateHash() truncateOnly mode
|
|
9
|
-
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
10
3
|
import * as mod from './modular.js';
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import { wNAF } from './group.js';
|
|
15
|
-
// DER encoding utilities
|
|
16
|
-
class DERError extends Error {
|
|
17
|
-
constructor(message) {
|
|
18
|
-
super(message);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function sliceDER(s) {
|
|
22
|
-
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)])
|
|
23
|
-
// Padding done by numberToHex
|
|
24
|
-
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
|
|
25
|
-
}
|
|
26
|
-
function parseDERInt(data) {
|
|
27
|
-
if (data.length < 2 || data[0] !== 0x02) {
|
|
28
|
-
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`);
|
|
29
|
-
}
|
|
30
|
-
const len = data[1];
|
|
31
|
-
const res = data.subarray(2, len + 2);
|
|
32
|
-
if (!len || res.length !== len) {
|
|
33
|
-
throw new DERError(`Invalid signature integer: wrong length`);
|
|
34
|
-
}
|
|
35
|
-
// Strange condition, its not about length, but about first bytes of number.
|
|
36
|
-
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
|
37
|
-
throw new DERError('Invalid signature integer: trailing length');
|
|
38
|
-
}
|
|
39
|
-
return { data: bytesToNumberBE(res), left: data.subarray(len + 2) };
|
|
40
|
-
}
|
|
41
|
-
function parseDERSignature(data) {
|
|
42
|
-
if (data.length < 2 || data[0] != 0x30) {
|
|
43
|
-
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`);
|
|
44
|
-
}
|
|
45
|
-
if (data[1] !== data.length - 2) {
|
|
46
|
-
throw new DERError('Invalid signature: incorrect length');
|
|
47
|
-
}
|
|
48
|
-
const { data: r, left: sBytes } = parseDERInt(data.subarray(2));
|
|
49
|
-
const { data: s, left: rBytesLeft } = parseDERInt(sBytes);
|
|
50
|
-
if (rBytesLeft.length) {
|
|
51
|
-
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`);
|
|
52
|
-
}
|
|
53
|
-
return { r, s };
|
|
54
|
-
}
|
|
55
|
-
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
56
|
-
const _0n = BigInt(0);
|
|
57
|
-
const _1n = BigInt(1);
|
|
58
|
-
const _3n = BigInt(3);
|
|
4
|
+
import * as ut from './utils.js';
|
|
5
|
+
import { ensureBytes } from './utils.js';
|
|
6
|
+
import { wNAF, validateAbsOpts, } from './curve.js';
|
|
59
7
|
function validatePointOpts(curve) {
|
|
60
|
-
const opts =
|
|
8
|
+
const opts = validateAbsOpts(curve);
|
|
61
9
|
const Fp = opts.Fp;
|
|
62
10
|
for (const i of ['a', 'b']) {
|
|
63
11
|
if (!Fp.isValid(curve[i]))
|
|
64
12
|
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
65
13
|
}
|
|
66
|
-
for (const i of ['isTorsionFree', 'clearCofactor'
|
|
14
|
+
for (const i of ['isTorsionFree', 'clearCofactor']) {
|
|
67
15
|
if (curve[i] === undefined)
|
|
68
16
|
continue; // Optional
|
|
69
17
|
if (typeof curve[i] !== 'function')
|
|
@@ -71,7 +19,7 @@ function validatePointOpts(curve) {
|
|
|
71
19
|
}
|
|
72
20
|
const endo = opts.endo;
|
|
73
21
|
if (endo) {
|
|
74
|
-
if (!Fp.
|
|
22
|
+
if (!Fp.eql(opts.a, Fp.ZERO)) {
|
|
75
23
|
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
76
24
|
}
|
|
77
25
|
if (typeof endo !== 'object' ||
|
|
@@ -84,128 +32,232 @@ function validatePointOpts(curve) {
|
|
|
84
32
|
throw new Error('Invalid fromBytes function');
|
|
85
33
|
if (typeof opts.toBytes !== 'function')
|
|
86
34
|
throw new Error('Invalid fromBytes function');
|
|
87
|
-
// Requires including hashToCurve file
|
|
88
|
-
if (opts.htfDefaults !== undefined)
|
|
89
|
-
validateHTFOpts(opts.htfDefaults);
|
|
90
35
|
// Set defaults
|
|
91
36
|
return Object.freeze({ ...opts });
|
|
92
37
|
}
|
|
38
|
+
// ASN.1 DER encoding utilities
|
|
39
|
+
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
40
|
+
const DER = {
|
|
41
|
+
// asn.1 DER encoding utils
|
|
42
|
+
Err: class DERErr extends Error {
|
|
43
|
+
constructor(m = '') {
|
|
44
|
+
super(m);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
_parseInt(data) {
|
|
48
|
+
const { Err: E } = DER;
|
|
49
|
+
if (data.length < 2 || data[0] !== 0x02)
|
|
50
|
+
throw new E('Invalid signature integer tag');
|
|
51
|
+
const len = data[1];
|
|
52
|
+
const res = data.subarray(2, len + 2);
|
|
53
|
+
if (!len || res.length !== len)
|
|
54
|
+
throw new E('Invalid signature integer: wrong length');
|
|
55
|
+
if (res[0] === 0x00 && res[1] <= 0x7f)
|
|
56
|
+
throw new E('Invalid signature integer: trailing length');
|
|
57
|
+
// ^ Weird condition: not about length, but about first bytes of number.
|
|
58
|
+
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
|
59
|
+
},
|
|
60
|
+
toSig(hex) {
|
|
61
|
+
// parse DER signature
|
|
62
|
+
const { Err: E } = DER;
|
|
63
|
+
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
64
|
+
if (!(data instanceof Uint8Array))
|
|
65
|
+
throw new Error('ui8a expected');
|
|
66
|
+
let l = data.length;
|
|
67
|
+
if (l < 2 || data[0] != 0x30)
|
|
68
|
+
throw new E('Invalid signature tag');
|
|
69
|
+
if (data[1] !== l - 2)
|
|
70
|
+
throw new E('Invalid signature: incorrect length');
|
|
71
|
+
const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
|
|
72
|
+
const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
|
|
73
|
+
if (rBytesLeft.length)
|
|
74
|
+
throw new E('Invalid signature: left bytes after parsing');
|
|
75
|
+
return { r, s };
|
|
76
|
+
},
|
|
77
|
+
hexFromSig(sig) {
|
|
78
|
+
const slice = (s) => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
|
|
79
|
+
const h = (num) => {
|
|
80
|
+
const hex = num.toString(16);
|
|
81
|
+
return hex.length & 1 ? `0${hex}` : hex;
|
|
82
|
+
};
|
|
83
|
+
const s = slice(h(sig.s));
|
|
84
|
+
const r = slice(h(sig.r));
|
|
85
|
+
const shl = s.length / 2;
|
|
86
|
+
const rhl = r.length / 2;
|
|
87
|
+
const sl = h(shl);
|
|
88
|
+
const rl = h(rhl);
|
|
89
|
+
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
93
|
+
const _0n = BigInt(0);
|
|
94
|
+
const _1n = BigInt(1);
|
|
93
95
|
export function weierstrassPoints(opts) {
|
|
94
96
|
const CURVE = validatePointOpts(opts);
|
|
95
|
-
const Fp = CURVE
|
|
96
|
-
// Lengths
|
|
97
|
-
// All curves has same field / group length as for now, but it can be different for other curves
|
|
98
|
-
const { nByteLength, nBitLength } = CURVE;
|
|
99
|
-
const groupLen = nByteLength;
|
|
100
|
-
// Not using ** operator with bigints for old engines.
|
|
101
|
-
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n)
|
|
102
|
-
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
|
|
103
|
-
// function numToFieldStr(num: bigint): string {
|
|
104
|
-
// if (typeof num !== 'bigint') throw new Error('Expected bigint');
|
|
105
|
-
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`);
|
|
106
|
-
// return num.toString(16).padStart(2 * fieldLen, '0');
|
|
107
|
-
// }
|
|
97
|
+
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
108
98
|
/**
|
|
109
99
|
* y² = x³ + ax + b: Short weierstrass curve formula
|
|
110
100
|
* @returns y²
|
|
111
101
|
*/
|
|
112
102
|
function weierstrassEquation(x) {
|
|
113
103
|
const { a, b } = CURVE;
|
|
114
|
-
const x2 = Fp.
|
|
104
|
+
const x2 = Fp.sqr(x); // x * x
|
|
115
105
|
const x3 = Fp.mul(x2, x); // x2 * x
|
|
116
106
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
117
107
|
}
|
|
108
|
+
// Valid group elements reside in range 1..n-1
|
|
118
109
|
function isWithinCurveOrder(num) {
|
|
119
|
-
return _0n < num && num < CURVE.n;
|
|
110
|
+
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
|
|
111
|
+
}
|
|
112
|
+
function assertGE(num) {
|
|
113
|
+
if (!isWithinCurveOrder(num))
|
|
114
|
+
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
120
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Validates if a private key is valid and converts it to bigint form.
|
|
118
|
+
* Supports two options, that are passed when CURVE is initialized:
|
|
119
|
+
* - `normalizePrivateKey()` executed before all checks
|
|
120
|
+
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n`
|
|
121
|
+
*/
|
|
121
122
|
function normalizePrivateKey(key) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n } = CURVE;
|
|
124
|
+
if (typeof custom === 'function')
|
|
125
|
+
key = custom(key);
|
|
125
126
|
let num;
|
|
126
127
|
if (typeof key === 'bigint') {
|
|
128
|
+
// Curve order check is done below
|
|
127
129
|
num = key;
|
|
128
130
|
}
|
|
129
|
-
else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) {
|
|
130
|
-
num = BigInt(key);
|
|
131
|
-
}
|
|
132
131
|
else if (typeof key === 'string') {
|
|
133
132
|
if (key.length !== 2 * groupLen)
|
|
134
|
-
throw new Error(`
|
|
135
|
-
|
|
133
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
134
|
+
// Validates individual octets
|
|
135
|
+
num = ut.bytesToNumberBE(ensureBytes(key));
|
|
136
136
|
}
|
|
137
137
|
else if (key instanceof Uint8Array) {
|
|
138
138
|
if (key.length !== groupLen)
|
|
139
|
-
throw new Error(`
|
|
140
|
-
num = bytesToNumberBE(key);
|
|
139
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
140
|
+
num = ut.bytesToNumberBE(key);
|
|
141
141
|
}
|
|
142
142
|
else {
|
|
143
|
-
throw new
|
|
143
|
+
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
|
144
144
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
// Useful for curves with cofactor != 1
|
|
146
|
+
if (wrapPrivateKey)
|
|
147
|
+
num = mod.mod(num, n);
|
|
148
|
+
assertGE(num);
|
|
149
149
|
return num;
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return num;
|
|
156
|
-
throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
|
|
151
|
+
const pointPrecomputes = new Map();
|
|
152
|
+
function assertPrjPoint(other) {
|
|
153
|
+
if (!(other instanceof Point))
|
|
154
|
+
throw new Error('ProjectivePoint expected');
|
|
157
155
|
}
|
|
158
156
|
/**
|
|
159
157
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
160
158
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
161
159
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
162
160
|
*/
|
|
163
|
-
class
|
|
164
|
-
constructor(
|
|
165
|
-
this.
|
|
166
|
-
this.
|
|
167
|
-
this.
|
|
161
|
+
class Point {
|
|
162
|
+
constructor(px, py, pz) {
|
|
163
|
+
this.px = px;
|
|
164
|
+
this.py = py;
|
|
165
|
+
this.pz = pz;
|
|
166
|
+
if (px == null || !Fp.isValid(px))
|
|
167
|
+
throw new Error('x required');
|
|
168
|
+
if (py == null || !Fp.isValid(py))
|
|
169
|
+
throw new Error('y required');
|
|
170
|
+
if (pz == null || !Fp.isValid(pz))
|
|
171
|
+
throw new Error('z required');
|
|
168
172
|
}
|
|
169
173
|
static fromAffine(p) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
const { x, y } = p || {};
|
|
175
|
+
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
176
|
+
throw new Error('invalid affine point');
|
|
177
|
+
if (p instanceof Point)
|
|
178
|
+
throw new Error('projective point not allowed');
|
|
179
|
+
const is0 = (i) => Fp.eql(i, Fp.ZERO);
|
|
173
180
|
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
176
|
-
return new
|
|
181
|
+
if (is0(x) && is0(y))
|
|
182
|
+
return Point.ZERO;
|
|
183
|
+
return new Point(x, y, Fp.ONE);
|
|
184
|
+
}
|
|
185
|
+
get x() {
|
|
186
|
+
return this.toAffine().x;
|
|
187
|
+
}
|
|
188
|
+
get y() {
|
|
189
|
+
return this.toAffine().y;
|
|
177
190
|
}
|
|
178
191
|
/**
|
|
179
192
|
* Takes a bunch of Projective Points but executes only one
|
|
180
|
-
*
|
|
193
|
+
* inversion on all of them. Inversion is very slow operation,
|
|
181
194
|
* so this improves performance massively.
|
|
195
|
+
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
182
196
|
*/
|
|
183
|
-
static toAffineBatch(points) {
|
|
184
|
-
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
|
185
|
-
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
186
|
-
}
|
|
187
197
|
static normalizeZ(points) {
|
|
188
|
-
|
|
198
|
+
const toInv = Fp.invertBatch(points.map((p) => p.pz));
|
|
199
|
+
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Converts hash string or Uint8Array to Point.
|
|
203
|
+
* @param hex short/long ECDSA hex
|
|
204
|
+
*/
|
|
205
|
+
static fromHex(hex) {
|
|
206
|
+
const P = Point.fromAffine(CURVE.fromBytes(ensureBytes(hex)));
|
|
207
|
+
P.assertValidity();
|
|
208
|
+
return P;
|
|
209
|
+
}
|
|
210
|
+
// Multiplies generator point by privateKey.
|
|
211
|
+
static fromPrivateKey(privateKey) {
|
|
212
|
+
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
213
|
+
}
|
|
214
|
+
// "Private method", don't use it directly
|
|
215
|
+
_setWindowSize(windowSize) {
|
|
216
|
+
this._WINDOW_SIZE = windowSize;
|
|
217
|
+
pointPrecomputes.delete(this);
|
|
218
|
+
}
|
|
219
|
+
// A point on curve is valid if it conforms to equation.
|
|
220
|
+
assertValidity() {
|
|
221
|
+
// Zero is valid point too!
|
|
222
|
+
if (this.is0()) {
|
|
223
|
+
if (CURVE.allowInfinityPoint)
|
|
224
|
+
return;
|
|
225
|
+
throw new Error('bad point: ZERO');
|
|
226
|
+
}
|
|
227
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
228
|
+
const { x, y } = this.toAffine();
|
|
229
|
+
// Check if x, y are valid field elements
|
|
230
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
231
|
+
throw new Error('bad point: x or y not FE');
|
|
232
|
+
const left = Fp.sqr(y); // y²
|
|
233
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
234
|
+
if (!Fp.eql(left, right))
|
|
235
|
+
throw new Error('bad point: equation left != right');
|
|
236
|
+
if (!this.isTorsionFree())
|
|
237
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
238
|
+
}
|
|
239
|
+
hasEvenY() {
|
|
240
|
+
const { y } = this.toAffine();
|
|
241
|
+
if (Fp.isOdd)
|
|
242
|
+
return !Fp.isOdd(y);
|
|
243
|
+
throw new Error("Field doesn't support isOdd");
|
|
189
244
|
}
|
|
190
245
|
/**
|
|
191
246
|
* Compare one point to another.
|
|
192
247
|
*/
|
|
193
248
|
equals(other) {
|
|
194
249
|
assertPrjPoint(other);
|
|
195
|
-
const {
|
|
196
|
-
const {
|
|
197
|
-
const U1 = Fp.
|
|
198
|
-
const U2 = Fp.
|
|
250
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
251
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
252
|
+
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
253
|
+
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
199
254
|
return U1 && U2;
|
|
200
255
|
}
|
|
201
256
|
/**
|
|
202
257
|
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
203
258
|
*/
|
|
204
259
|
negate() {
|
|
205
|
-
return new
|
|
206
|
-
}
|
|
207
|
-
doubleAdd() {
|
|
208
|
-
return this.add(this);
|
|
260
|
+
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
209
261
|
}
|
|
210
262
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
211
263
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -214,7 +266,7 @@ export function weierstrassPoints(opts) {
|
|
|
214
266
|
double() {
|
|
215
267
|
const { a, b } = CURVE;
|
|
216
268
|
const b3 = Fp.mul(b, 3n);
|
|
217
|
-
const {
|
|
269
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
218
270
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
219
271
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
220
272
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -247,7 +299,7 @@ export function weierstrassPoints(opts) {
|
|
|
247
299
|
Z3 = Fp.mul(t2, t1);
|
|
248
300
|
Z3 = Fp.add(Z3, Z3); // step 30
|
|
249
301
|
Z3 = Fp.add(Z3, Z3);
|
|
250
|
-
return new
|
|
302
|
+
return new Point(X3, Y3, Z3);
|
|
251
303
|
}
|
|
252
304
|
// Renes-Costello-Batina exception-free addition formula.
|
|
253
305
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -255,8 +307,8 @@ export function weierstrassPoints(opts) {
|
|
|
255
307
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
256
308
|
add(other) {
|
|
257
309
|
assertPrjPoint(other);
|
|
258
|
-
const {
|
|
259
|
-
const {
|
|
310
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
311
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
260
312
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
261
313
|
const a = CURVE.a;
|
|
262
314
|
const b3 = Fp.mul(CURVE.b, 3n);
|
|
@@ -300,30 +352,39 @@ export function weierstrassPoints(opts) {
|
|
|
300
352
|
t0 = Fp.mul(t3, t1);
|
|
301
353
|
Z3 = Fp.mul(t5, Z3);
|
|
302
354
|
Z3 = Fp.add(Z3, t0); // step 40
|
|
303
|
-
return new
|
|
355
|
+
return new Point(X3, Y3, Z3);
|
|
304
356
|
}
|
|
305
357
|
subtract(other) {
|
|
306
358
|
return this.add(other.negate());
|
|
307
359
|
}
|
|
360
|
+
is0() {
|
|
361
|
+
return this.equals(Point.ZERO);
|
|
362
|
+
}
|
|
363
|
+
wNAF(n) {
|
|
364
|
+
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
|
|
365
|
+
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
366
|
+
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
308
369
|
/**
|
|
309
370
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
310
371
|
* It's faster, but should only be used when you don't care about
|
|
311
372
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
312
373
|
*/
|
|
313
|
-
multiplyUnsafe(
|
|
314
|
-
const
|
|
315
|
-
if (
|
|
316
|
-
return
|
|
317
|
-
// Will throw on 0
|
|
318
|
-
let n = normalizeScalar(scalar);
|
|
374
|
+
multiplyUnsafe(n) {
|
|
375
|
+
const I = Point.ZERO;
|
|
376
|
+
if (n === _0n)
|
|
377
|
+
return I;
|
|
378
|
+
assertGE(n); // Will throw on 0
|
|
319
379
|
if (n === _1n)
|
|
320
380
|
return this;
|
|
321
|
-
|
|
381
|
+
const { endo } = CURVE;
|
|
382
|
+
if (!endo)
|
|
322
383
|
return wnaf.unsafeLadder(this, n);
|
|
323
384
|
// Apply endomorphism
|
|
324
|
-
let { k1neg, k1, k2neg, k2 } =
|
|
325
|
-
let k1p =
|
|
326
|
-
let k2p =
|
|
385
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
386
|
+
let k1p = I;
|
|
387
|
+
let k2p = I;
|
|
327
388
|
let d = this;
|
|
328
389
|
while (k1 > _0n || k2 > _0n) {
|
|
329
390
|
if (k1 & _1n)
|
|
@@ -338,27 +399,9 @@ export function weierstrassPoints(opts) {
|
|
|
338
399
|
k1p = k1p.negate();
|
|
339
400
|
if (k2neg)
|
|
340
401
|
k2p = k2p.negate();
|
|
341
|
-
k2p = new
|
|
402
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
342
403
|
return k1p.add(k2p);
|
|
343
404
|
}
|
|
344
|
-
/**
|
|
345
|
-
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
346
|
-
*/
|
|
347
|
-
wNAF(n, affinePoint) {
|
|
348
|
-
if (!affinePoint && this.equals(ProjectivePoint.BASE))
|
|
349
|
-
affinePoint = Point.BASE;
|
|
350
|
-
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
351
|
-
// Calculate precomputes on a first run, reuse them after
|
|
352
|
-
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
353
|
-
if (!precomputes) {
|
|
354
|
-
precomputes = wnaf.precomputeWindow(this, W);
|
|
355
|
-
if (affinePoint && W !== 1) {
|
|
356
|
-
precomputes = ProjectivePoint.normalizeZ(precomputes);
|
|
357
|
-
pointPrecomputes.set(affinePoint, precomputes);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return wnaf.wNAF(W, precomputes, n);
|
|
361
|
-
}
|
|
362
405
|
/**
|
|
363
406
|
* Constant time multiplication.
|
|
364
407
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -367,223 +410,96 @@ export function weierstrassPoints(opts) {
|
|
|
367
410
|
* @param affinePoint optional point ot save cached precompute windows on it
|
|
368
411
|
* @returns New point
|
|
369
412
|
*/
|
|
370
|
-
multiply(scalar
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
let point;
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
let { p:
|
|
379
|
-
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
|
413
|
+
multiply(scalar) {
|
|
414
|
+
assertGE(scalar);
|
|
415
|
+
let n = scalar;
|
|
416
|
+
let point, fake; // Fake point is used to const-time mult
|
|
417
|
+
const { endo } = CURVE;
|
|
418
|
+
if (endo) {
|
|
419
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
420
|
+
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
421
|
+
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
380
422
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
381
423
|
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
|
382
|
-
k2p = new
|
|
424
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
383
425
|
point = k1p.add(k2p);
|
|
384
426
|
fake = f1p.add(f2p);
|
|
385
427
|
}
|
|
386
428
|
else {
|
|
387
|
-
const { p, f } = this.wNAF(n
|
|
429
|
+
const { p, f } = this.wNAF(n);
|
|
388
430
|
point = p;
|
|
389
431
|
fake = f;
|
|
390
432
|
}
|
|
391
433
|
// Normalize `z` for both points, but return only real one
|
|
392
|
-
return
|
|
434
|
+
return Point.normalizeZ([point, fake])[0];
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
|
438
|
+
* @returns non-zero affine point
|
|
439
|
+
*/
|
|
440
|
+
multiplyAndAddUnsafe(Q, a, b) {
|
|
441
|
+
const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
|
|
442
|
+
const mul = (P, a // Select faster multiply() method
|
|
443
|
+
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
444
|
+
const sum = mul(this, a).add(mul(Q, b));
|
|
445
|
+
return sum.is0() ? undefined : sum;
|
|
393
446
|
}
|
|
394
447
|
// Converts Projective point to affine (x, y) coordinates.
|
|
395
448
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
396
449
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
397
|
-
toAffine(
|
|
398
|
-
const { x, y, z } = this;
|
|
399
|
-
const is0 = this.
|
|
450
|
+
toAffine(iz) {
|
|
451
|
+
const { px: x, py: y, pz: z } = this;
|
|
452
|
+
const is0 = this.is0();
|
|
400
453
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
401
454
|
// all operations, so we replace invZ with a random number, 1.
|
|
402
|
-
if (
|
|
403
|
-
|
|
404
|
-
const ax = Fp.mul(x,
|
|
405
|
-
const ay = Fp.mul(y,
|
|
406
|
-
const zz = Fp.mul(z,
|
|
455
|
+
if (iz == null)
|
|
456
|
+
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
457
|
+
const ax = Fp.mul(x, iz);
|
|
458
|
+
const ay = Fp.mul(y, iz);
|
|
459
|
+
const zz = Fp.mul(z, iz);
|
|
407
460
|
if (is0)
|
|
408
|
-
return
|
|
409
|
-
if (!Fp.
|
|
461
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
462
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
410
463
|
throw new Error('invZ was invalid');
|
|
411
|
-
return
|
|
464
|
+
return { x: ax, y: ay };
|
|
412
465
|
}
|
|
413
466
|
isTorsionFree() {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
throw new Error('
|
|
467
|
+
const { h: cofactor, isTorsionFree } = CURVE;
|
|
468
|
+
if (cofactor === _1n)
|
|
469
|
+
return true; // No subgroups, always torsion-free
|
|
470
|
+
if (isTorsionFree)
|
|
471
|
+
return isTorsionFree(Point, this);
|
|
472
|
+
throw new Error('isTorsionFree() has not been declared for the elliptic curve');
|
|
420
473
|
}
|
|
421
|
-
// Clear cofactor of G1
|
|
422
|
-
// https://eprint.iacr.org/2019/403
|
|
423
474
|
clearCofactor() {
|
|
424
|
-
|
|
475
|
+
const { h: cofactor, clearCofactor } = CURVE;
|
|
476
|
+
if (cofactor === _1n)
|
|
425
477
|
return this; // Fast-path
|
|
426
|
-
if (
|
|
427
|
-
return
|
|
478
|
+
if (clearCofactor)
|
|
479
|
+
return clearCofactor(Point, this);
|
|
428
480
|
return this.multiplyUnsafe(CURVE.h);
|
|
429
481
|
}
|
|
430
|
-
|
|
431
|
-
ProjectivePoint.BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
432
|
-
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
433
|
-
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? nBitLength / 2 : nBitLength);
|
|
434
|
-
function assertPrjPoint(other) {
|
|
435
|
-
if (!(other instanceof ProjectivePoint))
|
|
436
|
-
throw new TypeError('ProjectivePoint expected');
|
|
437
|
-
}
|
|
438
|
-
// Stores precomputed values for points.
|
|
439
|
-
const pointPrecomputes = new WeakMap();
|
|
440
|
-
/**
|
|
441
|
-
* Default Point works in default aka affine coordinates: (x, y)
|
|
442
|
-
*/
|
|
443
|
-
class Point {
|
|
444
|
-
constructor(x, y) {
|
|
445
|
-
this.x = x;
|
|
446
|
-
this.y = y;
|
|
447
|
-
}
|
|
448
|
-
// "Private method", don't use it directly
|
|
449
|
-
_setWindowSize(windowSize) {
|
|
450
|
-
this._WINDOW_SIZE = windowSize;
|
|
451
|
-
pointPrecomputes.delete(this);
|
|
452
|
-
}
|
|
453
|
-
// Checks for y % 2 == 0
|
|
454
|
-
hasEvenY() {
|
|
455
|
-
if (Fp.isOdd)
|
|
456
|
-
return !Fp.isOdd(this.y);
|
|
457
|
-
throw new Error("Field doesn't support isOdd");
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Converts hash string or Uint8Array to Point.
|
|
461
|
-
* @param hex short/long ECDSA hex
|
|
462
|
-
*/
|
|
463
|
-
static fromHex(hex) {
|
|
464
|
-
const { x, y } = CURVE.fromBytes(ensureBytes(hex));
|
|
465
|
-
const point = new Point(x, y);
|
|
466
|
-
point.assertValidity();
|
|
467
|
-
return point;
|
|
468
|
-
}
|
|
469
|
-
// Multiplies generator point by privateKey.
|
|
470
|
-
static fromPrivateKey(privateKey) {
|
|
471
|
-
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
472
|
-
}
|
|
473
|
-
toRawBytes(isCompressed = false) {
|
|
482
|
+
toRawBytes(isCompressed = true) {
|
|
474
483
|
this.assertValidity();
|
|
475
484
|
return CURVE.toBytes(Point, this, isCompressed);
|
|
476
485
|
}
|
|
477
|
-
toHex(isCompressed =
|
|
478
|
-
return bytesToHex(this.toRawBytes(isCompressed));
|
|
479
|
-
}
|
|
480
|
-
// A point on curve is valid if it conforms to equation.
|
|
481
|
-
assertValidity() {
|
|
482
|
-
// Zero is valid point too!
|
|
483
|
-
if (this.equals(Point.ZERO)) {
|
|
484
|
-
if (CURVE.allowInfinityPoint)
|
|
485
|
-
return;
|
|
486
|
-
throw new Error('Point is infinity');
|
|
487
|
-
}
|
|
488
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
489
|
-
const msg = 'Point is not on elliptic curve';
|
|
490
|
-
const { x, y } = this;
|
|
491
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
492
|
-
throw new Error(msg);
|
|
493
|
-
const left = Fp.square(y);
|
|
494
|
-
const right = weierstrassEquation(x);
|
|
495
|
-
if (!Fp.equals(left, right))
|
|
496
|
-
throw new Error(msg);
|
|
497
|
-
// TODO: flag to disable this?
|
|
498
|
-
if (!this.isTorsionFree())
|
|
499
|
-
throw new Error('Point must be of prime-order subgroup');
|
|
500
|
-
}
|
|
501
|
-
equals(other) {
|
|
502
|
-
if (!(other instanceof Point))
|
|
503
|
-
throw new TypeError('Point#equals: expected Point');
|
|
504
|
-
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
|
|
505
|
-
}
|
|
506
|
-
// Returns the same point with inverted `y`
|
|
507
|
-
negate() {
|
|
508
|
-
return new Point(this.x, Fp.negate(this.y));
|
|
509
|
-
}
|
|
510
|
-
// Adds point to itself
|
|
511
|
-
double() {
|
|
512
|
-
return ProjectivePoint.fromAffine(this).double().toAffine();
|
|
513
|
-
}
|
|
514
|
-
// Adds point to other point
|
|
515
|
-
add(other) {
|
|
516
|
-
return ProjectivePoint.fromAffine(this).add(ProjectivePoint.fromAffine(other)).toAffine();
|
|
517
|
-
}
|
|
518
|
-
// Subtracts other point from the point
|
|
519
|
-
subtract(other) {
|
|
520
|
-
return this.add(other.negate());
|
|
521
|
-
}
|
|
522
|
-
multiply(scalar) {
|
|
523
|
-
return ProjectivePoint.fromAffine(this).multiply(scalar, this).toAffine();
|
|
524
|
-
}
|
|
525
|
-
multiplyUnsafe(scalar) {
|
|
526
|
-
return ProjectivePoint.fromAffine(this).multiplyUnsafe(scalar).toAffine();
|
|
527
|
-
}
|
|
528
|
-
clearCofactor() {
|
|
529
|
-
return ProjectivePoint.fromAffine(this).clearCofactor().toAffine();
|
|
530
|
-
}
|
|
531
|
-
isTorsionFree() {
|
|
532
|
-
return ProjectivePoint.fromAffine(this).isTorsionFree();
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Efficiently calculate `aP + bQ`.
|
|
536
|
-
* Unsafe, can expose private key, if used incorrectly.
|
|
537
|
-
* TODO: Utilize Shamir's trick
|
|
538
|
-
* @returns non-zero affine point
|
|
539
|
-
*/
|
|
540
|
-
multiplyAndAddUnsafe(Q, a, b) {
|
|
541
|
-
const P = ProjectivePoint.fromAffine(this);
|
|
542
|
-
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
|
|
543
|
-
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
|
544
|
-
const sum = aP.add(bQ);
|
|
545
|
-
return sum.equals(ProjectivePoint.ZERO) ? undefined : sum.toAffine();
|
|
546
|
-
}
|
|
547
|
-
// Encodes byte string to elliptic curve
|
|
548
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
549
|
-
static hashToCurve(msg, options) {
|
|
550
|
-
if (!CURVE.mapToCurve)
|
|
551
|
-
throw new Error('No mapToCurve defined for curve');
|
|
552
|
-
msg = ensureBytes(msg);
|
|
553
|
-
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options });
|
|
554
|
-
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
|
|
555
|
-
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
|
|
556
|
-
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
557
|
-
return p;
|
|
558
|
-
}
|
|
559
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
560
|
-
static encodeToCurve(msg, options) {
|
|
561
|
-
if (!CURVE.mapToCurve)
|
|
562
|
-
throw new Error('No mapToCurve defined for curve');
|
|
563
|
-
msg = ensureBytes(msg);
|
|
564
|
-
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options });
|
|
565
|
-
const { x, y } = CURVE.mapToCurve(u[0]);
|
|
566
|
-
return new Point(x, y).clearCofactor();
|
|
486
|
+
toHex(isCompressed = true) {
|
|
487
|
+
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
567
488
|
}
|
|
568
489
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Identity point aka point at infinity. point = point + zero_point
|
|
575
|
-
*/
|
|
576
|
-
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
|
|
490
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
491
|
+
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
492
|
+
const _bits = CURVE.nBitLength;
|
|
493
|
+
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
577
494
|
return {
|
|
578
|
-
|
|
579
|
-
ProjectivePoint: ProjectivePoint,
|
|
495
|
+
ProjectivePoint: Point,
|
|
580
496
|
normalizePrivateKey,
|
|
581
497
|
weierstrassEquation,
|
|
582
498
|
isWithinCurveOrder,
|
|
583
499
|
};
|
|
584
500
|
}
|
|
585
501
|
function validateOpts(curve) {
|
|
586
|
-
const opts =
|
|
502
|
+
const opts = validateAbsOpts(curve);
|
|
587
503
|
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
588
504
|
throw new Error('Invalid hash function');
|
|
589
505
|
if (typeof opts.hmac !== 'function')
|
|
@@ -593,96 +509,109 @@ function validateOpts(curve) {
|
|
|
593
509
|
// Set defaults
|
|
594
510
|
return Object.freeze({ lowS: true, ...opts });
|
|
595
511
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
incr() {
|
|
620
|
-
if (this.counter >= 1000)
|
|
621
|
-
throw new Error('Tried 1,000 k values for sign(), all were invalid');
|
|
622
|
-
this.counter += 1;
|
|
623
|
-
}
|
|
624
|
-
reseedSync(seed = new Uint8Array()) {
|
|
625
|
-
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
|
|
626
|
-
this.v = this.hmacSync(this.v);
|
|
512
|
+
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
|
|
513
|
+
const u8fr = (arr) => Uint8Array.from(arr); // another shortcut
|
|
514
|
+
function hmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
515
|
+
if (typeof hashLen !== 'number' || hashLen < 2)
|
|
516
|
+
throw new Error('hashLen must be a number');
|
|
517
|
+
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
518
|
+
throw new Error('qByteLen must be a number');
|
|
519
|
+
if (typeof hmacFn !== 'function')
|
|
520
|
+
throw new Error('hmacFn must be a function');
|
|
521
|
+
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
522
|
+
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
523
|
+
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
|
524
|
+
let i = 0; // Iterations counter, will throw when over 1000
|
|
525
|
+
const reset = () => {
|
|
526
|
+
v.fill(1);
|
|
527
|
+
k.fill(0);
|
|
528
|
+
i = 0;
|
|
529
|
+
};
|
|
530
|
+
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
|
531
|
+
const reseed = (seed = u8n()) => {
|
|
532
|
+
// HMAC-DRBG reseed() function. Steps D-G
|
|
533
|
+
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
|
534
|
+
v = h(); // v = hmac(k || v)
|
|
627
535
|
if (seed.length === 0)
|
|
628
536
|
return;
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
537
|
+
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
|
538
|
+
v = h(); // v = hmac(k || v)
|
|
539
|
+
};
|
|
540
|
+
const gen = () => {
|
|
541
|
+
// HMAC-DRBG generate() function
|
|
542
|
+
if (i++ >= 1000)
|
|
543
|
+
throw new Error('drbg: tried 1000 values');
|
|
635
544
|
let len = 0;
|
|
636
545
|
const out = [];
|
|
637
|
-
while (len <
|
|
638
|
-
|
|
639
|
-
const sl =
|
|
546
|
+
while (len < qByteLen) {
|
|
547
|
+
v = h();
|
|
548
|
+
const sl = v.slice();
|
|
640
549
|
out.push(sl);
|
|
641
|
-
len +=
|
|
550
|
+
len += v.length;
|
|
642
551
|
}
|
|
643
|
-
return concatBytes(...out);
|
|
644
|
-
}
|
|
552
|
+
return ut.concatBytes(...out);
|
|
553
|
+
};
|
|
554
|
+
const genUntil = (seed, pred) => {
|
|
555
|
+
reset();
|
|
556
|
+
reseed(seed); // Steps D-G
|
|
557
|
+
let res = undefined; // Step H: grind until k is in [1..n-1]
|
|
558
|
+
while (!(res = pred(gen())))
|
|
559
|
+
reseed();
|
|
560
|
+
reset();
|
|
561
|
+
return res;
|
|
562
|
+
};
|
|
563
|
+
return genUntil;
|
|
645
564
|
}
|
|
646
565
|
export function weierstrass(curveDef) {
|
|
647
566
|
const CURVE = validateOpts(curveDef);
|
|
648
567
|
const CURVE_ORDER = CURVE.n;
|
|
649
568
|
const Fp = CURVE.Fp;
|
|
650
|
-
const compressedLen = Fp.BYTES + 1; // 33
|
|
651
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // 65
|
|
569
|
+
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
570
|
+
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
652
571
|
function isValidFieldElement(num) {
|
|
653
|
-
// 0 is
|
|
654
|
-
return _0n < num && num < Fp.ORDER;
|
|
572
|
+
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
655
573
|
}
|
|
656
|
-
|
|
574
|
+
function modN(a) {
|
|
575
|
+
return mod.mod(a, CURVE_ORDER);
|
|
576
|
+
}
|
|
577
|
+
function invN(a) {
|
|
578
|
+
return mod.invert(a, CURVE_ORDER);
|
|
579
|
+
}
|
|
580
|
+
const { ProjectivePoint: Point, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
657
581
|
...CURVE,
|
|
658
582
|
toBytes(c, point, isCompressed) {
|
|
583
|
+
const a = point.toAffine();
|
|
584
|
+
const x = Fp.toBytes(a.x);
|
|
585
|
+
const cat = ut.concatBytes;
|
|
659
586
|
if (isCompressed) {
|
|
660
|
-
|
|
587
|
+
// TODO: hasEvenY
|
|
588
|
+
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
661
589
|
}
|
|
662
590
|
else {
|
|
663
|
-
return
|
|
591
|
+
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
664
592
|
}
|
|
665
593
|
},
|
|
666
594
|
fromBytes(bytes) {
|
|
667
595
|
const len = bytes.length;
|
|
668
|
-
const
|
|
596
|
+
const head = bytes[0];
|
|
597
|
+
const tail = bytes.subarray(1);
|
|
669
598
|
// this.assertValidity() is done inside of fromHex
|
|
670
|
-
if (len === compressedLen && (
|
|
671
|
-
const x = bytesToNumberBE(
|
|
599
|
+
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
600
|
+
const x = ut.bytesToNumberBE(tail);
|
|
672
601
|
if (!isValidFieldElement(x))
|
|
673
602
|
throw new Error('Point is not on curve');
|
|
674
603
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
675
604
|
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
676
605
|
const isYOdd = (y & _1n) === _1n;
|
|
677
606
|
// ECDSA
|
|
678
|
-
const
|
|
679
|
-
if (
|
|
680
|
-
y = Fp.
|
|
607
|
+
const isHeadOdd = (head & 1) === 1;
|
|
608
|
+
if (isHeadOdd !== isYOdd)
|
|
609
|
+
y = Fp.neg(y);
|
|
681
610
|
return { x, y };
|
|
682
611
|
}
|
|
683
|
-
else if (len === uncompressedLen &&
|
|
684
|
-
const x = Fp.fromBytes(
|
|
685
|
-
const y = Fp.fromBytes(
|
|
612
|
+
else if (len === uncompressedLen && head === 0x04) {
|
|
613
|
+
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
614
|
+
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
686
615
|
return { x, y };
|
|
687
616
|
}
|
|
688
617
|
else {
|
|
@@ -690,50 +619,16 @@ export function weierstrass(curveDef) {
|
|
|
690
619
|
}
|
|
691
620
|
},
|
|
692
621
|
});
|
|
693
|
-
|
|
694
|
-
function numToField(num) {
|
|
695
|
-
if (typeof num !== 'bigint')
|
|
696
|
-
throw new Error('Expected bigint');
|
|
697
|
-
if (!(_0n <= num && num < Fp.MASK))
|
|
698
|
-
throw new Error(`Expected number < 2^${Fp.BYTES * 8}`);
|
|
699
|
-
return Fp.toBytes(num);
|
|
700
|
-
}
|
|
701
|
-
const numToFieldStr = (num) => bytesToHex(numToField(num));
|
|
702
|
-
/**
|
|
703
|
-
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
|
704
|
-
*/
|
|
705
|
-
function normalizePublicKey(publicKey) {
|
|
706
|
-
if (publicKey instanceof Point) {
|
|
707
|
-
publicKey.assertValidity();
|
|
708
|
-
return publicKey;
|
|
709
|
-
}
|
|
710
|
-
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
711
|
-
return Point.fromHex(publicKey);
|
|
712
|
-
// This can happen because PointType can be instance of different class
|
|
713
|
-
}
|
|
714
|
-
else
|
|
715
|
-
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
716
|
-
}
|
|
622
|
+
const numToNByteStr = (num) => ut.bytesToHex(ut.numberToBytesBE(num, CURVE.nByteLength));
|
|
717
623
|
function isBiggerThanHalfOrder(number) {
|
|
718
624
|
const HALF = CURVE_ORDER >> _1n;
|
|
719
625
|
return number > HALF;
|
|
720
626
|
}
|
|
721
627
|
function normalizeS(s) {
|
|
722
|
-
return isBiggerThanHalfOrder(s) ?
|
|
723
|
-
}
|
|
724
|
-
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
725
|
-
function _truncateHash(hash, truncateOnly = false) {
|
|
726
|
-
const { n, nBitLength } = CURVE;
|
|
727
|
-
const byteLength = hash.length;
|
|
728
|
-
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
729
|
-
let h = bytesToNumberBE(hash);
|
|
730
|
-
if (delta > 0)
|
|
731
|
-
h = h >> BigInt(delta);
|
|
732
|
-
if (!truncateOnly && h >= n)
|
|
733
|
-
h -= n;
|
|
734
|
-
return h;
|
|
628
|
+
return isBiggerThanHalfOrder(s) ? modN(-s) : s;
|
|
735
629
|
}
|
|
736
|
-
|
|
630
|
+
// slice bytes num
|
|
631
|
+
const slcNum = (b, from, to) => ut.bytesToNumberBE(b.slice(from, to));
|
|
737
632
|
/**
|
|
738
633
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
739
634
|
*/
|
|
@@ -744,108 +639,73 @@ export function weierstrass(curveDef) {
|
|
|
744
639
|
this.recovery = recovery;
|
|
745
640
|
this.assertValidity();
|
|
746
641
|
}
|
|
747
|
-
// pair (
|
|
642
|
+
// pair (bytes of r, bytes of s)
|
|
748
643
|
static fromCompact(hex) {
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
throw new TypeError(`${name}: Expected string or Uint8Array`);
|
|
753
|
-
const str = arr ? bytesToHex(hex) : hex;
|
|
754
|
-
if (str.length !== 128)
|
|
755
|
-
throw new Error(`${name}: Expected 64-byte hex`);
|
|
756
|
-
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128)));
|
|
644
|
+
const gl = CURVE.nByteLength;
|
|
645
|
+
hex = ensureBytes(hex, gl * 2);
|
|
646
|
+
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
|
757
647
|
}
|
|
758
648
|
// DER encoded ECDSA signature
|
|
759
649
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
760
650
|
static fromDER(hex) {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex));
|
|
651
|
+
if (typeof hex !== 'string' && !(hex instanceof Uint8Array))
|
|
652
|
+
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
|
653
|
+
const { r, s } = DER.toSig(ensureBytes(hex));
|
|
765
654
|
return new Signature(r, s);
|
|
766
655
|
}
|
|
767
656
|
assertValidity() {
|
|
768
|
-
|
|
769
|
-
if (!isWithinCurveOrder(r))
|
|
770
|
-
throw new Error('
|
|
771
|
-
if (!isWithinCurveOrder(s))
|
|
772
|
-
throw new Error('
|
|
657
|
+
// can use assertGE here
|
|
658
|
+
if (!isWithinCurveOrder(this.r))
|
|
659
|
+
throw new Error('r must be 0 < r < n');
|
|
660
|
+
if (!isWithinCurveOrder(this.s))
|
|
661
|
+
throw new Error('s must be 0 < s < n');
|
|
773
662
|
}
|
|
774
|
-
|
|
663
|
+
addRecoveryBit(recovery) {
|
|
775
664
|
return new Signature(this.r, this.s, recovery);
|
|
776
665
|
}
|
|
777
|
-
/**
|
|
778
|
-
* Recovers public key from signature with recovery bit. Throws on invalid hash.
|
|
779
|
-
* https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
|
|
780
|
-
*
|
|
781
|
-
* ```
|
|
782
|
-
* recover(r, s, h) where
|
|
783
|
-
* u1 = hs^-1 mod n
|
|
784
|
-
* u2 = sr^-1 mod n
|
|
785
|
-
* Q = u1⋅G + u2⋅R
|
|
786
|
-
* ```
|
|
787
|
-
*
|
|
788
|
-
* @param msgHash message hash
|
|
789
|
-
* @returns Point corresponding to public key
|
|
790
|
-
*/
|
|
791
666
|
recoverPublicKey(msgHash) {
|
|
792
|
-
const {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (
|
|
796
|
-
throw new Error('
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const
|
|
802
|
-
const
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2);
|
|
667
|
+
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
|
668
|
+
const { r, s, recovery: rec } = this;
|
|
669
|
+
const h = bits2int_modN(ensureBytes(msgHash)); // Truncate hash
|
|
670
|
+
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
671
|
+
throw new Error('recovery id invalid');
|
|
672
|
+
const radj = rec === 2 || rec === 3 ? r + N : r;
|
|
673
|
+
if (radj >= Fp.ORDER)
|
|
674
|
+
throw new Error('recovery id 2 or 3 invalid');
|
|
675
|
+
const prefix = (rec & 1) === 0 ? '02' : '03';
|
|
676
|
+
const R = Point.fromHex(prefix + numToNByteStr(radj));
|
|
677
|
+
const ir = invN(radj); // r^-1
|
|
678
|
+
const u1 = modN(-h * ir); // -hr^-1
|
|
679
|
+
const u2 = modN(s * ir); // sr^-1
|
|
680
|
+
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
|
|
806
681
|
if (!Q)
|
|
807
|
-
throw new Error('
|
|
682
|
+
throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
|
|
808
683
|
Q.assertValidity();
|
|
809
684
|
return Q;
|
|
810
685
|
}
|
|
811
|
-
|
|
812
|
-
* Default signatures are always low-s, to prevent malleability.
|
|
813
|
-
* `sign(lowS: true)` always produces low-s sigs.
|
|
814
|
-
* `verify(lowS: true)` always fails for high-s.
|
|
815
|
-
*/
|
|
686
|
+
// Signatures should be low-s, to prevent malleability.
|
|
816
687
|
hasHighS() {
|
|
817
688
|
return isBiggerThanHalfOrder(this.s);
|
|
818
689
|
}
|
|
819
690
|
normalizeS() {
|
|
820
|
-
return this.hasHighS()
|
|
821
|
-
? new Signature(this.r, mod.mod(-this.s, CURVE_ORDER), this.recovery)
|
|
822
|
-
: this;
|
|
691
|
+
return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
|
|
823
692
|
}
|
|
824
693
|
// DER-encoded
|
|
825
|
-
toDERRawBytes(
|
|
826
|
-
return hexToBytes(this.toDERHex(
|
|
694
|
+
toDERRawBytes() {
|
|
695
|
+
return ut.hexToBytes(this.toDERHex());
|
|
827
696
|
}
|
|
828
|
-
toDERHex(
|
|
829
|
-
|
|
830
|
-
if (isCompressed)
|
|
831
|
-
return sHex;
|
|
832
|
-
const rHex = sliceDER(numberToHexUnpadded(this.r));
|
|
833
|
-
const rLen = numberToHexUnpadded(rHex.length / 2);
|
|
834
|
-
const sLen = numberToHexUnpadded(sHex.length / 2);
|
|
835
|
-
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4);
|
|
836
|
-
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
|
697
|
+
toDERHex() {
|
|
698
|
+
return DER.hexFromSig({ r: this.r, s: this.s });
|
|
837
699
|
}
|
|
838
|
-
//
|
|
700
|
+
// padded bytes of r, then padded bytes of s
|
|
839
701
|
toCompactRawBytes() {
|
|
840
|
-
return hexToBytes(this.toCompactHex());
|
|
702
|
+
return ut.hexToBytes(this.toCompactHex());
|
|
841
703
|
}
|
|
842
704
|
toCompactHex() {
|
|
843
|
-
return
|
|
705
|
+
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
|
844
706
|
}
|
|
845
707
|
}
|
|
846
708
|
const utils = {
|
|
847
|
-
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo),
|
|
848
|
-
invert: Fp.invert,
|
|
849
709
|
isValidPrivateKey(privateKey) {
|
|
850
710
|
try {
|
|
851
711
|
normalizePrivateKey(privateKey);
|
|
@@ -855,17 +715,11 @@ export function weierstrass(curveDef) {
|
|
|
855
715
|
return false;
|
|
856
716
|
}
|
|
857
717
|
},
|
|
858
|
-
_bigintToBytes: numToField,
|
|
859
|
-
_bigintToString: numToFieldStr,
|
|
860
718
|
_normalizePrivateKey: normalizePrivateKey,
|
|
861
|
-
_normalizePublicKey: normalizePublicKey,
|
|
862
|
-
_isWithinCurveOrder: isWithinCurveOrder,
|
|
863
|
-
_isValidFieldElement: isValidFieldElement,
|
|
864
|
-
_weierstrassEquation: weierstrassEquation,
|
|
865
719
|
/**
|
|
866
720
|
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
867
721
|
*/
|
|
868
|
-
hashToPrivateKey: (hash) =>
|
|
722
|
+
hashToPrivateKey: (hash) => ut.numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
|
869
723
|
/**
|
|
870
724
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
871
725
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
@@ -880,19 +734,18 @@ export function weierstrass(curveDef) {
|
|
|
880
734
|
* @returns cached point
|
|
881
735
|
*/
|
|
882
736
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
return cached;
|
|
737
|
+
point._setWindowSize(windowSize);
|
|
738
|
+
point.multiply(BigInt(3));
|
|
739
|
+
return point;
|
|
887
740
|
},
|
|
888
741
|
};
|
|
889
742
|
/**
|
|
890
|
-
* Computes public key for a private key.
|
|
743
|
+
* Computes public key for a private key. Checks for validity of the private key.
|
|
891
744
|
* @param privateKey private key
|
|
892
|
-
* @param isCompressed whether to return compact, or full key
|
|
893
|
-
* @returns Public key, full
|
|
745
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
746
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
894
747
|
*/
|
|
895
|
-
function getPublicKey(privateKey, isCompressed =
|
|
748
|
+
function getPublicKey(privateKey, isCompressed = true) {
|
|
896
749
|
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
|
897
750
|
}
|
|
898
751
|
/**
|
|
@@ -911,112 +764,130 @@ export function weierstrass(curveDef) {
|
|
|
911
764
|
return false;
|
|
912
765
|
}
|
|
913
766
|
/**
|
|
914
|
-
* ECDH (Elliptic Curve Diffie Hellman)
|
|
915
|
-
*
|
|
916
|
-
*
|
|
767
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
768
|
+
* Computes shared public key from private key and public key.
|
|
769
|
+
* Checks: 1) private key validity 2) shared key is on-curve
|
|
917
770
|
* @param privateA private key
|
|
918
771
|
* @param publicB different public key
|
|
919
|
-
* @param isCompressed whether to return compact (
|
|
772
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
920
773
|
* @returns shared public key
|
|
921
774
|
*/
|
|
922
|
-
function getSharedSecret(privateA, publicB, isCompressed =
|
|
775
|
+
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
923
776
|
if (isProbPub(privateA))
|
|
924
|
-
throw new
|
|
777
|
+
throw new Error('first arg must be private key');
|
|
925
778
|
if (!isProbPub(publicB))
|
|
926
|
-
throw new
|
|
927
|
-
const b =
|
|
928
|
-
b.assertValidity();
|
|
779
|
+
throw new Error('second arg must be public key');
|
|
780
|
+
const b = Point.fromHex(publicB); // check for being on-curve
|
|
929
781
|
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
|
|
930
782
|
}
|
|
931
|
-
// RFC6979
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
783
|
+
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
784
|
+
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
785
|
+
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
786
|
+
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
787
|
+
const bits2int = CURVE.bits2int ||
|
|
788
|
+
function (bytes) {
|
|
789
|
+
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
790
|
+
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
791
|
+
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
|
792
|
+
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
|
793
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
794
|
+
};
|
|
795
|
+
const bits2int_modN = CURVE.bits2int_modN ||
|
|
796
|
+
function (bytes) {
|
|
797
|
+
return modN(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
798
|
+
};
|
|
799
|
+
// NOTE: pads output with zero as per spec
|
|
800
|
+
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
|
941
801
|
function int2octets(num) {
|
|
942
|
-
|
|
802
|
+
if (typeof num !== 'bigint')
|
|
803
|
+
throw new Error('Expected bigint');
|
|
804
|
+
if (!(_0n <= num && num < ORDER_MASK))
|
|
805
|
+
throw new Error(`Expected number < 2^${CURVE.nBitLength}`);
|
|
806
|
+
// works with order, can have different size than numToField!
|
|
807
|
+
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
943
808
|
}
|
|
944
809
|
// Steps A, D of RFC6979 3.2
|
|
945
810
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
946
|
-
|
|
811
|
+
// Used only in sign, not in verify.
|
|
812
|
+
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521.
|
|
813
|
+
// Also it can be bigger for P224 + SHA256
|
|
814
|
+
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
947
815
|
if (msgHash == null)
|
|
948
816
|
throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
|
|
817
|
+
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
818
|
+
// Ban legacy options
|
|
819
|
+
throw new Error('sign() legacy options not supported');
|
|
820
|
+
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
821
|
+
if (prehash)
|
|
822
|
+
msgHash = CURVE.hash(ensureBytes(msgHash));
|
|
823
|
+
if (lowS == null)
|
|
824
|
+
lowS = true; // RFC6979 3.2: we skip step A, because
|
|
949
825
|
// Step A is ignored, since we already provide hash instead of msg
|
|
950
|
-
|
|
826
|
+
// NOTE: instead of bits2int, we calling here truncateHash, since we need
|
|
827
|
+
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod
|
|
828
|
+
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken
|
|
829
|
+
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call.
|
|
830
|
+
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits)))
|
|
831
|
+
const h1int = bits2int_modN(ensureBytes(msgHash));
|
|
832
|
+
const h1octets = int2octets(h1int);
|
|
951
833
|
const d = normalizePrivateKey(privateKey);
|
|
952
834
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
953
|
-
const seedArgs = [int2octets(d),
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
const e = ensureBytes(
|
|
835
|
+
const seedArgs = [int2octets(d), h1octets];
|
|
836
|
+
if (ent != null) {
|
|
837
|
+
// RFC6979 3.6: additional k' (optional)
|
|
838
|
+
if (ent === true)
|
|
839
|
+
ent = CURVE.randomBytes(Fp.BYTES);
|
|
840
|
+
const e = ensureBytes(ent);
|
|
959
841
|
if (e.length !== Fp.BYTES)
|
|
960
842
|
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
|
961
843
|
seedArgs.push(e);
|
|
962
844
|
}
|
|
963
|
-
|
|
964
|
-
//
|
|
965
|
-
//
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
// r = x mod n
|
|
987
|
-
const r = mod.mod(q.x, n);
|
|
988
|
-
if (r === _0n)
|
|
989
|
-
return;
|
|
990
|
-
// s = (1/k * (m + dr) mod n
|
|
991
|
-
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
|
|
992
|
-
if (s === _0n)
|
|
993
|
-
return;
|
|
994
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
|
|
995
|
-
let normS = s;
|
|
996
|
-
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
997
|
-
normS = normalizeS(s);
|
|
998
|
-
recovery ^= 1;
|
|
845
|
+
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
846
|
+
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
847
|
+
// Converts signature params into point w r/s, checks result for validity.
|
|
848
|
+
function k2sig(kBytes) {
|
|
849
|
+
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
850
|
+
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
851
|
+
if (!isWithinCurveOrder(k))
|
|
852
|
+
return; // Important: all mod() calls here must be done over N
|
|
853
|
+
const ik = invN(k); // k^-1 mod n
|
|
854
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
855
|
+
const r = modN(q.x); // r = q.x mod n
|
|
856
|
+
if (r === _0n)
|
|
857
|
+
return;
|
|
858
|
+
const s = modN(ik * modN(m + modN(d * r))); // s = k^-1(m + rd) mod n
|
|
859
|
+
if (s === _0n)
|
|
860
|
+
return;
|
|
861
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
862
|
+
let normS = s;
|
|
863
|
+
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
864
|
+
normS = normalizeS(s); // if lowS was passed, ensure s is always
|
|
865
|
+
recovery ^= 1; // // in the bottom half of N
|
|
866
|
+
}
|
|
867
|
+
return new Signature(r, normS, recovery); // use normS, not s
|
|
999
868
|
}
|
|
1000
|
-
return
|
|
869
|
+
return { seed, k2sig };
|
|
1001
870
|
}
|
|
871
|
+
const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
|
|
872
|
+
const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
|
|
1002
873
|
/**
|
|
1003
874
|
* Signs message hash (not message: you need to hash it by yourself).
|
|
1004
|
-
*
|
|
875
|
+
* ```
|
|
876
|
+
* sign(m, d, k) where
|
|
877
|
+
* (x, y) = G × k
|
|
878
|
+
* r = x mod n
|
|
879
|
+
* s = (m + dr)/k mod n
|
|
880
|
+
* ```
|
|
881
|
+
* @param opts `lowS, extraEntropy, prehash`
|
|
1005
882
|
*/
|
|
1006
|
-
function sign(msgHash, privKey, opts =
|
|
1007
|
-
// Steps A, D of RFC6979 3.2.
|
|
1008
|
-
const
|
|
1009
|
-
// Steps B, C, D, E, F, G
|
|
1010
|
-
const drbg = new HmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
1011
|
-
drbg.reseedSync(seed);
|
|
1012
|
-
// Step H3, repeat until k is in range [1, n-1]
|
|
1013
|
-
let sig;
|
|
1014
|
-
while (!(sig = kmdToSig(drbg.generateSync(), m, d, opts.lowS)))
|
|
1015
|
-
drbg.reseedSync();
|
|
1016
|
-
return sig;
|
|
883
|
+
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
884
|
+
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
885
|
+
const genUntil = hmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
886
|
+
return genUntil(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1017
887
|
}
|
|
1018
888
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1019
889
|
Point.BASE._setWindowSize(8);
|
|
890
|
+
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1020
891
|
/**
|
|
1021
892
|
* Verifies a signature against message hash and public key.
|
|
1022
893
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1030,50 +901,47 @@ export function weierstrass(curveDef) {
|
|
|
1030
901
|
* mod(R.x, n) == r
|
|
1031
902
|
* ```
|
|
1032
903
|
*/
|
|
1033
|
-
function verify(signature, msgHash, publicKey, opts =
|
|
904
|
+
function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
|
|
905
|
+
let P;
|
|
906
|
+
let _sig = undefined;
|
|
907
|
+
if (publicKey instanceof Point)
|
|
908
|
+
throw new Error('publicKey must be hex');
|
|
1034
909
|
try {
|
|
1035
|
-
if (signature instanceof
|
|
1036
|
-
signature
|
|
910
|
+
if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) {
|
|
911
|
+
const { r, s } = signature;
|
|
912
|
+
_sig = new Signature(r, s); // assertValidity() is executed on creation
|
|
1037
913
|
}
|
|
1038
914
|
else {
|
|
1039
|
-
// Signature can be represented in 2 ways: compact (
|
|
1040
|
-
// Since DER can also be
|
|
915
|
+
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
|
916
|
+
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
|
1041
917
|
try {
|
|
1042
|
-
|
|
918
|
+
_sig = Signature.fromDER(signature);
|
|
1043
919
|
}
|
|
1044
920
|
catch (derError) {
|
|
1045
|
-
if (!(derError instanceof
|
|
921
|
+
if (!(derError instanceof DER.Err))
|
|
1046
922
|
throw derError;
|
|
1047
|
-
|
|
923
|
+
_sig = Signature.fromCompact(signature);
|
|
1048
924
|
}
|
|
1049
925
|
}
|
|
1050
926
|
msgHash = ensureBytes(msgHash);
|
|
927
|
+
P = Point.fromHex(publicKey);
|
|
1051
928
|
}
|
|
1052
929
|
catch (error) {
|
|
1053
930
|
return false;
|
|
1054
931
|
}
|
|
1055
|
-
if (opts.lowS &&
|
|
932
|
+
if (opts.lowS && _sig.hasHighS())
|
|
1056
933
|
return false;
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
const
|
|
1065
|
-
const { r, s } = signature;
|
|
1066
|
-
const h = truncateHash(msgHash);
|
|
1067
|
-
const sinv = mod.invert(s, n); // s^-1
|
|
1068
|
-
// R = u1⋅G - u2⋅P
|
|
1069
|
-
const u1 = mod.mod(h * sinv, n);
|
|
1070
|
-
const u2 = mod.mod(r * sinv, n);
|
|
1071
|
-
// Some implementations compare R.x in projective, without inversion.
|
|
1072
|
-
// The speed-up is <5%, so we don't complicate the code.
|
|
1073
|
-
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
|
|
934
|
+
if (opts.prehash)
|
|
935
|
+
msgHash = CURVE.hash(msgHash);
|
|
936
|
+
const { r, s } = _sig;
|
|
937
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
938
|
+
const is = invN(s); // s^-1
|
|
939
|
+
const u1 = modN(h * is); // u1 = hs^-1 mod n
|
|
940
|
+
const u2 = modN(r * is); // u2 = rs^-1 mod n
|
|
941
|
+
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
|
|
1074
942
|
if (!R)
|
|
1075
943
|
return false;
|
|
1076
|
-
const v =
|
|
944
|
+
const v = modN(R.x);
|
|
1077
945
|
return v === r;
|
|
1078
946
|
}
|
|
1079
947
|
return {
|
|
@@ -1082,14 +950,14 @@ export function weierstrass(curveDef) {
|
|
|
1082
950
|
getSharedSecret,
|
|
1083
951
|
sign,
|
|
1084
952
|
verify,
|
|
1085
|
-
Point,
|
|
1086
|
-
ProjectivePoint,
|
|
953
|
+
// Point,
|
|
954
|
+
ProjectivePoint: Point,
|
|
1087
955
|
Signature,
|
|
1088
956
|
utils,
|
|
1089
957
|
};
|
|
1090
958
|
}
|
|
1091
959
|
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
|
|
1092
|
-
// TODO: check if there is a way to merge this with
|
|
960
|
+
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
|
|
1093
961
|
// b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1094
962
|
// b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1095
963
|
export function SWUFpSqrtRatio(Fp, Z) {
|
|
@@ -1108,7 +976,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1108
976
|
let sqrtRatio = (u, v) => {
|
|
1109
977
|
let tv1 = c6; // 1. tv1 = c6
|
|
1110
978
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1111
|
-
let tv3 = Fp.
|
|
979
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1112
980
|
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1113
981
|
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1114
982
|
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
@@ -1117,7 +985,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1117
985
|
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1118
986
|
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1119
987
|
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1120
|
-
let isQR = Fp.
|
|
988
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1121
989
|
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1122
990
|
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1123
991
|
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
@@ -1126,7 +994,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1126
994
|
for (let i = c1; i > 1; i--) {
|
|
1127
995
|
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
|
1128
996
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1129
|
-
const e1 = Fp.
|
|
997
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1130
998
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1131
999
|
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1132
1000
|
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
@@ -1138,16 +1006,16 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1138
1006
|
if (Fp.ORDER % 4n === 3n) {
|
|
1139
1007
|
// sqrt_ratio_3mod4(u, v)
|
|
1140
1008
|
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1141
|
-
const c2 = Fp.sqrt(Fp.
|
|
1009
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1142
1010
|
sqrtRatio = (u, v) => {
|
|
1143
|
-
let tv1 = Fp.
|
|
1011
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1144
1012
|
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1145
1013
|
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1146
1014
|
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1147
1015
|
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1148
1016
|
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1149
|
-
const tv3 = Fp.mul(Fp.
|
|
1150
|
-
const isQR = Fp.
|
|
1017
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1018
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1151
1019
|
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1152
1020
|
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1153
1021
|
};
|
|
@@ -1169,16 +1037,16 @@ export function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1169
1037
|
return (u) => {
|
|
1170
1038
|
// prettier-ignore
|
|
1171
1039
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1172
|
-
tv1 = Fp.
|
|
1040
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1173
1041
|
tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
|
|
1174
|
-
tv2 = Fp.
|
|
1042
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1175
1043
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1176
1044
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1177
1045
|
tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
|
|
1178
|
-
tv4 = Fp.cmov(opts.Z, Fp.
|
|
1046
|
+
tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1179
1047
|
tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
|
|
1180
|
-
tv2 = Fp.
|
|
1181
|
-
tv6 = Fp.
|
|
1048
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1049
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1182
1050
|
tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
|
|
1183
1051
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1184
1052
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
@@ -1192,7 +1060,7 @@ export function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1192
1060
|
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1193
1061
|
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1194
1062
|
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1195
|
-
y = Fp.cmov(Fp.
|
|
1063
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1196
1064
|
x = Fp.div(x, tv4); // 25. x = x / tv4
|
|
1197
1065
|
return { x, y };
|
|
1198
1066
|
};
|