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