@noble/curves 1.9.2 → 1.9.4
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 +186 -206
- package/_shortw_utils.d.ts +1 -0
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +1 -0
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +87 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +170 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +109 -23
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +158 -156
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +126 -70
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +212 -62
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +8 -4
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +23 -11
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +8 -3
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +79 -35
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +17 -4
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +19 -3
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +3 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +145 -118
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +415 -336
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +4 -4
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +52 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +128 -155
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +57 -58
- package/ed448.d.ts.map +1 -1
- package/ed448.js +114 -131
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +1 -0
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +1 -0
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +87 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +171 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +109 -23
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +156 -155
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +126 -70
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +210 -62
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +8 -4
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +22 -11
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +8 -3
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +79 -35
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +17 -4
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +19 -3
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +3 -3
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +145 -118
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +412 -334
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +4 -4
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.d.ts +52 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +131 -157
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +57 -58
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +116 -132
- 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 +0 -3
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +0 -6
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +31 -15
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +4 -0
- 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 +4 -1
- 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 +4 -0
- 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 +32 -15
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +72 -67
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +1 -1
- package/esm/utils.js +1 -1
- 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 +0 -3
- package/misc.js.map +1 -1
- package/nist.d.ts +0 -6
- package/nist.d.ts.map +1 -1
- package/nist.js +31 -15
- package/nist.js.map +1 -1
- package/p256.d.ts +4 -0
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +4 -1
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +4 -0
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +4 -2
- package/secp256k1.d.ts +32 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +70 -65
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +1 -0
- package/src/abstract/bls.ts +319 -257
- package/src/abstract/curve.ts +226 -170
- package/src/abstract/edwards.ts +352 -139
- package/src/abstract/hash-to-curve.ts +33 -16
- package/src/abstract/modular.ts +86 -35
- package/src/abstract/montgomery.ts +36 -9
- package/src/abstract/tower.ts +4 -4
- package/src/abstract/weierstrass.ts +570 -476
- package/src/bls12-381.ts +28 -20
- package/src/ed25519.ts +161 -179
- package/src/ed448.ts +150 -156
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +3 -7
- package/src/nist.ts +40 -16
- package/src/p256.ts +4 -0
- package/src/p384.ts +4 -2
- package/src/p521.ts +4 -0
- package/src/secp256k1.ts +91 -73
- package/src/utils.ts +1 -1
- package/utils.d.ts +1 -1
- package/utils.js +1 -1
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
*/
|
|
27
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
28
28
|
import { hmac } from '@noble/hashes/hmac.js';
|
|
29
|
+
import { ahash } from '@noble/hashes/utils';
|
|
29
30
|
import {
|
|
30
31
|
_validateObject,
|
|
31
32
|
abool,
|
|
32
33
|
abytes,
|
|
33
34
|
aInRange,
|
|
35
|
+
bitLen,
|
|
34
36
|
bitMask,
|
|
35
37
|
bytesToHex,
|
|
36
38
|
bytesToNumberBE,
|
|
@@ -56,8 +58,9 @@ import {
|
|
|
56
58
|
wNAF,
|
|
57
59
|
type AffinePoint,
|
|
58
60
|
type BasicCurve,
|
|
59
|
-
type
|
|
60
|
-
type
|
|
61
|
+
type CurveInfo,
|
|
62
|
+
type CurvePoint,
|
|
63
|
+
type CurvePointCons,
|
|
61
64
|
} from './curve.ts';
|
|
62
65
|
import {
|
|
63
66
|
Field,
|
|
@@ -71,6 +74,8 @@ import {
|
|
|
71
74
|
|
|
72
75
|
export type { AffinePoint };
|
|
73
76
|
export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
77
|
+
|
|
78
|
+
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
|
74
79
|
/**
|
|
75
80
|
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
|
|
76
81
|
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
|
|
@@ -96,7 +101,8 @@ export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Ar
|
|
|
96
101
|
*/
|
|
97
102
|
export type EndomorphismOpts = {
|
|
98
103
|
beta: bigint;
|
|
99
|
-
|
|
104
|
+
basises?: EndoBasis;
|
|
105
|
+
splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
100
106
|
};
|
|
101
107
|
export type BasicWCurve<T> = BasicCurve<T> & {
|
|
102
108
|
// Params: a, b
|
|
@@ -109,86 +115,123 @@ export type BasicWCurve<T> = BasicCurve<T> & {
|
|
|
109
115
|
endo?: EndomorphismOpts;
|
|
110
116
|
// When a cofactor != 1, there can be an effective methods to:
|
|
111
117
|
// 1. Determine whether a point is torsion-free
|
|
112
|
-
isTorsionFree?: (c:
|
|
118
|
+
isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
113
119
|
// 2. Clear torsion component
|
|
114
|
-
clearCofactor?: (c:
|
|
120
|
+
clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
115
121
|
};
|
|
116
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';
|
|
117
156
|
export type Entropy = Hex | boolean;
|
|
118
|
-
export type SignOpts = {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
prehash
|
|
122
|
-
format
|
|
123
|
-
}
|
|
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
|
+
}>;
|
|
124
168
|
|
|
125
169
|
function validateSigVerOpts(opts: SignOpts | VerOpts) {
|
|
126
170
|
if (opts.lowS !== undefined) abool('lowS', opts.lowS);
|
|
127
171
|
if (opts.prehash !== undefined) abool('prehash', opts.prehash);
|
|
128
172
|
}
|
|
129
173
|
|
|
130
|
-
/** Instance methods for 3D XYZ points. */
|
|
131
|
-
export interface
|
|
132
|
-
/** projective
|
|
133
|
-
readonly
|
|
134
|
-
/** projective
|
|
135
|
-
readonly
|
|
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;
|
|
136
180
|
/** projective z coordinate */
|
|
137
|
-
readonly
|
|
138
|
-
/** affine x coordinate */
|
|
181
|
+
readonly Z: T;
|
|
182
|
+
/** affine x coordinate. Different from projective X. */
|
|
139
183
|
get x(): T;
|
|
140
|
-
/** affine y coordinate */
|
|
184
|
+
/** affine y coordinate. Different from projective Y. */
|
|
141
185
|
get y(): T;
|
|
142
|
-
assertValidity(): void;
|
|
143
|
-
clearCofactor(): ProjPointType<T>;
|
|
144
|
-
is0(): boolean;
|
|
145
|
-
isTorsionFree(): boolean;
|
|
146
|
-
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
|
147
|
-
/**
|
|
148
|
-
* Massively speeds up `p.multiply(n)` by using wnaf precompute tables (caching).
|
|
149
|
-
* Table generation takes 30MB of ram and 10ms on high-end CPU, but may take
|
|
150
|
-
* much longer on slow devices.
|
|
151
|
-
* Actual generation will happen on first call of `.multiply()`.
|
|
152
|
-
* By default, BASE point is precomputed.
|
|
153
|
-
* @param windowSize - table window size
|
|
154
|
-
* @param isLazy - (default true) allows to defer generation
|
|
155
|
-
*/
|
|
156
|
-
precompute(windowSize?: number, isLazy?: boolean): ProjPointType<T>;
|
|
157
|
-
|
|
158
|
-
/** Converts 3D XYZ projective point to 2D xy affine coordinates */
|
|
159
|
-
toAffine(invertedZ?: T): AffinePoint<T>;
|
|
160
186
|
/** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
|
|
161
187
|
toBytes(isCompressed?: boolean): Uint8Array;
|
|
162
188
|
toHex(isCompressed?: boolean): string;
|
|
163
189
|
|
|
190
|
+
/** @deprecated use .X */
|
|
191
|
+
readonly px: T;
|
|
192
|
+
/** @deprecated use .Y */
|
|
193
|
+
readonly py: T;
|
|
194
|
+
/** @deprecated use .Z */
|
|
195
|
+
readonly pz: T;
|
|
164
196
|
/** @deprecated use `toBytes` */
|
|
165
197
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
166
198
|
/** @deprecated use `multiplyUnsafe` */
|
|
167
|
-
multiplyAndAddUnsafe(
|
|
199
|
+
multiplyAndAddUnsafe(
|
|
200
|
+
Q: WeierstrassPoint<T>,
|
|
201
|
+
a: bigint,
|
|
202
|
+
b: bigint
|
|
203
|
+
): WeierstrassPoint<T> | undefined;
|
|
168
204
|
/** @deprecated use `p.y % 2n === 0n` */
|
|
169
205
|
hasEvenY(): boolean;
|
|
170
206
|
/** @deprecated use `p.precompute(windowSize)` */
|
|
171
207
|
_setWindowSize(windowSize: number): void;
|
|
172
208
|
}
|
|
173
209
|
|
|
174
|
-
/** Static methods for 3D XYZ points. */
|
|
175
|
-
export interface
|
|
176
|
-
Fp: IField<T>;
|
|
177
|
-
Fn: IField<bigint>;
|
|
210
|
+
/** Static methods for 3D XYZ projective points. */
|
|
211
|
+
export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPoint<T>> {
|
|
178
212
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
179
|
-
new (
|
|
180
|
-
/**
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
msm(points: ProjPointType<T>[], scalars: bigint[]): ProjPointType<T>;
|
|
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>;
|
|
187
220
|
}
|
|
188
221
|
|
|
222
|
+
/** @deprecated use WeierstrassPoint */
|
|
223
|
+
export type ProjPointType<T> = WeierstrassPoint<T>;
|
|
224
|
+
/** @deprecated use WeierstrassPointCons */
|
|
225
|
+
export type ProjConstructor<T> = WeierstrassPointCons<T>;
|
|
226
|
+
|
|
227
|
+
// TODO: remove
|
|
189
228
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
190
229
|
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
|
191
|
-
toBytes?: (
|
|
230
|
+
toBytes?: (
|
|
231
|
+
c: WeierstrassPointCons<T>,
|
|
232
|
+
point: WeierstrassPoint<T>,
|
|
233
|
+
isCompressed: boolean
|
|
234
|
+
) => Uint8Array;
|
|
192
235
|
};
|
|
193
236
|
|
|
194
237
|
// LegacyWeierstrassOpts
|
|
@@ -196,12 +239,13 @@ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial
|
|
|
196
239
|
|
|
197
240
|
// LegacyWeierstrass
|
|
198
241
|
export type CurvePointsRes<T> = {
|
|
199
|
-
|
|
242
|
+
Point: WeierstrassPointCons<T>;
|
|
243
|
+
|
|
244
|
+
/** @deprecated the property will be removed in next release */
|
|
200
245
|
CURVE: CurvePointsType<T>;
|
|
201
|
-
Point: ProjConstructor<T>;
|
|
202
246
|
/** @deprecated use `Point` */
|
|
203
|
-
ProjectivePoint:
|
|
204
|
-
/** @deprecated */
|
|
247
|
+
ProjectivePoint: WeierstrassPointCons<T>;
|
|
248
|
+
/** @deprecated use `Point.Fn.fromBytes(privateKey)` */
|
|
205
249
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
206
250
|
/** @deprecated */
|
|
207
251
|
weierstrassEquation: (x: T) => T;
|
|
@@ -245,45 +289,53 @@ export type WeierstrassOpts<T> = Readonly<{
|
|
|
245
289
|
export type WeierstrassExtraOpts<T> = Partial<{
|
|
246
290
|
Fp: IField<T>;
|
|
247
291
|
Fn: IField<bigint>;
|
|
248
|
-
// TODO: remove
|
|
249
|
-
allowedPrivateKeyLengths: readonly number[]; // for P521
|
|
250
292
|
allowInfinityPoint: boolean;
|
|
251
293
|
endo: EndomorphismOpts;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
clearCofactor: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
|
|
294
|
+
isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
295
|
+
clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
255
296
|
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
|
256
|
-
toBytes: (
|
|
297
|
+
toBytes: (
|
|
298
|
+
c: WeierstrassPointCons<T>,
|
|
299
|
+
point: WeierstrassPoint<T>,
|
|
300
|
+
isCompressed: boolean
|
|
301
|
+
) => Uint8Array;
|
|
257
302
|
}>;
|
|
258
303
|
|
|
259
304
|
/**
|
|
260
305
|
* Options for ECDSA signatures over a Weierstrass curve.
|
|
261
306
|
*/
|
|
262
|
-
export type ECDSAOpts = {
|
|
263
|
-
|
|
264
|
-
hmac
|
|
265
|
-
randomBytes
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
};
|
|
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
|
+
}>;
|
|
270
314
|
|
|
271
315
|
/** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
|
|
272
316
|
export interface ECDSA {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
sign: (msgHash: Hex,
|
|
317
|
+
keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
|
|
318
|
+
getPublicKey: (secretKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
319
|
+
sign: (msgHash: Hex, secretKey: PrivKey, opts?: SignOpts) => ECDSASigRecovered;
|
|
276
320
|
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
|
277
|
-
|
|
278
|
-
|
|
321
|
+
getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
322
|
+
Point: WeierstrassPointCons<bigint>;
|
|
323
|
+
Signature: ECDSASignatureCons;
|
|
279
324
|
utils: {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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()` */
|
|
283
334
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
284
|
-
/** @deprecated */
|
|
285
|
-
precompute: (windowSize?: number, point?:
|
|
335
|
+
/** @deprecated use `point.precompute()` */
|
|
336
|
+
precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
|
|
286
337
|
};
|
|
338
|
+
info: CurveInfo;
|
|
287
339
|
}
|
|
288
340
|
export class DERErr extends Error {
|
|
289
341
|
constructor(m = '') {
|
|
@@ -420,45 +472,27 @@ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
|
|
|
420
472
|
}
|
|
421
473
|
return weierstrassEquation;
|
|
422
474
|
}
|
|
423
|
-
export function
|
|
424
|
-
Fn: IField<bigint>,
|
|
425
|
-
allowedPrivateKeyLengths?: readonly number[],
|
|
426
|
-
wrapPrivateKey?: boolean
|
|
427
|
-
): (key: PrivKey) => bigint {
|
|
475
|
+
export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
|
|
428
476
|
const { BYTES: expected } = Fn;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
throw new Error('invalid private key');
|
|
439
|
-
const padded = new Uint8Array(expected);
|
|
440
|
-
padded.set(bytes, padded.length - bytes.length);
|
|
441
|
-
bytes = padded;
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
num = Fn.fromBytes(bytes);
|
|
445
|
-
} catch (error) {
|
|
446
|
-
throw new Error(
|
|
447
|
-
`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`
|
|
448
|
-
);
|
|
449
|
-
}
|
|
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}`);
|
|
450
486
|
}
|
|
451
|
-
if (wrapPrivateKey) num = Fn.create(num); // disabled by default, enabled for BLS
|
|
452
|
-
if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
|
|
453
|
-
return num;
|
|
454
487
|
}
|
|
455
|
-
|
|
488
|
+
if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
|
|
489
|
+
return num;
|
|
456
490
|
}
|
|
457
491
|
|
|
458
492
|
export function weierstrassN<T>(
|
|
459
493
|
CURVE: WeierstrassOpts<T>,
|
|
460
494
|
curveOpts: WeierstrassExtraOpts<T> = {}
|
|
461
|
-
):
|
|
495
|
+
): WeierstrassPointCons<T> {
|
|
462
496
|
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
463
497
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
464
498
|
_validateObject(
|
|
@@ -478,12 +512,8 @@ export function weierstrassN<T>(
|
|
|
478
512
|
const { endo } = curveOpts;
|
|
479
513
|
if (endo) {
|
|
480
514
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
typeof endo.beta !== 'bigint' ||
|
|
484
|
-
typeof endo.splitScalar !== 'function'
|
|
485
|
-
) {
|
|
486
|
-
throw new Error('invalid endo: expected "beta": bigint and "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');
|
|
487
517
|
}
|
|
488
518
|
}
|
|
489
519
|
|
|
@@ -493,8 +523,8 @@ export function weierstrassN<T>(
|
|
|
493
523
|
|
|
494
524
|
// Implements IEEE P1363 point encoding
|
|
495
525
|
function pointToBytes(
|
|
496
|
-
_c:
|
|
497
|
-
point:
|
|
526
|
+
_c: WeierstrassPointCons<T>,
|
|
527
|
+
point: WeierstrassPoint<T>,
|
|
498
528
|
isCompressed: boolean
|
|
499
529
|
): Uint8Array {
|
|
500
530
|
const { x, y } = point.toAffine();
|
|
@@ -578,25 +608,30 @@ export function weierstrassN<T>(
|
|
|
578
608
|
if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
|
|
579
609
|
}
|
|
580
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
|
+
|
|
581
616
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
582
617
|
|
|
583
618
|
// Converts Projective point to affine (x, y) coordinates.
|
|
584
619
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
585
620
|
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
586
621
|
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
|
|
587
|
-
const {
|
|
622
|
+
const { X, Y, Z } = p;
|
|
588
623
|
// Fast-path for normalized points
|
|
589
|
-
if (Fp.eql(
|
|
624
|
+
if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
|
|
590
625
|
const is0 = p.is0();
|
|
591
626
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
592
627
|
// all operations, so we replace invZ with a random number, 1.
|
|
593
|
-
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(
|
|
594
|
-
const
|
|
595
|
-
const
|
|
596
|
-
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);
|
|
597
632
|
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
598
633
|
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
599
|
-
return { x
|
|
634
|
+
return { x, y };
|
|
600
635
|
});
|
|
601
636
|
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
602
637
|
// Otherwise true will be return
|
|
@@ -605,7 +640,7 @@ export function weierstrassN<T>(
|
|
|
605
640
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
606
641
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
607
642
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
608
|
-
if (curveOpts.allowInfinityPoint && !Fp.is0(p.
|
|
643
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
|
|
609
644
|
throw new Error('bad point: ZERO');
|
|
610
645
|
}
|
|
611
646
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
@@ -623,7 +658,7 @@ export function weierstrassN<T>(
|
|
|
623
658
|
k1neg: boolean,
|
|
624
659
|
k2neg: boolean
|
|
625
660
|
) {
|
|
626
|
-
k2p = new Point(Fp.mul(k2p.
|
|
661
|
+
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
627
662
|
k1p = negateCt(k1neg, k1p);
|
|
628
663
|
k2p = negateCt(k2neg, k2p);
|
|
629
664
|
return k1p.add(k2p);
|
|
@@ -634,7 +669,7 @@ export function weierstrassN<T>(
|
|
|
634
669
|
* Default Point works in 2d / affine coordinates: (x, y).
|
|
635
670
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
636
671
|
*/
|
|
637
|
-
class Point implements
|
|
672
|
+
class Point implements WeierstrassPoint<T> {
|
|
638
673
|
// base / generator point
|
|
639
674
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
640
675
|
// zero / infinity / identity point
|
|
@@ -643,15 +678,15 @@ export function weierstrassN<T>(
|
|
|
643
678
|
static readonly Fp = Fp;
|
|
644
679
|
static readonly Fn = Fn;
|
|
645
680
|
|
|
646
|
-
readonly
|
|
647
|
-
readonly
|
|
648
|
-
readonly
|
|
681
|
+
readonly X: T;
|
|
682
|
+
readonly Y: T;
|
|
683
|
+
readonly Z: T;
|
|
649
684
|
|
|
650
685
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
651
|
-
constructor(
|
|
652
|
-
this.
|
|
653
|
-
this.
|
|
654
|
-
this.
|
|
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);
|
|
655
690
|
Object.freeze(this);
|
|
656
691
|
}
|
|
657
692
|
|
|
@@ -672,8 +707,18 @@ export function weierstrassN<T>(
|
|
|
672
707
|
return this.toAffine().y;
|
|
673
708
|
}
|
|
674
709
|
|
|
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
|
+
}
|
|
675
720
|
static normalizeZ(points: Point[]): Point[] {
|
|
676
|
-
return normalizeZ(Point,
|
|
721
|
+
return normalizeZ(Point, points);
|
|
677
722
|
}
|
|
678
723
|
|
|
679
724
|
static fromBytes(bytes: Uint8Array): Point {
|
|
@@ -690,18 +735,16 @@ export function weierstrassN<T>(
|
|
|
690
735
|
|
|
691
736
|
/** Multiplies generator point by privateKey. */
|
|
692
737
|
static fromPrivateKey(privateKey: PrivKey) {
|
|
693
|
-
|
|
694
|
-
Fn,
|
|
695
|
-
curveOpts.allowedPrivateKeyLengths,
|
|
696
|
-
curveOpts.wrapPrivateKey
|
|
697
|
-
);
|
|
698
|
-
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
738
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
699
739
|
}
|
|
700
740
|
|
|
701
|
-
|
|
741
|
+
// TODO: remove
|
|
702
742
|
static msm(points: Point[], scalars: bigint[]): Point {
|
|
703
743
|
return pippenger(Point, Fn, points, scalars);
|
|
704
744
|
}
|
|
745
|
+
_setWindowSize(windowSize: number) {
|
|
746
|
+
this.precompute(windowSize);
|
|
747
|
+
}
|
|
705
748
|
|
|
706
749
|
/**
|
|
707
750
|
*
|
|
@@ -710,16 +753,11 @@ export function weierstrassN<T>(
|
|
|
710
753
|
* @returns
|
|
711
754
|
*/
|
|
712
755
|
precompute(windowSize: number = 8, isLazy = true): Point {
|
|
713
|
-
wnaf.
|
|
756
|
+
wnaf.createCache(this, windowSize);
|
|
714
757
|
if (!isLazy) this.multiply(_3n); // random number
|
|
715
758
|
return this;
|
|
716
759
|
}
|
|
717
760
|
|
|
718
|
-
/** "Private method", don't use it directly */
|
|
719
|
-
_setWindowSize(windowSize: number) {
|
|
720
|
-
this.precompute(windowSize);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
761
|
// TODO: return `this`
|
|
724
762
|
/** A point on curve is valid if it conforms to equation. */
|
|
725
763
|
assertValidity(): void {
|
|
@@ -735,8 +773,8 @@ export function weierstrassN<T>(
|
|
|
735
773
|
/** Compare one point to another. */
|
|
736
774
|
equals(other: Point): boolean {
|
|
737
775
|
aprjpoint(other);
|
|
738
|
-
const {
|
|
739
|
-
const {
|
|
776
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
777
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
740
778
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
741
779
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
742
780
|
return U1 && U2;
|
|
@@ -744,7 +782,7 @@ export function weierstrassN<T>(
|
|
|
744
782
|
|
|
745
783
|
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
746
784
|
negate(): Point {
|
|
747
|
-
return new Point(this.
|
|
785
|
+
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|
748
786
|
}
|
|
749
787
|
|
|
750
788
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
@@ -754,7 +792,7 @@ export function weierstrassN<T>(
|
|
|
754
792
|
double() {
|
|
755
793
|
const { a, b } = CURVE;
|
|
756
794
|
const b3 = Fp.mul(b, _3n);
|
|
757
|
-
const {
|
|
795
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
758
796
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
759
797
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
760
798
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -796,8 +834,8 @@ export function weierstrassN<T>(
|
|
|
796
834
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
797
835
|
add(other: Point): Point {
|
|
798
836
|
aprjpoint(other);
|
|
799
|
-
const {
|
|
800
|
-
const {
|
|
837
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
838
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
801
839
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
802
840
|
const a = CURVE.a;
|
|
803
841
|
const b3 = Fp.mul(CURVE.b, _3n);
|
|
@@ -865,10 +903,10 @@ export function weierstrassN<T>(
|
|
|
865
903
|
const { endo } = curveOpts;
|
|
866
904
|
if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
867
905
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
868
|
-
const mul = (n: bigint) => wnaf.
|
|
906
|
+
const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
869
907
|
/** See docs for {@link EndomorphismOpts} */
|
|
870
908
|
if (endo) {
|
|
871
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
909
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
872
910
|
const { p: k1p, f: k1f } = mul(k1);
|
|
873
911
|
const { p: k2p, f: k2f } = mul(k2);
|
|
874
912
|
fake = k1f.add(k2f);
|
|
@@ -879,13 +917,13 @@ export function weierstrassN<T>(
|
|
|
879
917
|
fake = f;
|
|
880
918
|
}
|
|
881
919
|
// Normalize `z` for both points, but return only real one
|
|
882
|
-
return
|
|
920
|
+
return normalizeZ(Point, [point, fake])[0];
|
|
883
921
|
}
|
|
884
922
|
|
|
885
923
|
/**
|
|
886
924
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
887
925
|
* It's faster, but should only be used when you don't care about
|
|
888
|
-
* an exposed
|
|
926
|
+
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
889
927
|
*/
|
|
890
928
|
multiplyUnsafe(sc: bigint): Point {
|
|
891
929
|
const { endo } = curveOpts;
|
|
@@ -893,14 +931,13 @@ export function weierstrassN<T>(
|
|
|
893
931
|
if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
894
932
|
if (sc === _0n || p.is0()) return Point.ZERO;
|
|
895
933
|
if (sc === _1n) return p; // fast-path
|
|
896
|
-
if (wnaf.
|
|
934
|
+
if (wnaf.hasCache(this)) return this.multiply(sc);
|
|
897
935
|
if (endo) {
|
|
898
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
899
|
-
|
|
900
|
-
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
|
|
936
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
937
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
901
938
|
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
902
939
|
} else {
|
|
903
|
-
return wnaf.
|
|
940
|
+
return wnaf.unsafe(p, sc);
|
|
904
941
|
}
|
|
905
942
|
}
|
|
906
943
|
|
|
@@ -925,7 +962,7 @@ export function weierstrassN<T>(
|
|
|
925
962
|
const { isTorsionFree } = curveOpts;
|
|
926
963
|
if (cofactor === _1n) return true;
|
|
927
964
|
if (isTorsionFree) return isTorsionFree(Point, this);
|
|
928
|
-
return wnaf.
|
|
965
|
+
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
929
966
|
}
|
|
930
967
|
|
|
931
968
|
clearCofactor(): Point {
|
|
@@ -935,6 +972,11 @@ export function weierstrassN<T>(
|
|
|
935
972
|
return this.multiplyUnsafe(cofactor);
|
|
936
973
|
}
|
|
937
974
|
|
|
975
|
+
isSmallOrder(): boolean {
|
|
976
|
+
// can we use this.clearCofactor()?
|
|
977
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
978
|
+
}
|
|
979
|
+
|
|
938
980
|
toBytes(isCompressed = true): Uint8Array {
|
|
939
981
|
abool('isCompressed', isCompressed);
|
|
940
982
|
this.assertValidity();
|
|
@@ -955,12 +997,13 @@ export function weierstrassN<T>(
|
|
|
955
997
|
}
|
|
956
998
|
}
|
|
957
999
|
const bits = Fn.BITS;
|
|
958
|
-
const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
1000
|
+
const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
959
1001
|
return Point;
|
|
960
1002
|
}
|
|
961
1003
|
|
|
962
1004
|
// _legacyWeierstrass
|
|
963
|
-
|
|
1005
|
+
// TODO: remove
|
|
1006
|
+
/** @deprecated use `weierstrass` in newer releases */
|
|
964
1007
|
export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
|
|
965
1008
|
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
966
1009
|
const Point = weierstrassN(CURVE, curveOpts);
|
|
@@ -968,33 +1011,49 @@ export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePoin
|
|
|
968
1011
|
}
|
|
969
1012
|
|
|
970
1013
|
// Instance
|
|
971
|
-
export interface
|
|
1014
|
+
export interface ECDSASignature {
|
|
972
1015
|
readonly r: bigint;
|
|
973
1016
|
readonly s: bigint;
|
|
974
1017
|
readonly recovery?: number;
|
|
975
|
-
|
|
976
|
-
addRecoveryBit(recovery: number): RecoveredSignatureType;
|
|
1018
|
+
addRecoveryBit(recovery: number): ECDSASigRecovered;
|
|
977
1019
|
hasHighS(): boolean;
|
|
978
|
-
normalizeS():
|
|
979
|
-
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')` */
|
|
980
1028
|
toCompactRawBytes(): Uint8Array;
|
|
1029
|
+
/** @deprecated use `.toBytes('compact')` */
|
|
981
1030
|
toCompactHex(): string;
|
|
1031
|
+
/** @deprecated use `.toBytes('der')` */
|
|
982
1032
|
toDERRawBytes(): Uint8Array;
|
|
1033
|
+
/** @deprecated use `.toBytes('der')` */
|
|
983
1034
|
toDERHex(): string;
|
|
984
|
-
// toBytes(format?: string): Uint8Array;
|
|
985
1035
|
}
|
|
986
|
-
export type
|
|
1036
|
+
export type SignatureType = ECDSASignature;
|
|
1037
|
+
export type ECDSASigRecovered = ECDSASignature & {
|
|
987
1038
|
readonly recovery: number;
|
|
988
1039
|
};
|
|
1040
|
+
export type RecoveredSignatureType = ECDSASigRecovered;
|
|
989
1041
|
// Static methods
|
|
990
|
-
export type
|
|
991
|
-
new (r: bigint, s: bigint, recovery?: number):
|
|
992
|
-
|
|
993
|
-
|
|
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;
|
|
994
1051
|
};
|
|
995
1052
|
export type SignatureLike = { r: bigint; s: bigint };
|
|
996
|
-
|
|
1053
|
+
// TODO: remove
|
|
1054
|
+
export type PubKey = Hex | WeierstrassPoint<bigint>;
|
|
997
1055
|
|
|
1056
|
+
// TODO: remove
|
|
998
1057
|
export type CurveType = BasicWCurve<bigint> & {
|
|
999
1058
|
hash: CHash; // CHash not FHash because we need outputLen for DRBG
|
|
1000
1059
|
hmac?: HmacFnSync;
|
|
@@ -1009,32 +1068,168 @@ function pprefix(hasEvenY: boolean): Uint8Array {
|
|
|
1009
1068
|
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
1010
1069
|
}
|
|
1011
1070
|
|
|
1071
|
+
// TODO: remove
|
|
1012
1072
|
export type CurveFn = {
|
|
1073
|
+
/** @deprecated the property will be removed in next release */
|
|
1013
1074
|
CURVE: CurvePointsType<bigint>;
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1075
|
+
keygen: ECDSA['keygen'];
|
|
1076
|
+
getPublicKey: ECDSA['getPublicKey'];
|
|
1077
|
+
getSharedSecret: ECDSA['getSharedSecret'];
|
|
1078
|
+
sign: ECDSA['sign'];
|
|
1079
|
+
verify: ECDSA['verify'];
|
|
1080
|
+
Point: WeierstrassPointCons<bigint>;
|
|
1019
1081
|
/** @deprecated use `Point` */
|
|
1020
|
-
ProjectivePoint:
|
|
1021
|
-
Signature:
|
|
1022
|
-
utils:
|
|
1023
|
-
|
|
1024
|
-
isValidPrivateKey(privateKey: PrivKey): boolean;
|
|
1025
|
-
randomPrivateKey: () => Uint8Array;
|
|
1026
|
-
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
|
1027
|
-
};
|
|
1082
|
+
ProjectivePoint: WeierstrassPointCons<bigint>;
|
|
1083
|
+
Signature: ECDSASignatureCons;
|
|
1084
|
+
utils: ECDSA['utils'];
|
|
1085
|
+
info: CurveInfo;
|
|
1028
1086
|
};
|
|
1029
1087
|
|
|
1088
|
+
/**
|
|
1089
|
+
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
1090
|
+
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
1091
|
+
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1092
|
+
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1093
|
+
* @param Fp
|
|
1094
|
+
* @param Z
|
|
1095
|
+
* @returns
|
|
1096
|
+
*/
|
|
1097
|
+
export function SWUFpSqrtRatio<T>(
|
|
1098
|
+
Fp: IField<T>,
|
|
1099
|
+
Z: T
|
|
1100
|
+
): (u: T, v: T) => { isValid: boolean; value: T } {
|
|
1101
|
+
// Generic implementation
|
|
1102
|
+
const q = Fp.ORDER;
|
|
1103
|
+
let l = _0n;
|
|
1104
|
+
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
|
1105
|
+
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1106
|
+
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
1107
|
+
// 2n ** c1 == 2n << (c1-1)
|
|
1108
|
+
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
1109
|
+
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
1110
|
+
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1111
|
+
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1112
|
+
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1113
|
+
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1114
|
+
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1115
|
+
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1116
|
+
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1117
|
+
let tv1 = c6; // 1. tv1 = c6
|
|
1118
|
+
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1119
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1120
|
+
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1121
|
+
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1122
|
+
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1123
|
+
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1124
|
+
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1125
|
+
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1126
|
+
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1127
|
+
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1128
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1129
|
+
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1130
|
+
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1131
|
+
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1132
|
+
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1133
|
+
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1134
|
+
for (let i = c1; i > _1n; i--) {
|
|
1135
|
+
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1136
|
+
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1137
|
+
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1138
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1139
|
+
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1140
|
+
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1141
|
+
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1142
|
+
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1143
|
+
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1144
|
+
}
|
|
1145
|
+
return { isValid: isQR, value: tv3 };
|
|
1146
|
+
};
|
|
1147
|
+
if (Fp.ORDER % _4n === _3n) {
|
|
1148
|
+
// sqrt_ratio_3mod4(u, v)
|
|
1149
|
+
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1150
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1151
|
+
sqrtRatio = (u: T, v: T) => {
|
|
1152
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1153
|
+
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1154
|
+
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1155
|
+
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1156
|
+
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1157
|
+
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1158
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1159
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1160
|
+
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1161
|
+
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
// No curves uses that
|
|
1165
|
+
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
1166
|
+
return sqrtRatio;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1170
|
+
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1171
|
+
*/
|
|
1172
|
+
export function mapToCurveSimpleSWU<T>(
|
|
1173
|
+
Fp: IField<T>,
|
|
1174
|
+
opts: {
|
|
1175
|
+
A: T;
|
|
1176
|
+
B: T;
|
|
1177
|
+
Z: T;
|
|
1178
|
+
}
|
|
1179
|
+
): (u: T) => { x: T; y: T } {
|
|
1180
|
+
validateField(Fp);
|
|
1181
|
+
const { A, B, Z } = opts;
|
|
1182
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1183
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1184
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1185
|
+
if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
|
|
1186
|
+
// Input: u, an element of F.
|
|
1187
|
+
// Output: (x, y), a point on E.
|
|
1188
|
+
return (u: T): { x: T; y: T } => {
|
|
1189
|
+
// prettier-ignore
|
|
1190
|
+
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1191
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1192
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1193
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1194
|
+
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1195
|
+
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1196
|
+
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1197
|
+
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1198
|
+
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1199
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1200
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1201
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1202
|
+
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1203
|
+
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1204
|
+
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1205
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1206
|
+
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1207
|
+
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1208
|
+
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1209
|
+
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1210
|
+
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1211
|
+
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1212
|
+
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1213
|
+
const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1214
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1215
|
+
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1216
|
+
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1217
|
+
return { x, y };
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Creates ECDSA for given elliptic curve Point and hash function.
|
|
1223
|
+
*/
|
|
1030
1224
|
export function ecdsa(
|
|
1031
|
-
Point:
|
|
1032
|
-
|
|
1033
|
-
|
|
1225
|
+
Point: WeierstrassPointCons<bigint>,
|
|
1226
|
+
hash: CHash,
|
|
1227
|
+
ecdsaOpts: ECDSAOpts = {}
|
|
1034
1228
|
): ECDSA {
|
|
1229
|
+
ahash(hash);
|
|
1035
1230
|
_validateObject(
|
|
1036
1231
|
ecdsaOpts,
|
|
1037
|
-
{
|
|
1232
|
+
{},
|
|
1038
1233
|
{
|
|
1039
1234
|
hmac: 'function',
|
|
1040
1235
|
lowS: 'boolean',
|
|
@@ -1047,11 +1242,20 @@ export function ecdsa(
|
|
|
1047
1242
|
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
1048
1243
|
const hmac_: HmacFnSync =
|
|
1049
1244
|
ecdsaOpts.hmac ||
|
|
1050
|
-
(((key, ...msgs) => hmac(
|
|
1245
|
+
(((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1051
1246
|
|
|
1052
1247
|
const { Fp, Fn } = Point;
|
|
1053
1248
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
1054
1249
|
|
|
1250
|
+
const seedLen = getMinHashLength(CURVE_ORDER);
|
|
1251
|
+
const lengths = {
|
|
1252
|
+
secret: Fn.BYTES,
|
|
1253
|
+
public: 1 + Fp.BYTES,
|
|
1254
|
+
publicUncompressed: 1 + 2 * Fp.BYTES,
|
|
1255
|
+
signature: 2 * Fn.BYTES,
|
|
1256
|
+
seed: seedLen,
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1055
1259
|
function isBiggerThanHalfOrder(number: bigint) {
|
|
1056
1260
|
const HALF = CURVE_ORDER >> _1n;
|
|
1057
1261
|
return number > HALF;
|
|
@@ -1068,7 +1272,7 @@ export function ecdsa(
|
|
|
1068
1272
|
/**
|
|
1069
1273
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
1070
1274
|
*/
|
|
1071
|
-
class Signature implements
|
|
1275
|
+
class Signature implements ECDSASignature {
|
|
1072
1276
|
readonly r: bigint;
|
|
1073
1277
|
readonly s: bigint;
|
|
1074
1278
|
readonly recovery?: number;
|
|
@@ -1081,26 +1285,26 @@ export function ecdsa(
|
|
|
1081
1285
|
Object.freeze(this);
|
|
1082
1286
|
}
|
|
1083
1287
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1288
|
+
static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = 'compact') {
|
|
1289
|
+
if (format === 'compact') {
|
|
1290
|
+
const L = Fn.BYTES;
|
|
1291
|
+
abytes(bytes, L * 2);
|
|
1292
|
+
const r = bytes.subarray(0, L);
|
|
1293
|
+
const s = bytes.subarray(L, L * 2);
|
|
1294
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
|
|
1295
|
+
}
|
|
1296
|
+
if (format === 'der') {
|
|
1297
|
+
abytes(bytes);
|
|
1298
|
+
const { r, s } = DER.toSig(bytes);
|
|
1299
|
+
return new Signature(r, s);
|
|
1300
|
+
}
|
|
1301
|
+
throw new Error('invalid format');
|
|
1089
1302
|
}
|
|
1090
1303
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
static fromDER(hex: Hex) {
|
|
1094
|
-
const { r, s } = DER.toSig(ensureBytes('DER', hex));
|
|
1095
|
-
return new Signature(r, s);
|
|
1304
|
+
static fromHex(hex: string, format?: ECDSASigFormat) {
|
|
1305
|
+
return this.fromBytes(hexToBytes(hex), format);
|
|
1096
1306
|
}
|
|
1097
1307
|
|
|
1098
|
-
/**
|
|
1099
|
-
* @todo remove
|
|
1100
|
-
* @deprecated
|
|
1101
|
-
*/
|
|
1102
|
-
assertValidity(): void {}
|
|
1103
|
-
|
|
1104
1308
|
addRecoveryBit(recovery: number): RecoveredSignature {
|
|
1105
1309
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
1106
1310
|
}
|
|
@@ -1146,21 +1350,30 @@ export function ecdsa(
|
|
|
1146
1350
|
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1147
1351
|
}
|
|
1148
1352
|
|
|
1149
|
-
toBytes(format:
|
|
1353
|
+
toBytes(format: ECDSASigFormat = 'compact') {
|
|
1150
1354
|
if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
1151
1355
|
if (format === 'der') return hexToBytes(DER.hexFromSig(this));
|
|
1152
1356
|
throw new Error('invalid format');
|
|
1153
1357
|
}
|
|
1154
1358
|
|
|
1155
|
-
|
|
1359
|
+
toHex(format?: ECDSASigFormat) {
|
|
1360
|
+
return bytesToHex(this.toBytes(format));
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// TODO: remove
|
|
1364
|
+
assertValidity(): void {}
|
|
1365
|
+
static fromCompact(hex: Hex) {
|
|
1366
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
|
|
1367
|
+
}
|
|
1368
|
+
static fromDER(hex: Hex) {
|
|
1369
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
1370
|
+
}
|
|
1156
1371
|
toDERRawBytes() {
|
|
1157
1372
|
return this.toBytes('der');
|
|
1158
1373
|
}
|
|
1159
1374
|
toDERHex() {
|
|
1160
1375
|
return bytesToHex(this.toBytes('der'));
|
|
1161
1376
|
}
|
|
1162
|
-
|
|
1163
|
-
// padded bytes of r, then padded bytes of s
|
|
1164
1377
|
toCompactRawBytes() {
|
|
1165
1378
|
return this.toBytes('compact');
|
|
1166
1379
|
}
|
|
@@ -1170,80 +1383,81 @@ export function ecdsa(
|
|
|
1170
1383
|
}
|
|
1171
1384
|
type RecoveredSignature = Signature & { recovery: number };
|
|
1172
1385
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1386
|
+
function isValidSecretKey(privateKey: PrivKey) {
|
|
1387
|
+
try {
|
|
1388
|
+
return !!_normFnElement(Fn, privateKey);
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
|
|
1394
|
+
try {
|
|
1395
|
+
const l = publicKey.length;
|
|
1396
|
+
if (isCompressed === true && l !== lengths.public) return false;
|
|
1397
|
+
if (isCompressed === false && l !== lengths.publicUncompressed) return false;
|
|
1398
|
+
return !!Point.fromBytes(publicKey);
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
return false;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Produces cryptographically secure secret key from random of size
|
|
1405
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1406
|
+
*/
|
|
1407
|
+
function randomSecretKey(seed = randomBytes_(seedLen)): Uint8Array {
|
|
1408
|
+
return mapHashToField(seed, CURVE_ORDER);
|
|
1409
|
+
}
|
|
1178
1410
|
|
|
1179
1411
|
const utils = {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
return true;
|
|
1184
|
-
} catch (error) {
|
|
1185
|
-
return false;
|
|
1186
|
-
}
|
|
1187
|
-
},
|
|
1188
|
-
normPrivateKeyToScalar: normPrivateKeyToScalar,
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* Produces cryptographically secure private key from random of size
|
|
1192
|
-
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1193
|
-
*/
|
|
1194
|
-
randomPrivateKey: (): Uint8Array => {
|
|
1195
|
-
const n = CURVE_ORDER;
|
|
1196
|
-
return mapHashToField(randomBytes_(getMinHashLength(n)), n);
|
|
1197
|
-
},
|
|
1412
|
+
isValidSecretKey,
|
|
1413
|
+
isValidPublicKey,
|
|
1414
|
+
randomSecretKey,
|
|
1198
1415
|
|
|
1199
|
-
|
|
1416
|
+
// TODO: remove
|
|
1417
|
+
isValidPrivateKey: isValidSecretKey,
|
|
1418
|
+
randomPrivateKey: randomSecretKey,
|
|
1419
|
+
normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
|
|
1420
|
+
precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
|
|
1200
1421
|
return point.precompute(windowSize, false);
|
|
1201
1422
|
},
|
|
1202
1423
|
};
|
|
1203
1424
|
|
|
1204
1425
|
/**
|
|
1205
|
-
* Computes public key for a
|
|
1206
|
-
* @param privateKey private key
|
|
1426
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1207
1427
|
* @param isCompressed whether to return compact (default), or full key
|
|
1208
1428
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1209
1429
|
*/
|
|
1210
|
-
function getPublicKey(
|
|
1211
|
-
return Point.
|
|
1430
|
+
function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
|
|
1431
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1212
1432
|
}
|
|
1213
1433
|
|
|
1214
1434
|
/**
|
|
1215
1435
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1216
1436
|
*/
|
|
1217
1437
|
function isProbPub(item: PrivKey | PubKey): boolean | undefined {
|
|
1438
|
+
// TODO: remove
|
|
1218
1439
|
if (typeof item === 'bigint') return false;
|
|
1440
|
+
// TODO: remove
|
|
1219
1441
|
if (item instanceof Point) return true;
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
const LC = L + 1; // e.g. 33 for 32
|
|
1224
|
-
const LU = 2 * L + 1; // e.g. 65 for 32
|
|
1225
|
-
if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
|
|
1226
|
-
return undefined;
|
|
1227
|
-
} else {
|
|
1228
|
-
return length === LC || length === LU;
|
|
1229
|
-
}
|
|
1442
|
+
if (Fn.allowedLengths || lengths.secret === lengths.public) return undefined;
|
|
1443
|
+
const l = ensureBytes('key', item).length;
|
|
1444
|
+
return l === lengths.public || l === lengths.publicUncompressed;
|
|
1230
1445
|
}
|
|
1231
1446
|
|
|
1232
1447
|
/**
|
|
1233
1448
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1234
|
-
* Computes shared public key from
|
|
1235
|
-
* Checks: 1)
|
|
1449
|
+
* Computes shared public key from secret key A and public key B.
|
|
1450
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1236
1451
|
* Does NOT hash the result.
|
|
1237
|
-
* @param privateA private key
|
|
1238
|
-
* @param publicB different public key
|
|
1239
1452
|
* @param isCompressed whether to return compact (default), or full key
|
|
1240
1453
|
* @returns shared public key
|
|
1241
1454
|
*/
|
|
1242
|
-
function getSharedSecret(
|
|
1243
|
-
if (isProbPub(
|
|
1244
|
-
if (isProbPub(
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1455
|
+
function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
|
|
1456
|
+
if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
|
|
1457
|
+
if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
|
|
1458
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1459
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1460
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
1247
1461
|
}
|
|
1248
1462
|
|
|
1249
1463
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
@@ -1285,7 +1499,6 @@ export function ecdsa(
|
|
|
1285
1499
|
function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
|
|
1286
1500
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1287
1501
|
throw new Error('sign() legacy options not supported');
|
|
1288
|
-
const { hash } = ecdsaOpts;
|
|
1289
1502
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
1290
1503
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
1291
1504
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
@@ -1296,17 +1509,21 @@ export function ecdsa(
|
|
|
1296
1509
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1297
1510
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1298
1511
|
const h1int = bits2int_modN(msgHash);
|
|
1299
|
-
const d =
|
|
1512
|
+
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1300
1513
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1301
1514
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1302
1515
|
if (ent != null && ent !== false) {
|
|
1303
1516
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1304
|
-
const e = ent === true ? randomBytes_(
|
|
1517
|
+
const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
|
|
1305
1518
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
1306
1519
|
}
|
|
1307
1520
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
1308
1521
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
1309
1522
|
// Converts signature params into point w r/s, checks result for validity.
|
|
1523
|
+
// To transform k => Signature:
|
|
1524
|
+
// q = k⋅G
|
|
1525
|
+
// r = q.x mod n
|
|
1526
|
+
// s = k^-1(m + rd) mod n
|
|
1310
1527
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1311
1528
|
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1312
1529
|
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
@@ -1316,7 +1533,7 @@ export function ecdsa(
|
|
|
1316
1533
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
1317
1534
|
if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
|
|
1318
1535
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
1319
|
-
const q = Point.BASE.multiply(k).toAffine(); // q =
|
|
1536
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
1320
1537
|
const r = Fn.create(q.x); // r = q.x mod n
|
|
1321
1538
|
if (r === _0n) return;
|
|
1322
1539
|
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
@@ -1335,21 +1552,17 @@ export function ecdsa(
|
|
|
1335
1552
|
const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1336
1553
|
|
|
1337
1554
|
/**
|
|
1338
|
-
* Signs message hash with a
|
|
1555
|
+
* Signs message hash with a secret key.
|
|
1339
1556
|
* ```
|
|
1340
1557
|
* sign(m, d, k) where
|
|
1341
1558
|
* (x, y) = G × k
|
|
1342
1559
|
* r = x mod n
|
|
1343
1560
|
* s = (m + dr)/k mod n
|
|
1344
1561
|
* ```
|
|
1345
|
-
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
|
1346
|
-
* @param privKey private key
|
|
1347
|
-
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
|
1348
|
-
* @returns signature with recovery param
|
|
1349
1562
|
*/
|
|
1350
|
-
function sign(msgHash: Hex,
|
|
1351
|
-
const { seed, k2sig } = prepSig(msgHash,
|
|
1352
|
-
const drbg = createHmacDrbg<RecoveredSignature>(
|
|
1563
|
+
function sign(msgHash: Hex, secretKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
|
|
1564
|
+
const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1565
|
+
const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac_);
|
|
1353
1566
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1354
1567
|
}
|
|
1355
1568
|
|
|
@@ -1386,90 +1599,103 @@ export function ecdsa(
|
|
|
1386
1599
|
// TODO: remove
|
|
1387
1600
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
|
1388
1601
|
|
|
1389
|
-
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
1390
|
-
throw new Error('format must be "compact", "der" or "js"');
|
|
1391
|
-
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1392
|
-
const isObj =
|
|
1393
|
-
!isHex &&
|
|
1394
|
-
!format &&
|
|
1395
|
-
typeof sg === 'object' &&
|
|
1396
|
-
sg !== null &&
|
|
1397
|
-
typeof sg.r === 'bigint' &&
|
|
1398
|
-
typeof sg.s === 'bigint';
|
|
1399
|
-
if (!isHex && !isObj)
|
|
1400
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1401
1602
|
let _sig: Signature | undefined = undefined;
|
|
1402
|
-
let P:
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1603
|
+
let P: WeierstrassPoint<bigint>;
|
|
1604
|
+
|
|
1605
|
+
if (format === undefined) {
|
|
1606
|
+
// Try to deduce format
|
|
1607
|
+
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1608
|
+
const isObj =
|
|
1609
|
+
!isHex &&
|
|
1610
|
+
sg !== null &&
|
|
1611
|
+
typeof sg === 'object' &&
|
|
1612
|
+
typeof sg.r === 'bigint' &&
|
|
1613
|
+
typeof sg.s === 'bigint';
|
|
1614
|
+
if (!isHex && !isObj)
|
|
1615
|
+
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1415
1616
|
if (isObj) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
} else {
|
|
1419
|
-
throw new Error('invalid format');
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
if (isHex) {
|
|
1617
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1618
|
+
} else if (isHex) {
|
|
1423
1619
|
// TODO: remove this malleable check
|
|
1424
1620
|
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1425
1621
|
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1426
1622
|
try {
|
|
1427
|
-
|
|
1623
|
+
_sig = Signature.fromDER(sg);
|
|
1428
1624
|
} catch (derError) {
|
|
1429
1625
|
if (!(derError instanceof DER.Err)) throw derError;
|
|
1430
1626
|
}
|
|
1431
|
-
if (!_sig
|
|
1627
|
+
if (!_sig) {
|
|
1628
|
+
try {
|
|
1629
|
+
_sig = Signature.fromCompact(sg);
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
} else {
|
|
1636
|
+
if (format === 'compact' || format === 'der') {
|
|
1637
|
+
if (typeof sg !== 'string' && !isBytes(sg))
|
|
1638
|
+
throw new Error('"der" / "compact" format expects Uint8Array signature');
|
|
1639
|
+
_sig = Signature.fromBytes(ensureBytes('sig', sg), format);
|
|
1640
|
+
} else if (format === 'js') {
|
|
1641
|
+
if (!(sg instanceof Signature)) throw new Error('"js" format expects Signature instance');
|
|
1642
|
+
_sig = sg;
|
|
1643
|
+
} else {
|
|
1644
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1432
1645
|
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (!_sig) return false;
|
|
1649
|
+
try {
|
|
1433
1650
|
P = Point.fromHex(publicKey);
|
|
1434
|
-
|
|
1651
|
+
if (lowS && _sig.hasHighS()) return false;
|
|
1652
|
+
// todo: optional.hash => hash
|
|
1653
|
+
if (prehash) msgHash = hash(msgHash);
|
|
1654
|
+
const { r, s } = _sig;
|
|
1655
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1656
|
+
const is = Fn.inv(s); // s^-1
|
|
1657
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1658
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1659
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1660
|
+
if (R.is0()) return false;
|
|
1661
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1662
|
+
return v === r;
|
|
1663
|
+
} catch (e) {
|
|
1435
1664
|
return false;
|
|
1436
1665
|
}
|
|
1437
|
-
if (!_sig) return false;
|
|
1438
|
-
if (lowS && _sig.hasHighS()) return false;
|
|
1439
|
-
// todo: optional.hash => hash
|
|
1440
|
-
if (prehash) msgHash = ecdsaOpts.hash(msgHash);
|
|
1441
|
-
const { r, s } = _sig;
|
|
1442
|
-
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1443
|
-
const is = Fn.inv(s); // s^-1
|
|
1444
|
-
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1445
|
-
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1446
|
-
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1447
|
-
if (R.is0()) return false;
|
|
1448
|
-
const v = Fn.create(R.x); // v = r.x mod n
|
|
1449
|
-
return v === r;
|
|
1450
1666
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1667
|
+
|
|
1668
|
+
function keygen(seed?: Uint8Array) {
|
|
1669
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
1670
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1453
1673
|
return Object.freeze({
|
|
1674
|
+
keygen,
|
|
1454
1675
|
getPublicKey,
|
|
1455
|
-
getSharedSecret,
|
|
1456
1676
|
sign,
|
|
1457
1677
|
verify,
|
|
1678
|
+
getSharedSecret,
|
|
1458
1679
|
utils,
|
|
1459
1680
|
Point,
|
|
1460
1681
|
Signature,
|
|
1682
|
+
info: { type: 'weierstrass' as const, lengths, publicKeyHasPrefix: true },
|
|
1461
1683
|
});
|
|
1462
1684
|
}
|
|
1463
1685
|
|
|
1686
|
+
// TODO: remove
|
|
1464
1687
|
export type WsPointComposed<T> = {
|
|
1465
1688
|
CURVE: WeierstrassOpts<T>;
|
|
1466
1689
|
curveOpts: WeierstrassExtraOpts<T>;
|
|
1467
1690
|
};
|
|
1691
|
+
// TODO: remove
|
|
1468
1692
|
export type WsComposed = {
|
|
1469
1693
|
CURVE: WeierstrassOpts<bigint>;
|
|
1694
|
+
hash: CHash;
|
|
1470
1695
|
curveOpts: WeierstrassExtraOpts<bigint>;
|
|
1471
1696
|
ecdsaOpts: ECDSAOpts;
|
|
1472
1697
|
};
|
|
1698
|
+
// TODO: remove
|
|
1473
1699
|
function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
|
|
1474
1700
|
const CURVE: WeierstrassOpts<T> = {
|
|
1475
1701
|
a: c.a,
|
|
@@ -1481,14 +1707,19 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
|
|
|
1481
1707
|
Gy: c.Gy,
|
|
1482
1708
|
};
|
|
1483
1709
|
const Fp = c.Fp;
|
|
1484
|
-
|
|
1710
|
+
let allowedLengths = c.allowedPrivateKeyLengths
|
|
1711
|
+
? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
|
|
1712
|
+
: undefined;
|
|
1713
|
+
const Fn = Field(CURVE.n, {
|
|
1714
|
+
BITS: c.nBitLength,
|
|
1715
|
+
allowedLengths: allowedLengths,
|
|
1716
|
+
modOnDecode: c.wrapPrivateKey,
|
|
1717
|
+
});
|
|
1485
1718
|
const curveOpts: WeierstrassExtraOpts<T> = {
|
|
1486
1719
|
Fp,
|
|
1487
1720
|
Fn,
|
|
1488
|
-
allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
|
|
1489
1721
|
allowInfinityPoint: c.allowInfinityPoint,
|
|
1490
1722
|
endo: c.endo,
|
|
1491
|
-
wrapPrivateKey: c.wrapPrivateKey,
|
|
1492
1723
|
isTorsionFree: c.isTorsionFree,
|
|
1493
1724
|
clearCofactor: c.clearCofactor,
|
|
1494
1725
|
fromBytes: c.fromBytes,
|
|
@@ -1499,18 +1730,18 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
|
|
|
1499
1730
|
function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
|
|
1500
1731
|
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1501
1732
|
const ecdsaOpts: ECDSAOpts = {
|
|
1502
|
-
hash: c.hash,
|
|
1503
1733
|
hmac: c.hmac,
|
|
1504
1734
|
randomBytes: c.randomBytes,
|
|
1505
1735
|
lowS: c.lowS,
|
|
1506
1736
|
bits2int: c.bits2int,
|
|
1507
1737
|
bits2int_modN: c.bits2int_modN,
|
|
1508
1738
|
};
|
|
1509
|
-
return { CURVE, curveOpts, ecdsaOpts };
|
|
1739
|
+
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1510
1740
|
}
|
|
1741
|
+
// TODO: remove
|
|
1511
1742
|
function _weierstrass_new_output_to_legacy<T>(
|
|
1512
1743
|
c: CurvePointsType<T>,
|
|
1513
|
-
Point:
|
|
1744
|
+
Point: WeierstrassPointCons<T>
|
|
1514
1745
|
): CurvePointsRes<T> {
|
|
1515
1746
|
const { Fp, Fn } = Point;
|
|
1516
1747
|
// TODO: remove
|
|
@@ -1518,23 +1749,19 @@ function _weierstrass_new_output_to_legacy<T>(
|
|
|
1518
1749
|
return inRange(num, _1n, Fn.ORDER);
|
|
1519
1750
|
}
|
|
1520
1751
|
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1521
|
-
const normPrivateKeyToScalar = _legacyHelperNormPriv(
|
|
1522
|
-
Fn,
|
|
1523
|
-
c.allowedPrivateKeyLengths,
|
|
1524
|
-
c.wrapPrivateKey
|
|
1525
|
-
);
|
|
1526
1752
|
return Object.assign(
|
|
1527
1753
|
{},
|
|
1528
1754
|
{
|
|
1529
1755
|
CURVE: c,
|
|
1530
1756
|
Point: Point,
|
|
1531
1757
|
ProjectivePoint: Point,
|
|
1532
|
-
normPrivateKeyToScalar,
|
|
1758
|
+
normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
|
|
1533
1759
|
weierstrassEquation,
|
|
1534
1760
|
isWithinCurveOrder,
|
|
1535
1761
|
}
|
|
1536
1762
|
);
|
|
1537
1763
|
}
|
|
1764
|
+
// TODO: remove
|
|
1538
1765
|
function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
|
|
1539
1766
|
return Object.assign({}, ecdsa, {
|
|
1540
1767
|
ProjectivePoint: ecdsa.Point,
|
|
@@ -1544,141 +1771,8 @@ function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
|
|
|
1544
1771
|
|
|
1545
1772
|
// _ecdsa_legacy
|
|
1546
1773
|
export function weierstrass(c: CurveType): CurveFn {
|
|
1547
|
-
const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1774
|
+
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1548
1775
|
const Point = weierstrassN(CURVE, curveOpts);
|
|
1549
|
-
const signs = ecdsa(Point,
|
|
1776
|
+
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
1550
1777
|
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1551
1778
|
}
|
|
1552
|
-
|
|
1553
|
-
/**
|
|
1554
|
-
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
1555
|
-
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
1556
|
-
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1557
|
-
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1558
|
-
* @param Fp
|
|
1559
|
-
* @param Z
|
|
1560
|
-
* @returns
|
|
1561
|
-
*/
|
|
1562
|
-
export function SWUFpSqrtRatio<T>(
|
|
1563
|
-
Fp: IField<T>,
|
|
1564
|
-
Z: T
|
|
1565
|
-
): (u: T, v: T) => { isValid: boolean; value: T } {
|
|
1566
|
-
// Generic implementation
|
|
1567
|
-
const q = Fp.ORDER;
|
|
1568
|
-
let l = _0n;
|
|
1569
|
-
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
|
1570
|
-
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1571
|
-
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
1572
|
-
// 2n ** c1 == 2n << (c1-1)
|
|
1573
|
-
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
1574
|
-
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
1575
|
-
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1576
|
-
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1577
|
-
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1578
|
-
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1579
|
-
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1580
|
-
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1581
|
-
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1582
|
-
let tv1 = c6; // 1. tv1 = c6
|
|
1583
|
-
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1584
|
-
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1585
|
-
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1586
|
-
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1587
|
-
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1588
|
-
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1589
|
-
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1590
|
-
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1591
|
-
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1592
|
-
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1593
|
-
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1594
|
-
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1595
|
-
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1596
|
-
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1597
|
-
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1598
|
-
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1599
|
-
for (let i = c1; i > _1n; i--) {
|
|
1600
|
-
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1601
|
-
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1602
|
-
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1603
|
-
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1604
|
-
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1605
|
-
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1606
|
-
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1607
|
-
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1608
|
-
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1609
|
-
}
|
|
1610
|
-
return { isValid: isQR, value: tv3 };
|
|
1611
|
-
};
|
|
1612
|
-
if (Fp.ORDER % _4n === _3n) {
|
|
1613
|
-
// sqrt_ratio_3mod4(u, v)
|
|
1614
|
-
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1615
|
-
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1616
|
-
sqrtRatio = (u: T, v: T) => {
|
|
1617
|
-
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1618
|
-
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1619
|
-
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1620
|
-
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1621
|
-
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1622
|
-
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1623
|
-
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1624
|
-
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1625
|
-
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1626
|
-
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1627
|
-
};
|
|
1628
|
-
}
|
|
1629
|
-
// No curves uses that
|
|
1630
|
-
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
1631
|
-
return sqrtRatio;
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1635
|
-
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1636
|
-
*/
|
|
1637
|
-
export function mapToCurveSimpleSWU<T>(
|
|
1638
|
-
Fp: IField<T>,
|
|
1639
|
-
opts: {
|
|
1640
|
-
A: T;
|
|
1641
|
-
B: T;
|
|
1642
|
-
Z: T;
|
|
1643
|
-
}
|
|
1644
|
-
): (u: T) => { x: T; y: T } {
|
|
1645
|
-
validateField(Fp);
|
|
1646
|
-
const { A, B, Z } = opts;
|
|
1647
|
-
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1648
|
-
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1649
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1650
|
-
if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
|
|
1651
|
-
// Input: u, an element of F.
|
|
1652
|
-
// Output: (x, y), a point on E.
|
|
1653
|
-
return (u: T): { x: T; y: T } => {
|
|
1654
|
-
// prettier-ignore
|
|
1655
|
-
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1656
|
-
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1657
|
-
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1658
|
-
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1659
|
-
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1660
|
-
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1661
|
-
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1662
|
-
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1663
|
-
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1664
|
-
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1665
|
-
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1666
|
-
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1667
|
-
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1668
|
-
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1669
|
-
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1670
|
-
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1671
|
-
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1672
|
-
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1673
|
-
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1674
|
-
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1675
|
-
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1676
|
-
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1677
|
-
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1678
|
-
const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1679
|
-
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1680
|
-
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1681
|
-
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1682
|
-
return { x, y };
|
|
1683
|
-
};
|
|
1684
|
-
}
|