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