@noble/curves 0.5.1 → 0.5.2
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 +1 -4
- package/lib/_shortw_utils.d.ts +2 -6
- package/lib/abstract/bls.d.ts +17 -8
- package/lib/abstract/bls.js +15 -78
- package/lib/abstract/edwards.d.ts +7 -16
- package/lib/abstract/edwards.js +89 -106
- package/lib/abstract/modular.js +26 -23
- package/lib/abstract/montgomery.js +1 -1
- package/lib/abstract/utils.d.ts +5 -3
- package/lib/abstract/utils.js +22 -14
- package/lib/abstract/weierstrass.d.ts +8 -8
- package/lib/abstract/weierstrass.js +209 -168
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +13 -8
- package/lib/ed25519.js +5 -5
- package/lib/ed448.js +2 -1
- package/lib/esm/abstract/bls.js +19 -82
- package/lib/esm/abstract/edwards.js +90 -107
- package/lib/esm/abstract/modular.js +26 -23
- package/lib/esm/abstract/montgomery.js +2 -4
- package/lib/esm/abstract/utils.js +20 -13
- package/lib/esm/abstract/weierstrass.js +210 -169
- package/lib/esm/bls12-381.js +12 -7
- package/lib/esm/ed25519.js +5 -5
- package/lib/esm/ed448.js +2 -1
- package/lib/esm/jubjub.js +5 -4
- package/lib/esm/secp256k1.js +22 -25
- package/lib/esm/stark.js +3 -2
- package/lib/jubjub.d.ts +1 -0
- package/lib/jubjub.js +5 -4
- package/lib/p192.d.ts +4 -12
- package/lib/p224.d.ts +4 -12
- package/lib/p256.d.ts +4 -12
- package/lib/p384.d.ts +4 -12
- package/lib/p521.d.ts +4 -12
- package/lib/secp256k1.d.ts +2 -6
- package/lib/secp256k1.js +22 -25
- package/lib/stark.d.ts +0 -2
- package/lib/stark.js +3 -2
- package/package.json +2 -2
package/lib/bls12-381.d.ts
CHANGED
package/lib/bls12-381.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.bls12_381 = void 0;
|
|
4
|
-
|
|
5
|
+
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
|
|
6
|
+
// - Construct zk-SNARKs at the 128-bit security
|
|
7
|
+
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
|
|
8
|
+
// Differences from @noble/bls12-381 1.4:
|
|
9
|
+
// - PointG1 -> G1.Point
|
|
10
|
+
// - PointG2 -> G2.Point
|
|
11
|
+
// - PointG2.fromSignature -> Signature.decode
|
|
12
|
+
// - PointG2.toSignature -> Signature.encode
|
|
13
|
+
// - Fixed Fp2 ORDER
|
|
14
|
+
// - Points now have only two coordinates
|
|
5
15
|
const sha256_1 = require("@noble/hashes/sha256");
|
|
6
16
|
const utils_1 = require("@noble/hashes/utils");
|
|
7
17
|
const bls_js_1 = require("./abstract/bls.js");
|
|
@@ -10,13 +20,6 @@ const utils_js_1 = require("./abstract/utils.js");
|
|
|
10
20
|
// Types
|
|
11
21
|
const weierstrass_js_1 = require("./abstract/weierstrass.js");
|
|
12
22
|
const hash_to_curve_js_1 = require("./abstract/hash-to-curve.js");
|
|
13
|
-
// Differences from bls12-381:
|
|
14
|
-
// - PointG1 -> G1.Point
|
|
15
|
-
// - PointG2 -> G2.Point
|
|
16
|
-
// - PointG2.fromSignature -> Signature.decode
|
|
17
|
-
// - PointG2.toSignature -> Signature.encode
|
|
18
|
-
// - Fixed Fp2 ORDER
|
|
19
|
-
// Points now have only two coordinates
|
|
20
23
|
// CURVE FIELDS
|
|
21
24
|
// Finite field over p.
|
|
22
25
|
const Fp = mod.Fp(0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn);
|
|
@@ -100,6 +103,8 @@ const Fp2 = {
|
|
|
100
103
|
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
|
101
104
|
},
|
|
102
105
|
sqrt: (num) => {
|
|
106
|
+
if (Fp2.equals(num, Fp2.ZERO))
|
|
107
|
+
return Fp2.ZERO; // Algo doesn't handles this case
|
|
103
108
|
// TODO: Optimize this line. It's extremely slow.
|
|
104
109
|
// Speeding this up would boost aggregateSignatures.
|
|
105
110
|
// https://eprint.iacr.org/2012/685.pdf applicable?
|
package/lib/ed25519.js
CHANGED
|
@@ -225,13 +225,13 @@ const D_MINUS_ONE_SQ = BigInt('4044083434630853685810104246932319082624839914623
|
|
|
225
225
|
// Calculates 1/√(number)
|
|
226
226
|
const invertSqrt = (number) => uvRatio(_1n, number);
|
|
227
227
|
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
228
|
-
const bytes255ToNumberLE = (bytes) => exports.ed25519.
|
|
228
|
+
const bytes255ToNumberLE = (bytes) => exports.ed25519.CURVE.Fp.create((0, utils_js_1.bytesToNumberLE)(bytes) & MAX_255B);
|
|
229
229
|
// Computes Elligator map for Ristretto
|
|
230
230
|
// https://ristretto.group/formulas/elligator.html
|
|
231
231
|
function calcElligatorRistrettoMap(r0) {
|
|
232
232
|
const { d } = exports.ed25519.CURVE;
|
|
233
233
|
const P = exports.ed25519.CURVE.Fp.ORDER;
|
|
234
|
-
const
|
|
234
|
+
const mod = exports.ed25519.CURVE.Fp.create;
|
|
235
235
|
const r = mod(SQRT_M1 * r0 * r0); // 1
|
|
236
236
|
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
|
237
237
|
let c = BigInt(-1); // 3
|
|
@@ -289,7 +289,7 @@ class RistrettoPoint {
|
|
|
289
289
|
hex = (0, utils_js_1.ensureBytes)(hex, 32);
|
|
290
290
|
const { a, d } = exports.ed25519.CURVE;
|
|
291
291
|
const P = exports.ed25519.CURVE.Fp.ORDER;
|
|
292
|
-
const
|
|
292
|
+
const mod = exports.ed25519.CURVE.Fp.create;
|
|
293
293
|
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
|
294
294
|
const s = bytes255ToNumberLE(hex);
|
|
295
295
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
|
@@ -321,7 +321,7 @@ class RistrettoPoint {
|
|
|
321
321
|
toRawBytes() {
|
|
322
322
|
let { x, y, z, t } = this.ep;
|
|
323
323
|
const P = exports.ed25519.CURVE.Fp.ORDER;
|
|
324
|
-
const
|
|
324
|
+
const mod = exports.ed25519.CURVE.Fp.create;
|
|
325
325
|
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
|
326
326
|
const u2 = mod(x * y); // 2
|
|
327
327
|
// Square root always exists
|
|
@@ -359,7 +359,7 @@ class RistrettoPoint {
|
|
|
359
359
|
assertRstPoint(other);
|
|
360
360
|
const a = this.ep;
|
|
361
361
|
const b = other.ep;
|
|
362
|
-
const
|
|
362
|
+
const mod = exports.ed25519.CURVE.Fp.create;
|
|
363
363
|
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
|
364
364
|
const one = mod(a.x * b.y) === mod(a.y * b.x);
|
|
365
365
|
const two = mod(a.y * b.y) === mod(a.x * b.x);
|
package/lib/ed448.js
CHANGED
|
@@ -129,7 +129,8 @@ const ED448_DEF = {
|
|
|
129
129
|
d: BigInt('726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'),
|
|
130
130
|
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
|
131
131
|
Fp,
|
|
132
|
-
// Subgroup order: how many points
|
|
132
|
+
// Subgroup order: how many points curve has;
|
|
133
|
+
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
|
133
134
|
n: BigInt('181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'),
|
|
134
135
|
nBitLength: 456,
|
|
135
136
|
// Cofactor
|
package/lib/esm/abstract/bls.js
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// NOTE: only 12 supported for now
|
|
4
|
-
// Constructed from pair of weierstrass curves, based pairing logic
|
|
5
|
-
import * as mod from './modular.js';
|
|
6
|
-
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js';
|
|
7
|
-
// Types
|
|
8
|
-
import { hexToBytes, bytesToHex } from './utils.js';
|
|
9
|
-
import { stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js';
|
|
1
|
+
import * as ut from './utils.js';
|
|
2
|
+
import { stringToBytes, hash_to_field as hashToField, expand_message_xmd as expandMessageXMD, } from './hash-to-curve.js';
|
|
10
3
|
import { weierstrassPoints } from './weierstrass.js';
|
|
11
4
|
export function bls(CURVE) {
|
|
12
5
|
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
|
13
|
-
const Fp = CURVE
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const Fp6 = CURVE.Fp6;
|
|
17
|
-
const Fp12 = CURVE.Fp12;
|
|
18
|
-
const BLS_X_LEN = bitLen(CURVE.x);
|
|
6
|
+
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
|
7
|
+
const BLS_X_LEN = ut.bitLen(CURVE.x);
|
|
8
|
+
const groupLen = 32; // TODO: calculate; hardcoded for now
|
|
19
9
|
// Pre-compute coefficients for sparse multiplication
|
|
20
10
|
// Point addition and point double calculations is reused for coefficients
|
|
21
11
|
function calcPairingPrecomputes(x, y) {
|
|
@@ -39,7 +29,7 @@ export function bls(CURVE) {
|
|
|
39
29
|
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
|
40
30
|
Ry = Fp2.sub(Fp2.square(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.square(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
41
31
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
42
|
-
if (bitGet(CURVE.x, i)) {
|
|
32
|
+
if (ut.bitGet(CURVE.x, i)) {
|
|
43
33
|
// Addition
|
|
44
34
|
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
|
45
35
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
@@ -60,13 +50,14 @@ export function bls(CURVE) {
|
|
|
60
50
|
return ell_coeff;
|
|
61
51
|
}
|
|
62
52
|
function millerLoop(ell, g1) {
|
|
53
|
+
const { x } = CURVE;
|
|
63
54
|
const Px = g1[0];
|
|
64
55
|
const Py = g1[1];
|
|
65
56
|
let f12 = Fp12.ONE;
|
|
66
57
|
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
|
67
58
|
const E = ell[j];
|
|
68
59
|
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
|
69
|
-
if (bitGet(
|
|
60
|
+
if (ut.bitGet(x, i)) {
|
|
70
61
|
j += 1;
|
|
71
62
|
const F = ell[j];
|
|
72
63
|
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
|
@@ -76,79 +67,25 @@ export function bls(CURVE) {
|
|
|
76
67
|
}
|
|
77
68
|
return Fp12.conjugate(f12);
|
|
78
69
|
}
|
|
79
|
-
// bls12-381 is a construction of two curves:
|
|
80
|
-
// 1. Fp: (x, y)
|
|
81
|
-
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers)
|
|
82
|
-
//
|
|
83
|
-
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one:
|
|
84
|
-
// Fp₁₂ = e(Fp, Fp2)
|
|
85
|
-
// where Fp₁₂ = 12-degree polynomial
|
|
86
|
-
// Pairing is used to verify signatures.
|
|
87
|
-
//
|
|
88
|
-
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer).
|
|
89
|
-
// Some projects may prefer to swap this relation, it is not supported for now.
|
|
90
|
-
const htfDefaults = { ...CURVE.htfDefaults };
|
|
91
|
-
function isWithinCurveOrder(num) {
|
|
92
|
-
return 0 < num && num < CURVE.r;
|
|
93
|
-
}
|
|
94
70
|
const utils = {
|
|
95
|
-
hexToBytes: hexToBytes,
|
|
96
|
-
bytesToHex: bytesToHex,
|
|
97
|
-
|
|
98
|
-
stringToBytes,
|
|
71
|
+
hexToBytes: ut.hexToBytes,
|
|
72
|
+
bytesToHex: ut.bytesToHex,
|
|
73
|
+
stringToBytes: stringToBytes,
|
|
99
74
|
// TODO: do we need to export it here?
|
|
100
|
-
hashToField: (msg, count, options = {}) =>
|
|
101
|
-
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
107
|
-
* @param hash hash output from sha512, or a similar function
|
|
108
|
-
* @returns valid private key
|
|
109
|
-
*/
|
|
110
|
-
hashToPrivateKey: (hash) => {
|
|
111
|
-
hash = ensureBytes(hash);
|
|
112
|
-
if (hash.length < 40 || hash.length > 1024)
|
|
113
|
-
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
|
|
114
|
-
// hashToPrivateScalar(hash, CURVE.r)
|
|
115
|
-
// NOTE: doesn't add +/-1
|
|
116
|
-
const num = mod.mod(bytesToNumberBE(hash), CURVE.r);
|
|
117
|
-
// This should never happen
|
|
118
|
-
if (num === 0n || num === 1n)
|
|
119
|
-
throw new Error('Invalid private key');
|
|
120
|
-
return numberToBytesBE(num, 32);
|
|
121
|
-
},
|
|
122
|
-
randomBytes: (bytesLength = 32) => CURVE.randomBytes(bytesLength),
|
|
123
|
-
// NIST SP 800-56A rev 3, section 5.6.1.2.2
|
|
124
|
-
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
125
|
-
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(40)),
|
|
126
|
-
getDSTLabel: () => htfDefaults.DST,
|
|
75
|
+
hashToField: (msg, count, options = {}) => hashToField(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
76
|
+
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => expandMessageXMD(msg, DST, lenInBytes, H),
|
|
77
|
+
hashToPrivateKey: (hash) => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)),
|
|
78
|
+
randomBytes: (bytesLength = groupLen) => CURVE.randomBytes(bytesLength),
|
|
79
|
+
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
|
80
|
+
getDSTLabel: () => CURVE.htfDefaults.DST,
|
|
127
81
|
setDSTLabel(newLabel) {
|
|
128
82
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
|
129
83
|
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
|
130
84
|
throw new TypeError('Invalid DST');
|
|
131
85
|
}
|
|
132
|
-
htfDefaults.DST = newLabel;
|
|
86
|
+
CURVE.htfDefaults.DST = newLabel;
|
|
133
87
|
},
|
|
134
88
|
};
|
|
135
|
-
function normalizePrivKey(key) {
|
|
136
|
-
let int;
|
|
137
|
-
if (key instanceof Uint8Array && key.length === 32)
|
|
138
|
-
int = bytesToNumberBE(key);
|
|
139
|
-
else if (typeof key === 'string' && key.length === 64)
|
|
140
|
-
int = BigInt(`0x${key}`);
|
|
141
|
-
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key))
|
|
142
|
-
int = BigInt(key);
|
|
143
|
-
else if (typeof key === 'bigint' && key > 0n)
|
|
144
|
-
int = key;
|
|
145
|
-
else
|
|
146
|
-
throw new TypeError('Expected valid private key');
|
|
147
|
-
int = mod.mod(int, CURVE.r);
|
|
148
|
-
if (!isWithinCurveOrder(int))
|
|
149
|
-
throw new Error('Private key must be 0 < key < CURVE.r');
|
|
150
|
-
return int;
|
|
151
|
-
}
|
|
152
89
|
// Point on G1 curve: (x, y)
|
|
153
90
|
const G1 = weierstrassPoints({
|
|
154
91
|
n: Fr.ORDER,
|
|
@@ -202,7 +139,7 @@ export function bls(CURVE) {
|
|
|
202
139
|
function sign(message, privateKey) {
|
|
203
140
|
const msgPoint = normP2Hash(message);
|
|
204
141
|
msgPoint.assertValidity();
|
|
205
|
-
const sigPoint = msgPoint.multiply(
|
|
142
|
+
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
|
206
143
|
if (message instanceof G2.Point)
|
|
207
144
|
return sigPoint;
|
|
208
145
|
return Signature.encode(sigPoint);
|
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
3
3
|
// Differences from @noble/ed25519 1.7:
|
|
4
|
-
// 1.
|
|
4
|
+
// 1. Variable field element lengths between EDDSA/ECDH:
|
|
5
5
|
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
|
6
6
|
// 2. Different addition formula (doubling is same)
|
|
7
7
|
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
|
8
|
-
// 4. Point decompression code is different
|
|
8
|
+
// 4. Point decompression code is different (unexpected), now using generalized formula
|
|
9
9
|
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
|
|
10
10
|
import * as mod from './modular.js';
|
|
11
|
-
import
|
|
11
|
+
import * as ut from './utils.js';
|
|
12
|
+
import { ensureBytes } from './utils.js';
|
|
12
13
|
import { wNAF } from './group.js';
|
|
13
|
-
import { hash_to_field, validateHTFOpts } from './hash-to-curve.js';
|
|
14
|
+
import { hash_to_field as hashToField, validateHTFOpts } from './hash-to-curve.js';
|
|
14
15
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
15
16
|
const _0n = BigInt(0);
|
|
16
17
|
const _1n = BigInt(1);
|
|
17
18
|
const _2n = BigInt(2);
|
|
18
19
|
const _8n = BigInt(8);
|
|
19
|
-
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
20
20
|
function validateOpts(curve) {
|
|
21
|
-
const opts =
|
|
22
|
-
if (typeof opts.hash !== 'function' || !
|
|
21
|
+
const opts = ut.validateOpts(curve);
|
|
22
|
+
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
|
|
23
23
|
throw new Error('Invalid hash function');
|
|
24
24
|
for (const i of ['a', 'd']) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const val = opts[i];
|
|
26
|
+
if (typeof val !== 'bigint')
|
|
27
|
+
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
|
27
28
|
}
|
|
28
29
|
for (const fn of ['randomBytes']) {
|
|
29
30
|
if (typeof opts[fn] !== 'function')
|
|
30
31
|
throw new Error(`Invalid ${fn} function`);
|
|
31
32
|
}
|
|
32
|
-
for (const fn of [
|
|
33
|
-
'adjustScalarBytes',
|
|
34
|
-
'domain',
|
|
35
|
-
'uvRatio',
|
|
36
|
-
'mapToCurve',
|
|
37
|
-
'clearCofactor',
|
|
38
|
-
]) {
|
|
33
|
+
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
|
|
39
34
|
if (opts[fn] === undefined)
|
|
40
35
|
continue; // Optional
|
|
41
36
|
if (typeof opts[fn] !== 'function')
|
|
@@ -51,34 +46,27 @@ export function twistedEdwards(curveDef) {
|
|
|
51
46
|
const CURVE = validateOpts(curveDef);
|
|
52
47
|
const Fp = CURVE.Fp;
|
|
53
48
|
const CURVE_ORDER = CURVE.n;
|
|
54
|
-
const
|
|
55
|
-
if (fieldLen > 2048)
|
|
56
|
-
throw new Error('Field lengths over 2048 are not supported');
|
|
57
|
-
const groupLen = CURVE.nByteLength;
|
|
58
|
-
// (2n ** 256n).toString(16);
|
|
59
|
-
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256
|
|
49
|
+
const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8);
|
|
60
50
|
// Function overrides
|
|
61
51
|
const { randomBytes } = CURVE;
|
|
62
52
|
const modP = Fp.create;
|
|
63
53
|
// sqrt(u/v)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
const domain = CURVE.domain || _domain; // NOOP
|
|
54
|
+
const uvRatio = CURVE.uvRatio ||
|
|
55
|
+
((u, v) => {
|
|
56
|
+
try {
|
|
57
|
+
return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) };
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return { isValid: false, value: _0n };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
|
|
64
|
+
const domain = CURVE.domain ||
|
|
65
|
+
((data, ctx, phflag) => {
|
|
66
|
+
if (ctx.length || phflag)
|
|
67
|
+
throw new Error('Contexts/pre-hash are not supported');
|
|
68
|
+
return data;
|
|
69
|
+
}); // NOOP
|
|
82
70
|
/**
|
|
83
71
|
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
|
84
72
|
* Default Point works in affine coordinates: (x, y)
|
|
@@ -215,26 +203,28 @@ export function twistedEdwards(curveDef) {
|
|
|
215
203
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
216
204
|
// It's faster, but should only be used when you don't care about
|
|
217
205
|
// an exposed private key e.g. sig verification.
|
|
218
|
-
// Allows scalar bigger than curve order, but less than 2^256
|
|
219
206
|
multiplyUnsafe(scalar) {
|
|
220
207
|
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
|
221
|
-
const G = ExtendedPoint.BASE;
|
|
222
208
|
const P0 = ExtendedPoint.ZERO;
|
|
223
209
|
if (n === _0n)
|
|
224
210
|
return P0;
|
|
225
211
|
if (this.equals(P0) || n === _1n)
|
|
226
212
|
return this;
|
|
227
|
-
if (this.equals(
|
|
213
|
+
if (this.equals(ExtendedPoint.BASE))
|
|
228
214
|
return this.wNAF(n);
|
|
229
215
|
return wnaf.unsafeLadder(this, n);
|
|
230
216
|
}
|
|
217
|
+
// Checks if point is of small order.
|
|
218
|
+
// If you add something to small order point, you will have "dirty"
|
|
219
|
+
// point with torsion component.
|
|
231
220
|
// Multiplies point by cofactor and checks if the result is 0.
|
|
232
221
|
isSmallOrder() {
|
|
233
222
|
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
|
234
223
|
}
|
|
235
|
-
// Multiplies point by
|
|
224
|
+
// Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0.
|
|
225
|
+
// Returns `false` is the point is dirty.
|
|
236
226
|
isTorsionFree() {
|
|
237
|
-
return
|
|
227
|
+
return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO);
|
|
238
228
|
}
|
|
239
229
|
// Converts Extended point to default (x, y) coordinates.
|
|
240
230
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
@@ -253,18 +243,15 @@ export function twistedEdwards(curveDef) {
|
|
|
253
243
|
return new Point(ax, ay);
|
|
254
244
|
}
|
|
255
245
|
clearCofactor() {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (CURVE.clearCofactor)
|
|
261
|
-
return CURVE.clearCofactor(ExtendedPoint, this);
|
|
262
|
-
return this.multiplyUnsafe(CURVE.h);
|
|
246
|
+
const { h: cofactor } = CURVE;
|
|
247
|
+
if (cofactor === _1n)
|
|
248
|
+
return this;
|
|
249
|
+
return this.multiplyUnsafe(cofactor);
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
252
|
ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
266
253
|
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
|
267
|
-
const wnaf = wNAF(ExtendedPoint,
|
|
254
|
+
const wnaf = wNAF(ExtendedPoint, CURVE.nByteLength * 8);
|
|
268
255
|
function assertExtPoint(other) {
|
|
269
256
|
if (!(other instanceof ExtendedPoint))
|
|
270
257
|
throw new TypeError('ExtendedPoint expected');
|
|
@@ -288,20 +275,21 @@ export function twistedEdwards(curveDef) {
|
|
|
288
275
|
// Uses algo from RFC8032 5.1.3.
|
|
289
276
|
static fromHex(hex, strict = true) {
|
|
290
277
|
const { d, a } = CURVE;
|
|
291
|
-
|
|
278
|
+
const len = Fp.BYTES;
|
|
279
|
+
hex = ensureBytes(hex, len);
|
|
292
280
|
// 1. First, interpret the string as an integer in little-endian
|
|
293
281
|
// representation. Bit 255 of this number is the least significant
|
|
294
282
|
// bit of the x-coordinate and denote this value x_0. The
|
|
295
283
|
// y-coordinate is recovered simply by clearing this bit. If the
|
|
296
284
|
// resulting value is >= p, decoding fails.
|
|
297
285
|
const normed = hex.slice();
|
|
298
|
-
const lastByte = hex[
|
|
299
|
-
normed[
|
|
300
|
-
const y = bytesToNumberLE(normed);
|
|
286
|
+
const lastByte = hex[len - 1];
|
|
287
|
+
normed[len - 1] = lastByte & ~0x80;
|
|
288
|
+
const y = ut.bytesToNumberLE(normed);
|
|
301
289
|
if (strict && y >= Fp.ORDER)
|
|
302
290
|
throw new Error('Expected 0 < hex < P');
|
|
303
291
|
if (!strict && y >= maxGroupElement)
|
|
304
|
-
throw new Error('Expected 0 < hex <
|
|
292
|
+
throw new Error('Expected 0 < hex < CURVE.n');
|
|
305
293
|
// 2. To recover the x-coordinate, the curve equation implies
|
|
306
294
|
// Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
|
|
307
295
|
// Ed448: x² = (y² - 1) / (d y² - 1) (mod p).
|
|
@@ -333,14 +321,16 @@ export function twistedEdwards(curveDef) {
|
|
|
333
321
|
// When compressing point, it's enough to only store its y coordinate
|
|
334
322
|
// and use the last byte to encode sign of x.
|
|
335
323
|
toRawBytes() {
|
|
336
|
-
const bytes = numberToBytesLE(this.y,
|
|
337
|
-
bytes[
|
|
324
|
+
const bytes = ut.numberToBytesLE(this.y, Fp.BYTES);
|
|
325
|
+
bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0;
|
|
338
326
|
return bytes;
|
|
339
327
|
}
|
|
340
328
|
// Same as toRawBytes, but returns string.
|
|
341
329
|
toHex() {
|
|
342
|
-
return bytesToHex(this.toRawBytes());
|
|
330
|
+
return ut.bytesToHex(this.toRawBytes());
|
|
343
331
|
}
|
|
332
|
+
// Determines if point is in prime-order subgroup.
|
|
333
|
+
// Returns `false` is the point is dirty.
|
|
344
334
|
isTorsionFree() {
|
|
345
335
|
return ExtendedPoint.fromAffine(this).isTorsionFree();
|
|
346
336
|
}
|
|
@@ -375,22 +365,22 @@ export function twistedEdwards(curveDef) {
|
|
|
375
365
|
// Encodes byte string to elliptic curve
|
|
376
366
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
377
367
|
static hashToCurve(msg, options) {
|
|
378
|
-
|
|
368
|
+
const { mapToCurve, htfDefaults } = CURVE;
|
|
369
|
+
if (!mapToCurve)
|
|
379
370
|
throw new Error('No mapToCurve defined for curve');
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
const { x:
|
|
383
|
-
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
|
|
371
|
+
const u = hashToField(ensureBytes(msg), 2, { ...htfDefaults, ...options });
|
|
372
|
+
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
|
373
|
+
const { x: x1, y: y1 } = mapToCurve(u[1]);
|
|
384
374
|
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
385
375
|
return p;
|
|
386
376
|
}
|
|
387
377
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
388
378
|
static encodeToCurve(msg, options) {
|
|
389
|
-
|
|
379
|
+
const { mapToCurve, htfDefaults } = CURVE;
|
|
380
|
+
if (!mapToCurve)
|
|
390
381
|
throw new Error('No mapToCurve defined for curve');
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
const { x, y } = CURVE.mapToCurve(u[0]);
|
|
382
|
+
const u = hashToField(ensureBytes(msg), 1, { ...htfDefaults, ...options });
|
|
383
|
+
const { x, y } = mapToCurve(u[0]);
|
|
394
384
|
return new Point(x, y).clearCofactor();
|
|
395
385
|
}
|
|
396
386
|
}
|
|
@@ -410,9 +400,10 @@ export function twistedEdwards(curveDef) {
|
|
|
410
400
|
this.assertValidity();
|
|
411
401
|
}
|
|
412
402
|
static fromHex(hex) {
|
|
413
|
-
const
|
|
414
|
-
const
|
|
415
|
-
const
|
|
403
|
+
const len = Fp.BYTES;
|
|
404
|
+
const bytes = ensureBytes(hex, 2 * len);
|
|
405
|
+
const r = Point.fromHex(bytes.slice(0, len), false);
|
|
406
|
+
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len));
|
|
416
407
|
return new Signature(r, s);
|
|
417
408
|
}
|
|
418
409
|
assertValidity() {
|
|
@@ -424,15 +415,15 @@ export function twistedEdwards(curveDef) {
|
|
|
424
415
|
return this;
|
|
425
416
|
}
|
|
426
417
|
toRawBytes() {
|
|
427
|
-
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s,
|
|
418
|
+
return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES));
|
|
428
419
|
}
|
|
429
420
|
toHex() {
|
|
430
|
-
return bytesToHex(this.toRawBytes());
|
|
421
|
+
return ut.bytesToHex(this.toRawBytes());
|
|
431
422
|
}
|
|
432
423
|
}
|
|
433
424
|
// Little-endian SHA512 with modulo n
|
|
434
|
-
function
|
|
435
|
-
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER);
|
|
425
|
+
function modnLE(hash) {
|
|
426
|
+
return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER);
|
|
436
427
|
}
|
|
437
428
|
/**
|
|
438
429
|
* Checks for num to be in range:
|
|
@@ -443,7 +434,7 @@ export function twistedEdwards(curveDef) {
|
|
|
443
434
|
function normalizeScalar(num, max, strict = true) {
|
|
444
435
|
if (!max)
|
|
445
436
|
throw new TypeError('Specify max value');
|
|
446
|
-
if (
|
|
437
|
+
if (ut.isPositiveInt(num))
|
|
447
438
|
num = BigInt(num);
|
|
448
439
|
if (typeof num === 'bigint' && num < max) {
|
|
449
440
|
if (strict) {
|
|
@@ -455,36 +446,30 @@ export function twistedEdwards(curveDef) {
|
|
|
455
446
|
return num;
|
|
456
447
|
}
|
|
457
448
|
}
|
|
458
|
-
throw new TypeError(
|
|
449
|
+
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
|
|
459
450
|
}
|
|
460
|
-
|
|
451
|
+
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
452
|
+
function getExtendedPublicKey(key) {
|
|
453
|
+
const groupLen = CURVE.nByteLength;
|
|
461
454
|
// Normalize bigint / number / string to Uint8Array
|
|
462
|
-
key
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
// Takes 64 bytes
|
|
471
|
-
function getKeyFromHash(hashed) {
|
|
472
|
-
// First 32 bytes of 64b uniformingly random input are taken,
|
|
473
|
-
// clears 3 bits of it to produce a random field element.
|
|
455
|
+
const keyb = typeof key === 'bigint' || typeof key === 'number'
|
|
456
|
+
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
|
457
|
+
: key;
|
|
458
|
+
// Hash private key with curve's hash function to produce uniformingly random input
|
|
459
|
+
// We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key)))
|
|
460
|
+
const hashed = ensureBytes(CURVE.hash(ensureBytes(keyb, groupLen)), 2 * groupLen);
|
|
461
|
+
// First half's bits are cleared to produce a random field element.
|
|
474
462
|
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
|
475
|
-
// Second
|
|
463
|
+
// Second half is called key prefix (5.1.6)
|
|
476
464
|
const prefix = hashed.slice(groupLen, 2 * groupLen);
|
|
477
465
|
// The actual private scalar
|
|
478
|
-
const scalar =
|
|
466
|
+
const scalar = modnLE(head);
|
|
479
467
|
// Point on Edwards curve aka public key
|
|
480
468
|
const point = Point.BASE.multiply(scalar);
|
|
469
|
+
// Uint8Array representation
|
|
481
470
|
const pointBytes = point.toRawBytes();
|
|
482
471
|
return { head, prefix, scalar, point, pointBytes };
|
|
483
472
|
}
|
|
484
|
-
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
485
|
-
function getExtendedPublicKey(key) {
|
|
486
|
-
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
|
|
487
|
-
}
|
|
488
473
|
/**
|
|
489
474
|
* Calculates ed25519 public key. RFC8032 5.1.5
|
|
490
475
|
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
|
@@ -496,7 +481,7 @@ export function twistedEdwards(curveDef) {
|
|
|
496
481
|
const EMPTY = new Uint8Array();
|
|
497
482
|
function hashDomainToScalar(message, context = EMPTY) {
|
|
498
483
|
context = ensureBytes(context);
|
|
499
|
-
return
|
|
484
|
+
return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
|
500
485
|
}
|
|
501
486
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
502
487
|
function sign(message, privateKey, context) {
|
|
@@ -504,9 +489,9 @@ export function twistedEdwards(curveDef) {
|
|
|
504
489
|
if (CURVE.preHash)
|
|
505
490
|
message = CURVE.preHash(message);
|
|
506
491
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
|
507
|
-
const r = hashDomainToScalar(concatBytes(prefix, message), context);
|
|
492
|
+
const r = hashDomainToScalar(ut.concatBytes(prefix, message), context);
|
|
508
493
|
const R = Point.BASE.multiply(r); // R = rG
|
|
509
|
-
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
|
494
|
+
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
|
510
495
|
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
|
511
496
|
return new Signature(R, s).toRawBytes();
|
|
512
497
|
}
|
|
@@ -545,27 +530,25 @@ export function twistedEdwards(curveDef) {
|
|
|
545
530
|
throw new Error(`Wrong signature: ${sig}`);
|
|
546
531
|
const { r, s } = sig;
|
|
547
532
|
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
|
548
|
-
const k = hashDomainToScalar(concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
|
|
533
|
+
const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
|
|
549
534
|
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
|
550
535
|
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
|
551
536
|
// [8][S]B = [8]R + [8][k]A'
|
|
552
|
-
return RkA.subtract(SB).
|
|
537
|
+
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO);
|
|
553
538
|
}
|
|
554
539
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
555
540
|
Point.BASE._setWindowSize(8);
|
|
556
541
|
const utils = {
|
|
557
542
|
getExtendedPublicKey,
|
|
558
|
-
mod: modP,
|
|
559
|
-
invert: Fp.invert,
|
|
560
543
|
/**
|
|
561
544
|
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
|
562
545
|
*/
|
|
563
|
-
hashToPrivateScalar: (hash) => hashToPrivateScalar(hash, CURVE_ORDER, true),
|
|
546
|
+
hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true),
|
|
564
547
|
/**
|
|
565
548
|
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
|
|
566
549
|
* modulo bias like we do in secp256k1 randomPrivateKey()
|
|
567
550
|
*/
|
|
568
|
-
randomPrivateKey: () => randomBytes(
|
|
551
|
+
randomPrivateKey: () => randomBytes(Fp.BYTES),
|
|
569
552
|
/**
|
|
570
553
|
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
|
571
554
|
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
|