@protontech/openpgp 6.1.1-patch.4 → 6.2.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 +13 -2
- package/dist/lightweight/argon2id.min.mjs +2 -2
- package/dist/lightweight/argon2id.min.mjs.map +1 -1
- package/dist/lightweight/argon2id.mjs +4 -4
- package/dist/lightweight/legacy_ciphers.min.mjs +1 -1
- package/dist/lightweight/legacy_ciphers.min.mjs.map +1 -1
- package/dist/lightweight/legacy_ciphers.mjs +10 -10
- package/dist/lightweight/nacl-fast.min.mjs +3 -0
- package/dist/lightweight/nacl-fast.min.mjs.map +1 -0
- package/dist/lightweight/nacl-fast.mjs +1382 -0
- package/dist/lightweight/noble_curves.min.mjs +11 -12
- package/dist/lightweight/noble_curves.min.mjs.map +1 -1
- package/dist/lightweight/noble_curves.mjs +2175 -1752
- package/dist/lightweight/noble_hashes.min.mjs +2 -2
- package/dist/lightweight/noble_hashes.min.mjs.map +1 -1
- package/dist/lightweight/noble_hashes.mjs +80 -51
- package/dist/lightweight/noble_post_quantum.min.mjs +3 -4
- package/dist/lightweight/noble_post_quantum.min.mjs.map +1 -1
- package/dist/lightweight/noble_post_quantum.mjs +352 -10
- package/dist/lightweight/openpgp.min.mjs +3 -4
- package/dist/lightweight/openpgp.min.mjs.map +1 -1
- package/dist/lightweight/openpgp.mjs +998 -2820
- package/dist/lightweight/seek-bzip.min.mjs +2 -2
- package/dist/lightweight/seek-bzip.min.mjs.map +1 -1
- package/dist/lightweight/seek-bzip.mjs +780 -746
- package/dist/lightweight/sha512.min.mjs +4 -2
- package/dist/lightweight/sha512.min.mjs.map +1 -1
- package/dist/lightweight/sha512.mjs +672 -130
- package/dist/node/openpgp.cjs +10685 -10141
- package/dist/node/openpgp.min.cjs +14 -17
- package/dist/node/openpgp.min.cjs.map +1 -1
- package/dist/node/openpgp.min.mjs +14 -17
- package/dist/node/openpgp.min.mjs.map +1 -1
- package/dist/node/openpgp.mjs +10685 -10140
- package/dist/openpgp.js +11728 -11188
- package/dist/openpgp.min.js +14 -17
- package/dist/openpgp.min.js.map +1 -1
- package/dist/openpgp.min.mjs +14 -17
- package/dist/openpgp.min.mjs.map +1 -1
- package/dist/openpgp.mjs +11728 -11188
- package/{src → dist/types}/config/config.d.ts +1 -21
- package/{openpgp.d.ts → dist/types/index.d.ts} +94 -76
- package/dist/types/packet/grammar.d.ts +33 -0
- package/package.json +40 -39
- package/dist/lightweight/sha3.min.mjs +0 -4
- package/dist/lightweight/sha3.min.mjs.map +0 -1
- package/dist/lightweight/sha3.mjs +0 -401
- /package/{src → dist/types}/config/index.d.ts +0 -0
- /package/{src → dist/types}/enums.d.ts +0 -0
|
@@ -1,121 +1,38 @@
|
|
|
1
|
-
/*! OpenPGP.js v6.
|
|
1
|
+
/*! OpenPGP.js v6.2.1 - 2025-08-28 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
|
|
2
2
|
const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
3
3
|
|
|
4
|
-
import { s as sha256,
|
|
5
|
-
import { H as Hash, a as ahash, t as toBytes, b as aexists, c as abytes$1, d as concatBytes$1, r as randomBytes, w as wrapConstructor, u as utf8ToBytes$1, s as shake256 } from './sha3.mjs';
|
|
4
|
+
import { h as hexToBytes, a as abytes, b as bytesToHex, i as isBytes, c as concatBytes, d as anumber, H as Hash, e as ahash, t as toBytes, f as clean, g as aexists, r as randomBytes, s as sha256, j as sha384, k as sha512, l as createHasher, m as shake256, n as sha256$1, o as sha384$1, p as sha512$1 } from './sha512.mjs';
|
|
6
5
|
|
|
7
|
-
// HMAC (RFC 2104)
|
|
8
|
-
class HMAC extends Hash {
|
|
9
|
-
constructor(hash, _key) {
|
|
10
|
-
super();
|
|
11
|
-
this.finished = false;
|
|
12
|
-
this.destroyed = false;
|
|
13
|
-
ahash(hash);
|
|
14
|
-
const key = toBytes(_key);
|
|
15
|
-
this.iHash = hash.create();
|
|
16
|
-
if (typeof this.iHash.update !== 'function')
|
|
17
|
-
throw new Error('Expected instance of class which extends utils.Hash');
|
|
18
|
-
this.blockLen = this.iHash.blockLen;
|
|
19
|
-
this.outputLen = this.iHash.outputLen;
|
|
20
|
-
const blockLen = this.blockLen;
|
|
21
|
-
const pad = new Uint8Array(blockLen);
|
|
22
|
-
// blockLen can be bigger than outputLen
|
|
23
|
-
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
24
|
-
for (let i = 0; i < pad.length; i++)
|
|
25
|
-
pad[i] ^= 0x36;
|
|
26
|
-
this.iHash.update(pad);
|
|
27
|
-
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
|
28
|
-
this.oHash = hash.create();
|
|
29
|
-
// Undo internal XOR && apply outer XOR
|
|
30
|
-
for (let i = 0; i < pad.length; i++)
|
|
31
|
-
pad[i] ^= 0x36 ^ 0x5c;
|
|
32
|
-
this.oHash.update(pad);
|
|
33
|
-
pad.fill(0);
|
|
34
|
-
}
|
|
35
|
-
update(buf) {
|
|
36
|
-
aexists(this);
|
|
37
|
-
this.iHash.update(buf);
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
digestInto(out) {
|
|
41
|
-
aexists(this);
|
|
42
|
-
abytes$1(out, this.outputLen);
|
|
43
|
-
this.finished = true;
|
|
44
|
-
this.iHash.digestInto(out);
|
|
45
|
-
this.oHash.update(out);
|
|
46
|
-
this.oHash.digestInto(out);
|
|
47
|
-
this.destroy();
|
|
48
|
-
}
|
|
49
|
-
digest() {
|
|
50
|
-
const out = new Uint8Array(this.oHash.outputLen);
|
|
51
|
-
this.digestInto(out);
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
_cloneInto(to) {
|
|
55
|
-
// Create new instance without calling constructor since key already in state and we don't know it.
|
|
56
|
-
to || (to = Object.create(Object.getPrototypeOf(this), {}));
|
|
57
|
-
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
58
|
-
to = to;
|
|
59
|
-
to.finished = finished;
|
|
60
|
-
to.destroyed = destroyed;
|
|
61
|
-
to.blockLen = blockLen;
|
|
62
|
-
to.outputLen = outputLen;
|
|
63
|
-
to.oHash = oHash._cloneInto(to.oHash);
|
|
64
|
-
to.iHash = iHash._cloneInto(to.iHash);
|
|
65
|
-
return to;
|
|
66
|
-
}
|
|
67
|
-
destroy() {
|
|
68
|
-
this.destroyed = true;
|
|
69
|
-
this.oHash.destroy();
|
|
70
|
-
this.iHash.destroy();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
6
|
/**
|
|
74
|
-
*
|
|
75
|
-
* @
|
|
76
|
-
* @param key - message key
|
|
77
|
-
* @param message - message data
|
|
78
|
-
* @example
|
|
79
|
-
* import { hmac } from '@noble/hashes/hmac';
|
|
80
|
-
* import { sha256 } from '@noble/hashes/sha2';
|
|
81
|
-
* const mac1 = hmac(sha256, 'key', 'message');
|
|
7
|
+
* Hex, bytes and number utilities.
|
|
8
|
+
* @module
|
|
82
9
|
*/
|
|
83
|
-
const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
84
|
-
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
85
|
-
|
|
86
10
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
87
|
-
// 100 lines of code in the file are duplicated from noble-hashes (utils).
|
|
88
|
-
// This is OK: `abstract` directory does not use noble-hashes.
|
|
89
|
-
// User may opt-in into using different hashing library. This way, noble-hashes
|
|
90
|
-
// won't be included into their bundle.
|
|
91
11
|
const _0n$5 = /* @__PURE__ */ BigInt(0);
|
|
92
|
-
const _1n$
|
|
93
|
-
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!isBytes(item))
|
|
99
|
-
throw new Error('Uint8Array expected');
|
|
100
|
-
}
|
|
101
|
-
function abool(title, value) {
|
|
102
|
-
if (typeof value !== 'boolean')
|
|
103
|
-
throw new Error(title + ' boolean expected, got ' + value);
|
|
104
|
-
}
|
|
105
|
-
// Array where index 0xf0 (240) is mapped to string 'f0'
|
|
106
|
-
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
|
|
107
|
-
/**
|
|
108
|
-
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
|
109
|
-
*/
|
|
110
|
-
function bytesToHex(bytes) {
|
|
111
|
-
abytes(bytes);
|
|
112
|
-
// pre-caching improves the speed 6x
|
|
113
|
-
let hex = '';
|
|
114
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
115
|
-
hex += hexes[bytes[i]];
|
|
12
|
+
const _1n$6 = /* @__PURE__ */ BigInt(1);
|
|
13
|
+
// tmp name until v2
|
|
14
|
+
function _abool2(value, title = '') {
|
|
15
|
+
if (typeof value !== 'boolean') {
|
|
16
|
+
const prefix = title && `"${title}"`;
|
|
17
|
+
throw new Error(prefix + 'expected boolean, got type=' + typeof value);
|
|
116
18
|
}
|
|
117
|
-
return
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
// tmp name until v2
|
|
22
|
+
/** Asserts something is Uint8Array. */
|
|
23
|
+
function _abytes2(value, length, title = '') {
|
|
24
|
+
const bytes = isBytes(value);
|
|
25
|
+
const len = value?.length;
|
|
26
|
+
const needsLen = length !== undefined;
|
|
27
|
+
if (!bytes || (needsLen && len !== length)) {
|
|
28
|
+
const prefix = title && `"${title}" `;
|
|
29
|
+
const ofLen = needsLen ? ` of length ${length}` : '';
|
|
30
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
31
|
+
throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
118
34
|
}
|
|
35
|
+
// Used in weierstrass, der
|
|
119
36
|
function numberToHexUnpadded(num) {
|
|
120
37
|
const hex = num.toString(16);
|
|
121
38
|
return hex.length & 1 ? '0' + hex : hex;
|
|
@@ -125,39 +42,6 @@ function hexToNumber(hex) {
|
|
|
125
42
|
throw new Error('hex string expected, got ' + typeof hex);
|
|
126
43
|
return hex === '' ? _0n$5 : BigInt('0x' + hex); // Big Endian
|
|
127
44
|
}
|
|
128
|
-
// We use optimized technique to convert hex string to byte array
|
|
129
|
-
const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
|
|
130
|
-
function asciiToBase16(ch) {
|
|
131
|
-
if (ch >= asciis._0 && ch <= asciis._9)
|
|
132
|
-
return ch - asciis._0; // '2' => 50-48
|
|
133
|
-
if (ch >= asciis.A && ch <= asciis.F)
|
|
134
|
-
return ch - (asciis.A - 10); // 'B' => 66-(65-10)
|
|
135
|
-
if (ch >= asciis.a && ch <= asciis.f)
|
|
136
|
-
return ch - (asciis.a - 10); // 'b' => 98-(97-10)
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
|
141
|
-
*/
|
|
142
|
-
function hexToBytes(hex) {
|
|
143
|
-
if (typeof hex !== 'string')
|
|
144
|
-
throw new Error('hex string expected, got ' + typeof hex);
|
|
145
|
-
const hl = hex.length;
|
|
146
|
-
const al = hl / 2;
|
|
147
|
-
if (hl % 2)
|
|
148
|
-
throw new Error('hex string expected, got unpadded hex of length ' + hl);
|
|
149
|
-
const array = new Uint8Array(al);
|
|
150
|
-
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
151
|
-
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|
152
|
-
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|
153
|
-
if (n1 === undefined || n2 === undefined) {
|
|
154
|
-
const char = hex[hi] + hex[hi + 1];
|
|
155
|
-
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
156
|
-
}
|
|
157
|
-
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
|
|
158
|
-
}
|
|
159
|
-
return array;
|
|
160
|
-
}
|
|
161
45
|
// BE: Big Endian, LE: Little Endian
|
|
162
46
|
function bytesToNumberBE(bytes) {
|
|
163
47
|
return hexToNumber(bytesToHex(bytes));
|
|
@@ -172,15 +56,11 @@ function numberToBytesBE(n, len) {
|
|
|
172
56
|
function numberToBytesLE(n, len) {
|
|
173
57
|
return numberToBytesBE(n, len).reverse();
|
|
174
58
|
}
|
|
175
|
-
// Unpadded, rarely used
|
|
176
|
-
function numberToVarBytesBE(n) {
|
|
177
|
-
return hexToBytes(numberToHexUnpadded(n));
|
|
178
|
-
}
|
|
179
59
|
/**
|
|
180
60
|
* Takes hex string or Uint8Array, converts to Uint8Array.
|
|
181
61
|
* Validates output length.
|
|
182
62
|
* Will throw error for other types.
|
|
183
|
-
* @param title descriptive title for an error e.g. '
|
|
63
|
+
* @param title descriptive title for an error e.g. 'secret key'
|
|
184
64
|
* @param hex hex string or Uint8Array
|
|
185
65
|
* @param expectedLength optional, will compare to result array's length
|
|
186
66
|
* @returns
|
|
@@ -209,40 +89,35 @@ function ensureBytes(title, hex, expectedLength) {
|
|
|
209
89
|
return res;
|
|
210
90
|
}
|
|
211
91
|
/**
|
|
212
|
-
* Copies
|
|
92
|
+
* Copies Uint8Array. We can't use u8a.slice(), because u8a can be Buffer,
|
|
93
|
+
* and Buffer#slice creates mutable copy. Never use Buffers!
|
|
213
94
|
*/
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
217
|
-
const a = arrays[i];
|
|
218
|
-
abytes(a);
|
|
219
|
-
sum += a.length;
|
|
220
|
-
}
|
|
221
|
-
const res = new Uint8Array(sum);
|
|
222
|
-
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
223
|
-
const a = arrays[i];
|
|
224
|
-
res.set(a, pad);
|
|
225
|
-
pad += a.length;
|
|
226
|
-
}
|
|
227
|
-
return res;
|
|
95
|
+
function copyBytes(bytes) {
|
|
96
|
+
return Uint8Array.from(bytes);
|
|
228
97
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Decodes 7-bit ASCII string to Uint8Array, throws on non-ascii symbols
|
|
100
|
+
* Should be safe to use for things expected to be ASCII.
|
|
101
|
+
* Returns exact same result as utf8ToBytes for ASCII or throws.
|
|
102
|
+
*/
|
|
103
|
+
function asciiToBytes(ascii) {
|
|
104
|
+
return Uint8Array.from(ascii, (c, i) => {
|
|
105
|
+
const charCode = c.charCodeAt(0);
|
|
106
|
+
if (c.length !== 1 || charCode > 127) {
|
|
107
|
+
throw new Error(`string contains non-ASCII character "${ascii[i]}" with code ${charCode} at position ${i}`);
|
|
108
|
+
}
|
|
109
|
+
return charCode;
|
|
110
|
+
});
|
|
237
111
|
}
|
|
238
112
|
/**
|
|
239
113
|
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
|
240
114
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
115
|
+
// export const utf8ToBytes: typeof utf8ToBytes_ = utf8ToBytes_;
|
|
116
|
+
/**
|
|
117
|
+
* Converts bytes to string using UTF8 encoding.
|
|
118
|
+
* @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'
|
|
119
|
+
*/
|
|
120
|
+
// export const bytesToUtf8: typeof bytesToUtf8_ = bytesToUtf8_;
|
|
246
121
|
// Is positive bigint
|
|
247
122
|
const isPosBig = (n) => typeof n === 'bigint' && _0n$5 <= n;
|
|
248
123
|
function inRange(n, min, max) {
|
|
@@ -266,35 +141,19 @@ function aInRange(title, n, min, max) {
|
|
|
266
141
|
/**
|
|
267
142
|
* Calculates amount of bits in a bigint.
|
|
268
143
|
* Same as `n.toString(2).length`
|
|
144
|
+
* TODO: merge with nLength in modular
|
|
269
145
|
*/
|
|
270
146
|
function bitLen(n) {
|
|
271
147
|
let len;
|
|
272
|
-
for (len = 0; n > _0n$5; n >>= _1n$
|
|
148
|
+
for (len = 0; n > _0n$5; n >>= _1n$6, len += 1)
|
|
273
149
|
;
|
|
274
150
|
return len;
|
|
275
151
|
}
|
|
276
|
-
/**
|
|
277
|
-
* Gets single bit at position.
|
|
278
|
-
* NOTE: first bit position is 0 (same as arrays)
|
|
279
|
-
* Same as `!!+Array.from(n.toString(2)).reverse()[pos]`
|
|
280
|
-
*/
|
|
281
|
-
function bitGet(n, pos) {
|
|
282
|
-
return (n >> BigInt(pos)) & _1n$7;
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Sets single bit at position.
|
|
286
|
-
*/
|
|
287
|
-
function bitSet(n, pos, value) {
|
|
288
|
-
return n | ((value ? _1n$7 : _0n$5) << BigInt(pos));
|
|
289
|
-
}
|
|
290
152
|
/**
|
|
291
153
|
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.
|
|
292
154
|
* Same as BigInt(`0b${Array(i).fill('1').join('')}`)
|
|
293
155
|
*/
|
|
294
|
-
const bitMask = (n) => (
|
|
295
|
-
// DRBG
|
|
296
|
-
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
|
|
297
|
-
const u8fr = (arr) => Uint8Array.from(arr); // another shortcut
|
|
156
|
+
const bitMask = (n) => (_1n$6 << BigInt(n)) - _1n$6;
|
|
298
157
|
/**
|
|
299
158
|
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
300
159
|
* @returns function that will call DRBG until 2nd arg returns something meaningful
|
|
@@ -310,6 +169,8 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
310
169
|
if (typeof hmacFn !== 'function')
|
|
311
170
|
throw new Error('hmacFn must be a function');
|
|
312
171
|
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
172
|
+
const u8n = (len) => new Uint8Array(len); // creates Uint8Array
|
|
173
|
+
const u8of = (byte) => Uint8Array.of(byte); // another shortcut
|
|
313
174
|
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
314
175
|
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
|
315
176
|
let i = 0; // Iterations counter, will throw when over 1000
|
|
@@ -319,13 +180,13 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
319
180
|
i = 0;
|
|
320
181
|
};
|
|
321
182
|
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
|
322
|
-
const reseed = (seed = u8n()) => {
|
|
183
|
+
const reseed = (seed = u8n(0)) => {
|
|
323
184
|
// HMAC-DRBG reseed() function. Steps D-G
|
|
324
|
-
k = h(
|
|
185
|
+
k = h(u8of(0x00), seed); // k = hmac(k || v || 0x00 || seed)
|
|
325
186
|
v = h(); // v = hmac(k || v)
|
|
326
187
|
if (seed.length === 0)
|
|
327
188
|
return;
|
|
328
|
-
k = h(
|
|
189
|
+
k = h(u8of(0x01), seed); // k = hmac(k || v || 0x01 || seed)
|
|
329
190
|
v = h(); // v = hmac(k || v)
|
|
330
191
|
};
|
|
331
192
|
const gen = () => {
|
|
@@ -353,51 +214,20 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
353
214
|
};
|
|
354
215
|
return genUntil;
|
|
355
216
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
function
|
|
360
|
-
boolean: (val) => typeof val === 'boolean',
|
|
361
|
-
string: (val) => typeof val === 'string',
|
|
362
|
-
stringOrUint8Array: (val) => typeof val === 'string' || isBytes(val),
|
|
363
|
-
isSafeInteger: (val) => Number.isSafeInteger(val),
|
|
364
|
-
array: (val) => Array.isArray(val),
|
|
365
|
-
field: (val, object) => object.Fp.isValid(val),
|
|
366
|
-
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
|
|
367
|
-
};
|
|
368
|
-
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
|
|
369
|
-
function validateObject(object, validators, optValidators = {}) {
|
|
370
|
-
const checkField = (fieldName, type, isOptional) => {
|
|
371
|
-
const checkVal = validatorFns[type];
|
|
372
|
-
if (typeof checkVal !== 'function')
|
|
373
|
-
throw new Error('invalid validator function');
|
|
217
|
+
function _validateObject(object, fields, optFields = {}) {
|
|
218
|
+
if (!object || typeof object !== 'object')
|
|
219
|
+
throw new Error('expected valid options object');
|
|
220
|
+
function checkField(fieldName, expectedType, isOpt) {
|
|
374
221
|
const val = object[fieldName];
|
|
375
|
-
if (
|
|
222
|
+
if (isOpt && val === undefined)
|
|
376
223
|
return;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
checkField(fieldName, type, true);
|
|
385
|
-
return object;
|
|
386
|
-
}
|
|
387
|
-
// validate type tests
|
|
388
|
-
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
|
|
389
|
-
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
|
|
390
|
-
// // Should fail type-check
|
|
391
|
-
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
|
|
392
|
-
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
|
393
|
-
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
|
394
|
-
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|
|
395
|
-
/**
|
|
396
|
-
* throws not implemented error
|
|
397
|
-
*/
|
|
398
|
-
const notImplemented = () => {
|
|
399
|
-
throw new Error('not implemented');
|
|
400
|
-
};
|
|
224
|
+
const current = typeof val;
|
|
225
|
+
if (current !== expectedType || val === null)
|
|
226
|
+
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
|
|
227
|
+
}
|
|
228
|
+
Object.entries(fields).forEach(([k, v]) => checkField(k, v, false));
|
|
229
|
+
Object.entries(optFields).forEach(([k, v]) => checkField(k, v, true));
|
|
230
|
+
}
|
|
401
231
|
/**
|
|
402
232
|
* Memoizes (caches) computation result.
|
|
403
233
|
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
|
|
@@ -414,71 +244,25 @@ function memoized(fn) {
|
|
|
414
244
|
};
|
|
415
245
|
}
|
|
416
246
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
bitLen: bitLen,
|
|
424
|
-
bitMask: bitMask,
|
|
425
|
-
bitSet: bitSet,
|
|
426
|
-
bytesToHex: bytesToHex,
|
|
427
|
-
bytesToNumberBE: bytesToNumberBE,
|
|
428
|
-
bytesToNumberLE: bytesToNumberLE,
|
|
429
|
-
concatBytes: concatBytes,
|
|
430
|
-
createHmacDrbg: createHmacDrbg,
|
|
431
|
-
ensureBytes: ensureBytes,
|
|
432
|
-
equalBytes: equalBytes,
|
|
433
|
-
hexToBytes: hexToBytes,
|
|
434
|
-
hexToNumber: hexToNumber,
|
|
435
|
-
inRange: inRange,
|
|
436
|
-
isBytes: isBytes,
|
|
437
|
-
memoized: memoized,
|
|
438
|
-
notImplemented: notImplemented,
|
|
439
|
-
numberToBytesBE: numberToBytesBE,
|
|
440
|
-
numberToBytesLE: numberToBytesLE,
|
|
441
|
-
numberToHexUnpadded: numberToHexUnpadded,
|
|
442
|
-
numberToVarBytesBE: numberToVarBytesBE,
|
|
443
|
-
utf8ToBytes: utf8ToBytes,
|
|
444
|
-
validateObject: validateObject
|
|
445
|
-
});
|
|
446
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Utils for modular division and fields.
|
|
249
|
+
* Field over 11 is a finite (Galois) field is integer number operations `mod 11`.
|
|
250
|
+
* There is no division: it is replaced by modular multiplicative inverse.
|
|
251
|
+
* @module
|
|
252
|
+
*/
|
|
447
253
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
448
|
-
// Utilities for modular arithmetics and finite fields
|
|
449
254
|
// prettier-ignore
|
|
450
|
-
const _0n$4 = BigInt(0), _1n$
|
|
255
|
+
const _0n$4 = BigInt(0), _1n$5 = BigInt(1), _2n$5 = /* @__PURE__ */ BigInt(2), _3n$2 = /* @__PURE__ */ BigInt(3);
|
|
256
|
+
// prettier-ignore
|
|
257
|
+
const _4n$1 = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
|
|
451
258
|
// prettier-ignore
|
|
452
|
-
const
|
|
259
|
+
const _8n$1 = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
|
453
260
|
// Calculates a modulo b
|
|
454
261
|
function mod(a, b) {
|
|
455
262
|
const result = a % b;
|
|
456
263
|
return result >= _0n$4 ? result : b + result;
|
|
457
264
|
}
|
|
458
|
-
/**
|
|
459
|
-
* Efficiently raise num to power and do modular division.
|
|
460
|
-
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
461
|
-
* @example
|
|
462
|
-
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
|
463
|
-
*/
|
|
464
|
-
// TODO: use field version && remove
|
|
465
|
-
function pow(num, power, modulo) {
|
|
466
|
-
if (power < _0n$4)
|
|
467
|
-
throw new Error('invalid exponent, negatives unsupported');
|
|
468
|
-
if (modulo <= _0n$4)
|
|
469
|
-
throw new Error('invalid modulus');
|
|
470
|
-
if (modulo === _1n$6)
|
|
471
|
-
return _0n$4;
|
|
472
|
-
let res = _1n$6;
|
|
473
|
-
while (power > _0n$4) {
|
|
474
|
-
if (power & _1n$6)
|
|
475
|
-
res = (res * num) % modulo;
|
|
476
|
-
num = (num * num) % modulo;
|
|
477
|
-
power >>= _1n$6;
|
|
478
|
-
}
|
|
479
|
-
return res;
|
|
480
|
-
}
|
|
481
|
-
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
|
265
|
+
/** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
|
|
482
266
|
function pow2(x, power, modulo) {
|
|
483
267
|
let res = x;
|
|
484
268
|
while (power-- > _0n$4) {
|
|
@@ -487,18 +271,20 @@ function pow2(x, power, modulo) {
|
|
|
487
271
|
}
|
|
488
272
|
return res;
|
|
489
273
|
}
|
|
490
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Inverses number over modulo.
|
|
276
|
+
* Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
|
|
277
|
+
*/
|
|
491
278
|
function invert(number, modulo) {
|
|
492
279
|
if (number === _0n$4)
|
|
493
280
|
throw new Error('invert: expected non-zero number');
|
|
494
281
|
if (modulo <= _0n$4)
|
|
495
282
|
throw new Error('invert: expected positive modulus, got ' + modulo);
|
|
496
|
-
// Euclidean GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
|
497
283
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
|
498
284
|
let a = mod(number, modulo);
|
|
499
285
|
let b = modulo;
|
|
500
286
|
// prettier-ignore
|
|
501
|
-
let x = _0n$4, u = _1n$
|
|
287
|
+
let x = _0n$4, u = _1n$5;
|
|
502
288
|
while (a !== _0n$4) {
|
|
503
289
|
// JIT applies optimization if those two lines follow each other
|
|
504
290
|
const q = b / a;
|
|
@@ -508,111 +294,152 @@ function invert(number, modulo) {
|
|
|
508
294
|
b = a, a = r, x = u, u = m;
|
|
509
295
|
}
|
|
510
296
|
const gcd = b;
|
|
511
|
-
if (gcd !== _1n$
|
|
297
|
+
if (gcd !== _1n$5)
|
|
512
298
|
throw new Error('invert: does not exist');
|
|
513
299
|
return mod(x, modulo);
|
|
514
300
|
}
|
|
301
|
+
function assertIsSquare(Fp, root, n) {
|
|
302
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
303
|
+
throw new Error('Cannot find square root');
|
|
304
|
+
}
|
|
305
|
+
// Not all roots are possible! Example which will throw:
|
|
306
|
+
// const NUM =
|
|
307
|
+
// n = 72057594037927816n;
|
|
308
|
+
// Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
|
|
309
|
+
function sqrt3mod4(Fp, n) {
|
|
310
|
+
const p1div4 = (Fp.ORDER + _1n$5) / _4n$1;
|
|
311
|
+
const root = Fp.pow(n, p1div4);
|
|
312
|
+
assertIsSquare(Fp, root, n);
|
|
313
|
+
return root;
|
|
314
|
+
}
|
|
315
|
+
function sqrt5mod8(Fp, n) {
|
|
316
|
+
const p5div8 = (Fp.ORDER - _5n) / _8n$1;
|
|
317
|
+
const n2 = Fp.mul(n, _2n$5);
|
|
318
|
+
const v = Fp.pow(n2, p5div8);
|
|
319
|
+
const nv = Fp.mul(n, v);
|
|
320
|
+
const i = Fp.mul(Fp.mul(nv, _2n$5), v);
|
|
321
|
+
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
322
|
+
assertIsSquare(Fp, root, n);
|
|
323
|
+
return root;
|
|
324
|
+
}
|
|
325
|
+
// Based on RFC9380, Kong algorithm
|
|
326
|
+
// prettier-ignore
|
|
327
|
+
function sqrt9mod16(P) {
|
|
328
|
+
const Fp_ = Field(P);
|
|
329
|
+
const tn = tonelliShanks(P);
|
|
330
|
+
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
|
331
|
+
const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
|
332
|
+
const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
|
333
|
+
const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
|
334
|
+
return (Fp, n) => {
|
|
335
|
+
let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
|
|
336
|
+
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
|
|
337
|
+
const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
|
|
338
|
+
const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
|
|
339
|
+
const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
|
|
340
|
+
const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
|
|
341
|
+
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
|
342
|
+
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
|
343
|
+
const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
|
|
344
|
+
const root = Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
|
|
345
|
+
assertIsSquare(Fp, root, n);
|
|
346
|
+
return root;
|
|
347
|
+
};
|
|
348
|
+
}
|
|
515
349
|
/**
|
|
516
350
|
* Tonelli-Shanks square root search algorithm.
|
|
517
351
|
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
|
518
352
|
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
|
519
|
-
* Will start an infinite loop if field order P is not prime.
|
|
520
353
|
* @param P field order
|
|
521
354
|
* @returns function that takes field Fp (created from P) and number n
|
|
522
355
|
*/
|
|
523
356
|
function tonelliShanks(P) {
|
|
524
|
-
//
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
let
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
;
|
|
535
|
-
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
|
536
|
-
for (Z = _2n$3; Z < P && pow(Z, legendreC, P) !== P - _1n$6; Z++) {
|
|
537
|
-
// Crash instead of infinity loop, we cannot reasonable count until P.
|
|
538
|
-
if (Z > 1000)
|
|
539
|
-
throw new Error('Cannot find square root: likely non-prime P');
|
|
540
|
-
}
|
|
541
|
-
// Fast-path
|
|
542
|
-
if (S === 1) {
|
|
543
|
-
const p1div4 = (P + _1n$6) / _4n;
|
|
544
|
-
return function tonelliFast(Fp, n) {
|
|
545
|
-
const root = Fp.pow(n, p1div4);
|
|
546
|
-
if (!Fp.eql(Fp.sqr(root), n))
|
|
547
|
-
throw new Error('Cannot find square root');
|
|
548
|
-
return root;
|
|
549
|
-
};
|
|
357
|
+
// Initialization (precomputation).
|
|
358
|
+
// Caching initialization could boost perf by 7%.
|
|
359
|
+
if (P < _3n$2)
|
|
360
|
+
throw new Error('sqrt is not defined for small field');
|
|
361
|
+
// Factor P - 1 = Q * 2^S, where Q is odd
|
|
362
|
+
let Q = P - _1n$5;
|
|
363
|
+
let S = 0;
|
|
364
|
+
while (Q % _2n$5 === _0n$4) {
|
|
365
|
+
Q /= _2n$5;
|
|
366
|
+
S++;
|
|
550
367
|
}
|
|
368
|
+
// Find the first quadratic non-residue Z >= 2
|
|
369
|
+
let Z = _2n$5;
|
|
370
|
+
const _Fp = Field(P);
|
|
371
|
+
while (FpLegendre(_Fp, Z) === 1) {
|
|
372
|
+
// Basic primality test for P. After x iterations, chance of
|
|
373
|
+
// not finding quadratic non-residue is 2^x, so 2^1000.
|
|
374
|
+
if (Z++ > 1000)
|
|
375
|
+
throw new Error('Cannot find square root: probably non-prime P');
|
|
376
|
+
}
|
|
377
|
+
// Fast-path; usually done before Z, but we do "primality test".
|
|
378
|
+
if (S === 1)
|
|
379
|
+
return sqrt3mod4;
|
|
551
380
|
// Slow-path
|
|
552
|
-
|
|
381
|
+
// TODO: test on Fp2 and others
|
|
382
|
+
let cc = _Fp.pow(Z, Q); // c = z^Q
|
|
383
|
+
const Q1div2 = (Q + _1n$5) / _2n$5;
|
|
553
384
|
return function tonelliSlow(Fp, n) {
|
|
554
|
-
|
|
555
|
-
|
|
385
|
+
if (Fp.is0(n))
|
|
386
|
+
return n;
|
|
387
|
+
// Check if n is a quadratic residue using Legendre symbol
|
|
388
|
+
if (FpLegendre(Fp, n) !== 1)
|
|
556
389
|
throw new Error('Cannot find square root');
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
let
|
|
560
|
-
let
|
|
561
|
-
let
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
390
|
+
// Initialize variables for the main loop
|
|
391
|
+
let M = S;
|
|
392
|
+
let c = Fp.mul(Fp.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
|
|
393
|
+
let t = Fp.pow(n, Q); // t = n^Q, first guess at the fudge factor
|
|
394
|
+
let R = Fp.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
|
|
395
|
+
// Main loop
|
|
396
|
+
// while t != 1
|
|
397
|
+
while (!Fp.eql(t, Fp.ONE)) {
|
|
398
|
+
if (Fp.is0(t))
|
|
399
|
+
return Fp.ZERO; // if t=0 return R=0
|
|
400
|
+
let i = 1;
|
|
401
|
+
// Find the smallest i >= 1 such that t^(2^i) ≡ 1 (mod P)
|
|
402
|
+
let t_tmp = Fp.sqr(t); // t^(2^1)
|
|
403
|
+
while (!Fp.eql(t_tmp, Fp.ONE)) {
|
|
404
|
+
i++;
|
|
405
|
+
t_tmp = Fp.sqr(t_tmp); // t^(2^2)...
|
|
406
|
+
if (i === M)
|
|
407
|
+
throw new Error('Cannot find square root');
|
|
571
408
|
}
|
|
572
|
-
//
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
409
|
+
// Calculate the exponent for b: 2^(M - i - 1)
|
|
410
|
+
const exponent = _1n$5 << BigInt(M - i - 1); // bigint is important
|
|
411
|
+
const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
|
|
412
|
+
// Update variables
|
|
413
|
+
M = i;
|
|
414
|
+
c = Fp.sqr(b); // c = b^2
|
|
415
|
+
t = Fp.mul(t, c); // t = (t * b^2)
|
|
416
|
+
R = Fp.mul(R, b); // R = R*b
|
|
417
|
+
}
|
|
418
|
+
return R;
|
|
580
419
|
};
|
|
581
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Square root for a finite field. Will try optimized versions first:
|
|
423
|
+
*
|
|
424
|
+
* 1. P ≡ 3 (mod 4)
|
|
425
|
+
* 2. P ≡ 5 (mod 8)
|
|
426
|
+
* 3. P ≡ 9 (mod 16)
|
|
427
|
+
* 4. Tonelli-Shanks algorithm
|
|
428
|
+
*
|
|
429
|
+
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
|
430
|
+
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
|
431
|
+
*/
|
|
582
432
|
function FpSqrt(P) {
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
//
|
|
587
|
-
if (P %
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
return function sqrt3mod4(Fp, n) {
|
|
594
|
-
const root = Fp.pow(n, p1div4);
|
|
595
|
-
// Throw if root**2 != n
|
|
596
|
-
if (!Fp.eql(Fp.sqr(root), n))
|
|
597
|
-
throw new Error('Cannot find square root');
|
|
598
|
-
return root;
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
|
602
|
-
if (P % _8n$1 === _5n) {
|
|
603
|
-
const c1 = (P - _5n) / _8n$1;
|
|
604
|
-
return function sqrt5mod8(Fp, n) {
|
|
605
|
-
const n2 = Fp.mul(n, _2n$3);
|
|
606
|
-
const v = Fp.pow(n2, c1);
|
|
607
|
-
const nv = Fp.mul(n, v);
|
|
608
|
-
const i = Fp.mul(Fp.mul(nv, _2n$3), v);
|
|
609
|
-
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
610
|
-
if (!Fp.eql(Fp.sqr(root), n))
|
|
611
|
-
throw new Error('Cannot find square root');
|
|
612
|
-
return root;
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
// Other cases: Tonelli-Shanks algorithm
|
|
433
|
+
// P ≡ 3 (mod 4) => √n = n^((P+1)/4)
|
|
434
|
+
if (P % _4n$1 === _3n$2)
|
|
435
|
+
return sqrt3mod4;
|
|
436
|
+
// P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
|
|
437
|
+
if (P % _8n$1 === _5n)
|
|
438
|
+
return sqrt5mod8;
|
|
439
|
+
// P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
|
|
440
|
+
if (P % _16n === _9n)
|
|
441
|
+
return sqrt9mod16(P);
|
|
442
|
+
// Tonelli-Shanks algorithm
|
|
616
443
|
return tonelliShanks(P);
|
|
617
444
|
}
|
|
618
445
|
// prettier-ignore
|
|
@@ -625,99 +452,156 @@ function validateField(field) {
|
|
|
625
452
|
const initial = {
|
|
626
453
|
ORDER: 'bigint',
|
|
627
454
|
MASK: 'bigint',
|
|
628
|
-
BYTES: '
|
|
629
|
-
BITS: '
|
|
455
|
+
BYTES: 'number',
|
|
456
|
+
BITS: 'number',
|
|
630
457
|
};
|
|
631
458
|
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|
632
459
|
map[val] = 'function';
|
|
633
460
|
return map;
|
|
634
461
|
}, initial);
|
|
635
|
-
|
|
462
|
+
_validateObject(field, opts);
|
|
463
|
+
// const max = 16384;
|
|
464
|
+
// if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
|
|
465
|
+
// if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
|
|
466
|
+
return field;
|
|
636
467
|
}
|
|
637
468
|
// Generic field functions
|
|
638
469
|
/**
|
|
639
470
|
* Same as `pow` but for Fp: non-constant-time.
|
|
640
471
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
641
472
|
*/
|
|
642
|
-
function FpPow(
|
|
643
|
-
// Should have same speed as pow for bigints
|
|
644
|
-
// TODO: benchmark!
|
|
473
|
+
function FpPow(Fp, num, power) {
|
|
645
474
|
if (power < _0n$4)
|
|
646
475
|
throw new Error('invalid exponent, negatives unsupported');
|
|
647
476
|
if (power === _0n$4)
|
|
648
|
-
return
|
|
649
|
-
if (power === _1n$
|
|
477
|
+
return Fp.ONE;
|
|
478
|
+
if (power === _1n$5)
|
|
650
479
|
return num;
|
|
651
|
-
let p =
|
|
480
|
+
let p = Fp.ONE;
|
|
652
481
|
let d = num;
|
|
653
482
|
while (power > _0n$4) {
|
|
654
|
-
if (power & _1n$
|
|
655
|
-
p =
|
|
656
|
-
d =
|
|
657
|
-
power >>= _1n$
|
|
483
|
+
if (power & _1n$5)
|
|
484
|
+
p = Fp.mul(p, d);
|
|
485
|
+
d = Fp.sqr(d);
|
|
486
|
+
power >>= _1n$5;
|
|
658
487
|
}
|
|
659
488
|
return p;
|
|
660
489
|
}
|
|
661
490
|
/**
|
|
662
491
|
* Efficiently invert an array of Field elements.
|
|
663
|
-
*
|
|
492
|
+
* Exception-free. Will return `undefined` for 0 elements.
|
|
493
|
+
* @param passZero map 0 to 0 (instead of undefined)
|
|
664
494
|
*/
|
|
665
|
-
function FpInvertBatch(
|
|
666
|
-
const
|
|
495
|
+
function FpInvertBatch(Fp, nums, passZero = false) {
|
|
496
|
+
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
|
|
667
497
|
// Walk from first to last, multiply them by each other MOD p
|
|
668
|
-
const
|
|
669
|
-
if (
|
|
498
|
+
const multipliedAcc = nums.reduce((acc, num, i) => {
|
|
499
|
+
if (Fp.is0(num))
|
|
670
500
|
return acc;
|
|
671
|
-
|
|
672
|
-
return
|
|
673
|
-
},
|
|
501
|
+
inverted[i] = acc;
|
|
502
|
+
return Fp.mul(acc, num);
|
|
503
|
+
}, Fp.ONE);
|
|
674
504
|
// Invert last element
|
|
675
|
-
const
|
|
505
|
+
const invertedAcc = Fp.inv(multipliedAcc);
|
|
676
506
|
// Walk from last to first, multiply them by inverted each other MOD p
|
|
677
507
|
nums.reduceRight((acc, num, i) => {
|
|
678
|
-
if (
|
|
508
|
+
if (Fp.is0(num))
|
|
679
509
|
return acc;
|
|
680
|
-
|
|
681
|
-
return
|
|
682
|
-
},
|
|
683
|
-
return
|
|
510
|
+
inverted[i] = Fp.mul(acc, inverted[i]);
|
|
511
|
+
return Fp.mul(acc, num);
|
|
512
|
+
}, invertedAcc);
|
|
513
|
+
return inverted;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Legendre symbol.
|
|
517
|
+
* Legendre constant is used to calculate Legendre symbol (a | p)
|
|
518
|
+
* which denotes the value of a^((p-1)/2) (mod p).
|
|
519
|
+
*
|
|
520
|
+
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
|
|
521
|
+
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
|
|
522
|
+
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
|
523
|
+
*/
|
|
524
|
+
function FpLegendre(Fp, n) {
|
|
525
|
+
// We can use 3rd argument as optional cache of this value
|
|
526
|
+
// but seems unneeded for now. The operation is very fast.
|
|
527
|
+
const p1mod2 = (Fp.ORDER - _1n$5) / _2n$5;
|
|
528
|
+
const powered = Fp.pow(n, p1mod2);
|
|
529
|
+
const yes = Fp.eql(powered, Fp.ONE);
|
|
530
|
+
const zero = Fp.eql(powered, Fp.ZERO);
|
|
531
|
+
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
|
|
532
|
+
if (!yes && !zero && !no)
|
|
533
|
+
throw new Error('invalid Legendre symbol result');
|
|
534
|
+
return yes ? 1 : zero ? 0 : -1;
|
|
684
535
|
}
|
|
685
536
|
// CURVE.n lengths
|
|
686
537
|
function nLength(n, nBitLength) {
|
|
687
538
|
// Bit size, byte size of CURVE.n
|
|
539
|
+
if (nBitLength !== undefined)
|
|
540
|
+
anumber(nBitLength);
|
|
688
541
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
689
542
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
690
543
|
return { nBitLength: _nBitLength, nByteLength };
|
|
691
544
|
}
|
|
692
545
|
/**
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
* *
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
* NOTE: operations don't check 'isValid' for all elements for performance reasons,
|
|
546
|
+
* Creates a finite field. Major performance optimizations:
|
|
547
|
+
* * 1. Denormalized operations like mulN instead of mul.
|
|
548
|
+
* * 2. Identical object shape: never add or remove keys.
|
|
549
|
+
* * 3. `Object.freeze`.
|
|
550
|
+
* Fragile: always run a benchmark on a change.
|
|
551
|
+
* Security note: operations don't check 'isValid' for all elements for performance reasons,
|
|
700
552
|
* it is caller responsibility to check this.
|
|
701
|
-
* This is low-level code, please make sure you know what you doing.
|
|
702
|
-
*
|
|
553
|
+
* This is low-level code, please make sure you know what you're doing.
|
|
554
|
+
*
|
|
555
|
+
* Note about field properties:
|
|
556
|
+
* * CHARACTERISTIC p = prime number, number of elements in main subgroup.
|
|
557
|
+
* * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
|
|
558
|
+
*
|
|
559
|
+
* @param ORDER field order, probably prime, or could be composite
|
|
703
560
|
* @param bitLen how many bits the field consumes
|
|
704
|
-
* @param isLE (
|
|
561
|
+
* @param isLE (default: false) if encoding / decoding should be in little-endian
|
|
705
562
|
* @param redef optional faster redefinitions of sqrt and other methods
|
|
706
563
|
*/
|
|
707
|
-
function Field(ORDER,
|
|
564
|
+
function Field(ORDER, bitLenOrOpts, // TODO: use opts only in v2?
|
|
565
|
+
isLE = false, opts = {}) {
|
|
708
566
|
if (ORDER <= _0n$4)
|
|
709
567
|
throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
710
|
-
|
|
568
|
+
let _nbitLength = undefined;
|
|
569
|
+
let _sqrt = undefined;
|
|
570
|
+
let modFromBytes = false;
|
|
571
|
+
let allowedLengths = undefined;
|
|
572
|
+
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
|
|
573
|
+
if (opts.sqrt || isLE)
|
|
574
|
+
throw new Error('cannot specify opts in two arguments');
|
|
575
|
+
const _opts = bitLenOrOpts;
|
|
576
|
+
if (_opts.BITS)
|
|
577
|
+
_nbitLength = _opts.BITS;
|
|
578
|
+
if (_opts.sqrt)
|
|
579
|
+
_sqrt = _opts.sqrt;
|
|
580
|
+
if (typeof _opts.isLE === 'boolean')
|
|
581
|
+
isLE = _opts.isLE;
|
|
582
|
+
if (typeof _opts.modFromBytes === 'boolean')
|
|
583
|
+
modFromBytes = _opts.modFromBytes;
|
|
584
|
+
allowedLengths = _opts.allowedLengths;
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
if (typeof bitLenOrOpts === 'number')
|
|
588
|
+
_nbitLength = bitLenOrOpts;
|
|
589
|
+
if (opts.sqrt)
|
|
590
|
+
_sqrt = opts.sqrt;
|
|
591
|
+
}
|
|
592
|
+
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
|
|
711
593
|
if (BYTES > 2048)
|
|
712
594
|
throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
713
595
|
let sqrtP; // cached sqrtP
|
|
714
596
|
const f = Object.freeze({
|
|
715
597
|
ORDER,
|
|
598
|
+
isLE,
|
|
716
599
|
BITS,
|
|
717
600
|
BYTES,
|
|
718
601
|
MASK: bitMask(BITS),
|
|
719
602
|
ZERO: _0n$4,
|
|
720
|
-
ONE: _1n$
|
|
603
|
+
ONE: _1n$5,
|
|
604
|
+
allowedLengths: allowedLengths,
|
|
721
605
|
create: (num) => mod(num, ORDER),
|
|
722
606
|
isValid: (num) => {
|
|
723
607
|
if (typeof num !== 'bigint')
|
|
@@ -725,7 +609,9 @@ function Field(ORDER, bitLen, isLE = false, redef = {}) {
|
|
|
725
609
|
return _0n$4 <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
726
610
|
},
|
|
727
611
|
is0: (num) => num === _0n$4,
|
|
728
|
-
|
|
612
|
+
// is valid and invertible
|
|
613
|
+
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
|
|
614
|
+
isOdd: (num) => (num & _1n$5) === _1n$5,
|
|
729
615
|
neg: (num) => mod(-num, ORDER),
|
|
730
616
|
eql: (lhs, rhs) => lhs === rhs,
|
|
731
617
|
sqr: (num) => mod(num * num, ORDER),
|
|
@@ -740,22 +626,40 @@ function Field(ORDER, bitLen, isLE = false, redef = {}) {
|
|
|
740
626
|
subN: (lhs, rhs) => lhs - rhs,
|
|
741
627
|
mulN: (lhs, rhs) => lhs * rhs,
|
|
742
628
|
inv: (num) => invert(num, ORDER),
|
|
743
|
-
sqrt:
|
|
629
|
+
sqrt: _sqrt ||
|
|
744
630
|
((n) => {
|
|
745
631
|
if (!sqrtP)
|
|
746
632
|
sqrtP = FpSqrt(ORDER);
|
|
747
633
|
return sqrtP(f, n);
|
|
748
634
|
}),
|
|
749
|
-
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
750
|
-
// TODO: do we really need constant cmov?
|
|
751
|
-
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
752
|
-
cmov: (a, b, c) => (c ? b : a),
|
|
753
635
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
754
|
-
fromBytes: (bytes) => {
|
|
636
|
+
fromBytes: (bytes, skipValidation = true) => {
|
|
637
|
+
if (allowedLengths) {
|
|
638
|
+
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
639
|
+
throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
|
|
640
|
+
}
|
|
641
|
+
const padded = new Uint8Array(BYTES);
|
|
642
|
+
// isLE add 0 to right, !isLE to the left.
|
|
643
|
+
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
644
|
+
bytes = padded;
|
|
645
|
+
}
|
|
755
646
|
if (bytes.length !== BYTES)
|
|
756
647
|
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
757
|
-
|
|
648
|
+
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
649
|
+
if (modFromBytes)
|
|
650
|
+
scalar = mod(scalar, ORDER);
|
|
651
|
+
if (!skipValidation)
|
|
652
|
+
if (!f.isValid(scalar))
|
|
653
|
+
throw new Error('invalid field element: outside of range 0..ORDER');
|
|
654
|
+
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
|
655
|
+
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
|
656
|
+
return scalar;
|
|
758
657
|
},
|
|
658
|
+
// TODO: we don't need it here, move out to separate fn
|
|
659
|
+
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
660
|
+
// We can't move this out because Fp6, Fp12 implement it
|
|
661
|
+
// and it's unclear what to return in there.
|
|
662
|
+
cmov: (a, b, c) => (c ? b : a),
|
|
759
663
|
});
|
|
760
664
|
return Object.freeze(f);
|
|
761
665
|
}
|
|
@@ -802,29 +706,153 @@ function mapHashToField(key, fieldOrder, isLE = false) {
|
|
|
802
706
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
|
803
707
|
if (len < 16 || len < minLen || len > 1024)
|
|
804
708
|
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
|
|
805
|
-
const num = isLE ?
|
|
709
|
+
const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
|
|
806
710
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
807
|
-
const reduced = mod(num, fieldOrder - _1n$
|
|
711
|
+
const reduced = mod(num, fieldOrder - _1n$5) + _1n$5;
|
|
808
712
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|
809
713
|
}
|
|
810
714
|
|
|
715
|
+
/**
|
|
716
|
+
* HMAC: RFC2104 message authentication code.
|
|
717
|
+
* @module
|
|
718
|
+
*/
|
|
719
|
+
class HMAC extends Hash {
|
|
720
|
+
constructor(hash, _key) {
|
|
721
|
+
super();
|
|
722
|
+
this.finished = false;
|
|
723
|
+
this.destroyed = false;
|
|
724
|
+
ahash(hash);
|
|
725
|
+
const key = toBytes(_key);
|
|
726
|
+
this.iHash = hash.create();
|
|
727
|
+
if (typeof this.iHash.update !== 'function')
|
|
728
|
+
throw new Error('Expected instance of class which extends utils.Hash');
|
|
729
|
+
this.blockLen = this.iHash.blockLen;
|
|
730
|
+
this.outputLen = this.iHash.outputLen;
|
|
731
|
+
const blockLen = this.blockLen;
|
|
732
|
+
const pad = new Uint8Array(blockLen);
|
|
733
|
+
// blockLen can be bigger than outputLen
|
|
734
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
735
|
+
for (let i = 0; i < pad.length; i++)
|
|
736
|
+
pad[i] ^= 0x36;
|
|
737
|
+
this.iHash.update(pad);
|
|
738
|
+
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
|
739
|
+
this.oHash = hash.create();
|
|
740
|
+
// Undo internal XOR && apply outer XOR
|
|
741
|
+
for (let i = 0; i < pad.length; i++)
|
|
742
|
+
pad[i] ^= 0x36 ^ 0x5c;
|
|
743
|
+
this.oHash.update(pad);
|
|
744
|
+
clean(pad);
|
|
745
|
+
}
|
|
746
|
+
update(buf) {
|
|
747
|
+
aexists(this);
|
|
748
|
+
this.iHash.update(buf);
|
|
749
|
+
return this;
|
|
750
|
+
}
|
|
751
|
+
digestInto(out) {
|
|
752
|
+
aexists(this);
|
|
753
|
+
abytes(out, this.outputLen);
|
|
754
|
+
this.finished = true;
|
|
755
|
+
this.iHash.digestInto(out);
|
|
756
|
+
this.oHash.update(out);
|
|
757
|
+
this.oHash.digestInto(out);
|
|
758
|
+
this.destroy();
|
|
759
|
+
}
|
|
760
|
+
digest() {
|
|
761
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
762
|
+
this.digestInto(out);
|
|
763
|
+
return out;
|
|
764
|
+
}
|
|
765
|
+
_cloneInto(to) {
|
|
766
|
+
// Create new instance without calling constructor since key already in state and we don't know it.
|
|
767
|
+
to || (to = Object.create(Object.getPrototypeOf(this), {}));
|
|
768
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
769
|
+
to = to;
|
|
770
|
+
to.finished = finished;
|
|
771
|
+
to.destroyed = destroyed;
|
|
772
|
+
to.blockLen = blockLen;
|
|
773
|
+
to.outputLen = outputLen;
|
|
774
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
775
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
776
|
+
return to;
|
|
777
|
+
}
|
|
778
|
+
clone() {
|
|
779
|
+
return this._cloneInto();
|
|
780
|
+
}
|
|
781
|
+
destroy() {
|
|
782
|
+
this.destroyed = true;
|
|
783
|
+
this.oHash.destroy();
|
|
784
|
+
this.iHash.destroy();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* HMAC: RFC2104 message authentication code.
|
|
789
|
+
* @param hash - function that would be used e.g. sha256
|
|
790
|
+
* @param key - message key
|
|
791
|
+
* @param message - message data
|
|
792
|
+
* @example
|
|
793
|
+
* import { hmac } from '@noble/hashes/hmac';
|
|
794
|
+
* import { sha256 } from '@noble/hashes/sha2';
|
|
795
|
+
* const mac1 = hmac(sha256, 'key', 'message');
|
|
796
|
+
*/
|
|
797
|
+
const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
798
|
+
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Methods for elliptic curve multiplication by scalars.
|
|
802
|
+
* Contains wNAF, pippenger.
|
|
803
|
+
* @module
|
|
804
|
+
*/
|
|
811
805
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
812
|
-
// Abelian group utilities
|
|
813
806
|
const _0n$3 = BigInt(0);
|
|
814
|
-
const _1n$
|
|
815
|
-
function
|
|
807
|
+
const _1n$4 = BigInt(1);
|
|
808
|
+
function negateCt(condition, item) {
|
|
816
809
|
const neg = item.negate();
|
|
817
810
|
return condition ? neg : item;
|
|
818
811
|
}
|
|
812
|
+
/**
|
|
813
|
+
* Takes a bunch of Projective Points but executes only one
|
|
814
|
+
* inversion on all of them. Inversion is very slow operation,
|
|
815
|
+
* so this improves performance massively.
|
|
816
|
+
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
817
|
+
*/
|
|
818
|
+
function normalizeZ(c, points) {
|
|
819
|
+
const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
|
|
820
|
+
return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));
|
|
821
|
+
}
|
|
819
822
|
function validateW(W, bits) {
|
|
820
823
|
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
|
821
824
|
throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W);
|
|
822
825
|
}
|
|
823
|
-
function calcWOpts(W,
|
|
824
|
-
validateW(W,
|
|
825
|
-
const windows = Math.ceil(
|
|
826
|
-
const windowSize = 2 ** (W - 1); //
|
|
827
|
-
|
|
826
|
+
function calcWOpts(W, scalarBits) {
|
|
827
|
+
validateW(W, scalarBits);
|
|
828
|
+
const windows = Math.ceil(scalarBits / W) + 1; // W=8 33. Not 32, because we skip zero
|
|
829
|
+
const windowSize = 2 ** (W - 1); // W=8 128. Not 256, because we skip zero
|
|
830
|
+
const maxNumber = 2 ** W; // W=8 256
|
|
831
|
+
const mask = bitMask(W); // W=8 255 == mask 0b11111111
|
|
832
|
+
const shiftBy = BigInt(W); // W=8 8
|
|
833
|
+
return { windows, windowSize, mask, maxNumber, shiftBy };
|
|
834
|
+
}
|
|
835
|
+
function calcOffsets(n, window, wOpts) {
|
|
836
|
+
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
|
|
837
|
+
let wbits = Number(n & mask); // extract W bits.
|
|
838
|
+
let nextN = n >> shiftBy; // shift number by W bits.
|
|
839
|
+
// What actually happens here:
|
|
840
|
+
// const highestBit = Number(mask ^ (mask >> 1n));
|
|
841
|
+
// let wbits2 = wbits - 1; // skip zero
|
|
842
|
+
// if (wbits2 & highestBit) { wbits2 ^= Number(mask); // (~);
|
|
843
|
+
// split if bits > max: +224 => 256-32
|
|
844
|
+
if (wbits > windowSize) {
|
|
845
|
+
// we skip zero, which means instead of `>= size-1`, we do `> size`
|
|
846
|
+
wbits -= maxNumber; // -32, can be maxNumber - wbits, but then we need to set isNeg here.
|
|
847
|
+
nextN += _1n$4; // +256 (carry)
|
|
848
|
+
}
|
|
849
|
+
const offsetStart = window * windowSize;
|
|
850
|
+
const offset = offsetStart + Math.abs(wbits) - 1; // -1 because we skip zero
|
|
851
|
+
const isZero = wbits === 0; // is current window slice a 0?
|
|
852
|
+
const isNeg = wbits < 0; // is current window slice negative?
|
|
853
|
+
const isNegF = window % 2 !== 0; // fake random statement for noise
|
|
854
|
+
const offsetF = offsetStart; // fake offset for noise
|
|
855
|
+
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
|
|
828
856
|
}
|
|
829
857
|
function validateMSMPoints(points, c) {
|
|
830
858
|
if (!Array.isArray(points))
|
|
@@ -843,199 +871,213 @@ function validateMSMScalars(scalars, field) {
|
|
|
843
871
|
});
|
|
844
872
|
}
|
|
845
873
|
// Since points in different groups cannot be equal (different object constructor),
|
|
846
|
-
// we can have single place to store precomputes
|
|
874
|
+
// we can have single place to store precomputes.
|
|
875
|
+
// Allows to make points frozen / immutable.
|
|
847
876
|
const pointPrecomputes = new WeakMap();
|
|
848
|
-
const pointWindowSizes = new WeakMap();
|
|
877
|
+
const pointWindowSizes = new WeakMap();
|
|
849
878
|
function getW(P) {
|
|
879
|
+
// To disable precomputes:
|
|
880
|
+
// return 1;
|
|
850
881
|
return pointWindowSizes.get(P) || 1;
|
|
851
882
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
883
|
+
function assert0(n) {
|
|
884
|
+
if (n !== _0n$3)
|
|
885
|
+
throw new Error('invalid wNAF');
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Elliptic curve multiplication of Point by scalar. Fragile.
|
|
889
|
+
* Table generation takes **30MB of ram and 10ms on high-end CPU**,
|
|
890
|
+
* but may take much longer on slow devices. Actual generation will happen on
|
|
891
|
+
* first call of `multiply()`. By default, `BASE` point is precomputed.
|
|
892
|
+
*
|
|
893
|
+
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
|
894
|
+
* Creates precomputation tables for fast multiplication:
|
|
895
|
+
* - private scalar is split by fixed size windows of W bits
|
|
896
|
+
* - every window point is collected from window's table & added to accumulator
|
|
897
|
+
* - since windows are different, same point inside tables won't be accessed more than once per calc
|
|
898
|
+
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
|
899
|
+
* - +1 window is neccessary for wNAF
|
|
900
|
+
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
|
901
|
+
*
|
|
902
|
+
* @todo Research returning 2d JS array of windows, instead of a single window.
|
|
903
|
+
* This would allow windows to be in different memory locations
|
|
904
|
+
*/
|
|
905
|
+
class wNAF {
|
|
906
|
+
// Parametrized with a given Point class (not individual point)
|
|
907
|
+
constructor(Point, bits) {
|
|
908
|
+
this.BASE = Point.BASE;
|
|
909
|
+
this.ZERO = Point.ZERO;
|
|
910
|
+
this.Fn = Point.Fn;
|
|
911
|
+
this.bits = bits;
|
|
912
|
+
}
|
|
913
|
+
// non-const time multiplication ladder
|
|
914
|
+
_unsafeLadder(elm, n, p = this.ZERO) {
|
|
915
|
+
let d = elm;
|
|
916
|
+
while (n > _0n$3) {
|
|
917
|
+
if (n & _1n$4)
|
|
918
|
+
p = p.add(d);
|
|
919
|
+
d = d.double();
|
|
920
|
+
n >>= _1n$4;
|
|
921
|
+
}
|
|
922
|
+
return p;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Creates a wNAF precomputation window. Used for caching.
|
|
926
|
+
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
927
|
+
* Number of precomputed points depends on the curve size:
|
|
928
|
+
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
|
929
|
+
* - 𝑊 is the window size
|
|
930
|
+
* - 𝑛 is the bitlength of the curve order.
|
|
931
|
+
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
|
932
|
+
* @param point Point instance
|
|
933
|
+
* @param W window size
|
|
934
|
+
* @returns precomputed point tables flattened to a single array
|
|
935
|
+
*/
|
|
936
|
+
precomputeWindow(point, W) {
|
|
937
|
+
const { windows, windowSize } = calcWOpts(W, this.bits);
|
|
938
|
+
const points = [];
|
|
939
|
+
let p = point;
|
|
940
|
+
let base = p;
|
|
941
|
+
for (let window = 0; window < windows; window++) {
|
|
942
|
+
base = p;
|
|
943
|
+
points.push(base);
|
|
944
|
+
// i=1, bc we skip 0
|
|
945
|
+
for (let i = 1; i < windowSize; i++) {
|
|
946
|
+
base = base.add(p);
|
|
899
947
|
points.push(base);
|
|
900
|
-
// =1, because we skip zero
|
|
901
|
-
for (let i = 1; i < windowSize; i++) {
|
|
902
|
-
base = base.add(p);
|
|
903
|
-
points.push(base);
|
|
904
|
-
}
|
|
905
|
-
p = base.double();
|
|
906
948
|
}
|
|
907
|
-
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
|
939
|
-
// there is negate now: it is possible that negated element from low value
|
|
940
|
-
// would be the same as high element, which will create carry into next window.
|
|
941
|
-
// It's not obvious how this can fail, but still worth investigating later.
|
|
942
|
-
// Check if we're onto Zero point.
|
|
943
|
-
// Add random point inside current window to f.
|
|
944
|
-
const offset1 = offset;
|
|
945
|
-
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
|
|
946
|
-
const cond1 = window % 2 !== 0;
|
|
947
|
-
const cond2 = wbits < 0;
|
|
948
|
-
if (wbits === 0) {
|
|
949
|
-
// The most important part for const-time getPublicKey
|
|
950
|
-
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
|
|
954
|
-
}
|
|
949
|
+
p = base.double();
|
|
950
|
+
}
|
|
951
|
+
return points;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
|
955
|
+
* More compact implementation:
|
|
956
|
+
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
|
957
|
+
* @returns real and fake (for const-time) points
|
|
958
|
+
*/
|
|
959
|
+
wNAF(W, precomputes, n) {
|
|
960
|
+
// Scalar should be smaller than field order
|
|
961
|
+
if (!this.Fn.isValid(n))
|
|
962
|
+
throw new Error('invalid scalar');
|
|
963
|
+
// Accumulators
|
|
964
|
+
let p = this.ZERO;
|
|
965
|
+
let f = this.BASE;
|
|
966
|
+
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
|
967
|
+
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
|
968
|
+
// there is negate now: it is possible that negated element from low value
|
|
969
|
+
// would be the same as high element, which will create carry into next window.
|
|
970
|
+
// It's not obvious how this can fail, but still worth investigating later.
|
|
971
|
+
const wo = calcWOpts(W, this.bits);
|
|
972
|
+
for (let window = 0; window < wo.windows; window++) {
|
|
973
|
+
// (n === _0n) is handled and not early-exited. isEven and offsetF are used for noise
|
|
974
|
+
const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window, wo);
|
|
975
|
+
n = nextN;
|
|
976
|
+
if (isZero) {
|
|
977
|
+
// bits are 0: add garbage to fake point
|
|
978
|
+
// Important part for const-time getPublicKey: add random "noise" point to f.
|
|
979
|
+
f = f.add(negateCt(isNegF, precomputes[offsetF]));
|
|
955
980
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
// At this point there is a way to F be infinity-point even if p is not,
|
|
960
|
-
// which makes it less const-time: around 1 bigint multiply.
|
|
961
|
-
return { p, f };
|
|
962
|
-
},
|
|
963
|
-
/**
|
|
964
|
-
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
|
965
|
-
* @param W window size
|
|
966
|
-
* @param precomputes precomputed tables
|
|
967
|
-
* @param n scalar (we don't check here, but should be less than curve order)
|
|
968
|
-
* @param acc accumulator point to add result of multiplication
|
|
969
|
-
* @returns point
|
|
970
|
-
*/
|
|
971
|
-
wNAFUnsafe(W, precomputes, n, acc = c.ZERO) {
|
|
972
|
-
const { windows, windowSize } = calcWOpts(W, bits);
|
|
973
|
-
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
|
|
974
|
-
const maxNumber = 2 ** W;
|
|
975
|
-
const shiftBy = BigInt(W);
|
|
976
|
-
for (let window = 0; window < windows; window++) {
|
|
977
|
-
const offset = window * windowSize;
|
|
978
|
-
if (n === _0n$3)
|
|
979
|
-
break; // No need to go over empty scalar
|
|
980
|
-
// Extract W bits.
|
|
981
|
-
let wbits = Number(n & mask);
|
|
982
|
-
// Shift number by W bits.
|
|
983
|
-
n >>= shiftBy;
|
|
984
|
-
// If the bits are bigger than max size, we'll split those.
|
|
985
|
-
// +224 => 256 - 32
|
|
986
|
-
if (wbits > windowSize) {
|
|
987
|
-
wbits -= maxNumber;
|
|
988
|
-
n += _1n$5;
|
|
989
|
-
}
|
|
990
|
-
if (wbits === 0)
|
|
991
|
-
continue;
|
|
992
|
-
let curr = precomputes[offset + Math.abs(wbits) - 1]; // -1 because we skip zero
|
|
993
|
-
if (wbits < 0)
|
|
994
|
-
curr = curr.negate();
|
|
995
|
-
// NOTE: by re-using acc, we can save a lot of additions in case of MSM
|
|
996
|
-
acc = acc.add(curr);
|
|
981
|
+
else {
|
|
982
|
+
// bits are 1: add to result point
|
|
983
|
+
p = p.add(negateCt(isNeg, precomputes[offset]));
|
|
997
984
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
985
|
+
}
|
|
986
|
+
assert0(n);
|
|
987
|
+
// Return both real and fake points: JIT won't eliminate f.
|
|
988
|
+
// At this point there is a way to F be infinity-point even if p is not,
|
|
989
|
+
// which makes it less const-time: around 1 bigint multiply.
|
|
990
|
+
return { p, f };
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
|
994
|
+
* @param acc accumulator point to add result of multiplication
|
|
995
|
+
* @returns point
|
|
996
|
+
*/
|
|
997
|
+
wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
|
|
998
|
+
const wo = calcWOpts(W, this.bits);
|
|
999
|
+
for (let window = 0; window < wo.windows; window++) {
|
|
1000
|
+
if (n === _0n$3)
|
|
1001
|
+
break; // Early-exit, skip 0 value
|
|
1002
|
+
const { nextN, offset, isZero, isNeg } = calcOffsets(n, window, wo);
|
|
1003
|
+
n = nextN;
|
|
1004
|
+
if (isZero) {
|
|
1005
|
+
// Window bits are 0: skip processing.
|
|
1006
|
+
// Move to next window.
|
|
1007
|
+
continue;
|
|
1007
1008
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1009
|
+
else {
|
|
1010
|
+
const item = precomputes[offset];
|
|
1011
|
+
acc = acc.add(isNeg ? item.negate() : item); // Re-using acc allows to save adds in MSM
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
assert0(n);
|
|
1015
|
+
return acc;
|
|
1016
|
+
}
|
|
1017
|
+
getPrecomputes(W, point, transform) {
|
|
1018
|
+
// Calculate precomputes on a first run, reuse them after
|
|
1019
|
+
let comp = pointPrecomputes.get(point);
|
|
1020
|
+
if (!comp) {
|
|
1021
|
+
comp = this.precomputeWindow(point, W);
|
|
1022
|
+
if (W !== 1) {
|
|
1023
|
+
// Doing transform outside of if brings 15% perf hit
|
|
1024
|
+
if (typeof transform === 'function')
|
|
1025
|
+
comp = transform(comp);
|
|
1026
|
+
pointPrecomputes.set(point, comp);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return comp;
|
|
1030
|
+
}
|
|
1031
|
+
cached(point, scalar, transform) {
|
|
1032
|
+
const W = getW(point);
|
|
1033
|
+
return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
|
|
1034
|
+
}
|
|
1035
|
+
unsafe(point, scalar, transform, prev) {
|
|
1036
|
+
const W = getW(point);
|
|
1037
|
+
if (W === 1)
|
|
1038
|
+
return this._unsafeLadder(point, scalar, prev); // For W=1 ladder is ~x2 faster
|
|
1039
|
+
return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
|
|
1040
|
+
}
|
|
1041
|
+
// We calculate precomputes for elliptic curve point multiplication
|
|
1042
|
+
// using windowed method. This specifies window size and
|
|
1043
|
+
// stores precomputed values. Usually only base point would be precomputed.
|
|
1044
|
+
createCache(P, W) {
|
|
1045
|
+
validateW(W, this.bits);
|
|
1046
|
+
pointWindowSizes.set(P, W);
|
|
1047
|
+
pointPrecomputes.delete(P);
|
|
1048
|
+
}
|
|
1049
|
+
hasCache(elm) {
|
|
1050
|
+
return getW(elm) !== 1;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Endomorphism-specific multiplication for Koblitz curves.
|
|
1055
|
+
* Cost: 128 dbl, 0-256 adds.
|
|
1056
|
+
*/
|
|
1057
|
+
function mulEndoUnsafe(Point, point, k1, k2) {
|
|
1058
|
+
let acc = point;
|
|
1059
|
+
let p1 = Point.ZERO;
|
|
1060
|
+
let p2 = Point.ZERO;
|
|
1061
|
+
while (k1 > _0n$3 || k2 > _0n$3) {
|
|
1062
|
+
if (k1 & _1n$4)
|
|
1063
|
+
p1 = p1.add(acc);
|
|
1064
|
+
if (k2 & _1n$4)
|
|
1065
|
+
p2 = p2.add(acc);
|
|
1066
|
+
acc = acc.double();
|
|
1067
|
+
k1 >>= _1n$4;
|
|
1068
|
+
k2 >>= _1n$4;
|
|
1069
|
+
}
|
|
1070
|
+
return { p1, p2 };
|
|
1029
1071
|
}
|
|
1030
1072
|
/**
|
|
1031
1073
|
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
|
1032
|
-
* 30x faster vs naive addition on L=4096, 10x faster
|
|
1074
|
+
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
|
|
1033
1075
|
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
|
1034
1076
|
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
|
1035
1077
|
* @param c Curve Point constructor
|
|
1036
1078
|
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
|
1037
1079
|
* @param points array of L curve points
|
|
1038
|
-
* @param scalars array of L scalars (aka
|
|
1080
|
+
* @param scalars array of L scalars (aka secret keys / bigints)
|
|
1039
1081
|
*/
|
|
1040
1082
|
function pippenger(c, fieldN, points, scalars) {
|
|
1041
1083
|
// If we split scalars by some window (let's say 8 bits), every chunk will only
|
|
@@ -1046,20 +1088,29 @@ function pippenger(c, fieldN, points, scalars) {
|
|
|
1046
1088
|
// 0 is accepted in scalars
|
|
1047
1089
|
validateMSMPoints(points, c);
|
|
1048
1090
|
validateMSMScalars(scalars, fieldN);
|
|
1049
|
-
|
|
1091
|
+
const plength = points.length;
|
|
1092
|
+
const slength = scalars.length;
|
|
1093
|
+
if (plength !== slength)
|
|
1050
1094
|
throw new Error('arrays of points and scalars must have equal length');
|
|
1095
|
+
// if (plength === 0) throw new Error('array must be of length >= 2');
|
|
1051
1096
|
const zero = c.ZERO;
|
|
1052
|
-
const wbits = bitLen(BigInt(
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1097
|
+
const wbits = bitLen(BigInt(plength));
|
|
1098
|
+
let windowSize = 1; // bits
|
|
1099
|
+
if (wbits > 12)
|
|
1100
|
+
windowSize = wbits - 3;
|
|
1101
|
+
else if (wbits > 4)
|
|
1102
|
+
windowSize = wbits - 2;
|
|
1103
|
+
else if (wbits > 0)
|
|
1104
|
+
windowSize = 2;
|
|
1105
|
+
const MASK = bitMask(windowSize);
|
|
1106
|
+
const buckets = new Array(Number(MASK) + 1).fill(zero); // +1 for zero array
|
|
1056
1107
|
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
|
|
1057
1108
|
let sum = zero;
|
|
1058
1109
|
for (let i = lastBits; i >= 0; i -= windowSize) {
|
|
1059
1110
|
buckets.fill(zero);
|
|
1060
|
-
for (let j = 0; j <
|
|
1111
|
+
for (let j = 0; j < slength; j++) {
|
|
1061
1112
|
const scalar = scalars[j];
|
|
1062
|
-
const wbits = Number((scalar >> BigInt(i)) &
|
|
1113
|
+
const wbits = Number((scalar >> BigInt(i)) & MASK);
|
|
1063
1114
|
buckets[wbits] = buckets[wbits].add(points[j]);
|
|
1064
1115
|
}
|
|
1065
1116
|
let resI = zero; // not using this will do small speed-up, but will lose ct
|
|
@@ -1075,61 +1126,120 @@ function pippenger(c, fieldN, points, scalars) {
|
|
|
1075
1126
|
}
|
|
1076
1127
|
return sum;
|
|
1077
1128
|
}
|
|
1078
|
-
function
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1129
|
+
function createField(order, field, isLE) {
|
|
1130
|
+
if (field) {
|
|
1131
|
+
if (field.ORDER !== order)
|
|
1132
|
+
throw new Error('Field.ORDER must match order: Fp == p, Fn == n');
|
|
1133
|
+
validateField(field);
|
|
1134
|
+
return field;
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
return Field(order, { isLE });
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
/** Validates CURVE opts and creates fields */
|
|
1141
|
+
function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
|
1142
|
+
if (FpFnLE === undefined)
|
|
1143
|
+
FpFnLE = type === 'edwards';
|
|
1144
|
+
if (!CURVE || typeof CURVE !== 'object')
|
|
1145
|
+
throw new Error(`expected valid ${type} CURVE object`);
|
|
1146
|
+
for (const p of ['p', 'n', 'h']) {
|
|
1147
|
+
const val = CURVE[p];
|
|
1148
|
+
if (!(typeof val === 'bigint' && val > _0n$3))
|
|
1149
|
+
throw new Error(`CURVE.${p} must be positive bigint`);
|
|
1150
|
+
}
|
|
1151
|
+
const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE);
|
|
1152
|
+
const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE);
|
|
1153
|
+
const _b = type === 'weierstrass' ? 'b' : 'd';
|
|
1154
|
+
const params = ['Gx', 'Gy', 'a', _b];
|
|
1155
|
+
for (const p of params) {
|
|
1156
|
+
// @ts-ignore
|
|
1157
|
+
if (!Fp.isValid(CURVE[p]))
|
|
1158
|
+
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
|
|
1159
|
+
}
|
|
1160
|
+
CURVE = Object.freeze(Object.assign({}, CURVE));
|
|
1161
|
+
return { CURVE, Fp, Fn };
|
|
1095
1162
|
}
|
|
1096
1163
|
|
|
1164
|
+
/**
|
|
1165
|
+
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
1166
|
+
*
|
|
1167
|
+
* ### Design rationale for types
|
|
1168
|
+
*
|
|
1169
|
+
* * Interaction between classes from different curves should fail:
|
|
1170
|
+
* `k256.Point.BASE.add(p256.Point.BASE)`
|
|
1171
|
+
* * For this purpose we want to use `instanceof` operator, which is fast and works during runtime
|
|
1172
|
+
* * Different calls of `curve()` would return different classes -
|
|
1173
|
+
* `curve(params) !== curve(params)`: if somebody decided to monkey-patch their curve,
|
|
1174
|
+
* it won't affect others
|
|
1175
|
+
*
|
|
1176
|
+
* TypeScript can't infer types for classes created inside a function. Classes is one instance
|
|
1177
|
+
* of nominative types in TypeScript and interfaces only check for shape, so it's hard to create
|
|
1178
|
+
* unique type for every function call.
|
|
1179
|
+
*
|
|
1180
|
+
* We can use generic types via some param, like curve opts, but that would:
|
|
1181
|
+
* 1. Enable interaction between `curve(params)` and `curve(params)` (curves of same params)
|
|
1182
|
+
* which is hard to debug.
|
|
1183
|
+
* 2. Params can be generic and we can't enforce them to be constant value:
|
|
1184
|
+
* if somebody creates curve from non-constant params,
|
|
1185
|
+
* it would be allowed to interact with other curves with non-constant params
|
|
1186
|
+
*
|
|
1187
|
+
* @todo https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol
|
|
1188
|
+
* @module
|
|
1189
|
+
*/
|
|
1097
1190
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
1098
|
-
//
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1191
|
+
// We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
|
|
1192
|
+
const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n$4) / den;
|
|
1193
|
+
/**
|
|
1194
|
+
* Splits scalar for GLV endomorphism.
|
|
1195
|
+
*/
|
|
1196
|
+
function _splitEndoScalar(k, basis, n) {
|
|
1197
|
+
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
|
|
1198
|
+
// Since part can be negative, we need to do this on point.
|
|
1199
|
+
// TODO: verifyScalar function which consumes lambda
|
|
1200
|
+
const [[a1, b1], [a2, b2]] = basis;
|
|
1201
|
+
const c1 = divNearest(b2 * k, n);
|
|
1202
|
+
const c2 = divNearest(-b1 * k, n);
|
|
1203
|
+
// |k1|/|k2| is < sqrt(N), but can be negative.
|
|
1204
|
+
// If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
|
|
1205
|
+
let k1 = k - c1 * a1 - c2 * a2;
|
|
1206
|
+
let k2 = -c1 * b1 - c2 * b2;
|
|
1207
|
+
const k1neg = k1 < _0n$2;
|
|
1208
|
+
const k2neg = k2 < _0n$2;
|
|
1209
|
+
if (k1neg)
|
|
1210
|
+
k1 = -k1;
|
|
1211
|
+
if (k2neg)
|
|
1212
|
+
k2 = -k2;
|
|
1213
|
+
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
1214
|
+
// This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
|
|
1215
|
+
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n$3; // Half bits of N
|
|
1216
|
+
if (k1 < _0n$2 || k1 >= MAX_NUM || k2 < _0n$2 || k2 >= MAX_NUM) {
|
|
1217
|
+
throw new Error('splitScalar (endomorphism): failed, k=' + k);
|
|
1218
|
+
}
|
|
1219
|
+
return { k1neg, k1, k2neg, k2 };
|
|
1220
|
+
}
|
|
1221
|
+
function validateSigFormat(format) {
|
|
1222
|
+
if (!['compact', 'recovered', 'der'].includes(format))
|
|
1223
|
+
throw new Error('Signature format must be "compact", "recovered", or "der"');
|
|
1224
|
+
return format;
|
|
1225
|
+
}
|
|
1226
|
+
function validateSigOpts(opts, def) {
|
|
1227
|
+
const optsn = {};
|
|
1228
|
+
for (let optName of Object.keys(def)) {
|
|
1229
|
+
// @ts-ignore
|
|
1230
|
+
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
1231
|
+
}
|
|
1232
|
+
_abool2(optsn.lowS, 'lowS');
|
|
1233
|
+
_abool2(optsn.prehash, 'prehash');
|
|
1234
|
+
if (optsn.format !== undefined)
|
|
1235
|
+
validateSigFormat(optsn.format);
|
|
1236
|
+
return optsn;
|
|
1237
|
+
}
|
|
1238
|
+
class DERErr extends Error {
|
|
1239
|
+
constructor(m = '') {
|
|
1240
|
+
super(m);
|
|
1129
1241
|
}
|
|
1130
|
-
return Object.freeze({ ...opts });
|
|
1131
1242
|
}
|
|
1132
|
-
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
1133
1243
|
/**
|
|
1134
1244
|
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
|
|
1135
1245
|
*
|
|
@@ -1139,11 +1249,7 @@ const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
|
1139
1249
|
*/
|
|
1140
1250
|
const DER = {
|
|
1141
1251
|
// asn.1 DER encoding utils
|
|
1142
|
-
Err:
|
|
1143
|
-
constructor(m = '') {
|
|
1144
|
-
super(m);
|
|
1145
|
-
}
|
|
1146
|
-
},
|
|
1252
|
+
Err: DERErr,
|
|
1147
1253
|
// Basic building block is TLV (Tag-Length-Value)
|
|
1148
1254
|
_tlv: {
|
|
1149
1255
|
encode: (tag, data) => {
|
|
@@ -1221,14 +1327,13 @@ const DER = {
|
|
|
1221
1327
|
throw new E('invalid signature integer: negative');
|
|
1222
1328
|
if (data[0] === 0x00 && !(data[1] & 128))
|
|
1223
1329
|
throw new E('invalid signature integer: unnecessary leading zero');
|
|
1224
|
-
return
|
|
1330
|
+
return bytesToNumberBE(data);
|
|
1225
1331
|
},
|
|
1226
1332
|
},
|
|
1227
1333
|
toSig(hex) {
|
|
1228
1334
|
// parse DER signature
|
|
1229
1335
|
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|
1230
|
-
const data =
|
|
1231
|
-
abytes(data);
|
|
1336
|
+
const data = ensureBytes('signature', hex);
|
|
1232
1337
|
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
|
|
1233
1338
|
if (seqLeftBytes.length)
|
|
1234
1339
|
throw new E('invalid signature: left bytes after parsing');
|
|
@@ -1248,98 +1353,184 @@ const DER = {
|
|
|
1248
1353
|
};
|
|
1249
1354
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
1250
1355
|
// prettier-ignore
|
|
1251
|
-
const _0n$2 = BigInt(0), _1n$
|
|
1252
|
-
function
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
((_c, point, _isCompressed) => {
|
|
1258
|
-
const a = point.toAffine();
|
|
1259
|
-
return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
1260
|
-
});
|
|
1261
|
-
const fromBytes = CURVE.fromBytes ||
|
|
1262
|
-
((bytes) => {
|
|
1263
|
-
// const head = bytes[0];
|
|
1264
|
-
const tail = bytes.subarray(1);
|
|
1265
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
1266
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
1267
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
1268
|
-
return { x, y };
|
|
1269
|
-
});
|
|
1270
|
-
/**
|
|
1271
|
-
* y² = x³ + ax + b: Short weierstrass curve formula
|
|
1272
|
-
* @returns y²
|
|
1273
|
-
*/
|
|
1274
|
-
function weierstrassEquation(x) {
|
|
1275
|
-
const { a, b } = CURVE;
|
|
1276
|
-
const x2 = Fp.sqr(x); // x * x
|
|
1277
|
-
const x3 = Fp.mul(x2, x); // x2 * x
|
|
1278
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
1356
|
+
const _0n$2 = BigInt(0), _1n$3 = BigInt(1), _2n$4 = BigInt(2), _3n$1 = BigInt(3), _4n = BigInt(4);
|
|
1357
|
+
function _normFnElement(Fn, key) {
|
|
1358
|
+
const { BYTES: expected } = Fn;
|
|
1359
|
+
let num;
|
|
1360
|
+
if (typeof key === 'bigint') {
|
|
1361
|
+
num = key;
|
|
1279
1362
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
|
|
1283
|
-
// ProjectivePoint class has not been initialized yet.
|
|
1284
|
-
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
|
|
1285
|
-
throw new Error('bad generator point: equation left != right');
|
|
1286
|
-
// Valid group elements reside in range 1..n-1
|
|
1287
|
-
function isWithinCurveOrder(num) {
|
|
1288
|
-
return inRange(num, _1n$4, CURVE.n);
|
|
1289
|
-
}
|
|
1290
|
-
// Validates if priv key is valid and converts it to bigint.
|
|
1291
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
1292
|
-
function normPrivateKeyToScalar(key) {
|
|
1293
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
1294
|
-
if (lengths && typeof key !== 'bigint') {
|
|
1295
|
-
if (isBytes(key))
|
|
1296
|
-
key = bytesToHex(key);
|
|
1297
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
1298
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
1299
|
-
throw new Error('invalid private key');
|
|
1300
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
1301
|
-
}
|
|
1302
|
-
let num;
|
|
1363
|
+
else {
|
|
1364
|
+
let bytes = ensureBytes('private key', key);
|
|
1303
1365
|
try {
|
|
1304
|
-
num =
|
|
1305
|
-
typeof key === 'bigint'
|
|
1306
|
-
? key
|
|
1307
|
-
: bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
|
1366
|
+
num = Fn.fromBytes(bytes);
|
|
1308
1367
|
}
|
|
1309
1368
|
catch (error) {
|
|
1310
|
-
throw new Error(
|
|
1369
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
if (!Fn.isValidNot0(num))
|
|
1373
|
+
throw new Error('invalid private key: out of range [1..N-1]');
|
|
1374
|
+
return num;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Creates weierstrass Point constructor, based on specified curve options.
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
```js
|
|
1381
|
+
const opts = {
|
|
1382
|
+
p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
|
1383
|
+
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
|
1384
|
+
h: BigInt(1),
|
|
1385
|
+
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
|
1386
|
+
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
|
1387
|
+
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
|
1388
|
+
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
1389
|
+
};
|
|
1390
|
+
const p256_Point = weierstrass(opts);
|
|
1391
|
+
```
|
|
1392
|
+
*/
|
|
1393
|
+
function weierstrassN(params, extraOpts = {}) {
|
|
1394
|
+
const validated = _createCurveFields('weierstrass', params, extraOpts);
|
|
1395
|
+
const { Fp, Fn } = validated;
|
|
1396
|
+
let CURVE = validated.CURVE;
|
|
1397
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
1398
|
+
_validateObject(extraOpts, {}, {
|
|
1399
|
+
allowInfinityPoint: 'boolean',
|
|
1400
|
+
clearCofactor: 'function',
|
|
1401
|
+
isTorsionFree: 'function',
|
|
1402
|
+
fromBytes: 'function',
|
|
1403
|
+
toBytes: 'function',
|
|
1404
|
+
endo: 'object',
|
|
1405
|
+
wrapPrivateKey: 'boolean',
|
|
1406
|
+
});
|
|
1407
|
+
const { endo } = extraOpts;
|
|
1408
|
+
if (endo) {
|
|
1409
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
1410
|
+
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
1411
|
+
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const lengths = getWLengths(Fp, Fn);
|
|
1415
|
+
function assertCompressionIsSupported() {
|
|
1416
|
+
if (!Fp.isOdd)
|
|
1417
|
+
throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
1418
|
+
}
|
|
1419
|
+
// Implements IEEE P1363 point encoding
|
|
1420
|
+
function pointToBytes(_c, point, isCompressed) {
|
|
1421
|
+
const { x, y } = point.toAffine();
|
|
1422
|
+
const bx = Fp.toBytes(x);
|
|
1423
|
+
_abool2(isCompressed, 'isCompressed');
|
|
1424
|
+
if (isCompressed) {
|
|
1425
|
+
assertCompressionIsSupported();
|
|
1426
|
+
const hasEvenY = !Fp.isOdd(y);
|
|
1427
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
1311
1431
|
}
|
|
1312
|
-
if (wrapPrivateKey)
|
|
1313
|
-
num = mod(num, N); // disabled by default, enabled for BLS
|
|
1314
|
-
aInRange('private key', num, _1n$4, N); // num in range [1..N-1]
|
|
1315
|
-
return num;
|
|
1316
1432
|
}
|
|
1317
|
-
function
|
|
1433
|
+
function pointFromBytes(bytes) {
|
|
1434
|
+
_abytes2(bytes, undefined, 'Point');
|
|
1435
|
+
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
1436
|
+
const length = bytes.length;
|
|
1437
|
+
const head = bytes[0];
|
|
1438
|
+
const tail = bytes.subarray(1);
|
|
1439
|
+
// No actual validation is done here: use .assertValidity()
|
|
1440
|
+
if (length === comp && (head === 0x02 || head === 0x03)) {
|
|
1441
|
+
const x = Fp.fromBytes(tail);
|
|
1442
|
+
if (!Fp.isValid(x))
|
|
1443
|
+
throw new Error('bad point: is not on curve, wrong x');
|
|
1444
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
1445
|
+
let y;
|
|
1446
|
+
try {
|
|
1447
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
1448
|
+
}
|
|
1449
|
+
catch (sqrtError) {
|
|
1450
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
1451
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
1452
|
+
}
|
|
1453
|
+
assertCompressionIsSupported();
|
|
1454
|
+
const isYOdd = Fp.isOdd(y); // (y & _1n) === _1n;
|
|
1455
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
1456
|
+
if (isHeadOdd !== isYOdd)
|
|
1457
|
+
y = Fp.neg(y);
|
|
1458
|
+
return { x, y };
|
|
1459
|
+
}
|
|
1460
|
+
else if (length === uncomp && head === 0x04) {
|
|
1461
|
+
// TODO: more checks
|
|
1462
|
+
const L = Fp.BYTES;
|
|
1463
|
+
const x = Fp.fromBytes(tail.subarray(0, L));
|
|
1464
|
+
const y = Fp.fromBytes(tail.subarray(L, L * 2));
|
|
1465
|
+
if (!isValidXY(x, y))
|
|
1466
|
+
throw new Error('bad point: is not on curve');
|
|
1467
|
+
return { x, y };
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
const encodePoint = extraOpts.toBytes || pointToBytes;
|
|
1474
|
+
const decodePoint = extraOpts.fromBytes || pointFromBytes;
|
|
1475
|
+
function weierstrassEquation(x) {
|
|
1476
|
+
const x2 = Fp.sqr(x); // x * x
|
|
1477
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
1478
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
|
|
1479
|
+
}
|
|
1480
|
+
// TODO: move top-level
|
|
1481
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
1482
|
+
function isValidXY(x, y) {
|
|
1483
|
+
const left = Fp.sqr(y); // y²
|
|
1484
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
1485
|
+
return Fp.eql(left, right);
|
|
1486
|
+
}
|
|
1487
|
+
// Validate whether the passed curve params are valid.
|
|
1488
|
+
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
1489
|
+
if (!isValidXY(CURVE.Gx, CURVE.Gy))
|
|
1490
|
+
throw new Error('bad curve params: generator point');
|
|
1491
|
+
// Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
|
|
1492
|
+
// Guarantees curve is genus-1, smooth (non-singular).
|
|
1493
|
+
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n$1), _4n);
|
|
1494
|
+
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
1495
|
+
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
1496
|
+
throw new Error('bad curve params: a or b');
|
|
1497
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
1498
|
+
function acoord(title, n, banZero = false) {
|
|
1499
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n)))
|
|
1500
|
+
throw new Error(`bad point coordinate ${title}`);
|
|
1501
|
+
return n;
|
|
1502
|
+
}
|
|
1503
|
+
function aprjpoint(other) {
|
|
1318
1504
|
if (!(other instanceof Point))
|
|
1319
1505
|
throw new Error('ProjectivePoint expected');
|
|
1320
1506
|
}
|
|
1507
|
+
function splitEndoScalarN(k) {
|
|
1508
|
+
if (!endo || !endo.basises)
|
|
1509
|
+
throw new Error('no endo');
|
|
1510
|
+
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|
1511
|
+
}
|
|
1321
1512
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
1322
1513
|
// Converts Projective point to affine (x, y) coordinates.
|
|
1323
1514
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
1324
|
-
// (
|
|
1515
|
+
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
1325
1516
|
const toAffineMemo = memoized((p, iz) => {
|
|
1326
|
-
const {
|
|
1517
|
+
const { X, Y, Z } = p;
|
|
1327
1518
|
// Fast-path for normalized points
|
|
1328
|
-
if (Fp.eql(
|
|
1329
|
-
return { x, y };
|
|
1519
|
+
if (Fp.eql(Z, Fp.ONE))
|
|
1520
|
+
return { x: X, y: Y };
|
|
1330
1521
|
const is0 = p.is0();
|
|
1331
1522
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
1332
1523
|
// all operations, so we replace invZ with a random number, 1.
|
|
1333
1524
|
if (iz == null)
|
|
1334
|
-
iz = is0 ? Fp.ONE : Fp.inv(
|
|
1335
|
-
const
|
|
1336
|
-
const
|
|
1337
|
-
const zz = Fp.mul(
|
|
1525
|
+
iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
1526
|
+
const x = Fp.mul(X, iz);
|
|
1527
|
+
const y = Fp.mul(Y, iz);
|
|
1528
|
+
const zz = Fp.mul(Z, iz);
|
|
1338
1529
|
if (is0)
|
|
1339
1530
|
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
1340
1531
|
if (!Fp.eql(zz, Fp.ONE))
|
|
1341
1532
|
throw new Error('invZ was invalid');
|
|
1342
|
-
return { x
|
|
1533
|
+
return { x, y };
|
|
1343
1534
|
});
|
|
1344
1535
|
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
1345
1536
|
// Otherwise true will be return
|
|
@@ -1348,55 +1539,62 @@ function weierstrassPoints(opts) {
|
|
|
1348
1539
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
1349
1540
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
1350
1541
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
1351
|
-
if (
|
|
1542
|
+
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|
1352
1543
|
return;
|
|
1353
1544
|
throw new Error('bad point: ZERO');
|
|
1354
1545
|
}
|
|
1355
1546
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
1356
1547
|
const { x, y } = p.toAffine();
|
|
1357
|
-
// Check if x, y are valid field elements
|
|
1358
1548
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
1359
|
-
throw new Error('bad point: x or y not
|
|
1360
|
-
|
|
1361
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
1362
|
-
if (!Fp.eql(left, right))
|
|
1549
|
+
throw new Error('bad point: x or y not field elements');
|
|
1550
|
+
if (!isValidXY(x, y))
|
|
1363
1551
|
throw new Error('bad point: equation left != right');
|
|
1364
1552
|
if (!p.isTorsionFree())
|
|
1365
1553
|
throw new Error('bad point: not in prime-order subgroup');
|
|
1366
1554
|
return true;
|
|
1367
1555
|
});
|
|
1556
|
+
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
1557
|
+
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
1558
|
+
k1p = negateCt(k1neg, k1p);
|
|
1559
|
+
k2p = negateCt(k2neg, k2p);
|
|
1560
|
+
return k1p.add(k2p);
|
|
1561
|
+
}
|
|
1368
1562
|
/**
|
|
1369
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
1370
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
1563
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
1564
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
1371
1565
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
1372
1566
|
*/
|
|
1373
1567
|
class Point {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
this.
|
|
1377
|
-
this.
|
|
1378
|
-
|
|
1379
|
-
throw new Error('x required');
|
|
1380
|
-
if (py == null || !Fp.isValid(py))
|
|
1381
|
-
throw new Error('y required');
|
|
1382
|
-
if (pz == null || !Fp.isValid(pz))
|
|
1383
|
-
throw new Error('z required');
|
|
1568
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
1569
|
+
constructor(X, Y, Z) {
|
|
1570
|
+
this.X = acoord('x', X);
|
|
1571
|
+
this.Y = acoord('y', Y, true);
|
|
1572
|
+
this.Z = acoord('z', Z);
|
|
1384
1573
|
Object.freeze(this);
|
|
1385
1574
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1575
|
+
static CURVE() {
|
|
1576
|
+
return CURVE;
|
|
1577
|
+
}
|
|
1578
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
1388
1579
|
static fromAffine(p) {
|
|
1389
1580
|
const { x, y } = p || {};
|
|
1390
1581
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
1391
1582
|
throw new Error('invalid affine point');
|
|
1392
1583
|
if (p instanceof Point)
|
|
1393
1584
|
throw new Error('projective point not allowed');
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
if (is0(x) && is0(y))
|
|
1585
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
1586
|
+
if (Fp.is0(x) && Fp.is0(y))
|
|
1397
1587
|
return Point.ZERO;
|
|
1398
1588
|
return new Point(x, y, Fp.ONE);
|
|
1399
1589
|
}
|
|
1590
|
+
static fromBytes(bytes) {
|
|
1591
|
+
const P = Point.fromAffine(decodePoint(_abytes2(bytes, undefined, 'point')));
|
|
1592
|
+
P.assertValidity();
|
|
1593
|
+
return P;
|
|
1594
|
+
}
|
|
1595
|
+
static fromHex(hex) {
|
|
1596
|
+
return Point.fromBytes(ensureBytes('pointHex', hex));
|
|
1597
|
+
}
|
|
1400
1598
|
get x() {
|
|
1401
1599
|
return this.toAffine().x;
|
|
1402
1600
|
}
|
|
@@ -1404,62 +1602,40 @@ function weierstrassPoints(opts) {
|
|
|
1404
1602
|
return this.toAffine().y;
|
|
1405
1603
|
}
|
|
1406
1604
|
/**
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
1411
|
-
*/
|
|
1412
|
-
static normalizeZ(points) {
|
|
1413
|
-
const toInv = Fp.invertBatch(points.map((p) => p.pz));
|
|
1414
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
1415
|
-
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Converts hash string or Uint8Array to Point.
|
|
1418
|
-
* @param hex short/long ECDSA hex
|
|
1605
|
+
*
|
|
1606
|
+
* @param windowSize
|
|
1607
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
1608
|
+
* @returns
|
|
1419
1609
|
*/
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
}
|
|
1429
|
-
// Multiscalar Multiplication
|
|
1430
|
-
static msm(points, scalars) {
|
|
1431
|
-
return pippenger(Point, Fn, points, scalars);
|
|
1432
|
-
}
|
|
1433
|
-
// "Private method", don't use it directly
|
|
1434
|
-
_setWindowSize(windowSize) {
|
|
1435
|
-
wnaf.setWindowSize(this, windowSize);
|
|
1436
|
-
}
|
|
1437
|
-
// A point on curve is valid if it conforms to equation.
|
|
1610
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
1611
|
+
wnaf.createCache(this, windowSize);
|
|
1612
|
+
if (!isLazy)
|
|
1613
|
+
this.multiply(_3n$1); // random number
|
|
1614
|
+
return this;
|
|
1615
|
+
}
|
|
1616
|
+
// TODO: return `this`
|
|
1617
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
1438
1618
|
assertValidity() {
|
|
1439
1619
|
assertValidMemo(this);
|
|
1440
1620
|
}
|
|
1441
1621
|
hasEvenY() {
|
|
1442
1622
|
const { y } = this.toAffine();
|
|
1443
|
-
if (Fp.isOdd)
|
|
1444
|
-
|
|
1445
|
-
|
|
1623
|
+
if (!Fp.isOdd)
|
|
1624
|
+
throw new Error("Field doesn't support isOdd");
|
|
1625
|
+
return !Fp.isOdd(y);
|
|
1446
1626
|
}
|
|
1447
|
-
/**
|
|
1448
|
-
* Compare one point to another.
|
|
1449
|
-
*/
|
|
1627
|
+
/** Compare one point to another. */
|
|
1450
1628
|
equals(other) {
|
|
1451
|
-
|
|
1452
|
-
const {
|
|
1453
|
-
const {
|
|
1629
|
+
aprjpoint(other);
|
|
1630
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
1631
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
1454
1632
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
1455
1633
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
1456
1634
|
return U1 && U2;
|
|
1457
1635
|
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
1460
|
-
*/
|
|
1636
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
1461
1637
|
negate() {
|
|
1462
|
-
return new Point(this.
|
|
1638
|
+
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|
1463
1639
|
}
|
|
1464
1640
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
1465
1641
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -1468,7 +1644,7 @@ function weierstrassPoints(opts) {
|
|
|
1468
1644
|
double() {
|
|
1469
1645
|
const { a, b } = CURVE;
|
|
1470
1646
|
const b3 = Fp.mul(b, _3n$1);
|
|
1471
|
-
const {
|
|
1647
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
1472
1648
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
1473
1649
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
1474
1650
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -1508,9 +1684,9 @@ function weierstrassPoints(opts) {
|
|
|
1508
1684
|
// https://eprint.iacr.org/2015/1060, algorithm 1
|
|
1509
1685
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
1510
1686
|
add(other) {
|
|
1511
|
-
|
|
1512
|
-
const {
|
|
1513
|
-
const {
|
|
1687
|
+
aprjpoint(other);
|
|
1688
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
1689
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
1514
1690
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
1515
1691
|
const a = CURVE.a;
|
|
1516
1692
|
const b3 = Fp.mul(CURVE.b, _3n$1);
|
|
@@ -1562,46 +1738,6 @@ function weierstrassPoints(opts) {
|
|
|
1562
1738
|
is0() {
|
|
1563
1739
|
return this.equals(Point.ZERO);
|
|
1564
1740
|
}
|
|
1565
|
-
wNAF(n) {
|
|
1566
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
1567
|
-
}
|
|
1568
|
-
/**
|
|
1569
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
1570
|
-
* It's faster, but should only be used when you don't care about
|
|
1571
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
1572
|
-
*/
|
|
1573
|
-
multiplyUnsafe(sc) {
|
|
1574
|
-
const { endo, n: N } = CURVE;
|
|
1575
|
-
aInRange('scalar', sc, _0n$2, N);
|
|
1576
|
-
const I = Point.ZERO;
|
|
1577
|
-
if (sc === _0n$2)
|
|
1578
|
-
return I;
|
|
1579
|
-
if (this.is0() || sc === _1n$4)
|
|
1580
|
-
return this;
|
|
1581
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
1582
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
1583
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
1584
|
-
// Case c: endomorphism
|
|
1585
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
1586
|
-
let k1p = I;
|
|
1587
|
-
let k2p = I;
|
|
1588
|
-
let d = this;
|
|
1589
|
-
while (k1 > _0n$2 || k2 > _0n$2) {
|
|
1590
|
-
if (k1 & _1n$4)
|
|
1591
|
-
k1p = k1p.add(d);
|
|
1592
|
-
if (k2 & _1n$4)
|
|
1593
|
-
k2p = k2p.add(d);
|
|
1594
|
-
d = d.double();
|
|
1595
|
-
k1 >>= _1n$4;
|
|
1596
|
-
k2 >>= _1n$4;
|
|
1597
|
-
}
|
|
1598
|
-
if (k1neg)
|
|
1599
|
-
k1p = k1p.negate();
|
|
1600
|
-
if (k2neg)
|
|
1601
|
-
k2p = k2p.negate();
|
|
1602
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
1603
|
-
return k1p.add(k2p);
|
|
1604
|
-
}
|
|
1605
1741
|
/**
|
|
1606
1742
|
* Constant time multiplication.
|
|
1607
1743
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -1612,222 +1748,360 @@ function weierstrassPoints(opts) {
|
|
|
1612
1748
|
* @returns New point
|
|
1613
1749
|
*/
|
|
1614
1750
|
multiply(scalar) {
|
|
1615
|
-
const { endo
|
|
1616
|
-
|
|
1751
|
+
const { endo } = extraOpts;
|
|
1752
|
+
if (!Fn.isValidNot0(scalar))
|
|
1753
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
1617
1754
|
let point, fake; // Fake point is used to const-time mult
|
|
1755
|
+
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
1756
|
+
/** See docs for {@link EndomorphismOpts} */
|
|
1618
1757
|
if (endo) {
|
|
1619
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
1625
|
-
point = k1p.add(k2p);
|
|
1626
|
-
fake = f1p.add(f2p);
|
|
1758
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
1759
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
1760
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
1761
|
+
fake = k1f.add(k2f);
|
|
1762
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
1627
1763
|
}
|
|
1628
1764
|
else {
|
|
1629
|
-
const { p, f } =
|
|
1765
|
+
const { p, f } = mul(scalar);
|
|
1630
1766
|
point = p;
|
|
1631
1767
|
fake = f;
|
|
1632
1768
|
}
|
|
1633
1769
|
// Normalize `z` for both points, but return only real one
|
|
1634
|
-
return
|
|
1770
|
+
return normalizeZ(Point, [point, fake])[0];
|
|
1635
1771
|
}
|
|
1636
1772
|
/**
|
|
1637
|
-
*
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
* @returns non-zero affine point
|
|
1773
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
1774
|
+
* It's faster, but should only be used when you don't care about
|
|
1775
|
+
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
1641
1776
|
*/
|
|
1777
|
+
multiplyUnsafe(sc) {
|
|
1778
|
+
const { endo } = extraOpts;
|
|
1779
|
+
const p = this;
|
|
1780
|
+
if (!Fn.isValid(sc))
|
|
1781
|
+
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
1782
|
+
if (sc === _0n$2 || p.is0())
|
|
1783
|
+
return Point.ZERO;
|
|
1784
|
+
if (sc === _1n$3)
|
|
1785
|
+
return p; // fast-path
|
|
1786
|
+
if (wnaf.hasCache(this))
|
|
1787
|
+
return this.multiply(sc);
|
|
1788
|
+
if (endo) {
|
|
1789
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
1790
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
1791
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
1792
|
+
}
|
|
1793
|
+
else {
|
|
1794
|
+
return wnaf.unsafe(p, sc);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1642
1797
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
1643
|
-
const
|
|
1644
|
-
const mul = (P, a // Select faster multiply() method
|
|
1645
|
-
) => (a === _0n$2 || a === _1n$4 || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
1646
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
1798
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
1647
1799
|
return sum.is0() ? undefined : sum;
|
|
1648
1800
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1801
|
+
/**
|
|
1802
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
1803
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
1804
|
+
*/
|
|
1805
|
+
toAffine(invertedZ) {
|
|
1806
|
+
return toAffineMemo(this, invertedZ);
|
|
1654
1807
|
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
1810
|
+
* Always torsion-free for cofactor=1 curves.
|
|
1811
|
+
*/
|
|
1655
1812
|
isTorsionFree() {
|
|
1656
|
-
const {
|
|
1657
|
-
if (cofactor === _1n$
|
|
1658
|
-
return true;
|
|
1813
|
+
const { isTorsionFree } = extraOpts;
|
|
1814
|
+
if (cofactor === _1n$3)
|
|
1815
|
+
return true;
|
|
1659
1816
|
if (isTorsionFree)
|
|
1660
1817
|
return isTorsionFree(Point, this);
|
|
1661
|
-
|
|
1818
|
+
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
1662
1819
|
}
|
|
1663
1820
|
clearCofactor() {
|
|
1664
|
-
const {
|
|
1665
|
-
if (cofactor === _1n$
|
|
1821
|
+
const { clearCofactor } = extraOpts;
|
|
1822
|
+
if (cofactor === _1n$3)
|
|
1666
1823
|
return this; // Fast-path
|
|
1667
1824
|
if (clearCofactor)
|
|
1668
1825
|
return clearCofactor(Point, this);
|
|
1669
|
-
return this.multiplyUnsafe(
|
|
1826
|
+
return this.multiplyUnsafe(cofactor);
|
|
1670
1827
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1828
|
+
isSmallOrder() {
|
|
1829
|
+
// can we use this.clearCofactor()?
|
|
1830
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
1831
|
+
}
|
|
1832
|
+
toBytes(isCompressed = true) {
|
|
1833
|
+
_abool2(isCompressed, 'isCompressed');
|
|
1673
1834
|
this.assertValidity();
|
|
1674
|
-
return
|
|
1835
|
+
return encodePoint(Point, this, isCompressed);
|
|
1675
1836
|
}
|
|
1676
1837
|
toHex(isCompressed = true) {
|
|
1677
|
-
|
|
1678
|
-
|
|
1838
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
1839
|
+
}
|
|
1840
|
+
toString() {
|
|
1841
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
1842
|
+
}
|
|
1843
|
+
// TODO: remove
|
|
1844
|
+
get px() {
|
|
1845
|
+
return this.X;
|
|
1846
|
+
}
|
|
1847
|
+
get py() {
|
|
1848
|
+
return this.X;
|
|
1849
|
+
}
|
|
1850
|
+
get pz() {
|
|
1851
|
+
return this.Z;
|
|
1852
|
+
}
|
|
1853
|
+
toRawBytes(isCompressed = true) {
|
|
1854
|
+
return this.toBytes(isCompressed);
|
|
1855
|
+
}
|
|
1856
|
+
_setWindowSize(windowSize) {
|
|
1857
|
+
this.precompute(windowSize);
|
|
1858
|
+
}
|
|
1859
|
+
static normalizeZ(points) {
|
|
1860
|
+
return normalizeZ(Point, points);
|
|
1861
|
+
}
|
|
1862
|
+
static msm(points, scalars) {
|
|
1863
|
+
return pippenger(Point, Fn, points, scalars);
|
|
1864
|
+
}
|
|
1865
|
+
static fromPrivateKey(privateKey) {
|
|
1866
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
1679
1867
|
}
|
|
1680
1868
|
}
|
|
1869
|
+
// base / generator point
|
|
1681
1870
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1871
|
+
// zero / infinity / identity point
|
|
1872
|
+
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
1873
|
+
// math field
|
|
1874
|
+
Point.Fp = Fp;
|
|
1875
|
+
// scalar field
|
|
1876
|
+
Point.Fn = Fn;
|
|
1877
|
+
const bits = Fn.BITS;
|
|
1878
|
+
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
1879
|
+
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1880
|
+
return Point;
|
|
1881
|
+
}
|
|
1882
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
1883
|
+
function pprefix(hasEvenY) {
|
|
1884
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
1885
|
+
}
|
|
1886
|
+
function getWLengths(Fp, Fn) {
|
|
1686
1887
|
return {
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1888
|
+
secretKey: Fn.BYTES,
|
|
1889
|
+
publicKey: 1 + Fp.BYTES,
|
|
1890
|
+
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|
1891
|
+
publicKeyHasPrefix: true,
|
|
1892
|
+
signature: 2 * Fn.BYTES,
|
|
1692
1893
|
};
|
|
1693
1894
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1895
|
+
/**
|
|
1896
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
1897
|
+
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
1898
|
+
*/
|
|
1899
|
+
function ecdh(Point, ecdhOpts = {}) {
|
|
1900
|
+
const { Fn } = Point;
|
|
1901
|
+
const randomBytes_ = ecdhOpts.randomBytes || randomBytes;
|
|
1902
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
|
|
1903
|
+
function isValidSecretKey(secretKey) {
|
|
1904
|
+
try {
|
|
1905
|
+
return !!_normFnElement(Fn, secretKey);
|
|
1906
|
+
}
|
|
1907
|
+
catch (error) {
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
function isValidPublicKey(publicKey, isCompressed) {
|
|
1912
|
+
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
1913
|
+
try {
|
|
1914
|
+
const l = publicKey.length;
|
|
1915
|
+
if (isCompressed === true && l !== comp)
|
|
1916
|
+
return false;
|
|
1917
|
+
if (isCompressed === false && l !== publicKeyUncompressed)
|
|
1918
|
+
return false;
|
|
1919
|
+
return !!Point.fromBytes(publicKey);
|
|
1920
|
+
}
|
|
1921
|
+
catch (error) {
|
|
1922
|
+
return false;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Produces cryptographically secure secret key from random of size
|
|
1927
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1928
|
+
*/
|
|
1929
|
+
function randomSecretKey(seed = randomBytes_(lengths.seed)) {
|
|
1930
|
+
return mapHashToField(_abytes2(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1934
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
1935
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1936
|
+
*/
|
|
1937
|
+
function getPublicKey(secretKey, isCompressed = true) {
|
|
1938
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1939
|
+
}
|
|
1940
|
+
function keygen(seed) {
|
|
1941
|
+
const secretKey = randomSecretKey(seed);
|
|
1942
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1946
|
+
*/
|
|
1947
|
+
function isProbPub(item) {
|
|
1948
|
+
if (typeof item === 'bigint')
|
|
1949
|
+
return false;
|
|
1950
|
+
if (item instanceof Point)
|
|
1951
|
+
return true;
|
|
1952
|
+
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
1953
|
+
if (Fn.allowedLengths || secretKey === publicKey)
|
|
1954
|
+
return undefined;
|
|
1955
|
+
const l = ensureBytes('key', item).length;
|
|
1956
|
+
return l === publicKey || l === publicKeyUncompressed;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1960
|
+
* Computes shared public key from secret key A and public key B.
|
|
1961
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1962
|
+
* Does NOT hash the result.
|
|
1963
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
1964
|
+
* @returns shared public key
|
|
1965
|
+
*/
|
|
1966
|
+
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
1967
|
+
if (isProbPub(secretKeyA) === true)
|
|
1968
|
+
throw new Error('first arg must be private key');
|
|
1969
|
+
if (isProbPub(publicKeyB) === false)
|
|
1970
|
+
throw new Error('second arg must be public key');
|
|
1971
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1972
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1973
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
1974
|
+
}
|
|
1975
|
+
const utils = {
|
|
1976
|
+
isValidSecretKey,
|
|
1977
|
+
isValidPublicKey,
|
|
1978
|
+
randomSecretKey,
|
|
1979
|
+
// TODO: remove
|
|
1980
|
+
isValidPrivateKey: isValidSecretKey,
|
|
1981
|
+
randomPrivateKey: randomSecretKey,
|
|
1982
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
1983
|
+
precompute(windowSize = 8, point = Point.BASE) {
|
|
1984
|
+
return point.precompute(windowSize, false);
|
|
1985
|
+
},
|
|
1986
|
+
};
|
|
1987
|
+
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
1706
1988
|
}
|
|
1707
1989
|
/**
|
|
1708
|
-
* Creates
|
|
1990
|
+
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
1991
|
+
* We need `hash` for 2 features:
|
|
1992
|
+
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
1993
|
+
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
1994
|
+
*
|
|
1995
|
+
* ECDSAOpts are only rarely needed.
|
|
1996
|
+
*
|
|
1709
1997
|
* @example
|
|
1710
|
-
*
|
|
1711
|
-
*
|
|
1712
|
-
* const
|
|
1998
|
+
* ```js
|
|
1999
|
+
* const p256_Point = weierstrass(...);
|
|
2000
|
+
* const p256_sha256 = ecdsa(p256_Point, sha256);
|
|
2001
|
+
* const p256_sha224 = ecdsa(p256_Point, sha224);
|
|
2002
|
+
* const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
|
|
2003
|
+
* ```
|
|
1713
2004
|
*/
|
|
1714
|
-
function
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
function invN(a) {
|
|
1723
|
-
return invert(a, CURVE_ORDER);
|
|
1724
|
-
}
|
|
1725
|
-
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
1726
|
-
...CURVE,
|
|
1727
|
-
toBytes(_c, point, isCompressed) {
|
|
1728
|
-
const a = point.toAffine();
|
|
1729
|
-
const x = Fp.toBytes(a.x);
|
|
1730
|
-
const cat = concatBytes;
|
|
1731
|
-
abool('isCompressed', isCompressed);
|
|
1732
|
-
if (isCompressed) {
|
|
1733
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
1734
|
-
}
|
|
1735
|
-
else {
|
|
1736
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
1737
|
-
}
|
|
1738
|
-
},
|
|
1739
|
-
fromBytes(bytes) {
|
|
1740
|
-
const len = bytes.length;
|
|
1741
|
-
const head = bytes[0];
|
|
1742
|
-
const tail = bytes.subarray(1);
|
|
1743
|
-
// this.assertValidity() is done inside of fromHex
|
|
1744
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
1745
|
-
const x = bytesToNumberBE(tail);
|
|
1746
|
-
if (!inRange(x, _1n$4, Fp.ORDER))
|
|
1747
|
-
throw new Error('Point is not on curve');
|
|
1748
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
1749
|
-
let y;
|
|
1750
|
-
try {
|
|
1751
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
1752
|
-
}
|
|
1753
|
-
catch (sqrtError) {
|
|
1754
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
1755
|
-
throw new Error('Point is not on curve' + suffix);
|
|
1756
|
-
}
|
|
1757
|
-
const isYOdd = (y & _1n$4) === _1n$4;
|
|
1758
|
-
// ECDSA
|
|
1759
|
-
const isHeadOdd = (head & 1) === 1;
|
|
1760
|
-
if (isHeadOdd !== isYOdd)
|
|
1761
|
-
y = Fp.neg(y);
|
|
1762
|
-
return { x, y };
|
|
1763
|
-
}
|
|
1764
|
-
else if (len === uncompressedLen && head === 0x04) {
|
|
1765
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
1766
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
1767
|
-
return { x, y };
|
|
1768
|
-
}
|
|
1769
|
-
else {
|
|
1770
|
-
const cl = compressedLen;
|
|
1771
|
-
const ul = uncompressedLen;
|
|
1772
|
-
throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
|
|
1773
|
-
}
|
|
1774
|
-
},
|
|
2005
|
+
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
2006
|
+
ahash(hash);
|
|
2007
|
+
_validateObject(ecdsaOpts, {}, {
|
|
2008
|
+
hmac: 'function',
|
|
2009
|
+
lowS: 'boolean',
|
|
2010
|
+
randomBytes: 'function',
|
|
2011
|
+
bits2int: 'function',
|
|
2012
|
+
bits2int_modN: 'function',
|
|
1775
2013
|
});
|
|
1776
|
-
const
|
|
2014
|
+
const randomBytes$1 = ecdsaOpts.randomBytes || randomBytes;
|
|
2015
|
+
const hmac$1 = ecdsaOpts.hmac ||
|
|
2016
|
+
((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
|
|
2017
|
+
const { Fp, Fn } = Point;
|
|
2018
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
2019
|
+
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
2020
|
+
const defaultSigOpts = {
|
|
2021
|
+
prehash: false,
|
|
2022
|
+
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
|
|
2023
|
+
format: undefined, //'compact' as ECDSASigFormat,
|
|
2024
|
+
extraEntropy: false,
|
|
2025
|
+
};
|
|
2026
|
+
const defaultSigOpts_format = 'compact';
|
|
1777
2027
|
function isBiggerThanHalfOrder(number) {
|
|
1778
|
-
const HALF = CURVE_ORDER >> _1n$
|
|
2028
|
+
const HALF = CURVE_ORDER >> _1n$3;
|
|
1779
2029
|
return number > HALF;
|
|
1780
2030
|
}
|
|
1781
|
-
function
|
|
1782
|
-
|
|
2031
|
+
function validateRS(title, num) {
|
|
2032
|
+
if (!Fn.isValidNot0(num))
|
|
2033
|
+
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
2034
|
+
return num;
|
|
2035
|
+
}
|
|
2036
|
+
function validateSigLength(bytes, format) {
|
|
2037
|
+
validateSigFormat(format);
|
|
2038
|
+
const size = lengths.signature;
|
|
2039
|
+
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
2040
|
+
return _abytes2(bytes, sizer, `${format} signature`);
|
|
1783
2041
|
}
|
|
1784
|
-
// slice bytes num
|
|
1785
|
-
const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
1786
2042
|
/**
|
|
1787
|
-
* ECDSA signature with its (r, s) properties. Supports
|
|
2043
|
+
* ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
|
|
1788
2044
|
*/
|
|
1789
2045
|
class Signature {
|
|
1790
2046
|
constructor(r, s, recovery) {
|
|
1791
|
-
this.r = r;
|
|
1792
|
-
this.s = s;
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
// pair (bytes of r, bytes of s)
|
|
1797
|
-
static fromCompact(hex) {
|
|
1798
|
-
const l = CURVE.nByteLength;
|
|
1799
|
-
hex = ensureBytes('compactSignature', hex, l * 2);
|
|
1800
|
-
return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
|
|
2047
|
+
this.r = validateRS('r', r); // r in [1..N-1];
|
|
2048
|
+
this.s = validateRS('s', s); // s in [1..N-1];
|
|
2049
|
+
if (recovery != null)
|
|
2050
|
+
this.recovery = recovery;
|
|
2051
|
+
Object.freeze(this);
|
|
1801
2052
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2053
|
+
static fromBytes(bytes, format = defaultSigOpts_format) {
|
|
2054
|
+
validateSigLength(bytes, format);
|
|
2055
|
+
let recid;
|
|
2056
|
+
if (format === 'der') {
|
|
2057
|
+
const { r, s } = DER.toSig(_abytes2(bytes));
|
|
2058
|
+
return new Signature(r, s);
|
|
2059
|
+
}
|
|
2060
|
+
if (format === 'recovered') {
|
|
2061
|
+
recid = bytes[0];
|
|
2062
|
+
format = 'compact';
|
|
2063
|
+
bytes = bytes.subarray(1);
|
|
2064
|
+
}
|
|
2065
|
+
const L = Fn.BYTES;
|
|
2066
|
+
const r = bytes.subarray(0, L);
|
|
2067
|
+
const s = bytes.subarray(L, L * 2);
|
|
2068
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
1807
2069
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
aInRange('s', this.s, _1n$4, CURVE_ORDER); // s in [1..N]
|
|
2070
|
+
static fromHex(hex, format) {
|
|
2071
|
+
return this.fromBytes(hexToBytes(hex), format);
|
|
1811
2072
|
}
|
|
1812
2073
|
addRecoveryBit(recovery) {
|
|
1813
2074
|
return new Signature(this.r, this.s, recovery);
|
|
1814
2075
|
}
|
|
1815
|
-
recoverPublicKey(
|
|
2076
|
+
recoverPublicKey(messageHash) {
|
|
2077
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
1816
2078
|
const { r, s, recovery: rec } = this;
|
|
1817
|
-
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
1818
2079
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
1819
2080
|
throw new Error('recovery id invalid');
|
|
1820
|
-
|
|
1821
|
-
|
|
2081
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
2082
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
2083
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
2084
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
2085
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
2086
|
+
// To easily get i, we either need to:
|
|
2087
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
2088
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
2089
|
+
const hasCofactor = CURVE_ORDER * _2n$4 < FIELD_ORDER;
|
|
2090
|
+
if (hasCofactor && rec > 1)
|
|
2091
|
+
throw new Error('recovery id is ambiguous for h>1 curve');
|
|
2092
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
2093
|
+
if (!Fp.isValid(radj))
|
|
1822
2094
|
throw new Error('recovery id 2 or 3 invalid');
|
|
1823
|
-
const
|
|
1824
|
-
const R = Point.
|
|
1825
|
-
const ir =
|
|
1826
|
-
const
|
|
1827
|
-
const
|
|
1828
|
-
const
|
|
1829
|
-
|
|
1830
|
-
|
|
2095
|
+
const x = Fp.toBytes(radj);
|
|
2096
|
+
const R = Point.fromBytes(concatBytes(pprefix((rec & 1) === 0), x));
|
|
2097
|
+
const ir = Fn.inv(radj); // r^-1
|
|
2098
|
+
const h = bits2int_modN(ensureBytes('msgHash', messageHash)); // Truncate hash
|
|
2099
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
2100
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
2101
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
2102
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
2103
|
+
if (Q.is0())
|
|
2104
|
+
throw new Error('point at infinify');
|
|
1831
2105
|
Q.assertValidity();
|
|
1832
2106
|
return Q;
|
|
1833
2107
|
}
|
|
@@ -1835,466 +2109,484 @@ function weierstrass(curveDef) {
|
|
|
1835
2109
|
hasHighS() {
|
|
1836
2110
|
return isBiggerThanHalfOrder(this.s);
|
|
1837
2111
|
}
|
|
2112
|
+
toBytes(format = defaultSigOpts_format) {
|
|
2113
|
+
validateSigFormat(format);
|
|
2114
|
+
if (format === 'der')
|
|
2115
|
+
return hexToBytes(DER.hexFromSig(this));
|
|
2116
|
+
const r = Fn.toBytes(this.r);
|
|
2117
|
+
const s = Fn.toBytes(this.s);
|
|
2118
|
+
if (format === 'recovered') {
|
|
2119
|
+
if (this.recovery == null)
|
|
2120
|
+
throw new Error('recovery bit must be present');
|
|
2121
|
+
return concatBytes(Uint8Array.of(this.recovery), r, s);
|
|
2122
|
+
}
|
|
2123
|
+
return concatBytes(r, s);
|
|
2124
|
+
}
|
|
2125
|
+
toHex(format) {
|
|
2126
|
+
return bytesToHex(this.toBytes(format));
|
|
2127
|
+
}
|
|
2128
|
+
// TODO: remove
|
|
2129
|
+
assertValidity() { }
|
|
2130
|
+
static fromCompact(hex) {
|
|
2131
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
|
|
2132
|
+
}
|
|
2133
|
+
static fromDER(hex) {
|
|
2134
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
2135
|
+
}
|
|
1838
2136
|
normalizeS() {
|
|
1839
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
2137
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1840
2138
|
}
|
|
1841
|
-
// DER-encoded
|
|
1842
2139
|
toDERRawBytes() {
|
|
1843
|
-
return
|
|
2140
|
+
return this.toBytes('der');
|
|
1844
2141
|
}
|
|
1845
2142
|
toDERHex() {
|
|
1846
|
-
return
|
|
2143
|
+
return bytesToHex(this.toBytes('der'));
|
|
1847
2144
|
}
|
|
1848
|
-
// padded bytes of r, then padded bytes of s
|
|
1849
2145
|
toCompactRawBytes() {
|
|
1850
|
-
return
|
|
2146
|
+
return this.toBytes('compact');
|
|
1851
2147
|
}
|
|
1852
2148
|
toCompactHex() {
|
|
1853
|
-
return
|
|
2149
|
+
return bytesToHex(this.toBytes('compact'));
|
|
1854
2150
|
}
|
|
1855
2151
|
}
|
|
1856
|
-
const utils = {
|
|
1857
|
-
isValidPrivateKey(privateKey) {
|
|
1858
|
-
try {
|
|
1859
|
-
normPrivateKeyToScalar(privateKey);
|
|
1860
|
-
return true;
|
|
1861
|
-
}
|
|
1862
|
-
catch (error) {
|
|
1863
|
-
return false;
|
|
1864
|
-
}
|
|
1865
|
-
},
|
|
1866
|
-
normPrivateKeyToScalar: normPrivateKeyToScalar,
|
|
1867
|
-
/**
|
|
1868
|
-
* Produces cryptographically secure private key from random of size
|
|
1869
|
-
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1870
|
-
*/
|
|
1871
|
-
randomPrivateKey: () => {
|
|
1872
|
-
const length = getMinHashLength(CURVE.n);
|
|
1873
|
-
return mapHashToField(CURVE.randomBytes(length), CURVE.n);
|
|
1874
|
-
},
|
|
1875
|
-
/**
|
|
1876
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
1877
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
1878
|
-
* @returns cached point
|
|
1879
|
-
* @example
|
|
1880
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
1881
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
1882
|
-
*/
|
|
1883
|
-
precompute(windowSize = 8, point = Point.BASE) {
|
|
1884
|
-
point._setWindowSize(windowSize);
|
|
1885
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
1886
|
-
return point;
|
|
1887
|
-
},
|
|
1888
|
-
};
|
|
1889
|
-
/**
|
|
1890
|
-
* Computes public key for a private key. Checks for validity of the private key.
|
|
1891
|
-
* @param privateKey private key
|
|
1892
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1893
|
-
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1894
|
-
*/
|
|
1895
|
-
function getPublicKey(privateKey, isCompressed = true) {
|
|
1896
|
-
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
|
1897
|
-
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1900
|
-
*/
|
|
1901
|
-
function isProbPub(item) {
|
|
1902
|
-
const arr = isBytes(item);
|
|
1903
|
-
const str = typeof item === 'string';
|
|
1904
|
-
const len = (arr || str) && item.length;
|
|
1905
|
-
if (arr)
|
|
1906
|
-
return len === compressedLen || len === uncompressedLen;
|
|
1907
|
-
if (str)
|
|
1908
|
-
return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
|
1909
|
-
if (item instanceof Point)
|
|
1910
|
-
return true;
|
|
1911
|
-
return false;
|
|
1912
|
-
}
|
|
1913
|
-
/**
|
|
1914
|
-
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1915
|
-
* Computes shared public key from private key and public key.
|
|
1916
|
-
* Checks: 1) private key validity 2) shared key is on-curve.
|
|
1917
|
-
* Does NOT hash the result.
|
|
1918
|
-
* @param privateA private key
|
|
1919
|
-
* @param publicB different public key
|
|
1920
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1921
|
-
* @returns shared public key
|
|
1922
|
-
*/
|
|
1923
|
-
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
1924
|
-
if (isProbPub(privateA))
|
|
1925
|
-
throw new Error('first arg must be private key');
|
|
1926
|
-
if (!isProbPub(publicB))
|
|
1927
|
-
throw new Error('second arg must be public key');
|
|
1928
|
-
const b = Point.fromHex(publicB); // check for being on-curve
|
|
1929
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
|
|
1930
|
-
}
|
|
1931
2152
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1932
2153
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1933
2154
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1934
2155
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1935
|
-
const bits2int =
|
|
1936
|
-
function (bytes) {
|
|
1937
|
-
// Our custom check "just in case"
|
|
2156
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
2157
|
+
function bits2int_def(bytes) {
|
|
2158
|
+
// Our custom check "just in case", for protection against DoS
|
|
1938
2159
|
if (bytes.length > 8192)
|
|
1939
2160
|
throw new Error('input is too large');
|
|
1940
2161
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
1941
2162
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
1942
2163
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1943
|
-
const delta = bytes.length * 8 -
|
|
2164
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
1944
2165
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1945
2166
|
};
|
|
1946
|
-
const bits2int_modN =
|
|
1947
|
-
function (bytes) {
|
|
1948
|
-
return
|
|
2167
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
2168
|
+
function bits2int_modN_def(bytes) {
|
|
2169
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1949
2170
|
};
|
|
1950
|
-
//
|
|
1951
|
-
const ORDER_MASK = bitMask(
|
|
2171
|
+
// Pads output with zero as per spec
|
|
2172
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
2173
|
+
/** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
|
|
2174
|
+
function int2octets(num) {
|
|
2175
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
2176
|
+
aInRange('num < 2^' + fnBits, num, _0n$2, ORDER_MASK);
|
|
2177
|
+
return Fn.toBytes(num);
|
|
2178
|
+
}
|
|
2179
|
+
function validateMsgAndHash(message, prehash) {
|
|
2180
|
+
_abytes2(message, undefined, 'message');
|
|
2181
|
+
return prehash ? _abytes2(hash(message), undefined, 'prehashed message') : message;
|
|
2182
|
+
}
|
|
1952
2183
|
/**
|
|
1953
|
-
*
|
|
2184
|
+
* Steps A, D of RFC6979 3.2.
|
|
2185
|
+
* Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
2186
|
+
* Used only in sign, not in verify.
|
|
2187
|
+
*
|
|
2188
|
+
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
2189
|
+
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1954
2190
|
*/
|
|
1955
|
-
function
|
|
1956
|
-
aInRange('num < 2^' + CURVE.nBitLength, num, _0n$2, ORDER_MASK);
|
|
1957
|
-
// works with order, can have different size than numToField!
|
|
1958
|
-
return numberToBytesBE(num, CURVE.nByteLength);
|
|
1959
|
-
}
|
|
1960
|
-
// Steps A, D of RFC6979 3.2
|
|
1961
|
-
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
1962
|
-
// Used only in sign, not in verify.
|
|
1963
|
-
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order,
|
|
1964
|
-
// this will be invalid at least for P521. Also it can be bigger for P224 + SHA256
|
|
1965
|
-
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
2191
|
+
function prepSig(message, privateKey, opts) {
|
|
1966
2192
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1967
2193
|
throw new Error('sign() legacy options not supported');
|
|
1968
|
-
const {
|
|
1969
|
-
|
|
1970
|
-
if (lowS == null)
|
|
1971
|
-
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
1972
|
-
msgHash = ensureBytes('msgHash', msgHash);
|
|
1973
|
-
validateSigVerOpts(opts);
|
|
1974
|
-
if (prehash)
|
|
1975
|
-
msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
2194
|
+
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
2195
|
+
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
1976
2196
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1977
|
-
// with
|
|
2197
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1978
2198
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1979
|
-
const h1int = bits2int_modN(
|
|
1980
|
-
const d =
|
|
2199
|
+
const h1int = bits2int_modN(message);
|
|
2200
|
+
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1981
2201
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1982
2202
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1983
|
-
if (
|
|
2203
|
+
if (extraEntropy != null && extraEntropy !== false) {
|
|
1984
2204
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1985
|
-
|
|
2205
|
+
// gen random bytes OR pass as-is
|
|
2206
|
+
const e = extraEntropy === true ? randomBytes$1(lengths.secretKey) : extraEntropy;
|
|
1986
2207
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
1987
2208
|
}
|
|
1988
2209
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
1989
2210
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
1990
2211
|
// Converts signature params into point w r/s, checks result for validity.
|
|
2212
|
+
// To transform k => Signature:
|
|
2213
|
+
// q = k⋅G
|
|
2214
|
+
// r = q.x mod n
|
|
2215
|
+
// s = k^-1(m + rd) mod n
|
|
2216
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
2217
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
2218
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1991
2219
|
function k2sig(kBytes) {
|
|
1992
2220
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
const
|
|
1998
|
-
const
|
|
2221
|
+
// Important: all mod() calls here must be done over N
|
|
2222
|
+
const k = bits2int(kBytes); // mod n, not mod p
|
|
2223
|
+
if (!Fn.isValidNot0(k))
|
|
2224
|
+
return; // Valid scalars (including k) must be in 1..N-1
|
|
2225
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
2226
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
2227
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
1999
2228
|
if (r === _0n$2)
|
|
2000
2229
|
return;
|
|
2001
|
-
|
|
2002
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
2003
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
2004
|
-
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
|
2230
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
2005
2231
|
if (s === _0n$2)
|
|
2006
2232
|
return;
|
|
2007
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n$
|
|
2233
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n$3); // recovery bit (2 or 3, when q.x > n)
|
|
2008
2234
|
let normS = s;
|
|
2009
2235
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
2010
|
-
normS =
|
|
2236
|
+
normS = Fn.neg(s); // if lowS was passed, ensure s is always
|
|
2011
2237
|
recovery ^= 1; // // in the bottom half of N
|
|
2012
2238
|
}
|
|
2013
2239
|
return new Signature(r, normS, recovery); // use normS, not s
|
|
2014
2240
|
}
|
|
2015
2241
|
return { seed, k2sig };
|
|
2016
2242
|
}
|
|
2017
|
-
const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
|
|
2018
|
-
const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
|
|
2019
2243
|
/**
|
|
2020
|
-
* Signs message hash with a
|
|
2244
|
+
* Signs message hash with a secret key.
|
|
2245
|
+
*
|
|
2021
2246
|
* ```
|
|
2022
|
-
* sign(m, d
|
|
2247
|
+
* sign(m, d) where
|
|
2248
|
+
* k = rfc6979_hmac_drbg(m, d)
|
|
2023
2249
|
* (x, y) = G × k
|
|
2024
2250
|
* r = x mod n
|
|
2025
|
-
* s = (m + dr)/k mod n
|
|
2251
|
+
* s = (m + dr) / k mod n
|
|
2026
2252
|
* ```
|
|
2027
|
-
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
|
2028
|
-
* @param privKey private key
|
|
2029
|
-
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
|
2030
|
-
* @returns signature with recovery param
|
|
2031
2253
|
*/
|
|
2032
|
-
function sign(
|
|
2033
|
-
|
|
2034
|
-
const
|
|
2035
|
-
const drbg = createHmacDrbg(
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
* Verifies a signature against message hash and public key.
|
|
2043
|
-
* Rejects lowS signatures by default: to override,
|
|
2044
|
-
* specify option `{lowS: false}`. Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
|
|
2045
|
-
*
|
|
2046
|
-
* ```
|
|
2047
|
-
* verify(r, s, h, P) where
|
|
2048
|
-
* U1 = hs^-1 mod n
|
|
2049
|
-
* U2 = rs^-1 mod n
|
|
2050
|
-
* R = U1⋅G - U2⋅P
|
|
2051
|
-
* mod(R.x, n) == r
|
|
2052
|
-
* ```
|
|
2053
|
-
*/
|
|
2054
|
-
function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
|
|
2055
|
-
const sg = signature;
|
|
2056
|
-
msgHash = ensureBytes('msgHash', msgHash);
|
|
2057
|
-
publicKey = ensureBytes('publicKey', publicKey);
|
|
2058
|
-
const { lowS, prehash, format } = opts;
|
|
2059
|
-
// Verify opts, deduce signature format
|
|
2060
|
-
validateSigVerOpts(opts);
|
|
2061
|
-
if ('strict' in opts)
|
|
2062
|
-
throw new Error('options.strict was renamed to lowS');
|
|
2063
|
-
if (format !== undefined && format !== 'compact' && format !== 'der')
|
|
2064
|
-
throw new Error('format must be compact or der');
|
|
2254
|
+
function sign(message, secretKey, opts = {}) {
|
|
2255
|
+
message = ensureBytes('message', message);
|
|
2256
|
+
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
2257
|
+
const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac$1);
|
|
2258
|
+
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
2259
|
+
return sig;
|
|
2260
|
+
}
|
|
2261
|
+
function tryParsingSig(sg) {
|
|
2262
|
+
// Try to deduce format
|
|
2263
|
+
let sig = undefined;
|
|
2065
2264
|
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
2066
2265
|
const isObj = !isHex &&
|
|
2067
|
-
!format &&
|
|
2068
|
-
typeof sg === 'object' &&
|
|
2069
2266
|
sg !== null &&
|
|
2267
|
+
typeof sg === 'object' &&
|
|
2070
2268
|
typeof sg.r === 'bigint' &&
|
|
2071
2269
|
typeof sg.s === 'bigint';
|
|
2072
2270
|
if (!isHex && !isObj)
|
|
2073
2271
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2272
|
+
if (isObj) {
|
|
2273
|
+
sig = new Signature(sg.r, sg.s);
|
|
2274
|
+
}
|
|
2275
|
+
else if (isHex) {
|
|
2276
|
+
try {
|
|
2277
|
+
sig = Signature.fromBytes(ensureBytes('sig', sg), 'der');
|
|
2278
|
+
}
|
|
2279
|
+
catch (derError) {
|
|
2280
|
+
if (!(derError instanceof DER.Err))
|
|
2281
|
+
throw derError;
|
|
2282
|
+
}
|
|
2283
|
+
if (!sig) {
|
|
2082
2284
|
try {
|
|
2083
|
-
|
|
2084
|
-
_sig = Signature.fromDER(sg);
|
|
2285
|
+
sig = Signature.fromBytes(ensureBytes('sig', sg), 'compact');
|
|
2085
2286
|
}
|
|
2086
|
-
catch (
|
|
2087
|
-
|
|
2088
|
-
throw derError;
|
|
2287
|
+
catch (error) {
|
|
2288
|
+
return false;
|
|
2089
2289
|
}
|
|
2090
|
-
if (!_sig && format !== 'der')
|
|
2091
|
-
_sig = Signature.fromCompact(sg);
|
|
2092
2290
|
}
|
|
2093
|
-
P = Point.fromHex(publicKey);
|
|
2094
|
-
}
|
|
2095
|
-
catch (error) {
|
|
2096
|
-
return false;
|
|
2097
2291
|
}
|
|
2098
|
-
if (!
|
|
2292
|
+
if (!sig)
|
|
2099
2293
|
return false;
|
|
2100
|
-
|
|
2294
|
+
return sig;
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Verifies a signature against message and public key.
|
|
2298
|
+
* Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
|
|
2299
|
+
* Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
|
|
2300
|
+
*
|
|
2301
|
+
* ```
|
|
2302
|
+
* verify(r, s, h, P) where
|
|
2303
|
+
* u1 = hs^-1 mod n
|
|
2304
|
+
* u2 = rs^-1 mod n
|
|
2305
|
+
* R = u1⋅G + u2⋅P
|
|
2306
|
+
* mod(R.x, n) == r
|
|
2307
|
+
* ```
|
|
2308
|
+
*/
|
|
2309
|
+
function verify(signature, message, publicKey, opts = {}) {
|
|
2310
|
+
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
2311
|
+
publicKey = ensureBytes('publicKey', publicKey);
|
|
2312
|
+
message = validateMsgAndHash(ensureBytes('message', message), prehash);
|
|
2313
|
+
if ('strict' in opts)
|
|
2314
|
+
throw new Error('options.strict was renamed to lowS');
|
|
2315
|
+
const sig = format === undefined
|
|
2316
|
+
? tryParsingSig(signature)
|
|
2317
|
+
: Signature.fromBytes(ensureBytes('sig', signature), format);
|
|
2318
|
+
if (sig === false)
|
|
2101
2319
|
return false;
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2320
|
+
try {
|
|
2321
|
+
const P = Point.fromBytes(publicKey);
|
|
2322
|
+
if (lowS && sig.hasHighS())
|
|
2323
|
+
return false;
|
|
2324
|
+
const { r, s } = sig;
|
|
2325
|
+
const h = bits2int_modN(message); // mod n, not mod p
|
|
2326
|
+
const is = Fn.inv(s); // s^-1 mod n
|
|
2327
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
2328
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
2329
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
|
|
2330
|
+
if (R.is0())
|
|
2331
|
+
return false;
|
|
2332
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
2333
|
+
return v === r;
|
|
2334
|
+
}
|
|
2335
|
+
catch (e) {
|
|
2111
2336
|
return false;
|
|
2112
|
-
|
|
2113
|
-
return v === r;
|
|
2337
|
+
}
|
|
2114
2338
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2339
|
+
function recoverPublicKey(signature, message, opts = {}) {
|
|
2340
|
+
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
2341
|
+
message = validateMsgAndHash(message, prehash);
|
|
2342
|
+
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
2343
|
+
}
|
|
2344
|
+
return Object.freeze({
|
|
2345
|
+
keygen,
|
|
2117
2346
|
getPublicKey,
|
|
2118
2347
|
getSharedSecret,
|
|
2348
|
+
utils,
|
|
2349
|
+
lengths,
|
|
2350
|
+
Point,
|
|
2119
2351
|
sign,
|
|
2120
2352
|
verify,
|
|
2121
|
-
|
|
2353
|
+
recoverPublicKey,
|
|
2122
2354
|
Signature,
|
|
2123
|
-
|
|
2355
|
+
hash,
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
function _weierstrass_legacy_opts_to_new(c) {
|
|
2359
|
+
const CURVE = {
|
|
2360
|
+
a: c.a,
|
|
2361
|
+
b: c.b,
|
|
2362
|
+
p: c.Fp.ORDER,
|
|
2363
|
+
n: c.n,
|
|
2364
|
+
h: c.h,
|
|
2365
|
+
Gx: c.Gx,
|
|
2366
|
+
Gy: c.Gy,
|
|
2367
|
+
};
|
|
2368
|
+
const Fp = c.Fp;
|
|
2369
|
+
let allowedLengths = c.allowedPrivateKeyLengths
|
|
2370
|
+
? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
|
|
2371
|
+
: undefined;
|
|
2372
|
+
const Fn = Field(CURVE.n, {
|
|
2373
|
+
BITS: c.nBitLength,
|
|
2374
|
+
allowedLengths: allowedLengths,
|
|
2375
|
+
modFromBytes: c.wrapPrivateKey,
|
|
2376
|
+
});
|
|
2377
|
+
const curveOpts = {
|
|
2378
|
+
Fp,
|
|
2379
|
+
Fn,
|
|
2380
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
2381
|
+
endo: c.endo,
|
|
2382
|
+
isTorsionFree: c.isTorsionFree,
|
|
2383
|
+
clearCofactor: c.clearCofactor,
|
|
2384
|
+
fromBytes: c.fromBytes,
|
|
2385
|
+
toBytes: c.toBytes,
|
|
2386
|
+
};
|
|
2387
|
+
return { CURVE, curveOpts };
|
|
2388
|
+
}
|
|
2389
|
+
function _ecdsa_legacy_opts_to_new(c) {
|
|
2390
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
2391
|
+
const ecdsaOpts = {
|
|
2392
|
+
hmac: c.hmac,
|
|
2393
|
+
randomBytes: c.randomBytes,
|
|
2394
|
+
lowS: c.lowS,
|
|
2395
|
+
bits2int: c.bits2int,
|
|
2396
|
+
bits2int_modN: c.bits2int_modN,
|
|
2124
2397
|
};
|
|
2398
|
+
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
2399
|
+
}
|
|
2400
|
+
function _ecdsa_new_output_to_legacy(c, _ecdsa) {
|
|
2401
|
+
const Point = _ecdsa.Point;
|
|
2402
|
+
return Object.assign({}, _ecdsa, {
|
|
2403
|
+
ProjectivePoint: Point,
|
|
2404
|
+
CURVE: Object.assign({}, c, nLength(Point.Fn.ORDER, Point.Fn.BITS)),
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
// _ecdsa_legacy
|
|
2408
|
+
function weierstrass(c) {
|
|
2409
|
+
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
2410
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
2411
|
+
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
2412
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
2125
2413
|
}
|
|
2126
2414
|
|
|
2415
|
+
/**
|
|
2416
|
+
* Utilities for short weierstrass curves, combined with noble-hashes.
|
|
2417
|
+
* @module
|
|
2418
|
+
*/
|
|
2127
2419
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2128
|
-
|
|
2129
|
-
function getHash(hash) {
|
|
2130
|
-
return {
|
|
2131
|
-
hash,
|
|
2132
|
-
hmac: (key, ...msgs) => hmac(hash, key, concatBytes$1(...msgs)),
|
|
2133
|
-
randomBytes,
|
|
2134
|
-
};
|
|
2135
|
-
}
|
|
2420
|
+
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
|
2136
2421
|
function createCurve(curveDef, defHash) {
|
|
2137
|
-
const create = (hash) => weierstrass({ ...curveDef,
|
|
2138
|
-
return
|
|
2422
|
+
const create = (hash) => weierstrass({ ...curveDef, hash: hash });
|
|
2423
|
+
return { ...create(defHash), create };
|
|
2139
2424
|
}
|
|
2140
2425
|
|
|
2426
|
+
/**
|
|
2427
|
+
* Internal module for NIST P256, P384, P521 curves.
|
|
2428
|
+
* Do not use for now.
|
|
2429
|
+
* @module
|
|
2430
|
+
*/
|
|
2141
2431
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2142
|
-
//
|
|
2143
|
-
//
|
|
2144
|
-
const
|
|
2145
|
-
|
|
2146
|
-
const CURVE_B$4 = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b');
|
|
2147
|
-
// prettier-ignore
|
|
2148
|
-
const p256 = createCurve({
|
|
2149
|
-
a: CURVE_A$4, // Equation params: a, b
|
|
2150
|
-
b: CURVE_B$4,
|
|
2151
|
-
Fp: Fp256, // Field: 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
|
2152
|
-
// Curve order, total count of valid points in the field
|
|
2432
|
+
// p = 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n - 1n
|
|
2433
|
+
// a = Fp256.create(BigInt('-3'));
|
|
2434
|
+
const p256_CURVE = {
|
|
2435
|
+
p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
|
2153
2436
|
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
|
2154
|
-
|
|
2437
|
+
h: BigInt(1),
|
|
2438
|
+
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
|
2439
|
+
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
|
2155
2440
|
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
|
2156
2441
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2162
|
-
// NIST secp384r1 aka p384
|
|
2163
|
-
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
|
2164
|
-
// Field over which we'll do calculations.
|
|
2165
|
-
// prettier-ignore
|
|
2166
|
-
const P$1 = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
|
|
2167
|
-
const Fp384 = Field(P$1);
|
|
2168
|
-
const CURVE_A$3 = Fp384.create(BigInt('-3'));
|
|
2169
|
-
// prettier-ignore
|
|
2170
|
-
const CURVE_B$3 = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
|
2171
|
-
// prettier-ignore
|
|
2172
|
-
const p384 = createCurve({
|
|
2173
|
-
a: CURVE_A$3, // Equation params: a, b
|
|
2174
|
-
b: CURVE_B$3,
|
|
2175
|
-
Fp: Fp384, // Field: 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
|
2176
|
-
// Curve order, total count of valid points in the field.
|
|
2442
|
+
};
|
|
2443
|
+
// p = 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
|
2444
|
+
const p384_CURVE = {
|
|
2445
|
+
p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff'),
|
|
2177
2446
|
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
|
2178
|
-
|
|
2447
|
+
h: BigInt(1),
|
|
2448
|
+
a: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc'),
|
|
2449
|
+
b: BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef'),
|
|
2179
2450
|
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
|
2180
2451
|
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
|
2452
|
+
};
|
|
2453
|
+
// p = 2n**521n - 1n
|
|
2454
|
+
const p521_CURVE = {
|
|
2455
|
+
p: BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
|
|
2456
|
+
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
|
2181
2457
|
h: BigInt(1),
|
|
2182
|
-
|
|
2183
|
-
}, sha384);
|
|
2184
|
-
|
|
2185
|
-
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2186
|
-
// NIST secp521r1 aka p521
|
|
2187
|
-
// Note that it's 521, which differs from 512 of its hash function.
|
|
2188
|
-
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
|
|
2189
|
-
// Field over which we'll do calculations.
|
|
2190
|
-
// prettier-ignore
|
|
2191
|
-
const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
2192
|
-
const Fp521 = Field(P);
|
|
2193
|
-
const CURVE = {
|
|
2194
|
-
a: Fp521.create(BigInt('-3')),
|
|
2458
|
+
a: BigInt('0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc'),
|
|
2195
2459
|
b: BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'),
|
|
2196
|
-
Fp: Fp521,
|
|
2197
|
-
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
|
2198
2460
|
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
|
|
2199
2461
|
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
|
2200
|
-
h: BigInt(1),
|
|
2201
2462
|
};
|
|
2202
|
-
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2463
|
+
const Fp256 = Field(p256_CURVE.p);
|
|
2464
|
+
const Fp384 = Field(p384_CURVE.p);
|
|
2465
|
+
const Fp521 = Field(p521_CURVE.p);
|
|
2466
|
+
/** NIST P256 (aka secp256r1, prime256v1) curve, ECDSA and ECDH methods. */
|
|
2467
|
+
const p256$1 = createCurve({ ...p256_CURVE, Fp: Fp256, lowS: false }, sha256);
|
|
2468
|
+
// export const p256_oprf: OPRF = createORPF({
|
|
2469
|
+
// name: 'P256-SHA256',
|
|
2470
|
+
// Point: p256.Point,
|
|
2471
|
+
// hash: sha256,
|
|
2472
|
+
// hashToGroup: p256_hasher.hashToCurve,
|
|
2473
|
+
// hashToScalar: p256_hasher.hashToScalar,
|
|
2474
|
+
// });
|
|
2475
|
+
/** NIST P384 (aka secp384r1) curve, ECDSA and ECDH methods. */
|
|
2476
|
+
const p384$1 = createCurve({ ...p384_CURVE, Fp: Fp384, lowS: false }, sha384);
|
|
2477
|
+
// export const p384_oprf: OPRF = createORPF({
|
|
2478
|
+
// name: 'P384-SHA384',
|
|
2479
|
+
// Point: p384.Point,
|
|
2480
|
+
// hash: sha384,
|
|
2481
|
+
// hashToGroup: p384_hasher.hashToCurve,
|
|
2482
|
+
// hashToScalar: p384_hasher.hashToScalar,
|
|
2483
|
+
// });
|
|
2484
|
+
// const Fn521 = Field(p521_CURVE.n, { allowedScalarLengths: [65, 66] });
|
|
2485
|
+
/** NIST P521 (aka secp521r1) curve, ECDSA and ECDH methods. */
|
|
2486
|
+
const p521$1 = createCurve({ ...p521_CURVE, Fp: Fp521, lowS: false, allowedPrivateKeyLengths: [130, 131, 132] }, sha512);
|
|
2487
|
+
// export const p521_oprf: OPRF = createORPF({
|
|
2488
|
+
// name: 'P521-SHA512',
|
|
2489
|
+
// Point: p521.Point,
|
|
2490
|
+
// hash: sha512,
|
|
2491
|
+
// hashToGroup: p521_hasher.hashToCurve,
|
|
2492
|
+
// hashToScalar: p521_hasher.hashToScalar, // produces L=98 just like in RFC
|
|
2493
|
+
// });
|
|
2494
|
+
|
|
2495
|
+
/**
|
|
2496
|
+
* NIST secp256r1 aka p256.
|
|
2497
|
+
* @module
|
|
2498
|
+
*/
|
|
2499
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2500
|
+
/** @deprecated use `import { p256 } from '@noble/curves/nist.js';` */
|
|
2501
|
+
const p256 = p256$1;
|
|
2215
2502
|
|
|
2503
|
+
/**
|
|
2504
|
+
* NIST secp384r1 aka p384.
|
|
2505
|
+
* @module
|
|
2506
|
+
*/
|
|
2216
2507
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
const _0n$1 = BigInt(0), _1n$3 = BigInt(1), _2n$2 = BigInt(2), _8n = BigInt(8);
|
|
2221
|
-
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
|
2222
|
-
const VERIFY_DEFAULT = { zip215: true };
|
|
2223
|
-
function validateOpts$1(curve) {
|
|
2224
|
-
const opts = validateBasic(curve);
|
|
2225
|
-
validateObject(curve, {
|
|
2226
|
-
hash: 'function',
|
|
2227
|
-
a: 'bigint',
|
|
2228
|
-
d: 'bigint',
|
|
2229
|
-
randomBytes: 'function',
|
|
2230
|
-
}, {
|
|
2231
|
-
adjustScalarBytes: 'function',
|
|
2232
|
-
domain: 'function',
|
|
2233
|
-
uvRatio: 'function',
|
|
2234
|
-
mapToCurve: 'function',
|
|
2235
|
-
});
|
|
2236
|
-
// Set defaults
|
|
2237
|
-
return Object.freeze({ ...opts });
|
|
2238
|
-
}
|
|
2508
|
+
/** @deprecated use `import { p384 } from '@noble/curves/nist.js';` */
|
|
2509
|
+
const p384 = p384$1;
|
|
2510
|
+
|
|
2239
2511
|
/**
|
|
2240
|
-
*
|
|
2241
|
-
* @
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2512
|
+
* NIST secp521r1 aka p521.
|
|
2513
|
+
* @module
|
|
2514
|
+
*/
|
|
2515
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2516
|
+
/** @deprecated use `import { p521 } from '@noble/curves/nist.js';` */
|
|
2517
|
+
const p521 = p521$1;
|
|
2518
|
+
|
|
2519
|
+
/**
|
|
2520
|
+
* Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
|
|
2521
|
+
* For design rationale of types / exports, see weierstrass module documentation.
|
|
2522
|
+
* Untwisted Edwards curves exist, but they aren't used in real-world protocols.
|
|
2523
|
+
* @module
|
|
2245
2524
|
*/
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2525
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2526
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
2527
|
+
// prettier-ignore
|
|
2528
|
+
const _0n$1 = BigInt(0), _1n$2 = BigInt(1), _2n$3 = BigInt(2), _8n = BigInt(8);
|
|
2529
|
+
function isEdValidXY(Fp, CURVE, x, y) {
|
|
2530
|
+
const x2 = Fp.sqr(x);
|
|
2531
|
+
const y2 = Fp.sqr(y);
|
|
2532
|
+
const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
|
|
2533
|
+
const right = Fp.add(Fp.ONE, Fp.mul(CURVE.d, Fp.mul(x2, y2)));
|
|
2534
|
+
return Fp.eql(left, right);
|
|
2535
|
+
}
|
|
2536
|
+
function edwards(params, extraOpts = {}) {
|
|
2537
|
+
const validated = _createCurveFields('edwards', params, extraOpts, extraOpts.FpFnLE);
|
|
2538
|
+
const { Fp, Fn } = validated;
|
|
2539
|
+
let CURVE = validated.CURVE;
|
|
2540
|
+
const { h: cofactor } = CURVE;
|
|
2541
|
+
_validateObject(extraOpts, {}, { uvRatio: 'function' });
|
|
2249
2542
|
// Important:
|
|
2250
2543
|
// There are some places where Fp.BYTES is used instead of nByteLength.
|
|
2251
2544
|
// So far, everything has been tested with curves of Fp.BYTES == nByteLength.
|
|
2252
2545
|
// TODO: test and find curves which behave otherwise.
|
|
2253
|
-
const MASK = _2n$
|
|
2254
|
-
const modP = Fp.create; // Function overrides
|
|
2255
|
-
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
2546
|
+
const MASK = _2n$3 << (BigInt(Fn.BYTES * 8) - _1n$2);
|
|
2547
|
+
const modP = (n) => Fp.create(n); // Function overrides
|
|
2256
2548
|
// sqrt(u/v)
|
|
2257
|
-
const uvRatio =
|
|
2549
|
+
const uvRatio = extraOpts.uvRatio ||
|
|
2258
2550
|
((u, v) => {
|
|
2259
2551
|
try {
|
|
2260
|
-
return { isValid: true, value: Fp.sqrt(
|
|
2552
|
+
return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
|
|
2261
2553
|
}
|
|
2262
2554
|
catch (e) {
|
|
2263
2555
|
return { isValid: false, value: _0n$1 };
|
|
2264
2556
|
}
|
|
2265
2557
|
});
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2558
|
+
// Validate whether the passed curve params are valid.
|
|
2559
|
+
// equation ax² + y² = 1 + dx²y² should work for generator point.
|
|
2560
|
+
if (!isEdValidXY(Fp, CURVE, CURVE.Gx, CURVE.Gy))
|
|
2561
|
+
throw new Error('bad curve params: generator point');
|
|
2562
|
+
/**
|
|
2563
|
+
* Asserts coordinate is valid: 0 <= n < MASK.
|
|
2564
|
+
* Coordinates >= Fp.ORDER are allowed for zip215.
|
|
2565
|
+
*/
|
|
2566
|
+
function acoord(title, n, banZero = false) {
|
|
2567
|
+
const min = banZero ? _1n$2 : _0n$1;
|
|
2568
|
+
aInRange('coordinate ' + title, n, min, MASK);
|
|
2569
|
+
return n;
|
|
2278
2570
|
}
|
|
2279
|
-
function
|
|
2571
|
+
function aextpoint(other) {
|
|
2280
2572
|
if (!(other instanceof Point))
|
|
2281
2573
|
throw new Error('ExtendedPoint expected');
|
|
2282
2574
|
}
|
|
2283
2575
|
// Converts Extended point to default (x, y) coordinates.
|
|
2284
2576
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
2285
2577
|
const toAffineMemo = memoized((p, iz) => {
|
|
2286
|
-
const {
|
|
2578
|
+
const { X, Y, Z } = p;
|
|
2287
2579
|
const is0 = p.is0();
|
|
2288
2580
|
if (iz == null)
|
|
2289
|
-
iz = is0 ? _8n : Fp.inv(
|
|
2290
|
-
const
|
|
2291
|
-
const
|
|
2292
|
-
const zz =
|
|
2581
|
+
iz = is0 ? _8n : Fp.inv(Z); // 8 was chosen arbitrarily
|
|
2582
|
+
const x = modP(X * iz);
|
|
2583
|
+
const y = modP(Y * iz);
|
|
2584
|
+
const zz = Fp.mul(Z, iz);
|
|
2293
2585
|
if (is0)
|
|
2294
|
-
return { x: _0n$1, y: _1n$
|
|
2295
|
-
if (zz !== _1n$
|
|
2586
|
+
return { x: _0n$1, y: _1n$2 };
|
|
2587
|
+
if (zz !== _1n$2)
|
|
2296
2588
|
throw new Error('invZ was invalid');
|
|
2297
|
-
return { x
|
|
2589
|
+
return { x, y };
|
|
2298
2590
|
});
|
|
2299
2591
|
const assertValidMemo = memoized((p) => {
|
|
2300
2592
|
const { a, d } = CURVE;
|
|
@@ -2302,7 +2594,7 @@ function twistedEdwards(curveDef) {
|
|
|
2302
2594
|
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
2303
2595
|
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
2304
2596
|
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
2305
|
-
const {
|
|
2597
|
+
const { X, Y, Z, T } = p;
|
|
2306
2598
|
const X2 = modP(X * X); // X²
|
|
2307
2599
|
const Y2 = modP(Y * Y); // Y²
|
|
2308
2600
|
const Z2 = modP(Z * Z); // Z²
|
|
@@ -2319,56 +2611,84 @@ function twistedEdwards(curveDef) {
|
|
|
2319
2611
|
throw new Error('bad point: equation left != right (2)');
|
|
2320
2612
|
return true;
|
|
2321
2613
|
});
|
|
2322
|
-
// Extended Point works in extended coordinates: (
|
|
2614
|
+
// Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
|
|
2323
2615
|
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
|
2324
2616
|
class Point {
|
|
2325
|
-
constructor(
|
|
2326
|
-
this.
|
|
2327
|
-
this.
|
|
2328
|
-
this.
|
|
2329
|
-
this.
|
|
2330
|
-
aCoordinate('x', ex);
|
|
2331
|
-
aCoordinate('y', ey);
|
|
2332
|
-
aCoordinate('z', ez);
|
|
2333
|
-
aCoordinate('t', et);
|
|
2617
|
+
constructor(X, Y, Z, T) {
|
|
2618
|
+
this.X = acoord('x', X);
|
|
2619
|
+
this.Y = acoord('y', Y);
|
|
2620
|
+
this.Z = acoord('z', Z, true);
|
|
2621
|
+
this.T = acoord('t', T);
|
|
2334
2622
|
Object.freeze(this);
|
|
2335
2623
|
}
|
|
2336
|
-
|
|
2337
|
-
return
|
|
2338
|
-
}
|
|
2339
|
-
get y() {
|
|
2340
|
-
return this.toAffine().y;
|
|
2624
|
+
static CURVE() {
|
|
2625
|
+
return CURVE;
|
|
2341
2626
|
}
|
|
2342
2627
|
static fromAffine(p) {
|
|
2343
2628
|
if (p instanceof Point)
|
|
2344
2629
|
throw new Error('extended point not allowed');
|
|
2345
2630
|
const { x, y } = p || {};
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
return new Point(x, y, _1n$
|
|
2631
|
+
acoord('x', x);
|
|
2632
|
+
acoord('y', y);
|
|
2633
|
+
return new Point(x, y, _1n$2, modP(x * y));
|
|
2349
2634
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2635
|
+
// Uses algo from RFC8032 5.1.3.
|
|
2636
|
+
static fromBytes(bytes, zip215 = false) {
|
|
2637
|
+
const len = Fp.BYTES;
|
|
2638
|
+
const { a, d } = CURVE;
|
|
2639
|
+
bytes = copyBytes(_abytes2(bytes, len, 'point'));
|
|
2640
|
+
_abool2(zip215, 'zip215');
|
|
2641
|
+
const normed = copyBytes(bytes); // copy again, we'll manipulate it
|
|
2642
|
+
const lastByte = bytes[len - 1]; // select last byte
|
|
2643
|
+
normed[len - 1] = lastByte & -129; // clear last bit
|
|
2644
|
+
const y = bytesToNumberLE(normed);
|
|
2645
|
+
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
|
2646
|
+
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
|
2647
|
+
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
2648
|
+
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
2649
|
+
const max = zip215 ? MASK : Fp.ORDER;
|
|
2650
|
+
aInRange('point.y', y, _0n$1, max);
|
|
2651
|
+
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
|
2652
|
+
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
|
2653
|
+
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
|
2654
|
+
const u = modP(y2 - _1n$2); // u = y² - 1
|
|
2655
|
+
const v = modP(d * y2 - a); // v = d y² + 1.
|
|
2656
|
+
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
|
2657
|
+
if (!isValid)
|
|
2658
|
+
throw new Error('bad point: invalid y coordinate');
|
|
2659
|
+
const isXOdd = (x & _1n$2) === _1n$2; // There are 2 square roots. Use x_0 bit to select proper
|
|
2660
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
|
2661
|
+
if (!zip215 && x === _0n$1 && isLastByteOdd)
|
|
2662
|
+
// if x=0 and x_0 = 1, fail
|
|
2663
|
+
throw new Error('bad point: x=0 and x_0=1');
|
|
2664
|
+
if (isLastByteOdd !== isXOdd)
|
|
2665
|
+
x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
|
2666
|
+
return Point.fromAffine({ x, y });
|
|
2353
2667
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
return pippenger(Point, Fn, points, scalars);
|
|
2668
|
+
static fromHex(bytes, zip215 = false) {
|
|
2669
|
+
return Point.fromBytes(ensureBytes('point', bytes), zip215);
|
|
2357
2670
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2671
|
+
get x() {
|
|
2672
|
+
return this.toAffine().x;
|
|
2673
|
+
}
|
|
2674
|
+
get y() {
|
|
2675
|
+
return this.toAffine().y;
|
|
2361
2676
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2677
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
2678
|
+
wnaf.createCache(this, windowSize);
|
|
2679
|
+
if (!isLazy)
|
|
2680
|
+
this.multiply(_2n$3); // random number
|
|
2681
|
+
return this;
|
|
2682
|
+
}
|
|
2683
|
+
// Useful in fromAffine() - not for fromBytes(), which always created valid points.
|
|
2364
2684
|
assertValidity() {
|
|
2365
2685
|
assertValidMemo(this);
|
|
2366
2686
|
}
|
|
2367
2687
|
// Compare one point to another.
|
|
2368
2688
|
equals(other) {
|
|
2369
|
-
|
|
2370
|
-
const {
|
|
2371
|
-
const {
|
|
2689
|
+
aextpoint(other);
|
|
2690
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
2691
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
2372
2692
|
const X1Z2 = modP(X1 * Z2);
|
|
2373
2693
|
const X2Z1 = modP(X2 * Z1);
|
|
2374
2694
|
const Y1Z2 = modP(Y1 * Z2);
|
|
@@ -2380,17 +2700,17 @@ function twistedEdwards(curveDef) {
|
|
|
2380
2700
|
}
|
|
2381
2701
|
negate() {
|
|
2382
2702
|
// Flips point sign to a negative one (-x, y in affine coords)
|
|
2383
|
-
return new Point(modP(-this.
|
|
2703
|
+
return new Point(modP(-this.X), this.Y, this.Z, modP(-this.T));
|
|
2384
2704
|
}
|
|
2385
2705
|
// Fast algo for doubling Extended Point.
|
|
2386
2706
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
2387
2707
|
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
|
2388
2708
|
double() {
|
|
2389
2709
|
const { a } = CURVE;
|
|
2390
|
-
const {
|
|
2710
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
2391
2711
|
const A = modP(X1 * X1); // A = X12
|
|
2392
2712
|
const B = modP(Y1 * Y1); // B = Y12
|
|
2393
|
-
const C = modP(_2n$
|
|
2713
|
+
const C = modP(_2n$3 * modP(Z1 * Z1)); // C = 2*Z12
|
|
2394
2714
|
const D = modP(a * A); // D = a*A
|
|
2395
2715
|
const x1y1 = X1 + Y1;
|
|
2396
2716
|
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
|
|
@@ -2407,31 +2727,10 @@ function twistedEdwards(curveDef) {
|
|
|
2407
2727
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
|
2408
2728
|
// Cost: 9M + 1*a + 1*d + 7add.
|
|
2409
2729
|
add(other) {
|
|
2410
|
-
|
|
2730
|
+
aextpoint(other);
|
|
2411
2731
|
const { a, d } = CURVE;
|
|
2412
|
-
const {
|
|
2413
|
-
const {
|
|
2414
|
-
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
|
2415
|
-
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
|
2416
|
-
// Cost: 8M + 8add + 2*2.
|
|
2417
|
-
// Note: It does not check whether the `other` point is valid.
|
|
2418
|
-
if (a === BigInt(-1)) {
|
|
2419
|
-
const A = modP((Y1 - X1) * (Y2 + X2));
|
|
2420
|
-
const B = modP((Y1 + X1) * (Y2 - X2));
|
|
2421
|
-
const F = modP(B - A);
|
|
2422
|
-
if (F === _0n$1)
|
|
2423
|
-
return this.double(); // Same point. Tests say it doesn't affect timing
|
|
2424
|
-
const C = modP(Z1 * _2n$2 * T2);
|
|
2425
|
-
const D = modP(T1 * _2n$2 * Z2);
|
|
2426
|
-
const E = D + C;
|
|
2427
|
-
const G = B + A;
|
|
2428
|
-
const H = D - C;
|
|
2429
|
-
const X3 = modP(E * F);
|
|
2430
|
-
const Y3 = modP(G * H);
|
|
2431
|
-
const T3 = modP(E * H);
|
|
2432
|
-
const Z3 = modP(F * G);
|
|
2433
|
-
return new Point(X3, Y3, Z3, T3);
|
|
2434
|
-
}
|
|
2732
|
+
const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
|
|
2733
|
+
const { X: X2, Y: Y2, Z: Z2, T: T2 } = other;
|
|
2435
2734
|
const A = modP(X1 * X2); // A = X1*X2
|
|
2436
2735
|
const B = modP(Y1 * Y2); // B = Y1*Y2
|
|
2437
2736
|
const C = modP(T1 * d * T2); // C = T1*d*T2
|
|
@@ -2449,15 +2748,13 @@ function twistedEdwards(curveDef) {
|
|
|
2449
2748
|
subtract(other) {
|
|
2450
2749
|
return this.add(other.negate());
|
|
2451
2750
|
}
|
|
2452
|
-
wNAF(n) {
|
|
2453
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
2454
|
-
}
|
|
2455
2751
|
// Constant-time multiplication.
|
|
2456
2752
|
multiply(scalar) {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2753
|
+
// 1 <= scalar < L
|
|
2754
|
+
if (!Fn.isValidNot0(scalar))
|
|
2755
|
+
throw new Error('invalid scalar: expected 1 <= sc < curve.n');
|
|
2756
|
+
const { p, f } = wnaf.cached(this, scalar, (p) => normalizeZ(Point, p));
|
|
2757
|
+
return normalizeZ(Point, [p, f])[0];
|
|
2461
2758
|
}
|
|
2462
2759
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
2463
2760
|
// It's faster, but should only be used when you don't care about
|
|
@@ -2465,13 +2762,14 @@ function twistedEdwards(curveDef) {
|
|
|
2465
2762
|
// Does NOT allow scalars higher than CURVE.n.
|
|
2466
2763
|
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
|
|
2467
2764
|
multiplyUnsafe(scalar, acc = Point.ZERO) {
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2765
|
+
// 0 <= scalar < L
|
|
2766
|
+
if (!Fn.isValid(scalar))
|
|
2767
|
+
throw new Error('invalid scalar: expected 0 <= sc < curve.n');
|
|
2768
|
+
if (scalar === _0n$1)
|
|
2769
|
+
return Point.ZERO;
|
|
2770
|
+
if (this.is0() || scalar === _1n$2)
|
|
2473
2771
|
return this;
|
|
2474
|
-
return wnaf.
|
|
2772
|
+
return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p), acc);
|
|
2475
2773
|
}
|
|
2476
2774
|
// Checks if point is of small order.
|
|
2477
2775
|
// If you add something to small order point, you will have "dirty"
|
|
@@ -2483,80 +2781,102 @@ function twistedEdwards(curveDef) {
|
|
|
2483
2781
|
// Multiplies point by curve order and checks if the result is 0.
|
|
2484
2782
|
// Returns `false` is the point is dirty.
|
|
2485
2783
|
isTorsionFree() {
|
|
2486
|
-
return wnaf.
|
|
2784
|
+
return wnaf.unsafe(this, CURVE.n).is0();
|
|
2487
2785
|
}
|
|
2488
2786
|
// Converts Extended point to default (x, y) coordinates.
|
|
2489
2787
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
2490
|
-
toAffine(
|
|
2491
|
-
return toAffineMemo(this,
|
|
2788
|
+
toAffine(invertedZ) {
|
|
2789
|
+
return toAffineMemo(this, invertedZ);
|
|
2492
2790
|
}
|
|
2493
2791
|
clearCofactor() {
|
|
2494
|
-
|
|
2495
|
-
if (cofactor === _1n$3)
|
|
2792
|
+
if (cofactor === _1n$2)
|
|
2496
2793
|
return this;
|
|
2497
2794
|
return this.multiplyUnsafe(cofactor);
|
|
2498
2795
|
}
|
|
2499
|
-
|
|
2500
|
-
// Uses algo from RFC8032 5.1.3.
|
|
2501
|
-
static fromHex(hex, zip215 = false) {
|
|
2502
|
-
const { d, a } = CURVE;
|
|
2503
|
-
const len = Fp.BYTES;
|
|
2504
|
-
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
|
2505
|
-
abool('zip215', zip215);
|
|
2506
|
-
const normed = hex.slice(); // copy again, we'll manipulate it
|
|
2507
|
-
const lastByte = hex[len - 1]; // select last byte
|
|
2508
|
-
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
2509
|
-
const y = bytesToNumberLE(normed);
|
|
2510
|
-
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
|
2511
|
-
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
|
2512
|
-
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
2513
|
-
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
2514
|
-
const max = zip215 ? MASK : Fp.ORDER;
|
|
2515
|
-
aInRange('pointHex.y', y, _0n$1, max);
|
|
2516
|
-
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
|
2517
|
-
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
|
2518
|
-
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
|
2519
|
-
const u = modP(y2 - _1n$3); // u = y² - 1
|
|
2520
|
-
const v = modP(d * y2 - a); // v = d y² + 1.
|
|
2521
|
-
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
|
2522
|
-
if (!isValid)
|
|
2523
|
-
throw new Error('Point.fromHex: invalid y coordinate');
|
|
2524
|
-
const isXOdd = (x & _1n$3) === _1n$3; // There are 2 square roots. Use x_0 bit to select proper
|
|
2525
|
-
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
|
2526
|
-
if (!zip215 && x === _0n$1 && isLastByteOdd)
|
|
2527
|
-
// if x=0 and x_0 = 1, fail
|
|
2528
|
-
throw new Error('Point.fromHex: x=0 and x_0=1');
|
|
2529
|
-
if (isLastByteOdd !== isXOdd)
|
|
2530
|
-
x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
|
2531
|
-
return Point.fromAffine({ x, y });
|
|
2532
|
-
}
|
|
2533
|
-
static fromPrivateKey(privKey) {
|
|
2534
|
-
return getExtendedPublicKey(privKey).point;
|
|
2535
|
-
}
|
|
2536
|
-
toRawBytes() {
|
|
2796
|
+
toBytes() {
|
|
2537
2797
|
const { x, y } = this.toAffine();
|
|
2538
|
-
|
|
2539
|
-
bytes
|
|
2540
|
-
|
|
2798
|
+
// Fp.toBytes() allows non-canonical encoding of y (>= p).
|
|
2799
|
+
const bytes = Fp.toBytes(y);
|
|
2800
|
+
// Each y has 2 valid points: (x, y), (x,-y).
|
|
2801
|
+
// When compressing, it's enough to store y and use the last byte to encode sign of x
|
|
2802
|
+
bytes[bytes.length - 1] |= x & _1n$2 ? 0x80 : 0;
|
|
2803
|
+
return bytes;
|
|
2541
2804
|
}
|
|
2542
2805
|
toHex() {
|
|
2543
|
-
return bytesToHex(this.
|
|
2806
|
+
return bytesToHex(this.toBytes());
|
|
2807
|
+
}
|
|
2808
|
+
toString() {
|
|
2809
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
2810
|
+
}
|
|
2811
|
+
// TODO: remove
|
|
2812
|
+
get ex() {
|
|
2813
|
+
return this.X;
|
|
2814
|
+
}
|
|
2815
|
+
get ey() {
|
|
2816
|
+
return this.Y;
|
|
2817
|
+
}
|
|
2818
|
+
get ez() {
|
|
2819
|
+
return this.Z;
|
|
2820
|
+
}
|
|
2821
|
+
get et() {
|
|
2822
|
+
return this.T;
|
|
2823
|
+
}
|
|
2824
|
+
static normalizeZ(points) {
|
|
2825
|
+
return normalizeZ(Point, points);
|
|
2826
|
+
}
|
|
2827
|
+
static msm(points, scalars) {
|
|
2828
|
+
return pippenger(Point, Fn, points, scalars);
|
|
2829
|
+
}
|
|
2830
|
+
_setWindowSize(windowSize) {
|
|
2831
|
+
this.precompute(windowSize);
|
|
2832
|
+
}
|
|
2833
|
+
toRawBytes() {
|
|
2834
|
+
return this.toBytes();
|
|
2544
2835
|
}
|
|
2545
2836
|
}
|
|
2546
|
-
|
|
2547
|
-
Point.
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2837
|
+
// base / generator point
|
|
2838
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n$2, modP(CURVE.Gx * CURVE.Gy));
|
|
2839
|
+
// zero / infinity / identity point
|
|
2840
|
+
Point.ZERO = new Point(_0n$1, _1n$2, _1n$2, _0n$1); // 0, 1, 1, 0
|
|
2841
|
+
// math field
|
|
2842
|
+
Point.Fp = Fp;
|
|
2843
|
+
// scalar field
|
|
2844
|
+
Point.Fn = Fn;
|
|
2845
|
+
const wnaf = new wNAF(Point, Fn.BITS);
|
|
2846
|
+
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
2847
|
+
return Point;
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Initializes EdDSA signatures over given Edwards curve.
|
|
2851
|
+
*/
|
|
2852
|
+
function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
2853
|
+
if (typeof cHash !== 'function')
|
|
2854
|
+
throw new Error('"hash" function param is required');
|
|
2855
|
+
_validateObject(eddsaOpts, {}, {
|
|
2856
|
+
adjustScalarBytes: 'function',
|
|
2857
|
+
randomBytes: 'function',
|
|
2858
|
+
domain: 'function',
|
|
2859
|
+
prehash: 'function',
|
|
2860
|
+
mapToCurve: 'function',
|
|
2861
|
+
});
|
|
2862
|
+
const { prehash } = eddsaOpts;
|
|
2863
|
+
const { BASE, Fp, Fn } = Point;
|
|
2864
|
+
const randomBytes$1 = eddsaOpts.randomBytes || randomBytes;
|
|
2865
|
+
const adjustScalarBytes = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
|
2866
|
+
const domain = eddsaOpts.domain ||
|
|
2867
|
+
((data, ctx, phflag) => {
|
|
2868
|
+
_abool2(phflag, 'phflag');
|
|
2869
|
+
if (ctx.length || phflag)
|
|
2870
|
+
throw new Error('Contexts/pre-hash are not supported');
|
|
2871
|
+
return data;
|
|
2872
|
+
}); // NOOP
|
|
2553
2873
|
// Little-endian SHA512 with modulo n
|
|
2554
2874
|
function modN_LE(hash) {
|
|
2555
|
-
return
|
|
2875
|
+
return Fn.create(bytesToNumberLE(hash)); // Not Fn.fromBytes: it has length limit
|
|
2556
2876
|
}
|
|
2557
|
-
|
|
2558
|
-
function
|
|
2559
|
-
const len =
|
|
2877
|
+
// Get the hashed private scalar per RFC8032 5.1.5
|
|
2878
|
+
function getPrivateScalar(key) {
|
|
2879
|
+
const len = lengths.secretKey;
|
|
2560
2880
|
key = ensureBytes('private key', key, len);
|
|
2561
2881
|
// Hash private key with curve's hash function to produce uniformingly random input
|
|
2562
2882
|
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
|
@@ -2564,171 +2884,293 @@ function twistedEdwards(curveDef) {
|
|
|
2564
2884
|
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
|
2565
2885
|
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
|
2566
2886
|
const scalar = modN_LE(head); // The actual private scalar
|
|
2567
|
-
|
|
2568
|
-
|
|
2887
|
+
return { head, prefix, scalar };
|
|
2888
|
+
}
|
|
2889
|
+
/** Convenience method that creates public key from scalar. RFC8032 5.1.5 */
|
|
2890
|
+
function getExtendedPublicKey(secretKey) {
|
|
2891
|
+
const { head, prefix, scalar } = getPrivateScalar(secretKey);
|
|
2892
|
+
const point = BASE.multiply(scalar); // Point on Edwards curve aka public key
|
|
2893
|
+
const pointBytes = point.toBytes();
|
|
2569
2894
|
return { head, prefix, scalar, point, pointBytes };
|
|
2570
2895
|
}
|
|
2571
|
-
|
|
2572
|
-
function getPublicKey(
|
|
2573
|
-
return getExtendedPublicKey(
|
|
2896
|
+
/** Calculates EdDSA pub key. RFC8032 5.1.5. */
|
|
2897
|
+
function getPublicKey(secretKey) {
|
|
2898
|
+
return getExtendedPublicKey(secretKey).pointBytes;
|
|
2574
2899
|
}
|
|
2575
2900
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
2576
|
-
function hashDomainToScalar(context =
|
|
2901
|
+
function hashDomainToScalar(context = Uint8Array.of(), ...msgs) {
|
|
2577
2902
|
const msg = concatBytes(...msgs);
|
|
2578
2903
|
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
|
|
2579
2904
|
}
|
|
2580
2905
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
2581
|
-
function sign(msg,
|
|
2906
|
+
function sign(msg, secretKey, options = {}) {
|
|
2582
2907
|
msg = ensureBytes('message', msg);
|
|
2583
2908
|
if (prehash)
|
|
2584
2909
|
msg = prehash(msg); // for ed25519ph etc.
|
|
2585
|
-
const { prefix, scalar, pointBytes } = getExtendedPublicKey(
|
|
2910
|
+
const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
|
|
2586
2911
|
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
|
2587
|
-
const R =
|
|
2912
|
+
const R = BASE.multiply(r).toBytes(); // R = rG
|
|
2588
2913
|
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
|
2589
|
-
const s =
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2914
|
+
const s = Fn.create(r + k * scalar); // S = (r + k * s) mod L
|
|
2915
|
+
if (!Fn.isValid(s))
|
|
2916
|
+
throw new Error('sign failed: invalid s'); // 0 <= s < L
|
|
2917
|
+
const rs = concatBytes(R, Fn.toBytes(s));
|
|
2918
|
+
return _abytes2(rs, lengths.signature, 'result');
|
|
2593
2919
|
}
|
|
2594
|
-
|
|
2920
|
+
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
|
2921
|
+
const verifyOpts = { zip215: true };
|
|
2595
2922
|
/**
|
|
2596
2923
|
* Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
|
2597
2924
|
* An extended group equation is checked.
|
|
2598
2925
|
*/
|
|
2599
2926
|
function verify(sig, msg, publicKey, options = verifyOpts) {
|
|
2600
2927
|
const { context, zip215 } = options;
|
|
2601
|
-
const len =
|
|
2602
|
-
sig = ensureBytes('signature', sig,
|
|
2928
|
+
const len = lengths.signature;
|
|
2929
|
+
sig = ensureBytes('signature', sig, len);
|
|
2603
2930
|
msg = ensureBytes('message', msg);
|
|
2604
|
-
publicKey = ensureBytes('publicKey', publicKey,
|
|
2931
|
+
publicKey = ensureBytes('publicKey', publicKey, lengths.publicKey);
|
|
2605
2932
|
if (zip215 !== undefined)
|
|
2606
|
-
|
|
2933
|
+
_abool2(zip215, 'zip215');
|
|
2607
2934
|
if (prehash)
|
|
2608
2935
|
msg = prehash(msg); // for ed25519ph, etc
|
|
2609
|
-
const
|
|
2936
|
+
const mid = len / 2;
|
|
2937
|
+
const r = sig.subarray(0, mid);
|
|
2938
|
+
const s = bytesToNumberLE(sig.subarray(mid, len));
|
|
2610
2939
|
let A, R, SB;
|
|
2611
2940
|
try {
|
|
2612
2941
|
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
|
2613
2942
|
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
2614
2943
|
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
2615
|
-
A = Point.
|
|
2616
|
-
R = Point.
|
|
2617
|
-
SB =
|
|
2944
|
+
A = Point.fromBytes(publicKey, zip215);
|
|
2945
|
+
R = Point.fromBytes(r, zip215);
|
|
2946
|
+
SB = BASE.multiplyUnsafe(s); // 0 <= s < l is done inside
|
|
2618
2947
|
}
|
|
2619
2948
|
catch (error) {
|
|
2620
2949
|
return false;
|
|
2621
2950
|
}
|
|
2622
2951
|
if (!zip215 && A.isSmallOrder())
|
|
2623
|
-
return false;
|
|
2624
|
-
const k = hashDomainToScalar(context, R.
|
|
2952
|
+
return false; // zip215 allows public keys of small order
|
|
2953
|
+
const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
|
|
2625
2954
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
2626
2955
|
// Extended group equation
|
|
2627
2956
|
// [8][S]B = [8]R + [8][k]A'
|
|
2628
|
-
return RkA.subtract(SB).clearCofactor().
|
|
2957
|
+
return RkA.subtract(SB).clearCofactor().is0();
|
|
2958
|
+
}
|
|
2959
|
+
const _size = Fp.BYTES; // 32 for ed25519, 57 for ed448
|
|
2960
|
+
const lengths = {
|
|
2961
|
+
secretKey: _size,
|
|
2962
|
+
publicKey: _size,
|
|
2963
|
+
signature: 2 * _size,
|
|
2964
|
+
seed: _size,
|
|
2965
|
+
};
|
|
2966
|
+
function randomSecretKey(seed = randomBytes$1(lengths.seed)) {
|
|
2967
|
+
return _abytes2(seed, lengths.seed, 'seed');
|
|
2968
|
+
}
|
|
2969
|
+
function keygen(seed) {
|
|
2970
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
2971
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
2972
|
+
}
|
|
2973
|
+
function isValidSecretKey(key) {
|
|
2974
|
+
return isBytes(key) && key.length === Fn.BYTES;
|
|
2975
|
+
}
|
|
2976
|
+
function isValidPublicKey(key, zip215) {
|
|
2977
|
+
try {
|
|
2978
|
+
return !!Point.fromBytes(key, zip215);
|
|
2979
|
+
}
|
|
2980
|
+
catch (error) {
|
|
2981
|
+
return false;
|
|
2982
|
+
}
|
|
2629
2983
|
}
|
|
2630
|
-
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
2631
2984
|
const utils = {
|
|
2632
2985
|
getExtendedPublicKey,
|
|
2633
|
-
|
|
2634
|
-
|
|
2986
|
+
randomSecretKey,
|
|
2987
|
+
isValidSecretKey,
|
|
2988
|
+
isValidPublicKey,
|
|
2635
2989
|
/**
|
|
2636
|
-
*
|
|
2637
|
-
*
|
|
2638
|
-
*
|
|
2639
|
-
*
|
|
2990
|
+
* Converts ed public key to x public key. Uses formula:
|
|
2991
|
+
* - ed25519:
|
|
2992
|
+
* - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
|
|
2993
|
+
* - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
|
|
2994
|
+
* - ed448:
|
|
2995
|
+
* - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
|
2996
|
+
* - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
|
2640
2997
|
*/
|
|
2998
|
+
toMontgomery(publicKey) {
|
|
2999
|
+
const { y } = Point.fromBytes(publicKey);
|
|
3000
|
+
const size = lengths.publicKey;
|
|
3001
|
+
const is25519 = size === 32;
|
|
3002
|
+
if (!is25519 && size !== 57)
|
|
3003
|
+
throw new Error('only defined for 25519 and 448');
|
|
3004
|
+
const u = is25519 ? Fp.div(_1n$2 + y, _1n$2 - y) : Fp.div(y - _1n$2, y + _1n$2);
|
|
3005
|
+
return Fp.toBytes(u);
|
|
3006
|
+
},
|
|
3007
|
+
toMontgomeryPriv(secretKey) {
|
|
3008
|
+
const size = lengths.secretKey;
|
|
3009
|
+
_abytes2(secretKey, size);
|
|
3010
|
+
const hashed = cHash(secretKey.subarray(0, size));
|
|
3011
|
+
return adjustScalarBytes(hashed).subarray(0, size);
|
|
3012
|
+
},
|
|
3013
|
+
/** @deprecated */
|
|
3014
|
+
randomPrivateKey: randomSecretKey,
|
|
3015
|
+
/** @deprecated */
|
|
2641
3016
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
2642
|
-
point.
|
|
2643
|
-
point.multiply(BigInt(3));
|
|
2644
|
-
return point;
|
|
3017
|
+
return point.precompute(windowSize, false);
|
|
2645
3018
|
},
|
|
2646
3019
|
};
|
|
2647
|
-
return {
|
|
2648
|
-
|
|
3020
|
+
return Object.freeze({
|
|
3021
|
+
keygen,
|
|
2649
3022
|
getPublicKey,
|
|
2650
3023
|
sign,
|
|
2651
3024
|
verify,
|
|
2652
|
-
ExtendedPoint: Point,
|
|
2653
3025
|
utils,
|
|
3026
|
+
Point,
|
|
3027
|
+
lengths,
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
function _eddsa_legacy_opts_to_new(c) {
|
|
3031
|
+
const CURVE = {
|
|
3032
|
+
a: c.a,
|
|
3033
|
+
d: c.d,
|
|
3034
|
+
p: c.Fp.ORDER,
|
|
3035
|
+
n: c.n,
|
|
3036
|
+
h: c.h,
|
|
3037
|
+
Gx: c.Gx,
|
|
3038
|
+
Gy: c.Gy,
|
|
3039
|
+
};
|
|
3040
|
+
const Fp = c.Fp;
|
|
3041
|
+
const Fn = Field(CURVE.n, c.nBitLength, true);
|
|
3042
|
+
const curveOpts = { Fp, Fn, uvRatio: c.uvRatio };
|
|
3043
|
+
const eddsaOpts = {
|
|
3044
|
+
randomBytes: c.randomBytes,
|
|
3045
|
+
adjustScalarBytes: c.adjustScalarBytes,
|
|
3046
|
+
domain: c.domain,
|
|
3047
|
+
prehash: c.prehash,
|
|
3048
|
+
mapToCurve: c.mapToCurve,
|
|
2654
3049
|
};
|
|
3050
|
+
return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
|
|
3051
|
+
}
|
|
3052
|
+
function _eddsa_new_output_to_legacy(c, eddsa) {
|
|
3053
|
+
const Point = eddsa.Point;
|
|
3054
|
+
const legacy = Object.assign({}, eddsa, {
|
|
3055
|
+
ExtendedPoint: Point,
|
|
3056
|
+
CURVE: c,
|
|
3057
|
+
nBitLength: Point.Fn.BITS,
|
|
3058
|
+
nByteLength: Point.Fn.BYTES,
|
|
3059
|
+
});
|
|
3060
|
+
return legacy;
|
|
3061
|
+
}
|
|
3062
|
+
// TODO: remove. Use eddsa
|
|
3063
|
+
function twistedEdwards(c) {
|
|
3064
|
+
const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
|
|
3065
|
+
const Point = edwards(CURVE, curveOpts);
|
|
3066
|
+
const EDDSA = eddsa(Point, hash, eddsaOpts);
|
|
3067
|
+
return _eddsa_new_output_to_legacy(c, EDDSA);
|
|
2655
3068
|
}
|
|
2656
3069
|
|
|
3070
|
+
/**
|
|
3071
|
+
* Montgomery curve methods. It's not really whole montgomery curve,
|
|
3072
|
+
* just bunch of very specific methods for X25519 / X448 from
|
|
3073
|
+
* [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748)
|
|
3074
|
+
* @module
|
|
3075
|
+
*/
|
|
2657
3076
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2658
3077
|
const _0n = BigInt(0);
|
|
2659
|
-
const _1n$
|
|
3078
|
+
const _1n$1 = BigInt(1);
|
|
3079
|
+
const _2n$2 = BigInt(2);
|
|
2660
3080
|
function validateOpts(curve) {
|
|
2661
|
-
|
|
2662
|
-
a: 'bigint',
|
|
2663
|
-
}, {
|
|
2664
|
-
montgomeryBits: 'isSafeInteger',
|
|
2665
|
-
nByteLength: 'isSafeInteger',
|
|
3081
|
+
_validateObject(curve, {
|
|
2666
3082
|
adjustScalarBytes: 'function',
|
|
2667
|
-
domain: 'function',
|
|
2668
3083
|
powPminus2: 'function',
|
|
2669
|
-
Gu: 'bigint',
|
|
2670
3084
|
});
|
|
2671
|
-
// Set defaults
|
|
2672
3085
|
return Object.freeze({ ...curve });
|
|
2673
3086
|
}
|
|
2674
|
-
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
|
|
2675
|
-
// Uses only one coordinate instead of two
|
|
2676
3087
|
function montgomery(curveDef) {
|
|
2677
3088
|
const CURVE = validateOpts(curveDef);
|
|
2678
|
-
const { P } = CURVE;
|
|
3089
|
+
const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
|
|
3090
|
+
const is25519 = type === 'x25519';
|
|
3091
|
+
if (!is25519 && type !== 'x448')
|
|
3092
|
+
throw new Error('invalid type');
|
|
3093
|
+
const randomBytes_ = rand || randomBytes;
|
|
3094
|
+
const montgomeryBits = is25519 ? 255 : 448;
|
|
3095
|
+
const fieldLen = is25519 ? 32 : 56;
|
|
3096
|
+
const Gu = is25519 ? BigInt(9) : BigInt(5);
|
|
3097
|
+
// RFC 7748 #5:
|
|
3098
|
+
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 and
|
|
3099
|
+
// (156326 - 2) / 4 = 39081 for curve448/X448
|
|
3100
|
+
// const a = is25519 ? 156326n : 486662n;
|
|
3101
|
+
const a24 = is25519 ? BigInt(121665) : BigInt(39081);
|
|
3102
|
+
// RFC: x25519 "the resulting integer is of the form 2^254 plus
|
|
3103
|
+
// eight times a value between 0 and 2^251 - 1 (inclusive)"
|
|
3104
|
+
// x448: "2^447 plus four times a value between 0 and 2^445 - 1 (inclusive)"
|
|
3105
|
+
const minScalar = is25519 ? _2n$2 ** BigInt(254) : _2n$2 ** BigInt(447);
|
|
3106
|
+
const maxAdded = is25519
|
|
3107
|
+
? BigInt(8) * _2n$2 ** BigInt(251) - _1n$1
|
|
3108
|
+
: BigInt(4) * _2n$2 ** BigInt(445) - _1n$1;
|
|
3109
|
+
const maxScalar = minScalar + maxAdded + _1n$1; // (inclusive)
|
|
2679
3110
|
const modP = (n) => mod(n, P);
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
3111
|
+
const GuBytes = encodeU(Gu);
|
|
3112
|
+
function encodeU(u) {
|
|
3113
|
+
return numberToBytesLE(modP(u), fieldLen);
|
|
3114
|
+
}
|
|
3115
|
+
function decodeU(u) {
|
|
3116
|
+
const _u = ensureBytes('u coordinate', u, fieldLen);
|
|
3117
|
+
// RFC: When receiving such an array, implementations of X25519
|
|
3118
|
+
// (but not X448) MUST mask the most significant bit in the final byte.
|
|
3119
|
+
if (is25519)
|
|
3120
|
+
_u[31] &= 127; // 0b0111_1111
|
|
3121
|
+
// RFC: Implementations MUST accept non-canonical values and process them as
|
|
3122
|
+
// if they had been reduced modulo the field prime. The non-canonical
|
|
3123
|
+
// values are 2^255 - 19 through 2^255 - 1 for X25519 and 2^448 - 2^224
|
|
3124
|
+
// - 1 through 2^448 - 1 for X448.
|
|
3125
|
+
return modP(bytesToNumberLE(_u));
|
|
3126
|
+
}
|
|
3127
|
+
function decodeScalar(scalar) {
|
|
3128
|
+
return bytesToNumberLE(adjustScalarBytes(ensureBytes('scalar', scalar, fieldLen)));
|
|
3129
|
+
}
|
|
3130
|
+
function scalarMult(scalar, u) {
|
|
3131
|
+
const pu = montgomeryLadder(decodeU(u), decodeScalar(scalar));
|
|
3132
|
+
// Some public keys are useless, of low-order. Curve author doesn't think
|
|
3133
|
+
// it needs to be validated, but we do it nonetheless.
|
|
3134
|
+
// https://cr.yp.to/ecdh.html#validate
|
|
3135
|
+
if (pu === _0n)
|
|
3136
|
+
throw new Error('invalid private or public key received');
|
|
3137
|
+
return encodeU(pu);
|
|
3138
|
+
}
|
|
3139
|
+
// Computes public key from private. By doing scalar multiplication of base point.
|
|
3140
|
+
function scalarMultBase(scalar) {
|
|
3141
|
+
return scalarMult(scalar, GuBytes);
|
|
3142
|
+
}
|
|
3143
|
+
// cswap from RFC7748 "example code"
|
|
2695
3144
|
function cswap(swap, x_2, x_3) {
|
|
3145
|
+
// dummy = mask(swap) AND (x_2 XOR x_3)
|
|
3146
|
+
// Where mask(swap) is the all-1 or all-0 word of the same length as x_2
|
|
3147
|
+
// and x_3, computed, e.g., as mask(swap) = 0 - swap.
|
|
2696
3148
|
const dummy = modP(swap * (x_2 - x_3));
|
|
2697
|
-
x_2 = modP(x_2 - dummy);
|
|
2698
|
-
x_3 = modP(x_3 + dummy);
|
|
2699
|
-
return
|
|
3149
|
+
x_2 = modP(x_2 - dummy); // x_2 = x_2 XOR dummy
|
|
3150
|
+
x_3 = modP(x_3 + dummy); // x_3 = x_3 XOR dummy
|
|
3151
|
+
return { x_2, x_3 };
|
|
2700
3152
|
}
|
|
2701
|
-
// x25519 from 4
|
|
2702
|
-
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
2703
|
-
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
|
|
2704
3153
|
/**
|
|
2705
|
-
*
|
|
3154
|
+
* Montgomery x-only multiplication ladder.
|
|
2706
3155
|
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
|
2707
3156
|
* @param scalar by which the point would be multiplied
|
|
2708
3157
|
* @returns new Point on Montgomery curve
|
|
2709
3158
|
*/
|
|
2710
3159
|
function montgomeryLadder(u, scalar) {
|
|
2711
3160
|
aInRange('u', u, _0n, P);
|
|
2712
|
-
aInRange('scalar', scalar,
|
|
2713
|
-
// Section 5: Implementations MUST accept non-canonical values and process them as
|
|
2714
|
-
// if they had been reduced modulo the field prime.
|
|
3161
|
+
aInRange('scalar', scalar, minScalar, maxScalar);
|
|
2715
3162
|
const k = scalar;
|
|
2716
3163
|
const x_1 = u;
|
|
2717
|
-
let x_2 = _1n$
|
|
3164
|
+
let x_2 = _1n$1;
|
|
2718
3165
|
let z_2 = _0n;
|
|
2719
3166
|
let x_3 = u;
|
|
2720
|
-
let z_3 = _1n$
|
|
3167
|
+
let z_3 = _1n$1;
|
|
2721
3168
|
let swap = _0n;
|
|
2722
|
-
let sw;
|
|
2723
3169
|
for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
|
|
2724
|
-
const k_t = (k >> t) & _1n$
|
|
3170
|
+
const k_t = (k >> t) & _1n$1;
|
|
2725
3171
|
swap ^= k_t;
|
|
2726
|
-
|
|
2727
|
-
x_2 =
|
|
2728
|
-
x_3 = sw[1];
|
|
2729
|
-
sw = cswap(swap, z_2, z_3);
|
|
2730
|
-
z_2 = sw[0];
|
|
2731
|
-
z_3 = sw[1];
|
|
3172
|
+
({ x_2, x_3 } = cswap(swap, x_2, x_3));
|
|
3173
|
+
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
|
|
2732
3174
|
swap = k_t;
|
|
2733
3175
|
const A = x_2 + z_2;
|
|
2734
3176
|
const AA = modP(A * A);
|
|
@@ -2746,84 +3188,82 @@ function montgomery(curveDef) {
|
|
|
2746
3188
|
x_2 = modP(AA * BB);
|
|
2747
3189
|
z_2 = modP(E * (AA + modP(a24 * E)));
|
|
2748
3190
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
// (z_2, z_3) = cswap(swap, z_2, z_3)
|
|
2754
|
-
sw = cswap(swap, z_2, z_3);
|
|
2755
|
-
z_2 = sw[0];
|
|
2756
|
-
z_3 = sw[1];
|
|
2757
|
-
// z_2^(p - 2)
|
|
2758
|
-
const z2 = powPminus2(z_2);
|
|
2759
|
-
// Return x_2 * (z_2^(p - 2))
|
|
2760
|
-
return modP(x_2 * z2);
|
|
2761
|
-
}
|
|
2762
|
-
function encodeUCoordinate(u) {
|
|
2763
|
-
return numberToBytesLE(modP(u), montgomeryBytes);
|
|
2764
|
-
}
|
|
2765
|
-
function decodeUCoordinate(uEnc) {
|
|
2766
|
-
// Section 5: When receiving such an array, implementations of X25519
|
|
2767
|
-
// MUST mask the most significant bit in the final byte.
|
|
2768
|
-
const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
|
|
2769
|
-
if (fieldLen === 32)
|
|
2770
|
-
u[31] &= 127; // 0b0111_1111
|
|
2771
|
-
return bytesToNumberLE(u);
|
|
2772
|
-
}
|
|
2773
|
-
function decodeScalar(n) {
|
|
2774
|
-
const bytes = ensureBytes('scalar', n);
|
|
2775
|
-
const len = bytes.length;
|
|
2776
|
-
if (len !== montgomeryBytes && len !== fieldLen) {
|
|
2777
|
-
let valid = '' + montgomeryBytes + ' or ' + fieldLen;
|
|
2778
|
-
throw new Error('invalid scalar, expected ' + valid + ' bytes, got ' + len);
|
|
2779
|
-
}
|
|
2780
|
-
return bytesToNumberLE(adjustScalarBytes(bytes));
|
|
3191
|
+
({ x_2, x_3 } = cswap(swap, x_2, x_3));
|
|
3192
|
+
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
|
|
3193
|
+
const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent
|
|
3194
|
+
return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
|
|
2781
3195
|
}
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
function scalarMultBase(scalar) {
|
|
2795
|
-
return scalarMult(scalar, GuBytes);
|
|
3196
|
+
const lengths = {
|
|
3197
|
+
secretKey: fieldLen,
|
|
3198
|
+
publicKey: fieldLen,
|
|
3199
|
+
seed: fieldLen,
|
|
3200
|
+
};
|
|
3201
|
+
const randomSecretKey = (seed = randomBytes_(fieldLen)) => {
|
|
3202
|
+
abytes(seed, lengths.seed);
|
|
3203
|
+
return seed;
|
|
3204
|
+
};
|
|
3205
|
+
function keygen(seed) {
|
|
3206
|
+
const secretKey = randomSecretKey(seed);
|
|
3207
|
+
return { secretKey, publicKey: scalarMultBase(secretKey) };
|
|
2796
3208
|
}
|
|
3209
|
+
const utils = {
|
|
3210
|
+
randomSecretKey,
|
|
3211
|
+
randomPrivateKey: randomSecretKey,
|
|
3212
|
+
};
|
|
2797
3213
|
return {
|
|
3214
|
+
keygen,
|
|
3215
|
+
getSharedSecret: (secretKey, publicKey) => scalarMult(secretKey, publicKey),
|
|
3216
|
+
getPublicKey: (secretKey) => scalarMultBase(secretKey),
|
|
2798
3217
|
scalarMult,
|
|
2799
3218
|
scalarMultBase,
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
GuBytes: GuBytes,
|
|
3219
|
+
utils,
|
|
3220
|
+
GuBytes: GuBytes.slice(),
|
|
3221
|
+
lengths,
|
|
2804
3222
|
};
|
|
2805
3223
|
}
|
|
2806
3224
|
|
|
2807
|
-
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2808
3225
|
/**
|
|
2809
3226
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
|
2810
3227
|
* - X448 ECDH
|
|
2811
3228
|
* - Decaf cofactor elimination
|
|
2812
3229
|
* - Elligator hash-to-group / point indistinguishability
|
|
2813
3230
|
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
|
|
3231
|
+
* @module
|
|
2814
3232
|
*/
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
3233
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
3234
|
+
// edwards448 curve
|
|
3235
|
+
// a = 1n
|
|
3236
|
+
// d = Fp.neg(39081n)
|
|
3237
|
+
// Finite field 2n**448n - 2n**224n - 1n
|
|
3238
|
+
// Subgroup order
|
|
3239
|
+
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
|
3240
|
+
const ed448_CURVE = {
|
|
3241
|
+
p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
|
|
3242
|
+
n: BigInt('0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3'),
|
|
3243
|
+
h: BigInt(4),
|
|
3244
|
+
a: BigInt(1),
|
|
3245
|
+
d: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756'),
|
|
3246
|
+
Gx: BigInt('0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e'),
|
|
3247
|
+
Gy: BigInt('0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14'),
|
|
3248
|
+
};
|
|
3249
|
+
// E448 NIST curve is identical to edwards448, except for:
|
|
3250
|
+
// d = 39082/39081
|
|
3251
|
+
// Gx = 3/2
|
|
3252
|
+
const E448_CURVE = Object.assign({}, ed448_CURVE, {
|
|
3253
|
+
d: BigInt('0xd78b4bdc7f0daf19f24f38c29373a2ccad46157242a50f37809b1da3412a12e79ccc9c81264cfe9ad080997058fb61c4243cc32dbaa156b9'),
|
|
3254
|
+
Gx: BigInt('0x79a70b2b70400553ae7c9df416c792c61128751ac92969240c25a07d728bdc93e21f7787ed6972249de732f38496cd11698713093e9c04fc'),
|
|
3255
|
+
Gy: BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000001'),
|
|
3256
|
+
});
|
|
3257
|
+
const shake256_114 = /* @__PURE__ */ createHasher(() => shake256.create({ dkLen: 114 }));
|
|
2818
3258
|
// prettier-ignore
|
|
2819
|
-
const _1n
|
|
3259
|
+
const _1n = BigInt(1), _2n$1 = BigInt(2), _3n = BigInt(3); BigInt(4); const _11n = BigInt(11);
|
|
2820
3260
|
// prettier-ignore
|
|
2821
3261
|
const _22n = BigInt(22), _44n = BigInt(44), _88n = BigInt(88), _223n = BigInt(223);
|
|
2822
3262
|
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
|
|
2823
3263
|
// Used for efficient square root calculation.
|
|
2824
3264
|
// ((P-3)/4).toString(2) would produce bits [223x 1, 0, 222x 1]
|
|
2825
3265
|
function ed448_pow_Pminus3div4(x) {
|
|
2826
|
-
const P =
|
|
3266
|
+
const P = ed448_CURVE.p;
|
|
2827
3267
|
const b2 = (x * x * x) % P;
|
|
2828
3268
|
const b3 = (b2 * b2 * x) % P;
|
|
2829
3269
|
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
|
@@ -2835,23 +3275,22 @@ function ed448_pow_Pminus3div4(x) {
|
|
|
2835
3275
|
const b176 = (pow2(b88, _88n, P) * b88) % P;
|
|
2836
3276
|
const b220 = (pow2(b176, _44n, P) * b44) % P;
|
|
2837
3277
|
const b222 = (pow2(b220, _2n$1, P) * b2) % P;
|
|
2838
|
-
const b223 = (pow2(b222, _1n
|
|
3278
|
+
const b223 = (pow2(b222, _1n, P) * x) % P;
|
|
2839
3279
|
return (pow2(b223, _223n, P) * b222) % P;
|
|
2840
3280
|
}
|
|
2841
3281
|
function adjustScalarBytes(bytes) {
|
|
2842
|
-
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0,
|
|
2843
|
-
// significant bit of the last byte to 1.
|
|
3282
|
+
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0,
|
|
2844
3283
|
bytes[0] &= 252; // 0b11111100
|
|
2845
3284
|
// and the most significant bit of the last byte to 1.
|
|
2846
3285
|
bytes[55] |= 128; // 0b10000000
|
|
2847
|
-
// NOTE: is
|
|
3286
|
+
// NOTE: is NOOP for 56 bytes scalars (X25519/X448)
|
|
2848
3287
|
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
|
|
2849
3288
|
return bytes;
|
|
2850
3289
|
}
|
|
2851
3290
|
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
|
|
2852
3291
|
// Uses algo from RFC8032 5.1.3.
|
|
2853
3292
|
function uvRatio(u, v) {
|
|
2854
|
-
const P =
|
|
3293
|
+
const P = ed448_CURVE.p;
|
|
2855
3294
|
// https://www.rfc-editor.org/rfc/rfc8032#section-5.2.3
|
|
2856
3295
|
// To compute the square root of (u/v), the first step is to compute the
|
|
2857
3296
|
// candidate root x = (u/v)^((p+1)/4). This can be done using the
|
|
@@ -2869,81 +3308,99 @@ function uvRatio(u, v) {
|
|
|
2869
3308
|
// square root exists, and the decoding fails.
|
|
2870
3309
|
return { isValid: mod(x2 * v, P) === u, value: x };
|
|
2871
3310
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
3311
|
+
// Finite field 2n**448n - 2n**224n - 1n
|
|
3312
|
+
// The value fits in 448 bits, but we use 456-bit (57-byte) elements because of bitflags.
|
|
3313
|
+
// - ed25519 fits in 255 bits, allowing using last 1 byte for specifying bit flag of point negation.
|
|
3314
|
+
// - ed448 fits in 448 bits. We can't use last 1 byte: we can only use a bit 224 in the middle.
|
|
3315
|
+
const Fp$3 = /* @__PURE__ */ (() => Field(ed448_CURVE.p, { BITS: 456, isLE: true }))();
|
|
3316
|
+
const Fn = /* @__PURE__ */ (() => Field(ed448_CURVE.n, { BITS: 456, isLE: true }))();
|
|
3317
|
+
// SHAKE256(dom4(phflag,context)||x, 114)
|
|
3318
|
+
function dom4(data, ctx, phflag) {
|
|
3319
|
+
if (ctx.length > 255)
|
|
3320
|
+
throw new Error('context must be smaller than 255, got: ' + ctx.length);
|
|
3321
|
+
return concatBytes(asciiToBytes('SigEd448'), new Uint8Array([phflag ? 1 : 0, ctx.length]), ctx, data);
|
|
3322
|
+
}
|
|
3323
|
+
// const ed448_eddsa_opts = { adjustScalarBytes, domain: dom4 };
|
|
3324
|
+
// const ed448_Point = edwards(ed448_CURVE, { Fp, Fn, uvRatio });
|
|
3325
|
+
const ED448_DEF = /* @__PURE__ */ (() => ({
|
|
3326
|
+
...ed448_CURVE,
|
|
2879
3327
|
Fp: Fp$3,
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
n: BigInt('181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'),
|
|
2883
|
-
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
|
2884
|
-
nBitLength: 456,
|
|
2885
|
-
// Cofactor
|
|
2886
|
-
h: BigInt(4),
|
|
2887
|
-
// Base point (x, y) aka generator point
|
|
2888
|
-
Gx: BigInt('224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710'),
|
|
2889
|
-
Gy: BigInt('298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660'),
|
|
2890
|
-
// SHAKE256(dom4(phflag,context)||x, 114)
|
|
3328
|
+
Fn,
|
|
3329
|
+
nBitLength: Fn.BITS,
|
|
2891
3330
|
hash: shake256_114,
|
|
2892
|
-
randomBytes,
|
|
2893
3331
|
adjustScalarBytes,
|
|
2894
|
-
|
|
2895
|
-
domain: (data, ctx, phflag) => {
|
|
2896
|
-
if (ctx.length > 255)
|
|
2897
|
-
throw new Error('context must be smaller than 255, got: ' + ctx.length);
|
|
2898
|
-
return concatBytes$1(utf8ToBytes$1('SigEd448'), new Uint8Array([phflag ? 1 : 0, ctx.length]), ctx, data);
|
|
2899
|
-
},
|
|
3332
|
+
domain: dom4,
|
|
2900
3333
|
uvRatio,
|
|
2901
|
-
};
|
|
2902
|
-
const ed448 = /* @__PURE__ */ twistedEdwards(ED448_DEF);
|
|
2903
|
-
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
|
|
2904
|
-
/* @__PURE__ */ twistedEdwards({ ...ED448_DEF, prehash: shake256_64 });
|
|
2905
|
-
const x448 = /* @__PURE__ */ (() => montgomery({
|
|
2906
|
-
a: BigInt(156326),
|
|
2907
|
-
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
|
2908
|
-
montgomeryBits: 448,
|
|
2909
|
-
nByteLength: 56,
|
|
2910
|
-
P: ed448P,
|
|
2911
|
-
Gu: BigInt(5),
|
|
2912
|
-
powPminus2: (x) => {
|
|
2913
|
-
const P = ed448P;
|
|
2914
|
-
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
|
2915
|
-
const Pminus3 = pow2(Pminus3div4, BigInt(2), P);
|
|
2916
|
-
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
|
2917
|
-
},
|
|
2918
|
-
adjustScalarBytes,
|
|
2919
|
-
randomBytes,
|
|
2920
3334
|
}))();
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
3335
|
+
/**
|
|
3336
|
+
* ed448 EdDSA curve and methods.
|
|
3337
|
+
* @example
|
|
3338
|
+
* import { ed448 } from '@noble/curves/ed448';
|
|
3339
|
+
* const { secretKey, publicKey } = ed448.keygen();
|
|
3340
|
+
* const msg = new TextEncoder().encode('hello');
|
|
3341
|
+
* const sig = ed448.sign(msg, secretKey);
|
|
3342
|
+
* const isValid = ed448.verify(sig, msg, publicKey);
|
|
3343
|
+
*/
|
|
3344
|
+
const ed448 = twistedEdwards(ED448_DEF);
|
|
3345
|
+
/**
|
|
3346
|
+
* E448 curve, defined by NIST.
|
|
3347
|
+
* E448 != edwards448 used in ed448.
|
|
3348
|
+
* E448 is birationally equivalent to edwards448.
|
|
3349
|
+
*/
|
|
3350
|
+
edwards(E448_CURVE);
|
|
3351
|
+
/**
|
|
3352
|
+
* ECDH using curve448 aka x448.
|
|
3353
|
+
* x448 has 56-byte keys as per RFC 7748, while
|
|
3354
|
+
* ed448 has 57-byte keys as per RFC 8032.
|
|
3355
|
+
*/
|
|
3356
|
+
const x448 = /* @__PURE__ */ (() => {
|
|
3357
|
+
const P = ed448_CURVE.p;
|
|
3358
|
+
return montgomery({
|
|
3359
|
+
P,
|
|
3360
|
+
type: 'x448',
|
|
3361
|
+
powPminus2: (x) => {
|
|
3362
|
+
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
|
3363
|
+
const Pminus3 = pow2(Pminus3div4, _2n$1, P);
|
|
3364
|
+
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
|
3365
|
+
},
|
|
3366
|
+
adjustScalarBytes,
|
|
3367
|
+
});
|
|
3368
|
+
})();
|
|
2934
3369
|
|
|
3370
|
+
/**
|
|
3371
|
+
* SECG secp256k1. See [pdf](https://www.secg.org/sec2-v2.pdf).
|
|
3372
|
+
*
|
|
3373
|
+
* Belongs to Koblitz curves: it has efficiently-computable GLV endomorphism ψ,
|
|
3374
|
+
* check out {@link EndomorphismOpts}. Seems to be rigid (not backdoored).
|
|
3375
|
+
* @module
|
|
3376
|
+
*/
|
|
2935
3377
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
const
|
|
2940
|
-
|
|
3378
|
+
// Seems like generator was produced from some seed:
|
|
3379
|
+
// `Point.BASE.multiply(Point.Fn.inv(2n, N)).toAffine().x`
|
|
3380
|
+
// // gives short x 0x3b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63n
|
|
3381
|
+
const secp256k1_CURVE = {
|
|
3382
|
+
p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
|
|
3383
|
+
n: BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'),
|
|
3384
|
+
h: BigInt(1),
|
|
3385
|
+
a: BigInt(0),
|
|
3386
|
+
b: BigInt(7),
|
|
3387
|
+
Gx: BigInt('0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
|
|
3388
|
+
Gy: BigInt('0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'),
|
|
3389
|
+
};
|
|
3390
|
+
const secp256k1_ENDO = {
|
|
3391
|
+
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
|
3392
|
+
basises: [
|
|
3393
|
+
[BigInt('0x3086d221a7d46bcde86c90e49284eb15'), -BigInt('0xe4437ed6010e88286f547fa90abfe4c3')],
|
|
3394
|
+
[BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'), BigInt('0x3086d221a7d46bcde86c90e49284eb15')],
|
|
3395
|
+
],
|
|
3396
|
+
};
|
|
3397
|
+
const _2n = /* @__PURE__ */ BigInt(2);
|
|
2941
3398
|
/**
|
|
2942
3399
|
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
|
2943
3400
|
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
|
2944
3401
|
*/
|
|
2945
3402
|
function sqrtMod(y) {
|
|
2946
|
-
const P =
|
|
3403
|
+
const P = secp256k1_CURVE.p;
|
|
2947
3404
|
// prettier-ignore
|
|
2948
3405
|
const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
|
|
2949
3406
|
// prettier-ignore
|
|
@@ -2966,56 +3423,22 @@ function sqrtMod(y) {
|
|
|
2966
3423
|
throw new Error('Cannot find square root');
|
|
2967
3424
|
return root;
|
|
2968
3425
|
}
|
|
2969
|
-
const Fpk1 = Field(
|
|
3426
|
+
const Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod });
|
|
2970
3427
|
/**
|
|
2971
|
-
* secp256k1
|
|
3428
|
+
* secp256k1 curve, ECDSA and ECDH methods.
|
|
3429
|
+
*
|
|
3430
|
+
* Field: `2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n`
|
|
3431
|
+
*
|
|
3432
|
+
* @example
|
|
3433
|
+
* ```js
|
|
3434
|
+
* import { secp256k1 } from '@noble/curves/secp256k1';
|
|
3435
|
+
* const { secretKey, publicKey } = secp256k1.keygen();
|
|
3436
|
+
* const msg = new TextEncoder().encode('hello');
|
|
3437
|
+
* const sig = secp256k1.sign(msg, secretKey);
|
|
3438
|
+
* const isValid = secp256k1.verify(sig, msg, publicKey) === true;
|
|
3439
|
+
* ```
|
|
2972
3440
|
*/
|
|
2973
|
-
const secp256k1 = createCurve({
|
|
2974
|
-
a: BigInt(0), // equation params: a, b
|
|
2975
|
-
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
|
2976
|
-
Fp: Fpk1, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
|
2977
|
-
n: secp256k1N, // Curve order, total count of valid points in the field
|
|
2978
|
-
// Base point (x, y) aka generator point
|
|
2979
|
-
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
|
2980
|
-
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
|
2981
|
-
h: BigInt(1), // Cofactor
|
|
2982
|
-
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
|
2983
|
-
/**
|
|
2984
|
-
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
|
2985
|
-
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
|
2986
|
-
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
|
2987
|
-
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
|
2988
|
-
*/
|
|
2989
|
-
endo: {
|
|
2990
|
-
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
|
2991
|
-
splitScalar: (k) => {
|
|
2992
|
-
const n = secp256k1N;
|
|
2993
|
-
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
|
2994
|
-
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
|
2995
|
-
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
|
2996
|
-
const b2 = a1;
|
|
2997
|
-
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
|
2998
|
-
const c1 = divNearest(b2 * k, n);
|
|
2999
|
-
const c2 = divNearest(-b1 * k, n);
|
|
3000
|
-
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
|
3001
|
-
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
|
3002
|
-
const k1neg = k1 > POW_2_128;
|
|
3003
|
-
const k2neg = k2 > POW_2_128;
|
|
3004
|
-
if (k1neg)
|
|
3005
|
-
k1 = n - k1;
|
|
3006
|
-
if (k2neg)
|
|
3007
|
-
k2 = n - k2;
|
|
3008
|
-
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
|
3009
|
-
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
|
3010
|
-
}
|
|
3011
|
-
return { k1neg, k1, k2neg, k2 };
|
|
3012
|
-
},
|
|
3013
|
-
},
|
|
3014
|
-
}, sha256);
|
|
3015
|
-
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
|
3016
|
-
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
3017
|
-
BigInt(0);
|
|
3018
|
-
secp256k1.ProjectivePoint;
|
|
3441
|
+
const secp256k1 = createCurve({ ...secp256k1_CURVE, Fp: Fpk1, lowS: true, endo: secp256k1_ENDO }, sha256);
|
|
3019
3442
|
|
|
3020
3443
|
// brainpoolP256r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.4
|
|
3021
3444
|
// eslint-disable-next-line new-cap
|
|
@@ -3034,7 +3457,7 @@ const brainpoolP256r1 = createCurve({
|
|
|
3034
3457
|
Gy: BigInt('0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997'),
|
|
3035
3458
|
h: BigInt(1),
|
|
3036
3459
|
lowS: false
|
|
3037
|
-
}, sha256);
|
|
3460
|
+
}, sha256$1);
|
|
3038
3461
|
|
|
3039
3462
|
// brainpoolP384 r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.6
|
|
3040
3463
|
// eslint-disable-next-line new-cap
|
|
@@ -3053,7 +3476,7 @@ const brainpoolP384r1 = createCurve({
|
|
|
3053
3476
|
Gy: BigInt('0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315'),
|
|
3054
3477
|
h: BigInt(1),
|
|
3055
3478
|
lowS: false
|
|
3056
|
-
}, sha384);
|
|
3479
|
+
}, sha384$1);
|
|
3057
3480
|
|
|
3058
3481
|
// brainpoolP512r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.7
|
|
3059
3482
|
// eslint-disable-next-line new-cap
|
|
@@ -3072,7 +3495,7 @@ const brainpoolP512r1 = createCurve({
|
|
|
3072
3495
|
Gy: BigInt('0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892'),
|
|
3073
3496
|
h: BigInt(1),
|
|
3074
3497
|
lowS: false
|
|
3075
|
-
}, sha512);
|
|
3498
|
+
}, sha512$1);
|
|
3076
3499
|
|
|
3077
3500
|
/**
|
|
3078
3501
|
* This file is needed to dynamic import the noble-curves.
|