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