@noble/curves 1.9.1 → 1.9.3
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 +238 -227
- package/_shortw_utils.d.ts +8 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +3 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +123 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +219 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +142 -21
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +224 -143
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +190 -49
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +322 -136
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +31 -13
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +34 -19
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +31 -13
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +125 -52
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +18 -5
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +23 -6
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +23 -49
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +206 -124
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +747 -604
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +55 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +172 -186
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +60 -57
- package/ed448.d.ts.map +1 -1
- package/ed448.js +172 -166
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +8 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +3 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +123 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +220 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +142 -21
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +219 -143
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +190 -49
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +320 -138
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +31 -13
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +33 -19
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +31 -13
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +124 -51
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +18 -5
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +23 -6
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +23 -49
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +206 -124
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +743 -605
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +55 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +170 -183
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +60 -57
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +169 -162
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +3 -3
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +3 -3
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +3 -5
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -29
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -22
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +106 -101
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +7 -3
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +4 -0
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +7 -4
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +4 -1
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +7 -3
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +4 -0
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts +38 -21
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +112 -104
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/index.js +7 -9
- package/index.js.map +1 -1
- package/jubjub.d.ts +3 -3
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +3 -3
- package/jubjub.js.map +1 -1
- package/misc.d.ts +3 -5
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -33
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -22
- package/nist.d.ts.map +1 -1
- package/nist.js +106 -101
- package/nist.js.map +1 -1
- package/p256.d.ts +7 -3
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +7 -4
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +7 -3
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +17 -6
- package/secp256k1.d.ts +38 -21
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +112 -104
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +6 -15
- package/src/abstract/bls.ts +428 -251
- package/src/abstract/curve.ts +307 -149
- package/src/abstract/edwards.ts +555 -203
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +75 -34
- package/src/abstract/modular.ts +131 -59
- package/src/abstract/montgomery.ts +44 -15
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +40 -71
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +1086 -746
- package/src/bls12-381.ts +549 -490
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +214 -216
- package/src/ed448.ts +251 -220
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +41 -40
- package/src/nist.ts +161 -126
- package/src/p256.ts +7 -3
- package/src/p384.ts +7 -5
- package/src/p521.ts +7 -3
- package/src/secp256k1.ts +145 -115
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
3
3
|
*
|
|
4
|
-
* ### Parameters
|
|
5
|
-
*
|
|
6
|
-
* To initialize a weierstrass curve, one needs to pass following params:
|
|
7
|
-
*
|
|
8
|
-
* * a: formula param
|
|
9
|
-
* * b: formula param
|
|
10
|
-
* * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
|
|
11
|
-
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
12
|
-
* * Gx: Base point (x, y) aka generator point. Gx = x coordinate
|
|
13
|
-
* * Gy: ...y coordinate
|
|
14
|
-
* * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
|
|
15
|
-
* * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
|
|
16
|
-
*
|
|
17
4
|
* ### Design rationale for types
|
|
18
5
|
*
|
|
19
6
|
* * Interaction between classes from different curves should fail:
|
|
@@ -38,29 +25,57 @@
|
|
|
38
25
|
* @module
|
|
39
26
|
*/
|
|
40
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
41
|
-
|
|
28
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
29
|
+
import { ahash } from '@noble/hashes/utils';
|
|
30
|
+
import {
|
|
31
|
+
_validateObject,
|
|
32
|
+
abool,
|
|
33
|
+
abytes,
|
|
34
|
+
aInRange,
|
|
35
|
+
bitLen,
|
|
36
|
+
bitMask,
|
|
37
|
+
bytesToHex,
|
|
38
|
+
bytesToNumberBE,
|
|
39
|
+
concatBytes,
|
|
40
|
+
createHmacDrbg,
|
|
41
|
+
ensureBytes,
|
|
42
|
+
hexToBytes,
|
|
43
|
+
inRange,
|
|
44
|
+
isBytes,
|
|
45
|
+
memoized,
|
|
46
|
+
numberToHexUnpadded,
|
|
47
|
+
randomBytes,
|
|
48
|
+
type CHash,
|
|
49
|
+
type Hex,
|
|
50
|
+
type PrivKey,
|
|
51
|
+
} from '../utils.ts';
|
|
42
52
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
_createCurveFields,
|
|
54
|
+
mulEndoUnsafe,
|
|
55
|
+
negateCt,
|
|
56
|
+
normalizeZ,
|
|
57
|
+
pippenger,
|
|
58
|
+
wNAF,
|
|
59
|
+
type AffinePoint,
|
|
60
|
+
type BasicCurve,
|
|
61
|
+
type CurveInfo,
|
|
62
|
+
type CurvePoint,
|
|
63
|
+
type CurvePointCons,
|
|
45
64
|
} from './curve.ts';
|
|
46
|
-
// prettier-ignore
|
|
47
65
|
import {
|
|
48
66
|
Field,
|
|
49
67
|
FpInvertBatch,
|
|
50
|
-
getMinHashLength,
|
|
51
|
-
|
|
68
|
+
getMinHashLength,
|
|
69
|
+
mapHashToField,
|
|
70
|
+
validateField,
|
|
71
|
+
type IField,
|
|
72
|
+
type NLength,
|
|
52
73
|
} from './modular.ts';
|
|
53
|
-
// prettier-ignore
|
|
54
|
-
import {
|
|
55
|
-
aInRange, abool,
|
|
56
|
-
bitMask,
|
|
57
|
-
bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
|
|
58
|
-
inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject,
|
|
59
|
-
type CHash, type Hex, type PrivKey
|
|
60
|
-
} from './utils.ts';
|
|
61
74
|
|
|
62
75
|
export type { AffinePoint };
|
|
63
|
-
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
76
|
+
export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
77
|
+
|
|
78
|
+
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
|
64
79
|
/**
|
|
65
80
|
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
|
|
66
81
|
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
|
|
@@ -86,7 +101,8 @@ type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
|
86
101
|
*/
|
|
87
102
|
export type EndomorphismOpts = {
|
|
88
103
|
beta: bigint;
|
|
89
|
-
|
|
104
|
+
basises?: EndoBasis;
|
|
105
|
+
splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
90
106
|
};
|
|
91
107
|
export type BasicWCurve<T> = BasicCurve<T> & {
|
|
92
108
|
// Params: a, b
|
|
@@ -99,101 +115,228 @@ export type BasicWCurve<T> = BasicCurve<T> & {
|
|
|
99
115
|
endo?: EndomorphismOpts;
|
|
100
116
|
// When a cofactor != 1, there can be an effective methods to:
|
|
101
117
|
// 1. Determine whether a point is torsion-free
|
|
102
|
-
isTorsionFree?: (c:
|
|
118
|
+
isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
103
119
|
// 2. Clear torsion component
|
|
104
|
-
clearCofactor?: (c:
|
|
120
|
+
clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
105
121
|
};
|
|
106
122
|
|
|
123
|
+
// We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
|
|
124
|
+
const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
|
|
125
|
+
|
|
126
|
+
export type ScalarEndoParts = { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Splits scalar for GLV endomorphism.
|
|
130
|
+
*/
|
|
131
|
+
export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts {
|
|
132
|
+
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
|
|
133
|
+
// Since part can be negative, we need to do this on point.
|
|
134
|
+
// TODO: verifyScalar function which consumes lambda
|
|
135
|
+
const [[a1, b1], [a2, b2]] = basis;
|
|
136
|
+
const c1 = divNearest(b2 * k, n);
|
|
137
|
+
const c2 = divNearest(-b1 * k, n);
|
|
138
|
+
// |k1|/|k2| is < sqrt(N), but can be negative.
|
|
139
|
+
// If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
|
|
140
|
+
let k1 = k - c1 * a1 - c2 * a2;
|
|
141
|
+
let k2 = -c1 * b1 - c2 * b2;
|
|
142
|
+
const k1neg = k1 < _0n;
|
|
143
|
+
const k2neg = k2 < _0n;
|
|
144
|
+
if (k1neg) k1 = -k1;
|
|
145
|
+
if (k2neg) k2 = -k2;
|
|
146
|
+
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
147
|
+
// This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
|
|
148
|
+
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
|
|
149
|
+
if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
|
|
150
|
+
throw new Error('splitScalar (endomorphism): failed, k=' + k);
|
|
151
|
+
}
|
|
152
|
+
return { k1neg, k1, k2neg, k2 };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type ECDSASigFormat = 'compact' | 'der';
|
|
107
156
|
export type Entropy = Hex | boolean;
|
|
108
|
-
export type SignOpts = {
|
|
109
|
-
|
|
157
|
+
export type SignOpts = Partial<{
|
|
158
|
+
lowS: boolean;
|
|
159
|
+
extraEntropy: Entropy;
|
|
160
|
+
prehash: boolean;
|
|
161
|
+
format: ECDSASigFormat | 'js';
|
|
162
|
+
}>;
|
|
163
|
+
export type VerOpts = Partial<{
|
|
164
|
+
lowS: boolean;
|
|
165
|
+
prehash: boolean;
|
|
166
|
+
format: ECDSASigFormat | 'js' | undefined;
|
|
167
|
+
}>;
|
|
110
168
|
|
|
111
169
|
function validateSigVerOpts(opts: SignOpts | VerOpts) {
|
|
112
170
|
if (opts.lowS !== undefined) abool('lowS', opts.lowS);
|
|
113
171
|
if (opts.prehash !== undefined) abool('prehash', opts.prehash);
|
|
114
172
|
}
|
|
115
173
|
|
|
116
|
-
|
|
117
|
-
export interface
|
|
118
|
-
|
|
119
|
-
readonly
|
|
120
|
-
|
|
174
|
+
/** Instance methods for 3D XYZ projective points. */
|
|
175
|
+
export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
|
|
176
|
+
/** projective X coordinate. Different from affine x. */
|
|
177
|
+
readonly X: T;
|
|
178
|
+
/** projective Y coordinate. Different from affine y. */
|
|
179
|
+
readonly Y: T;
|
|
180
|
+
/** projective z coordinate */
|
|
181
|
+
readonly Z: T;
|
|
182
|
+
/** affine x coordinate. Different from projective X. */
|
|
121
183
|
get x(): T;
|
|
184
|
+
/** affine y coordinate. Different from projective Y. */
|
|
122
185
|
get y(): T;
|
|
123
|
-
|
|
186
|
+
/** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
|
|
187
|
+
toBytes(isCompressed?: boolean): Uint8Array;
|
|
124
188
|
toHex(isCompressed?: boolean): string;
|
|
125
|
-
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
126
189
|
|
|
127
|
-
|
|
190
|
+
/** @deprecated use .X */
|
|
191
|
+
readonly px: T;
|
|
192
|
+
/** @deprecated use .Y */
|
|
193
|
+
readonly py: T;
|
|
194
|
+
/** @deprecated use .Z */
|
|
195
|
+
readonly pz: T;
|
|
196
|
+
/** @deprecated use `toBytes` */
|
|
197
|
+
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
198
|
+
/** @deprecated use `multiplyUnsafe` */
|
|
199
|
+
multiplyAndAddUnsafe(
|
|
200
|
+
Q: WeierstrassPoint<T>,
|
|
201
|
+
a: bigint,
|
|
202
|
+
b: bigint
|
|
203
|
+
): WeierstrassPoint<T> | undefined;
|
|
204
|
+
/** @deprecated use `p.y % 2n === 0n` */
|
|
128
205
|
hasEvenY(): boolean;
|
|
129
|
-
|
|
130
|
-
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
|
131
|
-
isTorsionFree(): boolean;
|
|
132
|
-
clearCofactor(): ProjPointType<T>;
|
|
206
|
+
/** @deprecated use `p.precompute(windowSize)` */
|
|
133
207
|
_setWindowSize(windowSize: number): void;
|
|
134
208
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
209
|
+
|
|
210
|
+
/** Static methods for 3D XYZ projective points. */
|
|
211
|
+
export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPoint<T>> {
|
|
212
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
213
|
+
new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
|
|
214
|
+
/** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
|
|
215
|
+
fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
|
|
216
|
+
/** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
|
|
217
|
+
normalizeZ(points: WeierstrassPoint<T>[]): WeierstrassPoint<T>[];
|
|
218
|
+
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
|
|
219
|
+
msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
|
|
143
220
|
}
|
|
144
221
|
|
|
222
|
+
/** @deprecated use WeierstrassPoint */
|
|
223
|
+
export type ProjPointType<T> = WeierstrassPoint<T>;
|
|
224
|
+
/** @deprecated use WeierstrassPointCons */
|
|
225
|
+
export type ProjConstuctor<T> = WeierstrassPointCons<T>;
|
|
226
|
+
|
|
227
|
+
// TODO: remove
|
|
145
228
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
146
|
-
// Bytes
|
|
147
229
|
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
|
148
|
-
toBytes?: (
|
|
230
|
+
toBytes?: (
|
|
231
|
+
c: WeierstrassPointCons<T>,
|
|
232
|
+
point: WeierstrassPoint<T>,
|
|
233
|
+
isCompressed: boolean
|
|
234
|
+
) => Uint8Array;
|
|
149
235
|
};
|
|
150
236
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
>;
|
|
154
|
-
|
|
155
|
-
function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLength<T> {
|
|
156
|
-
const opts = validateBasic(curve);
|
|
157
|
-
validateObject(
|
|
158
|
-
opts,
|
|
159
|
-
{
|
|
160
|
-
a: 'field',
|
|
161
|
-
b: 'field',
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
allowInfinityPoint: 'boolean',
|
|
165
|
-
allowedPrivateKeyLengths: 'array',
|
|
166
|
-
clearCofactor: 'function',
|
|
167
|
-
fromBytes: 'function',
|
|
168
|
-
isTorsionFree: 'function',
|
|
169
|
-
toBytes: 'function',
|
|
170
|
-
wrapPrivateKey: 'boolean',
|
|
171
|
-
}
|
|
172
|
-
);
|
|
173
|
-
const { endo, Fp, a } = opts;
|
|
174
|
-
if (endo) {
|
|
175
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
176
|
-
throw new Error('invalid endo: CURVE.a must be 0');
|
|
177
|
-
}
|
|
178
|
-
if (
|
|
179
|
-
typeof endo !== 'object' ||
|
|
180
|
-
typeof endo.beta !== 'bigint' ||
|
|
181
|
-
typeof endo.splitScalar !== 'function'
|
|
182
|
-
) {
|
|
183
|
-
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return Object.freeze({ ...opts } as const);
|
|
187
|
-
}
|
|
237
|
+
// LegacyWeierstrassOpts
|
|
238
|
+
export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
|
|
188
239
|
|
|
240
|
+
// LegacyWeierstrass
|
|
189
241
|
export type CurvePointsRes<T> = {
|
|
190
|
-
|
|
191
|
-
|
|
242
|
+
Point: WeierstrassPointCons<T>;
|
|
243
|
+
|
|
244
|
+
/** @deprecated import individual CURVE params */
|
|
245
|
+
CURVE: CurvePointsType<T>;
|
|
246
|
+
/** @deprecated use `Point` */
|
|
247
|
+
ProjectivePoint: WeierstrassPointCons<T>;
|
|
248
|
+
/** @deprecated use `Point.Fn.fromBytes(privateKey)` */
|
|
192
249
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
250
|
+
/** @deprecated */
|
|
193
251
|
weierstrassEquation: (x: T) => T;
|
|
252
|
+
/** @deprecated use `Point.Fn.isValidNot0(num)` */
|
|
194
253
|
isWithinCurveOrder: (num: bigint) => boolean;
|
|
195
254
|
};
|
|
196
255
|
|
|
256
|
+
// Aliases to legacy types
|
|
257
|
+
// export type CurveType = LegacyECDSAOpts;
|
|
258
|
+
// export type CurveFn = LegacyECDSA;
|
|
259
|
+
// export type CurvePointsRes<T> = LegacyWeierstrass<T>;
|
|
260
|
+
// export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
|
|
261
|
+
// export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
|
|
262
|
+
// export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Weierstrass curve options.
|
|
266
|
+
*
|
|
267
|
+
* * p: prime characteristic (order) of finite field, in which arithmetics is done
|
|
268
|
+
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
269
|
+
* * h: cofactor, usually 1. h*n is group order; n is subgroup order
|
|
270
|
+
* * a: formula param, must be in field of p
|
|
271
|
+
* * b: formula param, must be in field of p
|
|
272
|
+
* * Gx: x coordinate of generator point a.k.a. base point
|
|
273
|
+
* * Gy: y coordinate of generator point
|
|
274
|
+
*/
|
|
275
|
+
export type WeierstrassOpts<T> = Readonly<{
|
|
276
|
+
p: bigint;
|
|
277
|
+
n: bigint;
|
|
278
|
+
h: bigint;
|
|
279
|
+
a: T;
|
|
280
|
+
b: T;
|
|
281
|
+
Gx: T;
|
|
282
|
+
Gy: T;
|
|
283
|
+
}>;
|
|
284
|
+
|
|
285
|
+
// When a cofactor != 1, there can be an effective methods to:
|
|
286
|
+
// 1. Determine whether a point is torsion-free
|
|
287
|
+
// 2. Clear torsion component
|
|
288
|
+
// wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
289
|
+
export type WeierstrassExtraOpts<T> = Partial<{
|
|
290
|
+
Fp: IField<T>;
|
|
291
|
+
Fn: IField<bigint>;
|
|
292
|
+
allowInfinityPoint: boolean;
|
|
293
|
+
endo: EndomorphismOpts;
|
|
294
|
+
isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
295
|
+
clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
296
|
+
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
|
297
|
+
toBytes: (
|
|
298
|
+
c: WeierstrassPointCons<T>,
|
|
299
|
+
point: WeierstrassPoint<T>,
|
|
300
|
+
isCompressed: boolean
|
|
301
|
+
) => Uint8Array;
|
|
302
|
+
}>;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Options for ECDSA signatures over a Weierstrass curve.
|
|
306
|
+
*/
|
|
307
|
+
export type ECDSAOpts = Partial<{
|
|
308
|
+
lowS: boolean;
|
|
309
|
+
hmac: HmacFnSync;
|
|
310
|
+
randomBytes: (bytesLength?: number) => Uint8Array;
|
|
311
|
+
bits2int: (bytes: Uint8Array) => bigint;
|
|
312
|
+
bits2int_modN: (bytes: Uint8Array) => bigint;
|
|
313
|
+
}>;
|
|
314
|
+
|
|
315
|
+
/** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
|
|
316
|
+
export interface ECDSA {
|
|
317
|
+
keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
|
|
318
|
+
getPublicKey: (secretKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
319
|
+
sign: (msgHash: Hex, secretKey: PrivKey, opts?: SignOpts) => ECDSASigRecovered;
|
|
320
|
+
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
|
321
|
+
getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
322
|
+
Point: WeierstrassPointCons<bigint>;
|
|
323
|
+
Signature: ECDSASignatureCons;
|
|
324
|
+
utils: {
|
|
325
|
+
isValidSecretKey: (secretKey: PrivKey) => boolean;
|
|
326
|
+
isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
|
|
327
|
+
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
|
|
328
|
+
|
|
329
|
+
/** @deprecated use `randomSecretKey` */
|
|
330
|
+
randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
|
|
331
|
+
/** @deprecated use `isValidSecretKey` */
|
|
332
|
+
isValidPrivateKey: (secretKey: PrivKey) => boolean;
|
|
333
|
+
/** @deprecated use `Point.Fn.fromBytes()` */
|
|
334
|
+
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
335
|
+
/** @deprecated use `point.precompute()` */
|
|
336
|
+
precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
|
|
337
|
+
};
|
|
338
|
+
info: CurveInfo;
|
|
339
|
+
}
|
|
197
340
|
export class DERErr extends Error {
|
|
198
341
|
constructor(m = '') {
|
|
199
342
|
super(m);
|
|
@@ -312,47 +455,133 @@ export const DER: IDER = {
|
|
|
312
455
|
},
|
|
313
456
|
};
|
|
314
457
|
|
|
315
|
-
function numToSizedHex(num: bigint, size: number): string {
|
|
316
|
-
return bytesToHex(numberToBytesBE(num, size));
|
|
317
|
-
}
|
|
318
|
-
|
|
319
458
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
320
459
|
// prettier-ignore
|
|
321
460
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
322
461
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
326
|
-
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
327
|
-
|
|
328
|
-
const toBytes =
|
|
329
|
-
CURVE.toBytes ||
|
|
330
|
-
((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
|
|
331
|
-
const a = point.toAffine();
|
|
332
|
-
return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
333
|
-
});
|
|
334
|
-
const fromBytes =
|
|
335
|
-
CURVE.fromBytes ||
|
|
336
|
-
((bytes: Uint8Array) => {
|
|
337
|
-
// const head = bytes[0];
|
|
338
|
-
const tail = bytes.subarray(1);
|
|
339
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
340
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
341
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
342
|
-
return { x, y };
|
|
343
|
-
});
|
|
344
|
-
|
|
462
|
+
// TODO: remove
|
|
463
|
+
export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
|
|
345
464
|
/**
|
|
346
465
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
347
466
|
* @returns y²
|
|
348
467
|
*/
|
|
349
468
|
function weierstrassEquation(x: T): T {
|
|
350
|
-
const { a, b } = CURVE;
|
|
351
469
|
const x2 = Fp.sqr(x); // x * x
|
|
352
470
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
353
471
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
354
472
|
}
|
|
473
|
+
return weierstrassEquation;
|
|
474
|
+
}
|
|
475
|
+
export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
|
|
476
|
+
const { BYTES: expected } = Fn;
|
|
477
|
+
let num: bigint;
|
|
478
|
+
if (typeof key === 'bigint') {
|
|
479
|
+
num = key;
|
|
480
|
+
} else {
|
|
481
|
+
let bytes = ensureBytes('private key', key);
|
|
482
|
+
try {
|
|
483
|
+
num = Fn.fromBytes(bytes);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
|
|
489
|
+
return num;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function weierstrassN<T>(
|
|
493
|
+
CURVE: WeierstrassOpts<T>,
|
|
494
|
+
curveOpts: WeierstrassExtraOpts<T> = {}
|
|
495
|
+
): WeierstrassPointCons<T> {
|
|
496
|
+
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
497
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
498
|
+
_validateObject(
|
|
499
|
+
curveOpts,
|
|
500
|
+
{},
|
|
501
|
+
{
|
|
502
|
+
allowInfinityPoint: 'boolean',
|
|
503
|
+
clearCofactor: 'function',
|
|
504
|
+
isTorsionFree: 'function',
|
|
505
|
+
fromBytes: 'function',
|
|
506
|
+
toBytes: 'function',
|
|
507
|
+
endo: 'object',
|
|
508
|
+
wrapPrivateKey: 'boolean',
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
const { endo } = curveOpts;
|
|
513
|
+
if (endo) {
|
|
514
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
515
|
+
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
516
|
+
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function assertCompressionIsSupported() {
|
|
521
|
+
if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Implements IEEE P1363 point encoding
|
|
525
|
+
function pointToBytes(
|
|
526
|
+
_c: WeierstrassPointCons<T>,
|
|
527
|
+
point: WeierstrassPoint<T>,
|
|
528
|
+
isCompressed: boolean
|
|
529
|
+
): Uint8Array {
|
|
530
|
+
const { x, y } = point.toAffine();
|
|
531
|
+
const bx = Fp.toBytes(x);
|
|
532
|
+
abool('isCompressed', isCompressed);
|
|
533
|
+
if (isCompressed) {
|
|
534
|
+
assertCompressionIsSupported();
|
|
535
|
+
const hasEvenY = !Fp.isOdd!(y);
|
|
536
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
537
|
+
} else {
|
|
538
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function pointFromBytes(bytes: Uint8Array) {
|
|
542
|
+
abytes(bytes);
|
|
543
|
+
const L = Fp.BYTES;
|
|
544
|
+
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
545
|
+
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
546
|
+
const length = bytes.length;
|
|
547
|
+
const head = bytes[0];
|
|
548
|
+
const tail = bytes.subarray(1);
|
|
549
|
+
// No actual validation is done here: use .assertValidity()
|
|
550
|
+
if (length === LC && (head === 0x02 || head === 0x03)) {
|
|
551
|
+
const x = Fp.fromBytes(tail);
|
|
552
|
+
if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
|
|
553
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
554
|
+
let y: T;
|
|
555
|
+
try {
|
|
556
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
557
|
+
} catch (sqrtError) {
|
|
558
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
559
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
560
|
+
}
|
|
561
|
+
assertCompressionIsSupported();
|
|
562
|
+
const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
|
|
563
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
564
|
+
if (isHeadOdd !== isYOdd) y = Fp.neg(y);
|
|
565
|
+
return { x, y };
|
|
566
|
+
} else if (length === LU && head === 0x04) {
|
|
567
|
+
// TODO: more checks
|
|
568
|
+
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
|
|
569
|
+
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
|
|
570
|
+
if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
|
|
571
|
+
return { x, y };
|
|
572
|
+
} else {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
355
578
|
|
|
579
|
+
const toBytes = curveOpts.toBytes || pointToBytes;
|
|
580
|
+
const fromBytes = curveOpts.fromBytes || pointFromBytes;
|
|
581
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
|
|
582
|
+
|
|
583
|
+
// TODO: move top-level
|
|
584
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
356
585
|
function isValidXY(x: T, y: T): boolean {
|
|
357
586
|
const left = Fp.sqr(y); // y²
|
|
358
587
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
@@ -369,60 +598,40 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
369
598
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
370
599
|
if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
|
|
371
600
|
|
|
372
|
-
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
// Validates if priv key is valid and converts it to bigint.
|
|
377
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
378
|
-
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
|
379
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
380
|
-
if (lengths && typeof key !== 'bigint') {
|
|
381
|
-
if (isBytes(key)) key = bytesToHex(key);
|
|
382
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
383
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
384
|
-
throw new Error('invalid private key');
|
|
385
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
386
|
-
}
|
|
387
|
-
let num: bigint;
|
|
388
|
-
try {
|
|
389
|
-
num =
|
|
390
|
-
typeof key === 'bigint'
|
|
391
|
-
? key
|
|
392
|
-
: bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
|
393
|
-
} catch (error) {
|
|
394
|
-
throw new Error(
|
|
395
|
-
'invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
if (wrapPrivateKey) num = mod(num, N); // disabled by default, enabled for BLS
|
|
399
|
-
aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
400
|
-
return num;
|
|
601
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
602
|
+
function acoord(title: string, n: T, banZero = false) {
|
|
603
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n))) throw new Error(`bad point coordinate ${title}`);
|
|
604
|
+
return n;
|
|
401
605
|
}
|
|
402
606
|
|
|
403
607
|
function aprjpoint(other: unknown) {
|
|
404
608
|
if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
|
|
405
609
|
}
|
|
406
610
|
|
|
611
|
+
function splitEndoScalarN(k: bigint) {
|
|
612
|
+
if (!endo || !endo.basises) throw new Error('no endo');
|
|
613
|
+
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|
614
|
+
}
|
|
615
|
+
|
|
407
616
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
408
617
|
|
|
409
618
|
// Converts Projective point to affine (x, y) coordinates.
|
|
410
619
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
411
620
|
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
412
621
|
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
|
|
413
|
-
const {
|
|
622
|
+
const { X, Y, Z } = p;
|
|
414
623
|
// Fast-path for normalized points
|
|
415
|
-
if (Fp.eql(
|
|
624
|
+
if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
|
|
416
625
|
const is0 = p.is0();
|
|
417
626
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
418
627
|
// all operations, so we replace invZ with a random number, 1.
|
|
419
|
-
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
const zz = Fp.mul(
|
|
628
|
+
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
629
|
+
const x = Fp.mul(X, iz);
|
|
630
|
+
const y = Fp.mul(Y, iz);
|
|
631
|
+
const zz = Fp.mul(Z, iz);
|
|
423
632
|
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
424
633
|
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
425
|
-
return { x
|
|
634
|
+
return { x, y };
|
|
426
635
|
});
|
|
427
636
|
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
428
637
|
// Otherwise true will be return
|
|
@@ -431,51 +640,63 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
431
640
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
432
641
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
433
642
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
434
|
-
if (
|
|
643
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
|
|
435
644
|
throw new Error('bad point: ZERO');
|
|
436
645
|
}
|
|
437
646
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
438
647
|
const { x, y } = p.toAffine();
|
|
439
|
-
|
|
440
|
-
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
|
|
648
|
+
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
|
|
441
649
|
if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
|
|
442
650
|
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
443
651
|
return true;
|
|
444
652
|
});
|
|
445
653
|
|
|
654
|
+
function finishEndo(
|
|
655
|
+
endoBeta: EndomorphismOpts['beta'],
|
|
656
|
+
k1p: Point,
|
|
657
|
+
k2p: Point,
|
|
658
|
+
k1neg: boolean,
|
|
659
|
+
k2neg: boolean
|
|
660
|
+
) {
|
|
661
|
+
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
662
|
+
k1p = negateCt(k1neg, k1p);
|
|
663
|
+
k2p = negateCt(k2neg, k2p);
|
|
664
|
+
return k1p.add(k2p);
|
|
665
|
+
}
|
|
666
|
+
|
|
446
667
|
/**
|
|
447
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
448
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
668
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
669
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
449
670
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
450
671
|
*/
|
|
451
|
-
class Point implements
|
|
672
|
+
class Point implements WeierstrassPoint<T> {
|
|
452
673
|
// base / generator point
|
|
453
674
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
454
675
|
// zero / infinity / identity point
|
|
455
676
|
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
456
|
-
|
|
457
|
-
readonly
|
|
458
|
-
readonly
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
this.
|
|
677
|
+
// fields
|
|
678
|
+
static readonly Fp = Fp;
|
|
679
|
+
static readonly Fn = Fn;
|
|
680
|
+
|
|
681
|
+
readonly X: T;
|
|
682
|
+
readonly Y: T;
|
|
683
|
+
readonly Z: T;
|
|
684
|
+
|
|
685
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
686
|
+
constructor(X: T, Y: T, Z: T) {
|
|
687
|
+
this.X = acoord('x', X);
|
|
688
|
+
this.Y = acoord('y', Y, true);
|
|
689
|
+
this.Z = acoord('z', Z);
|
|
467
690
|
Object.freeze(this);
|
|
468
691
|
}
|
|
469
692
|
|
|
470
|
-
|
|
471
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
693
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
472
694
|
static fromAffine(p: AffinePoint<T>): Point {
|
|
473
695
|
const { x, y } = p || {};
|
|
474
696
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
|
|
475
697
|
if (p instanceof Point) throw new Error('projective point not allowed');
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (is0(x) && is0(y)) return Point.ZERO;
|
|
698
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
699
|
+
if (Fp.is0(x) && Fp.is0(y)) return Point.ZERO;
|
|
479
700
|
return new Point(x, y, Fp.ONE);
|
|
480
701
|
}
|
|
481
702
|
|
|
@@ -486,73 +707,82 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
486
707
|
return this.toAffine().y;
|
|
487
708
|
}
|
|
488
709
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
710
|
+
// TODO: remove
|
|
711
|
+
get px(): T {
|
|
712
|
+
return this.X;
|
|
713
|
+
}
|
|
714
|
+
get py(): T {
|
|
715
|
+
return this.X;
|
|
716
|
+
}
|
|
717
|
+
get pz(): T {
|
|
718
|
+
return this.Z;
|
|
719
|
+
}
|
|
495
720
|
static normalizeZ(points: Point[]): Point[] {
|
|
496
|
-
|
|
497
|
-
Fp,
|
|
498
|
-
points.map((p) => p.pz)
|
|
499
|
-
);
|
|
500
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
721
|
+
return normalizeZ(Point, points);
|
|
501
722
|
}
|
|
502
723
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
724
|
+
static fromBytes(bytes: Uint8Array): Point {
|
|
725
|
+
abytes(bytes);
|
|
726
|
+
return Point.fromHex(bytes);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
507
730
|
static fromHex(hex: Hex): Point {
|
|
508
731
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
509
732
|
P.assertValidity();
|
|
510
733
|
return P;
|
|
511
734
|
}
|
|
512
735
|
|
|
513
|
-
|
|
736
|
+
/** Multiplies generator point by privateKey. */
|
|
514
737
|
static fromPrivateKey(privateKey: PrivKey) {
|
|
515
|
-
return Point.BASE.multiply(
|
|
738
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
516
739
|
}
|
|
517
740
|
|
|
518
|
-
//
|
|
741
|
+
// TODO: remove
|
|
519
742
|
static msm(points: Point[], scalars: bigint[]): Point {
|
|
520
743
|
return pippenger(Point, Fn, points, scalars);
|
|
521
744
|
}
|
|
522
|
-
|
|
523
|
-
// "Private method", don't use it directly
|
|
524
745
|
_setWindowSize(windowSize: number) {
|
|
525
|
-
|
|
746
|
+
this.precompute(windowSize);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
*
|
|
751
|
+
* @param windowSize
|
|
752
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
753
|
+
* @returns
|
|
754
|
+
*/
|
|
755
|
+
precompute(windowSize: number = 8, isLazy = true): Point {
|
|
756
|
+
wnaf.createCache(this, windowSize);
|
|
757
|
+
if (!isLazy) this.multiply(_3n); // random number
|
|
758
|
+
return this;
|
|
526
759
|
}
|
|
527
760
|
|
|
528
|
-
//
|
|
761
|
+
// TODO: return `this`
|
|
762
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
529
763
|
assertValidity(): void {
|
|
530
764
|
assertValidMemo(this);
|
|
531
765
|
}
|
|
532
766
|
|
|
533
767
|
hasEvenY(): boolean {
|
|
534
768
|
const { y } = this.toAffine();
|
|
535
|
-
if (Fp.isOdd)
|
|
536
|
-
|
|
769
|
+
if (!Fp.isOdd) throw new Error("Field doesn't support isOdd");
|
|
770
|
+
return !Fp.isOdd(y);
|
|
537
771
|
}
|
|
538
772
|
|
|
539
|
-
/**
|
|
540
|
-
* Compare one point to another.
|
|
541
|
-
*/
|
|
773
|
+
/** Compare one point to another. */
|
|
542
774
|
equals(other: Point): boolean {
|
|
543
775
|
aprjpoint(other);
|
|
544
|
-
const {
|
|
545
|
-
const {
|
|
776
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
777
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
546
778
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
547
779
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
548
780
|
return U1 && U2;
|
|
549
781
|
}
|
|
550
782
|
|
|
551
|
-
/**
|
|
552
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
553
|
-
*/
|
|
783
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
554
784
|
negate(): Point {
|
|
555
|
-
return new Point(this.
|
|
785
|
+
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|
556
786
|
}
|
|
557
787
|
|
|
558
788
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
@@ -562,7 +792,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
562
792
|
double() {
|
|
563
793
|
const { a, b } = CURVE;
|
|
564
794
|
const b3 = Fp.mul(b, _3n);
|
|
565
|
-
const {
|
|
795
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
566
796
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
567
797
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
568
798
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -604,8 +834,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
604
834
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
605
835
|
add(other: Point): Point {
|
|
606
836
|
aprjpoint(other);
|
|
607
|
-
const {
|
|
608
|
-
const {
|
|
837
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
838
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
609
839
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
610
840
|
const a = CURVE.a;
|
|
611
841
|
const b3 = Fp.mul(CURVE.b, _3n);
|
|
@@ -656,49 +886,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
656
886
|
return this.add(other.negate());
|
|
657
887
|
}
|
|
658
888
|
|
|
659
|
-
is0() {
|
|
889
|
+
is0(): boolean {
|
|
660
890
|
return this.equals(Point.ZERO);
|
|
661
891
|
}
|
|
662
892
|
|
|
663
|
-
private wNAF(n: bigint): { p: Point; f: Point } {
|
|
664
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
669
|
-
* It's faster, but should only be used when you don't care about
|
|
670
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
671
|
-
*/
|
|
672
|
-
multiplyUnsafe(sc: bigint): Point {
|
|
673
|
-
const { endo, n: N } = CURVE;
|
|
674
|
-
aInRange('scalar', sc, _0n, N);
|
|
675
|
-
const I = Point.ZERO;
|
|
676
|
-
if (sc === _0n) return I;
|
|
677
|
-
if (this.is0() || sc === _1n) return this;
|
|
678
|
-
|
|
679
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
680
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
681
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
682
|
-
|
|
683
|
-
// Case c: endomorphism
|
|
684
|
-
/** See docs for {@link EndomorphismOpts} */
|
|
685
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
686
|
-
let k1p = I;
|
|
687
|
-
let k2p = I;
|
|
688
|
-
let d: Point = this;
|
|
689
|
-
while (k1 > _0n || k2 > _0n) {
|
|
690
|
-
if (k1 & _1n) k1p = k1p.add(d);
|
|
691
|
-
if (k2 & _1n) k2p = k2p.add(d);
|
|
692
|
-
d = d.double();
|
|
693
|
-
k1 >>= _1n;
|
|
694
|
-
k2 >>= _1n;
|
|
695
|
-
}
|
|
696
|
-
if (k1neg) k1p = k1p.negate();
|
|
697
|
-
if (k2neg) k2p = k2p.negate();
|
|
698
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
699
|
-
return k1p.add(k2p);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
893
|
/**
|
|
703
894
|
* Constant time multiplication.
|
|
704
895
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -709,295 +900,442 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
709
900
|
* @returns New point
|
|
710
901
|
*/
|
|
711
902
|
multiply(scalar: bigint): Point {
|
|
712
|
-
const { endo
|
|
713
|
-
|
|
903
|
+
const { endo } = curveOpts;
|
|
904
|
+
if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
714
905
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
906
|
+
const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
715
907
|
/** See docs for {@link EndomorphismOpts} */
|
|
716
908
|
if (endo) {
|
|
717
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
723
|
-
point = k1p.add(k2p);
|
|
724
|
-
fake = f1p.add(f2p);
|
|
909
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
910
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
911
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
912
|
+
fake = k1f.add(k2f);
|
|
913
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
725
914
|
} else {
|
|
726
|
-
const { p, f } =
|
|
915
|
+
const { p, f } = mul(scalar);
|
|
727
916
|
point = p;
|
|
728
917
|
fake = f;
|
|
729
918
|
}
|
|
730
919
|
// Normalize `z` for both points, but return only real one
|
|
731
|
-
return
|
|
920
|
+
return normalizeZ(Point, [point, fake])[0];
|
|
732
921
|
}
|
|
733
922
|
|
|
734
923
|
/**
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
* @returns non-zero affine point
|
|
924
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
925
|
+
* It's faster, but should only be used when you don't care about
|
|
926
|
+
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
739
927
|
*/
|
|
928
|
+
multiplyUnsafe(sc: bigint): Point {
|
|
929
|
+
const { endo } = curveOpts;
|
|
930
|
+
const p = this;
|
|
931
|
+
if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
932
|
+
if (sc === _0n || p.is0()) return Point.ZERO;
|
|
933
|
+
if (sc === _1n) return p; // fast-path
|
|
934
|
+
if (wnaf.hasCache(this)) return this.multiply(sc);
|
|
935
|
+
if (endo) {
|
|
936
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
937
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
938
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
939
|
+
} else {
|
|
940
|
+
return wnaf.unsafe(p, sc);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
740
944
|
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
|
741
|
-
const
|
|
742
|
-
const mul = (
|
|
743
|
-
P: Point,
|
|
744
|
-
a: bigint // Select faster multiply() method
|
|
745
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
746
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
945
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
747
946
|
return sum.is0() ? undefined : sum;
|
|
748
947
|
}
|
|
749
948
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
949
|
+
/**
|
|
950
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
951
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
952
|
+
*/
|
|
953
|
+
toAffine(invertedZ?: T): AffinePoint<T> {
|
|
954
|
+
return toAffineMemo(this, invertedZ);
|
|
755
955
|
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
959
|
+
* Always torsion-free for cofactor=1 curves.
|
|
960
|
+
*/
|
|
756
961
|
isTorsionFree(): boolean {
|
|
757
|
-
const {
|
|
758
|
-
if (cofactor === _1n) return true;
|
|
962
|
+
const { isTorsionFree } = curveOpts;
|
|
963
|
+
if (cofactor === _1n) return true;
|
|
759
964
|
if (isTorsionFree) return isTorsionFree(Point, this);
|
|
760
|
-
|
|
965
|
+
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
761
966
|
}
|
|
967
|
+
|
|
762
968
|
clearCofactor(): Point {
|
|
763
|
-
const {
|
|
969
|
+
const { clearCofactor } = curveOpts;
|
|
764
970
|
if (cofactor === _1n) return this; // Fast-path
|
|
765
971
|
if (clearCofactor) return clearCofactor(Point, this) as Point;
|
|
766
|
-
return this.multiplyUnsafe(
|
|
972
|
+
return this.multiplyUnsafe(cofactor);
|
|
767
973
|
}
|
|
768
974
|
|
|
769
|
-
|
|
975
|
+
isSmallOrder(): boolean {
|
|
976
|
+
// can we use this.clearCofactor()?
|
|
977
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
toBytes(isCompressed = true): Uint8Array {
|
|
770
981
|
abool('isCompressed', isCompressed);
|
|
771
982
|
this.assertValidity();
|
|
772
983
|
return toBytes(Point, this, isCompressed);
|
|
773
984
|
}
|
|
774
985
|
|
|
986
|
+
/** @deprecated use `toBytes` */
|
|
987
|
+
toRawBytes(isCompressed = true): Uint8Array {
|
|
988
|
+
return this.toBytes(isCompressed);
|
|
989
|
+
}
|
|
990
|
+
|
|
775
991
|
toHex(isCompressed = true): string {
|
|
776
|
-
|
|
777
|
-
|
|
992
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
toString() {
|
|
996
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
778
997
|
}
|
|
779
998
|
}
|
|
780
|
-
const
|
|
781
|
-
const wnaf = wNAF(Point, endo ? Math.ceil(
|
|
782
|
-
return
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
999
|
+
const bits = Fn.BITS;
|
|
1000
|
+
const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
1001
|
+
return Point;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// _legacyWeierstrass
|
|
1005
|
+
// TODO: remove
|
|
1006
|
+
/** @deprecated use `weierstrassN` */
|
|
1007
|
+
export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
|
|
1008
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1009
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1010
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
789
1011
|
}
|
|
790
1012
|
|
|
791
1013
|
// Instance
|
|
792
|
-
export interface
|
|
1014
|
+
export interface ECDSASignature {
|
|
793
1015
|
readonly r: bigint;
|
|
794
1016
|
readonly s: bigint;
|
|
795
1017
|
readonly recovery?: number;
|
|
796
|
-
|
|
797
|
-
addRecoveryBit(recovery: number): RecoveredSignatureType;
|
|
1018
|
+
addRecoveryBit(recovery: number): ECDSASigRecovered;
|
|
798
1019
|
hasHighS(): boolean;
|
|
799
|
-
normalizeS():
|
|
800
|
-
recoverPublicKey(msgHash: Hex):
|
|
1020
|
+
normalizeS(): ECDSASignature;
|
|
1021
|
+
recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
|
|
1022
|
+
toBytes(format?: string): Uint8Array;
|
|
1023
|
+
toHex(format?: string): string;
|
|
1024
|
+
|
|
1025
|
+
/** @deprecated */
|
|
1026
|
+
assertValidity(): void;
|
|
1027
|
+
/** @deprecated use `.toBytes('compact')` */
|
|
801
1028
|
toCompactRawBytes(): Uint8Array;
|
|
1029
|
+
/** @deprecated use `.toBytes('compact')` */
|
|
802
1030
|
toCompactHex(): string;
|
|
803
|
-
|
|
804
|
-
|
|
1031
|
+
/** @deprecated use `.toBytes('der')` */
|
|
1032
|
+
toDERRawBytes(): Uint8Array;
|
|
1033
|
+
/** @deprecated use `.toBytes('der')` */
|
|
1034
|
+
toDERHex(): string;
|
|
805
1035
|
}
|
|
806
|
-
export type
|
|
1036
|
+
export type SignatureType = ECDSASignature;
|
|
1037
|
+
export type ECDSASigRecovered = ECDSASignature & {
|
|
807
1038
|
readonly recovery: number;
|
|
808
1039
|
};
|
|
1040
|
+
export type RecoveredSignatureType = ECDSASigRecovered;
|
|
809
1041
|
// Static methods
|
|
810
|
-
export type
|
|
811
|
-
new (r: bigint, s: bigint):
|
|
812
|
-
|
|
813
|
-
|
|
1042
|
+
export type ECDSASignatureCons = {
|
|
1043
|
+
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
|
|
1044
|
+
fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
|
|
1045
|
+
fromHex(hex: string, format?: ECDSASigFormat): ECDSASignature;
|
|
1046
|
+
|
|
1047
|
+
/** @deprecated use `.fromBytes(bytes, 'compact')` */
|
|
1048
|
+
fromCompact(hex: Hex): ECDSASignature;
|
|
1049
|
+
/** @deprecated use `.fromBytes(bytes, 'der')` */
|
|
1050
|
+
fromDER(hex: Hex): ECDSASignature;
|
|
814
1051
|
};
|
|
815
|
-
type SignatureLike = { r: bigint; s: bigint };
|
|
816
|
-
|
|
817
|
-
export type PubKey = Hex |
|
|
1052
|
+
export type SignatureLike = { r: bigint; s: bigint };
|
|
1053
|
+
// TODO: remove
|
|
1054
|
+
export type PubKey = Hex | WeierstrassPoint<bigint>;
|
|
818
1055
|
|
|
1056
|
+
// TODO: remove
|
|
819
1057
|
export type CurveType = BasicWCurve<bigint> & {
|
|
820
1058
|
hash: CHash; // CHash not FHash because we need outputLen for DRBG
|
|
821
|
-
hmac
|
|
822
|
-
randomBytes
|
|
1059
|
+
hmac?: HmacFnSync;
|
|
1060
|
+
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
823
1061
|
lowS?: boolean;
|
|
824
1062
|
bits2int?: (bytes: Uint8Array) => bigint;
|
|
825
1063
|
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
|
826
1064
|
};
|
|
827
1065
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const opts = validateBasic(curve);
|
|
832
|
-
validateObject(
|
|
833
|
-
opts,
|
|
834
|
-
{
|
|
835
|
-
hash: 'hash',
|
|
836
|
-
hmac: 'function',
|
|
837
|
-
randomBytes: 'function',
|
|
838
|
-
},
|
|
839
|
-
{
|
|
840
|
-
bits2int: 'function',
|
|
841
|
-
bits2int_modN: 'function',
|
|
842
|
-
lowS: 'boolean',
|
|
843
|
-
}
|
|
844
|
-
);
|
|
845
|
-
return Object.freeze({ lowS: true, ...opts } as const);
|
|
1066
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
1067
|
+
function pprefix(hasEvenY: boolean): Uint8Array {
|
|
1068
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
846
1069
|
}
|
|
847
1070
|
|
|
1071
|
+
// TODO: remove
|
|
848
1072
|
export type CurveFn = {
|
|
849
|
-
CURVE:
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
};
|
|
1073
|
+
CURVE: CurvePointsType<bigint>;
|
|
1074
|
+
keygen: ECDSA['keygen'];
|
|
1075
|
+
getPublicKey: ECDSA['getPublicKey'];
|
|
1076
|
+
getSharedSecret: ECDSA['getSharedSecret'];
|
|
1077
|
+
sign: ECDSA['sign'];
|
|
1078
|
+
verify: ECDSA['verify'];
|
|
1079
|
+
Point: WeierstrassPointCons<bigint>;
|
|
1080
|
+
/** @deprecated use `Point` */
|
|
1081
|
+
ProjectivePoint: WeierstrassPointCons<bigint>;
|
|
1082
|
+
Signature: ECDSASignatureCons;
|
|
1083
|
+
utils: ECDSA['utils'];
|
|
1084
|
+
info: CurveInfo;
|
|
862
1085
|
};
|
|
863
1086
|
|
|
864
1087
|
/**
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
1088
|
+
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
1089
|
+
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
1090
|
+
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1091
|
+
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1092
|
+
* @param Fp
|
|
1093
|
+
* @param Z
|
|
1094
|
+
* @returns
|
|
870
1095
|
*/
|
|
871
|
-
export function
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1096
|
+
export function SWUFpSqrtRatio<T>(
|
|
1097
|
+
Fp: IField<T>,
|
|
1098
|
+
Z: T
|
|
1099
|
+
): (u: T, v: T) => { isValid: boolean; value: T } {
|
|
1100
|
+
// Generic implementation
|
|
1101
|
+
const q = Fp.ORDER;
|
|
1102
|
+
let l = _0n;
|
|
1103
|
+
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
|
1104
|
+
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1105
|
+
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
1106
|
+
// 2n ** c1 == 2n << (c1-1)
|
|
1107
|
+
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
1108
|
+
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
1109
|
+
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1110
|
+
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1111
|
+
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1112
|
+
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1113
|
+
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1114
|
+
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1115
|
+
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1116
|
+
let tv1 = c6; // 1. tv1 = c6
|
|
1117
|
+
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1118
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1119
|
+
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1120
|
+
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1121
|
+
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1122
|
+
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1123
|
+
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1124
|
+
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1125
|
+
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1126
|
+
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1127
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1128
|
+
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1129
|
+
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1130
|
+
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1131
|
+
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1132
|
+
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1133
|
+
for (let i = c1; i > _1n; i--) {
|
|
1134
|
+
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1135
|
+
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1136
|
+
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1137
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1138
|
+
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1139
|
+
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1140
|
+
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1141
|
+
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1142
|
+
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1143
|
+
}
|
|
1144
|
+
return { isValid: isQR, value: tv3 };
|
|
1145
|
+
};
|
|
1146
|
+
if (Fp.ORDER % _4n === _3n) {
|
|
1147
|
+
// sqrt_ratio_3mod4(u, v)
|
|
1148
|
+
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1149
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1150
|
+
sqrtRatio = (u: T, v: T) => {
|
|
1151
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1152
|
+
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1153
|
+
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1154
|
+
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1155
|
+
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1156
|
+
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1157
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1158
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1159
|
+
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1160
|
+
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
// No curves uses that
|
|
1164
|
+
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
1165
|
+
return sqrtRatio;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1169
|
+
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1170
|
+
*/
|
|
1171
|
+
export function mapToCurveSimpleSWU<T>(
|
|
1172
|
+
Fp: IField<T>,
|
|
1173
|
+
opts: {
|
|
1174
|
+
A: T;
|
|
1175
|
+
B: T;
|
|
1176
|
+
Z: T;
|
|
1177
|
+
}
|
|
1178
|
+
): (u: T) => { x: T; y: T } {
|
|
1179
|
+
validateField(Fp);
|
|
1180
|
+
const { A, B, Z } = opts;
|
|
1181
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1182
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1183
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1184
|
+
if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
|
|
1185
|
+
// Input: u, an element of F.
|
|
1186
|
+
// Output: (x, y), a point on E.
|
|
1187
|
+
return (u: T): { x: T; y: T } => {
|
|
1188
|
+
// prettier-ignore
|
|
1189
|
+
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1190
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1191
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1192
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1193
|
+
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1194
|
+
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1195
|
+
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1196
|
+
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1197
|
+
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1198
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1199
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1200
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1201
|
+
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1202
|
+
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1203
|
+
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1204
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1205
|
+
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1206
|
+
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1207
|
+
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1208
|
+
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1209
|
+
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1210
|
+
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1211
|
+
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1212
|
+
const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1213
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1214
|
+
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1215
|
+
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1216
|
+
return { x, y };
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Creates ECDSA for given elliptic curve Point and hash function.
|
|
1222
|
+
*/
|
|
1223
|
+
export function ecdsa(
|
|
1224
|
+
Point: WeierstrassPointCons<bigint>,
|
|
1225
|
+
hash: CHash,
|
|
1226
|
+
ecdsaOpts: ECDSAOpts = {}
|
|
1227
|
+
): ECDSA {
|
|
1228
|
+
ahash(hash);
|
|
1229
|
+
_validateObject(
|
|
1230
|
+
ecdsaOpts,
|
|
1231
|
+
{},
|
|
1232
|
+
{
|
|
1233
|
+
hmac: 'function',
|
|
1234
|
+
lowS: 'boolean',
|
|
1235
|
+
randomBytes: 'function',
|
|
1236
|
+
bits2int: 'function',
|
|
1237
|
+
bits2int_modN: 'function',
|
|
1238
|
+
}
|
|
1239
|
+
);
|
|
1240
|
+
|
|
1241
|
+
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
1242
|
+
const hmac_: HmacFnSync =
|
|
1243
|
+
ecdsaOpts.hmac ||
|
|
1244
|
+
(((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1245
|
+
|
|
1246
|
+
const { Fp, Fn } = Point;
|
|
1247
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
1248
|
+
|
|
1249
|
+
const seedLen = getMinHashLength(CURVE_ORDER);
|
|
1250
|
+
const lengths = {
|
|
1251
|
+
secret: Fn.BYTES,
|
|
1252
|
+
public: 1 + Fp.BYTES,
|
|
1253
|
+
publicUncompressed: 1 + 2 * Fp.BYTES,
|
|
1254
|
+
signature: 2 * Fn.BYTES,
|
|
1255
|
+
seed: seedLen,
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
function isBiggerThanHalfOrder(number: bigint) {
|
|
1259
|
+
const HALF = CURVE_ORDER >> _1n;
|
|
1260
|
+
return number > HALF;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function normalizeS(s: bigint) {
|
|
1264
|
+
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
1265
|
+
}
|
|
1266
|
+
function aValidRS(title: string, num: bigint) {
|
|
1267
|
+
if (!Fn.isValidNot0(num))
|
|
1268
|
+
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
1273
|
+
*/
|
|
1274
|
+
class Signature implements ECDSASignature {
|
|
1275
|
+
readonly r: bigint;
|
|
1276
|
+
readonly s: bigint;
|
|
1277
|
+
readonly recovery?: number;
|
|
1278
|
+
constructor(r: bigint, s: bigint, recovery?: number) {
|
|
1279
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
1280
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
1281
|
+
this.r = r;
|
|
1282
|
+
this.s = s;
|
|
1283
|
+
if (recovery != null) this.recovery = recovery;
|
|
1284
|
+
Object.freeze(this);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = 'compact') {
|
|
1288
|
+
if (format === 'compact') {
|
|
1289
|
+
const L = Fn.BYTES;
|
|
1290
|
+
abytes(bytes, L * 2);
|
|
1291
|
+
const r = bytes.subarray(0, L);
|
|
1292
|
+
const s = bytes.subarray(L, L * 2);
|
|
1293
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
|
|
1294
|
+
}
|
|
1295
|
+
if (format === 'der') {
|
|
1296
|
+
abytes(bytes);
|
|
1297
|
+
const { r, s } = DER.toSig(bytes);
|
|
1298
|
+
return new Signature(r, s);
|
|
1299
|
+
}
|
|
1300
|
+
throw new Error('invalid format');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
static fromHex(hex: string, format?: ECDSASigFormat) {
|
|
1304
|
+
return this.fromBytes(hexToBytes(hex), format);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
addRecoveryBit(recovery: number): RecoveredSignature {
|
|
1308
|
+
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// ProjPointType<bigint>
|
|
1312
|
+
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
|
1313
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
1314
|
+
const { r, s, recovery: rec } = this;
|
|
1315
|
+
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
|
1316
|
+
|
|
1317
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
1318
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
1319
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
1320
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
1321
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
1322
|
+
// To easily get i, we either need to:
|
|
1323
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
1324
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
1325
|
+
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
1326
|
+
if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
|
|
1327
|
+
|
|
1328
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
1329
|
+
if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
|
|
1330
|
+
const x = Fp.toBytes(radj);
|
|
1331
|
+
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
|
|
1332
|
+
const ir = Fn.inv(radj); // r^-1
|
|
1333
|
+
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
1334
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
1335
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
1336
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
1337
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
1338
|
+
if (Q.is0()) throw new Error('point at infinify');
|
|
1001
1339
|
Q.assertValidity();
|
|
1002
1340
|
return Q;
|
|
1003
1341
|
}
|
|
@@ -1008,106 +1346,117 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1008
1346
|
}
|
|
1009
1347
|
|
|
1010
1348
|
normalizeS() {
|
|
1011
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
1349
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
toBytes(format: ECDSASigFormat = 'compact') {
|
|
1353
|
+
if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
1354
|
+
if (format === 'der') return hexToBytes(DER.hexFromSig(this));
|
|
1355
|
+
throw new Error('invalid format');
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
toHex(format?: ECDSASigFormat) {
|
|
1359
|
+
return bytesToHex(this.toBytes(format));
|
|
1012
1360
|
}
|
|
1013
1361
|
|
|
1014
|
-
//
|
|
1362
|
+
// TODO: remove
|
|
1363
|
+
assertValidity(): void {}
|
|
1364
|
+
static fromCompact(hex: Hex) {
|
|
1365
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
|
|
1366
|
+
}
|
|
1367
|
+
static fromDER(hex: Hex) {
|
|
1368
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
1369
|
+
}
|
|
1015
1370
|
toDERRawBytes() {
|
|
1016
|
-
return
|
|
1371
|
+
return this.toBytes('der');
|
|
1017
1372
|
}
|
|
1018
1373
|
toDERHex() {
|
|
1019
|
-
return
|
|
1374
|
+
return bytesToHex(this.toBytes('der'));
|
|
1020
1375
|
}
|
|
1021
|
-
|
|
1022
|
-
// padded bytes of r, then padded bytes of s
|
|
1023
1376
|
toCompactRawBytes() {
|
|
1024
|
-
return
|
|
1377
|
+
return this.toBytes('compact');
|
|
1025
1378
|
}
|
|
1026
1379
|
toCompactHex() {
|
|
1027
|
-
|
|
1028
|
-
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
1380
|
+
return bytesToHex(this.toBytes('compact'));
|
|
1029
1381
|
}
|
|
1030
1382
|
}
|
|
1031
1383
|
type RecoveredSignature = Signature & { recovery: number };
|
|
1032
1384
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1385
|
+
function isValidSecretKey(privateKey: PrivKey) {
|
|
1386
|
+
try {
|
|
1387
|
+
return !!_normFnElement(Fn, privateKey);
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
|
|
1393
|
+
try {
|
|
1394
|
+
const l = publicKey.length;
|
|
1395
|
+
if (isCompressed === true && l !== lengths.public) return false;
|
|
1396
|
+
if (isCompressed === false && l !== lengths.publicUncompressed) return false;
|
|
1397
|
+
return !!Point.fromBytes(publicKey);
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Produces cryptographically secure secret key from random of size
|
|
1404
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1405
|
+
*/
|
|
1406
|
+
function randomSecretKey(seed = randomBytes_(seedLen)): Uint8Array {
|
|
1407
|
+
return mapHashToField(seed, CURVE_ORDER);
|
|
1408
|
+
}
|
|
1052
1409
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
point.
|
|
1064
|
-
return point;
|
|
1410
|
+
const utils = {
|
|
1411
|
+
isValidSecretKey,
|
|
1412
|
+
isValidPublicKey,
|
|
1413
|
+
randomSecretKey,
|
|
1414
|
+
|
|
1415
|
+
// TODO: remove
|
|
1416
|
+
isValidPrivateKey: isValidSecretKey,
|
|
1417
|
+
randomPrivateKey: randomSecretKey,
|
|
1418
|
+
normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
|
|
1419
|
+
precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
|
|
1420
|
+
return point.precompute(windowSize, false);
|
|
1065
1421
|
},
|
|
1066
1422
|
};
|
|
1067
1423
|
|
|
1068
1424
|
/**
|
|
1069
|
-
* Computes public key for a
|
|
1070
|
-
* @param privateKey private key
|
|
1425
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1071
1426
|
* @param isCompressed whether to return compact (default), or full key
|
|
1072
1427
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1073
1428
|
*/
|
|
1074
|
-
function getPublicKey(
|
|
1075
|
-
return Point.
|
|
1429
|
+
function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
|
|
1430
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1076
1431
|
}
|
|
1077
1432
|
|
|
1078
1433
|
/**
|
|
1079
1434
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1080
1435
|
*/
|
|
1081
1436
|
function isProbPub(item: PrivKey | PubKey): boolean | undefined {
|
|
1437
|
+
// TODO: remove
|
|
1082
1438
|
if (typeof item === 'bigint') return false;
|
|
1439
|
+
// TODO: remove
|
|
1083
1440
|
if (item instanceof Point) return true;
|
|
1084
|
-
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
const compLen = fpl + 1; // e.g. 33 for 32
|
|
1088
|
-
const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
|
|
1089
|
-
if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
|
|
1090
|
-
return undefined;
|
|
1091
|
-
} else {
|
|
1092
|
-
return len === compLen || len === uncompLen;
|
|
1093
|
-
}
|
|
1441
|
+
if (Fn.allowedLengths || lengths.secret === lengths.public) return undefined;
|
|
1442
|
+
const l = ensureBytes('key', item).length;
|
|
1443
|
+
return l === lengths.public || l === lengths.publicUncompressed;
|
|
1094
1444
|
}
|
|
1095
1445
|
|
|
1096
1446
|
/**
|
|
1097
1447
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1098
|
-
* Computes shared public key from
|
|
1099
|
-
* Checks: 1)
|
|
1448
|
+
* Computes shared public key from secret key A and public key B.
|
|
1449
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1100
1450
|
* Does NOT hash the result.
|
|
1101
|
-
* @param privateA private key
|
|
1102
|
-
* @param publicB different public key
|
|
1103
1451
|
* @param isCompressed whether to return compact (default), or full key
|
|
1104
1452
|
* @returns shared public key
|
|
1105
1453
|
*/
|
|
1106
|
-
function getSharedSecret(
|
|
1107
|
-
if (isProbPub(
|
|
1108
|
-
if (isProbPub(
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1454
|
+
function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
|
|
1455
|
+
if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
|
|
1456
|
+
if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
|
|
1457
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1458
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1459
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
1111
1460
|
}
|
|
1112
1461
|
|
|
1113
1462
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
@@ -1115,30 +1464,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1115
1464
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1116
1465
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1117
1466
|
const bits2int =
|
|
1118
|
-
|
|
1467
|
+
ecdsaOpts.bits2int ||
|
|
1119
1468
|
function (bytes: Uint8Array): bigint {
|
|
1120
1469
|
// Our custom check "just in case", for protection against DoS
|
|
1121
1470
|
if (bytes.length > 8192) throw new Error('input is too large');
|
|
1122
1471
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
1123
1472
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
1124
1473
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1125
|
-
const delta = bytes.length * 8 -
|
|
1474
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
1126
1475
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1127
1476
|
};
|
|
1128
1477
|
const bits2int_modN =
|
|
1129
|
-
|
|
1478
|
+
ecdsaOpts.bits2int_modN ||
|
|
1130
1479
|
function (bytes: Uint8Array): bigint {
|
|
1131
|
-
return
|
|
1480
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1132
1481
|
};
|
|
1133
1482
|
// NOTE: pads output with zero as per spec
|
|
1134
|
-
const ORDER_MASK = bitMask(
|
|
1483
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
1135
1484
|
/**
|
|
1136
1485
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
1137
1486
|
*/
|
|
1138
1487
|
function int2octets(num: bigint): Uint8Array {
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
return
|
|
1488
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1489
|
+
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1490
|
+
return Fn.toBytes(num);
|
|
1142
1491
|
}
|
|
1143
1492
|
|
|
1144
1493
|
// Steps A, D of RFC6979 3.2
|
|
@@ -1149,7 +1498,6 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1149
1498
|
function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
|
|
1150
1499
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1151
1500
|
throw new Error('sign() legacy options not supported');
|
|
1152
|
-
const { hash, randomBytes } = CURVE;
|
|
1153
1501
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
1154
1502
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
1155
1503
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
@@ -1157,32 +1505,37 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1157
1505
|
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
1158
1506
|
|
|
1159
1507
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1160
|
-
// with
|
|
1508
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1161
1509
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1162
1510
|
const h1int = bits2int_modN(msgHash);
|
|
1163
|
-
const d =
|
|
1511
|
+
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1164
1512
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1165
1513
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1166
1514
|
if (ent != null && ent !== false) {
|
|
1167
1515
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1168
|
-
const e = ent === true ?
|
|
1516
|
+
const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
|
|
1169
1517
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
1170
1518
|
}
|
|
1171
1519
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
1172
1520
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
1173
1521
|
// Converts signature params into point w r/s, checks result for validity.
|
|
1522
|
+
// To transform k => Signature:
|
|
1523
|
+
// q = k⋅G
|
|
1524
|
+
// r = q.x mod n
|
|
1525
|
+
// s = k^-1(m + rd) mod n
|
|
1526
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1527
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1528
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1174
1529
|
function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
|
|
1175
1530
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1531
|
+
// Important: all mod() calls here must be done over N
|
|
1176
1532
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
1177
|
-
if (!
|
|
1178
|
-
const ik =
|
|
1179
|
-
const q = Point.BASE.multiply(k).toAffine(); // q =
|
|
1180
|
-
const r =
|
|
1533
|
+
if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
|
|
1534
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
1535
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
1536
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
1181
1537
|
if (r === _0n) return;
|
|
1182
|
-
|
|
1183
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1184
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1185
|
-
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
|
1538
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
1186
1539
|
if (s === _0n) return;
|
|
1187
1540
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
1188
1541
|
let normS = s;
|
|
@@ -1194,32 +1547,26 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1194
1547
|
}
|
|
1195
1548
|
return { seed, k2sig };
|
|
1196
1549
|
}
|
|
1197
|
-
const defaultSigOpts: SignOpts = { lowS:
|
|
1198
|
-
const defaultVerOpts: VerOpts = { lowS:
|
|
1550
|
+
const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1551
|
+
const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1199
1552
|
|
|
1200
1553
|
/**
|
|
1201
|
-
* Signs message hash with a
|
|
1554
|
+
* Signs message hash with a secret key.
|
|
1202
1555
|
* ```
|
|
1203
1556
|
* sign(m, d, k) where
|
|
1204
1557
|
* (x, y) = G × k
|
|
1205
1558
|
* r = x mod n
|
|
1206
1559
|
* s = (m + dr)/k mod n
|
|
1207
1560
|
* ```
|
|
1208
|
-
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
|
1209
|
-
* @param privKey private key
|
|
1210
|
-
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
|
1211
|
-
* @returns signature with recovery param
|
|
1212
1561
|
*/
|
|
1213
|
-
function sign(msgHash: Hex,
|
|
1214
|
-
const { seed, k2sig } = prepSig(msgHash,
|
|
1215
|
-
const
|
|
1216
|
-
const drbg = createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
1562
|
+
function sign(msgHash: Hex, secretKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
|
|
1563
|
+
const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1564
|
+
const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac_);
|
|
1217
1565
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1218
1566
|
}
|
|
1219
1567
|
|
|
1220
1568
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1221
|
-
Point.BASE.
|
|
1222
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1569
|
+
Point.BASE.precompute(8);
|
|
1223
1570
|
|
|
1224
1571
|
/**
|
|
1225
1572
|
* Verifies a signature against message hash and public key.
|
|
@@ -1243,195 +1590,188 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1243
1590
|
const sg = signature;
|
|
1244
1591
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1245
1592
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1246
|
-
const { lowS, prehash, format } = opts;
|
|
1247
1593
|
|
|
1248
|
-
// Verify opts
|
|
1594
|
+
// Verify opts
|
|
1249
1595
|
validateSigVerOpts(opts);
|
|
1596
|
+
const { lowS, prehash, format } = opts;
|
|
1597
|
+
|
|
1598
|
+
// TODO: remove
|
|
1250
1599
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
|
1251
|
-
if (format !== undefined && format !== 'compact' && format !== 'der')
|
|
1252
|
-
throw new Error('format must be compact or der');
|
|
1253
|
-
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1254
|
-
const isObj =
|
|
1255
|
-
!isHex &&
|
|
1256
|
-
!format &&
|
|
1257
|
-
typeof sg === 'object' &&
|
|
1258
|
-
sg !== null &&
|
|
1259
|
-
typeof sg.r === 'bigint' &&
|
|
1260
|
-
typeof sg.s === 'bigint';
|
|
1261
|
-
if (!isHex && !isObj)
|
|
1262
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1263
1600
|
|
|
1264
1601
|
let _sig: Signature | undefined = undefined;
|
|
1265
|
-
let P:
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1602
|
+
let P: WeierstrassPoint<bigint>;
|
|
1603
|
+
|
|
1604
|
+
if (format === undefined) {
|
|
1605
|
+
// Try to deduce format
|
|
1606
|
+
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1607
|
+
const isObj =
|
|
1608
|
+
!isHex &&
|
|
1609
|
+
sg !== null &&
|
|
1610
|
+
typeof sg === 'object' &&
|
|
1611
|
+
typeof sg.r === 'bigint' &&
|
|
1612
|
+
typeof sg.s === 'bigint';
|
|
1613
|
+
if (!isHex && !isObj)
|
|
1614
|
+
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1615
|
+
if (isObj) {
|
|
1616
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1617
|
+
} else if (isHex) {
|
|
1618
|
+
// TODO: remove this malleable check
|
|
1619
|
+
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1620
|
+
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1271
1621
|
try {
|
|
1272
|
-
|
|
1622
|
+
_sig = Signature.fromDER(sg);
|
|
1273
1623
|
} catch (derError) {
|
|
1274
1624
|
if (!(derError instanceof DER.Err)) throw derError;
|
|
1275
1625
|
}
|
|
1276
|
-
if (!_sig
|
|
1626
|
+
if (!_sig) {
|
|
1627
|
+
try {
|
|
1628
|
+
_sig = Signature.fromCompact(sg);
|
|
1629
|
+
} catch (error) {
|
|
1630
|
+
return false;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1277
1633
|
}
|
|
1634
|
+
} else {
|
|
1635
|
+
if (format === 'compact' || format === 'der') {
|
|
1636
|
+
if (typeof sg !== 'string' && !isBytes(sg))
|
|
1637
|
+
throw new Error('"der" / "compact" format expects Uint8Array signature');
|
|
1638
|
+
_sig = Signature.fromBytes(ensureBytes('sig', sg), format);
|
|
1639
|
+
} else if (format === 'js') {
|
|
1640
|
+
if (!(sg instanceof Signature)) throw new Error('"js" format expects Signature instance');
|
|
1641
|
+
_sig = sg;
|
|
1642
|
+
} else {
|
|
1643
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
if (!_sig) return false;
|
|
1648
|
+
try {
|
|
1278
1649
|
P = Point.fromHex(publicKey);
|
|
1279
|
-
|
|
1650
|
+
if (lowS && _sig.hasHighS()) return false;
|
|
1651
|
+
// todo: optional.hash => hash
|
|
1652
|
+
if (prehash) msgHash = hash(msgHash);
|
|
1653
|
+
const { r, s } = _sig;
|
|
1654
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1655
|
+
const is = Fn.inv(s); // s^-1
|
|
1656
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1657
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1658
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1659
|
+
if (R.is0()) return false;
|
|
1660
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1661
|
+
return v === r;
|
|
1662
|
+
} catch (e) {
|
|
1280
1663
|
return false;
|
|
1281
1664
|
}
|
|
1282
|
-
if (!_sig) return false;
|
|
1283
|
-
if (lowS && _sig.hasHighS()) return false;
|
|
1284
|
-
if (prehash) msgHash = CURVE.hash(msgHash);
|
|
1285
|
-
const { r, s } = _sig;
|
|
1286
|
-
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1287
|
-
const is = invN(s); // s^-1
|
|
1288
|
-
const u1 = modN(h * is); // u1 = hs^-1 mod n
|
|
1289
|
-
const u2 = modN(r * is); // u2 = rs^-1 mod n
|
|
1290
|
-
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
|
|
1291
|
-
if (!R) return false;
|
|
1292
|
-
const v = modN(R.x);
|
|
1293
|
-
return v === r;
|
|
1294
1665
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1666
|
+
|
|
1667
|
+
function keygen(seed?: Uint8Array) {
|
|
1668
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
1669
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
return Object.freeze({
|
|
1673
|
+
keygen,
|
|
1297
1674
|
getPublicKey,
|
|
1298
|
-
getSharedSecret,
|
|
1299
1675
|
sign,
|
|
1300
1676
|
verify,
|
|
1301
|
-
|
|
1302
|
-
Signature,
|
|
1677
|
+
getSharedSecret,
|
|
1303
1678
|
utils,
|
|
1304
|
-
|
|
1679
|
+
Point,
|
|
1680
|
+
Signature,
|
|
1681
|
+
info: { type: 'weierstrass' as const, lengths, publicKeyHasPrefix: true },
|
|
1682
|
+
});
|
|
1305
1683
|
}
|
|
1306
1684
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1330
|
-
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1331
|
-
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1332
|
-
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1333
|
-
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1334
|
-
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1335
|
-
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1336
|
-
let tv1 = c6; // 1. tv1 = c6
|
|
1337
|
-
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1338
|
-
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1339
|
-
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1340
|
-
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1341
|
-
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1342
|
-
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1343
|
-
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1344
|
-
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1345
|
-
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1346
|
-
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1347
|
-
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1348
|
-
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1349
|
-
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1350
|
-
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1351
|
-
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1352
|
-
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1353
|
-
for (let i = c1; i > _1n; i--) {
|
|
1354
|
-
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1355
|
-
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1356
|
-
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1357
|
-
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1358
|
-
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1359
|
-
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1360
|
-
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1361
|
-
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1362
|
-
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1363
|
-
}
|
|
1364
|
-
return { isValid: isQR, value: tv3 };
|
|
1685
|
+
// TODO: remove
|
|
1686
|
+
export type WsPointComposed<T> = {
|
|
1687
|
+
CURVE: WeierstrassOpts<T>;
|
|
1688
|
+
curveOpts: WeierstrassExtraOpts<T>;
|
|
1689
|
+
};
|
|
1690
|
+
// TODO: remove
|
|
1691
|
+
export type WsComposed = {
|
|
1692
|
+
CURVE: WeierstrassOpts<bigint>;
|
|
1693
|
+
hash: CHash;
|
|
1694
|
+
curveOpts: WeierstrassExtraOpts<bigint>;
|
|
1695
|
+
ecdsaOpts: ECDSAOpts;
|
|
1696
|
+
};
|
|
1697
|
+
// TODO: remove
|
|
1698
|
+
function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
|
|
1699
|
+
const CURVE: WeierstrassOpts<T> = {
|
|
1700
|
+
a: c.a,
|
|
1701
|
+
b: c.b,
|
|
1702
|
+
p: c.Fp.ORDER,
|
|
1703
|
+
n: c.n,
|
|
1704
|
+
h: c.h,
|
|
1705
|
+
Gx: c.Gx,
|
|
1706
|
+
Gy: c.Gy,
|
|
1365
1707
|
};
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
return
|
|
1708
|
+
const Fp = c.Fp;
|
|
1709
|
+
let allowedLengths = c.allowedPrivateKeyLengths
|
|
1710
|
+
? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
|
|
1711
|
+
: undefined;
|
|
1712
|
+
const Fn = Field(CURVE.n, {
|
|
1713
|
+
BITS: c.nBitLength,
|
|
1714
|
+
allowedLengths: allowedLengths,
|
|
1715
|
+
modOnDecode: c.wrapPrivateKey,
|
|
1716
|
+
});
|
|
1717
|
+
const curveOpts: WeierstrassExtraOpts<T> = {
|
|
1718
|
+
Fp,
|
|
1719
|
+
Fn,
|
|
1720
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
1721
|
+
endo: c.endo,
|
|
1722
|
+
isTorsionFree: c.isTorsionFree,
|
|
1723
|
+
clearCofactor: c.clearCofactor,
|
|
1724
|
+
fromBytes: c.fromBytes,
|
|
1725
|
+
toBytes: c.toBytes,
|
|
1726
|
+
};
|
|
1727
|
+
return { CURVE, curveOpts };
|
|
1386
1728
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
B: T;
|
|
1396
|
-
Z: T;
|
|
1397
|
-
}
|
|
1398
|
-
): (u: T) => { x: T; y: T } {
|
|
1399
|
-
validateField(Fp);
|
|
1400
|
-
if (!Fp.isValid(opts.A) || !Fp.isValid(opts.B) || !Fp.isValid(opts.Z))
|
|
1401
|
-
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1402
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp, opts.Z);
|
|
1403
|
-
if (!Fp.isOdd) throw new Error('Fp.isOdd is not implemented!');
|
|
1404
|
-
// Input: u, an element of F.
|
|
1405
|
-
// Output: (x, y), a point on E.
|
|
1406
|
-
return (u: T): { x: T; y: T } => {
|
|
1407
|
-
// prettier-ignore
|
|
1408
|
-
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1409
|
-
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1410
|
-
tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
|
|
1411
|
-
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1412
|
-
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1413
|
-
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1414
|
-
tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
|
|
1415
|
-
tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1416
|
-
tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
|
|
1417
|
-
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1418
|
-
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1419
|
-
tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
|
|
1420
|
-
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1421
|
-
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1422
|
-
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1423
|
-
tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
|
|
1424
|
-
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1425
|
-
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1426
|
-
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1427
|
-
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1428
|
-
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1429
|
-
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1430
|
-
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1431
|
-
const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1432
|
-
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1433
|
-
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1434
|
-
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1435
|
-
return { x, y };
|
|
1729
|
+
function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
|
|
1730
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1731
|
+
const ecdsaOpts: ECDSAOpts = {
|
|
1732
|
+
hmac: c.hmac,
|
|
1733
|
+
randomBytes: c.randomBytes,
|
|
1734
|
+
lowS: c.lowS,
|
|
1735
|
+
bits2int: c.bits2int,
|
|
1736
|
+
bits2int_modN: c.bits2int_modN,
|
|
1436
1737
|
};
|
|
1738
|
+
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1739
|
+
}
|
|
1740
|
+
// TODO: remove
|
|
1741
|
+
function _weierstrass_new_output_to_legacy<T>(
|
|
1742
|
+
c: CurvePointsType<T>,
|
|
1743
|
+
Point: WeierstrassPointCons<T>
|
|
1744
|
+
): CurvePointsRes<T> {
|
|
1745
|
+
const { Fp, Fn } = Point;
|
|
1746
|
+
// TODO: remove
|
|
1747
|
+
function isWithinCurveOrder(num: bigint): boolean {
|
|
1748
|
+
return inRange(num, _1n, Fn.ORDER);
|
|
1749
|
+
}
|
|
1750
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1751
|
+
return Object.assign(
|
|
1752
|
+
{},
|
|
1753
|
+
{
|
|
1754
|
+
CURVE: c,
|
|
1755
|
+
Point: Point,
|
|
1756
|
+
ProjectivePoint: Point,
|
|
1757
|
+
normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
|
|
1758
|
+
weierstrassEquation,
|
|
1759
|
+
isWithinCurveOrder,
|
|
1760
|
+
}
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
// TODO: remove
|
|
1764
|
+
function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
|
|
1765
|
+
return Object.assign({}, ecdsa, {
|
|
1766
|
+
ProjectivePoint: ecdsa.Point,
|
|
1767
|
+
CURVE: c,
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// _ecdsa_legacy
|
|
1772
|
+
export function weierstrass(c: CurveType): CurveFn {
|
|
1773
|
+
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1774
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1775
|
+
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
1776
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1437
1777
|
}
|