@noble/curves 1.9.4 → 1.9.6
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/abstract/bls.d.ts +2 -2
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/curve.d.ts +47 -46
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +9 -6
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +41 -33
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +166 -170
- package/abstract/edwards.js.map +1 -1
- package/abstract/modular.d.ts +1 -1
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +4 -4
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +2 -6
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +13 -10
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +9 -7
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +569 -357
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +162 -92
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +394 -336
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +9 -42
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts.map +1 -1
- package/bn254.js +2 -34
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +15 -15
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +51 -48
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +16 -17
- package/ed448.d.ts.map +1 -1
- package/ed448.js +67 -48
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.d.ts +2 -2
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/curve.d.ts +47 -46
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +9 -6
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +41 -33
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +167 -171
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/modular.d.ts +1 -1
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +4 -4
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +2 -6
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +14 -11
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +9 -7
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +570 -358
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +162 -92
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +395 -338
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +10 -43
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +3 -35
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +15 -15
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +51 -48
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +16 -17
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +68 -49
- package/esm/ed448.js.map +1 -1
- package/esm/misc.js +2 -2
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +6 -0
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +6 -0
- package/esm/nist.js.map +1 -1
- package/esm/secp256k1.d.ts +2 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +34 -35
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +14 -0
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +43 -0
- package/esm/utils.js.map +1 -1
- package/misc.js +2 -2
- package/misc.js.map +1 -1
- package/nist.d.ts +6 -0
- package/nist.d.ts.map +1 -1
- package/nist.js +7 -1
- package/nist.js.map +1 -1
- package/package.json +2 -2
- package/secp256k1.d.ts +2 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +33 -34
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +2 -2
- package/src/abstract/curve.ts +131 -68
- package/src/abstract/edwards.ts +210 -219
- package/src/abstract/modular.ts +4 -4
- package/src/abstract/montgomery.ts +16 -16
- package/src/abstract/tower.ts +630 -382
- package/src/abstract/weierstrass.ts +587 -484
- package/src/bls12-381.ts +10 -42
- package/src/bn254.ts +3 -34
- package/src/ed25519.ts +62 -58
- package/src/ed448.ts +74 -77
- package/src/misc.ts +2 -2
- package/src/nist.ts +7 -0
- package/src/secp256k1.ts +35 -36
- package/src/utils.ts +48 -0
- package/utils.d.ts +14 -0
- package/utils.d.ts.map +1 -1
- package/utils.js +47 -0
- package/utils.js.map +1 -1
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
* @module
|
|
26
26
|
*/
|
|
27
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
28
|
-
import { hmac } from '@noble/hashes/hmac.js';
|
|
28
|
+
import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
|
|
29
29
|
import { ahash } from '@noble/hashes/utils';
|
|
30
30
|
import {
|
|
31
31
|
_validateObject,
|
|
32
|
-
abool,
|
|
33
|
-
abytes,
|
|
32
|
+
_abool2 as abool,
|
|
33
|
+
_abytes2 as abytes,
|
|
34
34
|
aInRange,
|
|
35
35
|
bitLen,
|
|
36
36
|
bitMask,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
isBytes,
|
|
45
45
|
memoized,
|
|
46
46
|
numberToHexUnpadded,
|
|
47
|
-
randomBytes,
|
|
47
|
+
randomBytes as randomBytesWeb,
|
|
48
48
|
type CHash,
|
|
49
49
|
type Hex,
|
|
50
50
|
type PrivKey,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
wNAF,
|
|
59
59
|
type AffinePoint,
|
|
60
60
|
type BasicCurve,
|
|
61
|
-
type
|
|
61
|
+
type CurveLengths,
|
|
62
62
|
type CurvePoint,
|
|
63
63
|
type CurvePointCons,
|
|
64
64
|
} from './curve.ts';
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
FpInvertBatch,
|
|
68
68
|
getMinHashLength,
|
|
69
69
|
mapHashToField,
|
|
70
|
+
nLength,
|
|
70
71
|
validateField,
|
|
71
72
|
type IField,
|
|
72
73
|
type NLength,
|
|
@@ -104,21 +105,6 @@ export type EndomorphismOpts = {
|
|
|
104
105
|
basises?: EndoBasis;
|
|
105
106
|
splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
106
107
|
};
|
|
107
|
-
export type BasicWCurve<T> = BasicCurve<T> & {
|
|
108
|
-
// Params: a, b
|
|
109
|
-
a: T;
|
|
110
|
-
b: T;
|
|
111
|
-
|
|
112
|
-
// Optional params
|
|
113
|
-
allowedPrivateKeyLengths?: readonly number[]; // for P521
|
|
114
|
-
wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
115
|
-
endo?: EndomorphismOpts;
|
|
116
|
-
// When a cofactor != 1, there can be an effective methods to:
|
|
117
|
-
// 1. Determine whether a point is torsion-free
|
|
118
|
-
isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
119
|
-
// 2. Clear torsion component
|
|
120
|
-
clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
121
|
-
};
|
|
122
108
|
|
|
123
109
|
// 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
110
|
const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
|
|
@@ -152,23 +138,41 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
|
|
|
152
138
|
return { k1neg, k1, k2neg, k2 };
|
|
153
139
|
}
|
|
154
140
|
|
|
155
|
-
export type ECDSASigFormat = 'compact' | 'der';
|
|
156
|
-
export type
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
prehash
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
prehash
|
|
166
|
-
|
|
167
|
-
|
|
141
|
+
export type ECDSASigFormat = 'compact' | 'recovered' | 'der';
|
|
142
|
+
export type ECDSARecoverOpts = {
|
|
143
|
+
prehash?: boolean;
|
|
144
|
+
};
|
|
145
|
+
export type ECDSAVerifyOpts = {
|
|
146
|
+
prehash?: boolean;
|
|
147
|
+
lowS?: boolean;
|
|
148
|
+
format?: ECDSASigFormat;
|
|
149
|
+
};
|
|
150
|
+
export type ECDSASignOpts = {
|
|
151
|
+
prehash?: boolean;
|
|
152
|
+
lowS?: boolean;
|
|
153
|
+
format?: ECDSASigFormat;
|
|
154
|
+
extraEntropy?: Uint8Array | boolean;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
function validateSigFormat(format: string): ECDSASigFormat {
|
|
158
|
+
if (!['compact', 'recovered', 'der'].includes(format))
|
|
159
|
+
throw new Error('Signature format must be "compact", "recovered", or "der"');
|
|
160
|
+
return format as ECDSASigFormat;
|
|
161
|
+
}
|
|
168
162
|
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
function validateSigOpts<T extends ECDSASignOpts, D extends Required<ECDSASignOpts>>(
|
|
164
|
+
opts: T,
|
|
165
|
+
def: D
|
|
166
|
+
): Required<ECDSASignOpts> {
|
|
167
|
+
const optsn: ECDSASignOpts = {};
|
|
168
|
+
for (let optName of Object.keys(def)) {
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
171
|
+
}
|
|
172
|
+
abool(optsn.lowS!, 'lowS');
|
|
173
|
+
abool(optsn.prehash!, 'prehash');
|
|
174
|
+
if (optsn.format !== undefined) validateSigFormat(optsn.format);
|
|
175
|
+
return optsn as Required<ECDSASignOpts>;
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
/** Instance methods for 3D XYZ projective points. */
|
|
@@ -187,11 +191,11 @@ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>>
|
|
|
187
191
|
toBytes(isCompressed?: boolean): Uint8Array;
|
|
188
192
|
toHex(isCompressed?: boolean): string;
|
|
189
193
|
|
|
190
|
-
/** @deprecated use
|
|
194
|
+
/** @deprecated use `.X` */
|
|
191
195
|
readonly px: T;
|
|
192
|
-
/** @deprecated use
|
|
196
|
+
/** @deprecated use `.Y` */
|
|
193
197
|
readonly py: T;
|
|
194
|
-
/** @deprecated use
|
|
198
|
+
/** @deprecated use `.Z` */
|
|
195
199
|
readonly pz: T;
|
|
196
200
|
/** @deprecated use `toBytes` */
|
|
197
201
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
@@ -208,9 +212,10 @@ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>>
|
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
/** Static methods for 3D XYZ projective points. */
|
|
211
|
-
export interface WeierstrassPointCons<T> extends CurvePointCons<
|
|
215
|
+
export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint<T>> {
|
|
212
216
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
213
217
|
new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
|
|
218
|
+
CURVE(): WeierstrassOpts<T>;
|
|
214
219
|
/** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
|
|
215
220
|
fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
|
|
216
221
|
/** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
|
|
@@ -219,48 +224,6 @@ export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPo
|
|
|
219
224
|
msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
|
|
220
225
|
}
|
|
221
226
|
|
|
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
|
|
228
|
-
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
229
|
-
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
|
230
|
-
toBytes?: (
|
|
231
|
-
c: WeierstrassPointCons<T>,
|
|
232
|
-
point: WeierstrassPoint<T>,
|
|
233
|
-
isCompressed: boolean
|
|
234
|
-
) => Uint8Array;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// LegacyWeierstrassOpts
|
|
238
|
-
export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
|
|
239
|
-
|
|
240
|
-
// LegacyWeierstrass
|
|
241
|
-
export type CurvePointsRes<T> = {
|
|
242
|
-
Point: WeierstrassPointCons<T>;
|
|
243
|
-
|
|
244
|
-
/** @deprecated the property will be removed in next release */
|
|
245
|
-
CURVE: CurvePointsType<T>;
|
|
246
|
-
/** @deprecated use `Point` */
|
|
247
|
-
ProjectivePoint: WeierstrassPointCons<T>;
|
|
248
|
-
/** @deprecated use `Point.Fn.fromBytes(privateKey)` */
|
|
249
|
-
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
250
|
-
/** @deprecated */
|
|
251
|
-
weierstrassEquation: (x: T) => T;
|
|
252
|
-
/** @deprecated use `Point.Fn.isValidNot0(num)` */
|
|
253
|
-
isWithinCurveOrder: (num: bigint) => boolean;
|
|
254
|
-
};
|
|
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
227
|
/**
|
|
265
228
|
* Weierstrass curve options.
|
|
266
229
|
*
|
|
@@ -303,6 +266,11 @@ export type WeierstrassExtraOpts<T> = Partial<{
|
|
|
303
266
|
|
|
304
267
|
/**
|
|
305
268
|
* Options for ECDSA signatures over a Weierstrass curve.
|
|
269
|
+
*
|
|
270
|
+
* * lowS: (default: true) whether produced / verified signatures occupy low half of ecdsaOpts.p. Prevents malleability.
|
|
271
|
+
* * hmac: (default: noble-hashes hmac) function, would be used to init hmac-drbg for k generation.
|
|
272
|
+
* * randomBytes: (default: webcrypto os-level CSPRNG) custom method for fetching secure randomness.
|
|
273
|
+
* * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves
|
|
306
274
|
*/
|
|
307
275
|
export type ECDSAOpts = Partial<{
|
|
308
276
|
lowS: boolean;
|
|
@@ -312,20 +280,19 @@ export type ECDSAOpts = Partial<{
|
|
|
312
280
|
bits2int_modN: (bytes: Uint8Array) => bigint;
|
|
313
281
|
}>;
|
|
314
282
|
|
|
315
|
-
/**
|
|
316
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Elliptic Curve Diffie-Hellman interface.
|
|
285
|
+
* Provides keygen, secret-to-public conversion, calculating shared secrets.
|
|
286
|
+
*/
|
|
287
|
+
export interface ECDH {
|
|
317
288
|
keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
|
|
318
289
|
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
290
|
getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
322
291
|
Point: WeierstrassPointCons<bigint>;
|
|
323
|
-
Signature: ECDSASignatureCons;
|
|
324
292
|
utils: {
|
|
325
293
|
isValidSecretKey: (secretKey: PrivKey) => boolean;
|
|
326
294
|
isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
|
|
327
295
|
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
|
|
328
|
-
|
|
329
296
|
/** @deprecated use `randomSecretKey` */
|
|
330
297
|
randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
|
|
331
298
|
/** @deprecated use `isValidSecretKey` */
|
|
@@ -335,7 +302,23 @@ export interface ECDSA {
|
|
|
335
302
|
/** @deprecated use `point.precompute()` */
|
|
336
303
|
precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
|
|
337
304
|
};
|
|
338
|
-
|
|
305
|
+
lengths: CurveLengths;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* ECDSA interface.
|
|
310
|
+
* Only supported for prime fields, not Fp2 (extension fields).
|
|
311
|
+
*/
|
|
312
|
+
export interface ECDSA extends ECDH {
|
|
313
|
+
sign: (message: Hex, secretKey: PrivKey, opts?: ECDSASignOpts) => ECDSASigRecovered;
|
|
314
|
+
verify: (
|
|
315
|
+
signature: Uint8Array,
|
|
316
|
+
message: Uint8Array,
|
|
317
|
+
publicKey: Uint8Array,
|
|
318
|
+
opts?: ECDSAVerifyOpts
|
|
319
|
+
) => boolean;
|
|
320
|
+
recoverPublicKey(signature: Uint8Array, message: Uint8Array, opts?: ECDSARecoverOpts): Uint8Array;
|
|
321
|
+
Signature: ECDSASignatureCons;
|
|
339
322
|
}
|
|
340
323
|
export class DERErr extends Error {
|
|
341
324
|
constructor(m = '') {
|
|
@@ -459,19 +442,6 @@ export const DER: IDER = {
|
|
|
459
442
|
// prettier-ignore
|
|
460
443
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
461
444
|
|
|
462
|
-
// TODO: remove
|
|
463
|
-
export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
|
|
464
|
-
/**
|
|
465
|
-
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
466
|
-
* @returns y²
|
|
467
|
-
*/
|
|
468
|
-
function weierstrassEquation(x: T): T {
|
|
469
|
-
const x2 = Fp.sqr(x); // x * x
|
|
470
|
-
const x3 = Fp.mul(x2, x); // x² * x
|
|
471
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
472
|
-
}
|
|
473
|
-
return weierstrassEquation;
|
|
474
|
-
}
|
|
475
445
|
export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
|
|
476
446
|
const { BYTES: expected } = Fn;
|
|
477
447
|
let num: bigint;
|
|
@@ -489,14 +459,33 @@ export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
|
|
|
489
459
|
return num;
|
|
490
460
|
}
|
|
491
461
|
|
|
462
|
+
/**
|
|
463
|
+
* Creates weierstrass Point constructor, based on specified curve options.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
```js
|
|
467
|
+
const opts = {
|
|
468
|
+
p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
|
469
|
+
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
|
470
|
+
h: BigInt(1),
|
|
471
|
+
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
|
472
|
+
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
|
473
|
+
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
|
474
|
+
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
475
|
+
};
|
|
476
|
+
const p256_Point = weierstrass(opts);
|
|
477
|
+
```
|
|
478
|
+
*/
|
|
492
479
|
export function weierstrassN<T>(
|
|
493
|
-
|
|
494
|
-
|
|
480
|
+
params: WeierstrassOpts<T>,
|
|
481
|
+
extraOpts: WeierstrassExtraOpts<T> = {}
|
|
495
482
|
): WeierstrassPointCons<T> {
|
|
496
|
-
const
|
|
483
|
+
const validated = _createCurveFields('weierstrass', params, extraOpts);
|
|
484
|
+
const { Fp, Fn } = validated;
|
|
485
|
+
let CURVE = validated.CURVE as WeierstrassOpts<T>;
|
|
497
486
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
498
487
|
_validateObject(
|
|
499
|
-
|
|
488
|
+
extraOpts,
|
|
500
489
|
{},
|
|
501
490
|
{
|
|
502
491
|
allowInfinityPoint: 'boolean',
|
|
@@ -509,7 +498,7 @@ export function weierstrassN<T>(
|
|
|
509
498
|
}
|
|
510
499
|
);
|
|
511
500
|
|
|
512
|
-
const { endo } =
|
|
501
|
+
const { endo } = extraOpts;
|
|
513
502
|
if (endo) {
|
|
514
503
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
515
504
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
@@ -517,6 +506,8 @@ export function weierstrassN<T>(
|
|
|
517
506
|
}
|
|
518
507
|
}
|
|
519
508
|
|
|
509
|
+
const lengths = getWLengths(Fp, Fn);
|
|
510
|
+
|
|
520
511
|
function assertCompressionIsSupported() {
|
|
521
512
|
if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
522
513
|
}
|
|
@@ -529,7 +520,7 @@ export function weierstrassN<T>(
|
|
|
529
520
|
): Uint8Array {
|
|
530
521
|
const { x, y } = point.toAffine();
|
|
531
522
|
const bx = Fp.toBytes(x);
|
|
532
|
-
abool(
|
|
523
|
+
abool(isCompressed, 'isCompressed');
|
|
533
524
|
if (isCompressed) {
|
|
534
525
|
assertCompressionIsSupported();
|
|
535
526
|
const hasEvenY = !Fp.isOdd!(y);
|
|
@@ -539,15 +530,13 @@ export function weierstrassN<T>(
|
|
|
539
530
|
}
|
|
540
531
|
}
|
|
541
532
|
function pointFromBytes(bytes: Uint8Array) {
|
|
542
|
-
abytes(bytes);
|
|
543
|
-
const
|
|
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
|
|
533
|
+
abytes(bytes, undefined, 'Point');
|
|
534
|
+
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
546
535
|
const length = bytes.length;
|
|
547
536
|
const head = bytes[0];
|
|
548
537
|
const tail = bytes.subarray(1);
|
|
549
538
|
// No actual validation is done here: use .assertValidity()
|
|
550
|
-
if (length ===
|
|
539
|
+
if (length === comp && (head === 0x02 || head === 0x03)) {
|
|
551
540
|
const x = Fp.fromBytes(tail);
|
|
552
541
|
if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
|
|
553
542
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
@@ -563,22 +552,27 @@ export function weierstrassN<T>(
|
|
|
563
552
|
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
564
553
|
if (isHeadOdd !== isYOdd) y = Fp.neg(y);
|
|
565
554
|
return { x, y };
|
|
566
|
-
} else if (length ===
|
|
555
|
+
} else if (length === uncomp && head === 0x04) {
|
|
567
556
|
// TODO: more checks
|
|
568
|
-
const
|
|
569
|
-
const
|
|
557
|
+
const L = Fp.BYTES;
|
|
558
|
+
const x = Fp.fromBytes(tail.subarray(0, L));
|
|
559
|
+
const y = Fp.fromBytes(tail.subarray(L, L * 2));
|
|
570
560
|
if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
|
|
571
561
|
return { x, y };
|
|
572
562
|
} else {
|
|
573
563
|
throw new Error(
|
|
574
|
-
`bad point: got length ${length}, expected compressed=${
|
|
564
|
+
`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`
|
|
575
565
|
);
|
|
576
566
|
}
|
|
577
567
|
}
|
|
578
568
|
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
|
|
569
|
+
const encodePoint = extraOpts.toBytes || pointToBytes;
|
|
570
|
+
const decodePoint = extraOpts.fromBytes || pointFromBytes;
|
|
571
|
+
function weierstrassEquation(x: T): T {
|
|
572
|
+
const x2 = Fp.sqr(x); // x * x
|
|
573
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
574
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
|
|
575
|
+
}
|
|
582
576
|
|
|
583
577
|
// TODO: move top-level
|
|
584
578
|
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
@@ -640,7 +634,7 @@ export function weierstrassN<T>(
|
|
|
640
634
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
641
635
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
642
636
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
643
|
-
if (
|
|
637
|
+
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
|
|
644
638
|
throw new Error('bad point: ZERO');
|
|
645
639
|
}
|
|
646
640
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
@@ -674,8 +668,9 @@ export function weierstrassN<T>(
|
|
|
674
668
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
675
669
|
// zero / infinity / identity point
|
|
676
670
|
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
677
|
-
//
|
|
671
|
+
// math field
|
|
678
672
|
static readonly Fp = Fp;
|
|
673
|
+
// scalar field
|
|
679
674
|
static readonly Fn = Fn;
|
|
680
675
|
|
|
681
676
|
readonly X: T;
|
|
@@ -690,6 +685,10 @@ export function weierstrassN<T>(
|
|
|
690
685
|
Object.freeze(this);
|
|
691
686
|
}
|
|
692
687
|
|
|
688
|
+
static CURVE(): WeierstrassOpts<T> {
|
|
689
|
+
return CURVE;
|
|
690
|
+
}
|
|
691
|
+
|
|
693
692
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
694
693
|
static fromAffine(p: AffinePoint<T>): Point {
|
|
695
694
|
const { x, y } = p || {};
|
|
@@ -700,50 +699,20 @@ export function weierstrassN<T>(
|
|
|
700
699
|
return new Point(x, y, Fp.ONE);
|
|
701
700
|
}
|
|
702
701
|
|
|
703
|
-
get x(): T {
|
|
704
|
-
return this.toAffine().x;
|
|
705
|
-
}
|
|
706
|
-
get y(): T {
|
|
707
|
-
return this.toAffine().y;
|
|
708
|
-
}
|
|
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
|
-
}
|
|
720
|
-
static normalizeZ(points: Point[]): Point[] {
|
|
721
|
-
return normalizeZ(Point, points);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
702
|
static fromBytes(bytes: Uint8Array): Point {
|
|
725
|
-
abytes(bytes);
|
|
726
|
-
return Point.fromHex(bytes);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/** Converts hash string or Uint8Array to Point. */
|
|
730
|
-
static fromHex(hex: Hex): Point {
|
|
731
|
-
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
703
|
+
const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, 'point')));
|
|
732
704
|
P.assertValidity();
|
|
733
705
|
return P;
|
|
734
706
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
static fromPrivateKey(privateKey: PrivKey) {
|
|
738
|
-
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
707
|
+
static fromHex(hex: Hex): Point {
|
|
708
|
+
return Point.fromBytes(ensureBytes('pointHex', hex));
|
|
739
709
|
}
|
|
740
710
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
return pippenger(Point, Fn, points, scalars);
|
|
711
|
+
get x(): T {
|
|
712
|
+
return this.toAffine().x;
|
|
744
713
|
}
|
|
745
|
-
|
|
746
|
-
this.
|
|
714
|
+
get y(): T {
|
|
715
|
+
return this.toAffine().y;
|
|
747
716
|
}
|
|
748
717
|
|
|
749
718
|
/**
|
|
@@ -900,7 +869,7 @@ export function weierstrassN<T>(
|
|
|
900
869
|
* @returns New point
|
|
901
870
|
*/
|
|
902
871
|
multiply(scalar: bigint): Point {
|
|
903
|
-
const { endo } =
|
|
872
|
+
const { endo } = extraOpts;
|
|
904
873
|
if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
905
874
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
906
875
|
const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
@@ -926,8 +895,8 @@ export function weierstrassN<T>(
|
|
|
926
895
|
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
927
896
|
*/
|
|
928
897
|
multiplyUnsafe(sc: bigint): Point {
|
|
929
|
-
const { endo } =
|
|
930
|
-
const p = this;
|
|
898
|
+
const { endo } = extraOpts;
|
|
899
|
+
const p = this as Point;
|
|
931
900
|
if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
932
901
|
if (sc === _0n || p.is0()) return Point.ZERO;
|
|
933
902
|
if (sc === _1n) return p; // fast-path
|
|
@@ -959,14 +928,14 @@ export function weierstrassN<T>(
|
|
|
959
928
|
* Always torsion-free for cofactor=1 curves.
|
|
960
929
|
*/
|
|
961
930
|
isTorsionFree(): boolean {
|
|
962
|
-
const { isTorsionFree } =
|
|
931
|
+
const { isTorsionFree } = extraOpts;
|
|
963
932
|
if (cofactor === _1n) return true;
|
|
964
933
|
if (isTorsionFree) return isTorsionFree(Point, this);
|
|
965
934
|
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
966
935
|
}
|
|
967
936
|
|
|
968
937
|
clearCofactor(): Point {
|
|
969
|
-
const { clearCofactor } =
|
|
938
|
+
const { clearCofactor } = extraOpts;
|
|
970
939
|
if (cofactor === _1n) return this; // Fast-path
|
|
971
940
|
if (clearCofactor) return clearCofactor(Point, this) as Point;
|
|
972
941
|
return this.multiplyUnsafe(cofactor);
|
|
@@ -978,14 +947,9 @@ export function weierstrassN<T>(
|
|
|
978
947
|
}
|
|
979
948
|
|
|
980
949
|
toBytes(isCompressed = true): Uint8Array {
|
|
981
|
-
abool(
|
|
950
|
+
abool(isCompressed, 'isCompressed');
|
|
982
951
|
this.assertValidity();
|
|
983
|
-
return
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/** @deprecated use `toBytes` */
|
|
987
|
-
toRawBytes(isCompressed = true): Uint8Array {
|
|
988
|
-
return this.toBytes(isCompressed);
|
|
952
|
+
return encodePoint(Point, this, isCompressed);
|
|
989
953
|
}
|
|
990
954
|
|
|
991
955
|
toHex(isCompressed = true): string {
|
|
@@ -995,35 +959,55 @@ export function weierstrassN<T>(
|
|
|
995
959
|
toString() {
|
|
996
960
|
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
997
961
|
}
|
|
962
|
+
|
|
963
|
+
// TODO: remove
|
|
964
|
+
get px(): T {
|
|
965
|
+
return this.X;
|
|
966
|
+
}
|
|
967
|
+
get py(): T {
|
|
968
|
+
return this.X;
|
|
969
|
+
}
|
|
970
|
+
get pz(): T {
|
|
971
|
+
return this.Z;
|
|
972
|
+
}
|
|
973
|
+
toRawBytes(isCompressed = true): Uint8Array {
|
|
974
|
+
return this.toBytes(isCompressed);
|
|
975
|
+
}
|
|
976
|
+
_setWindowSize(windowSize: number) {
|
|
977
|
+
this.precompute(windowSize);
|
|
978
|
+
}
|
|
979
|
+
static normalizeZ(points: Point[]): Point[] {
|
|
980
|
+
return normalizeZ(Point, points);
|
|
981
|
+
}
|
|
982
|
+
static msm(points: Point[], scalars: bigint[]): Point {
|
|
983
|
+
return pippenger(Point, Fn, points, scalars);
|
|
984
|
+
}
|
|
985
|
+
static fromPrivateKey(privateKey: PrivKey) {
|
|
986
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
987
|
+
}
|
|
998
988
|
}
|
|
999
989
|
const bits = Fn.BITS;
|
|
1000
|
-
const wnaf = new wNAF(Point,
|
|
990
|
+
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
991
|
+
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1001
992
|
return Point;
|
|
1002
993
|
}
|
|
1003
994
|
|
|
1004
|
-
|
|
1005
|
-
// TODO: remove
|
|
1006
|
-
/** @deprecated use `weierstrass` in newer releases */
|
|
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);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// Instance
|
|
995
|
+
/** Methods of ECDSA signature instance. */
|
|
1014
996
|
export interface ECDSASignature {
|
|
1015
997
|
readonly r: bigint;
|
|
1016
998
|
readonly s: bigint;
|
|
1017
999
|
readonly recovery?: number;
|
|
1018
1000
|
addRecoveryBit(recovery: number): ECDSASigRecovered;
|
|
1019
1001
|
hasHighS(): boolean;
|
|
1020
|
-
normalizeS(): ECDSASignature;
|
|
1021
|
-
recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
|
|
1022
1002
|
toBytes(format?: string): Uint8Array;
|
|
1023
1003
|
toHex(format?: string): string;
|
|
1024
1004
|
|
|
1025
1005
|
/** @deprecated */
|
|
1026
1006
|
assertValidity(): void;
|
|
1007
|
+
/** @deprecated */
|
|
1008
|
+
normalizeS(): ECDSASignature;
|
|
1009
|
+
/** @deprecated use standalone method `curve.recoverPublicKey(sig.toBytes('recovered'), msg)` */
|
|
1010
|
+
recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
|
|
1027
1011
|
/** @deprecated use `.toBytes('compact')` */
|
|
1028
1012
|
toCompactRawBytes(): Uint8Array;
|
|
1029
1013
|
/** @deprecated use `.toBytes('compact')` */
|
|
@@ -1033,12 +1017,10 @@ export interface ECDSASignature {
|
|
|
1033
1017
|
/** @deprecated use `.toBytes('der')` */
|
|
1034
1018
|
toDERHex(): string;
|
|
1035
1019
|
}
|
|
1036
|
-
export type SignatureType = ECDSASignature;
|
|
1037
1020
|
export type ECDSASigRecovered = ECDSASignature & {
|
|
1038
1021
|
readonly recovery: number;
|
|
1039
1022
|
};
|
|
1040
|
-
|
|
1041
|
-
// Static methods
|
|
1023
|
+
/** Methods of ECDSA signature constructor. */
|
|
1042
1024
|
export type ECDSASignatureCons = {
|
|
1043
1025
|
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
|
|
1044
1026
|
fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
|
|
@@ -1049,42 +1031,12 @@ export type ECDSASignatureCons = {
|
|
|
1049
1031
|
/** @deprecated use `.fromBytes(bytes, 'der')` */
|
|
1050
1032
|
fromDER(hex: Hex): ECDSASignature;
|
|
1051
1033
|
};
|
|
1052
|
-
export type SignatureLike = { r: bigint; s: bigint };
|
|
1053
|
-
// TODO: remove
|
|
1054
|
-
export type PubKey = Hex | WeierstrassPoint<bigint>;
|
|
1055
|
-
|
|
1056
|
-
// TODO: remove
|
|
1057
|
-
export type CurveType = BasicWCurve<bigint> & {
|
|
1058
|
-
hash: CHash; // CHash not FHash because we need outputLen for DRBG
|
|
1059
|
-
hmac?: HmacFnSync;
|
|
1060
|
-
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
1061
|
-
lowS?: boolean;
|
|
1062
|
-
bits2int?: (bytes: Uint8Array) => bigint;
|
|
1063
|
-
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
|
1064
|
-
};
|
|
1065
1034
|
|
|
1066
1035
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
1067
1036
|
function pprefix(hasEvenY: boolean): Uint8Array {
|
|
1068
1037
|
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
1069
1038
|
}
|
|
1070
1039
|
|
|
1071
|
-
// TODO: remove
|
|
1072
|
-
export type CurveFn = {
|
|
1073
|
-
/** @deprecated the property will be removed in next release */
|
|
1074
|
-
CURVE: CurvePointsType<bigint>;
|
|
1075
|
-
keygen: ECDSA['keygen'];
|
|
1076
|
-
getPublicKey: ECDSA['getPublicKey'];
|
|
1077
|
-
getSharedSecret: ECDSA['getSharedSecret'];
|
|
1078
|
-
sign: ECDSA['sign'];
|
|
1079
|
-
verify: ECDSA['verify'];
|
|
1080
|
-
Point: WeierstrassPointCons<bigint>;
|
|
1081
|
-
/** @deprecated use `Point` */
|
|
1082
|
-
ProjectivePoint: WeierstrassPointCons<bigint>;
|
|
1083
|
-
Signature: ECDSASignatureCons;
|
|
1084
|
-
utils: ECDSA['utils'];
|
|
1085
|
-
info: CurveInfo;
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
1040
|
/**
|
|
1089
1041
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
1090
1042
|
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
@@ -1218,87 +1170,211 @@ export function mapToCurveSimpleSWU<T>(
|
|
|
1218
1170
|
};
|
|
1219
1171
|
}
|
|
1220
1172
|
|
|
1173
|
+
function getWLengths<T>(Fp: IField<T>, Fn: IField<bigint>) {
|
|
1174
|
+
return {
|
|
1175
|
+
secretKey: Fn.BYTES,
|
|
1176
|
+
publicKey: 1 + Fp.BYTES,
|
|
1177
|
+
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|
1178
|
+
publicKeyHasPrefix: true,
|
|
1179
|
+
signature: 2 * Fn.BYTES,
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1221
1183
|
/**
|
|
1222
|
-
*
|
|
1184
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
1185
|
+
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
1223
1186
|
*/
|
|
1224
|
-
export function
|
|
1187
|
+
export function ecdh(
|
|
1225
1188
|
Point: WeierstrassPointCons<bigint>,
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
ecdsaOpts,
|
|
1232
|
-
{},
|
|
1233
|
-
{
|
|
1234
|
-
hmac: 'function',
|
|
1235
|
-
lowS: 'boolean',
|
|
1236
|
-
randomBytes: 'function',
|
|
1237
|
-
bits2int: 'function',
|
|
1238
|
-
bits2int_modN: 'function',
|
|
1239
|
-
}
|
|
1240
|
-
);
|
|
1241
|
-
|
|
1242
|
-
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
1243
|
-
const hmac_: HmacFnSync =
|
|
1244
|
-
ecdsaOpts.hmac ||
|
|
1245
|
-
(((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1246
|
-
|
|
1247
|
-
const { Fp, Fn } = Point;
|
|
1248
|
-
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
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
|
-
};
|
|
1189
|
+
ecdhOpts: { randomBytes?: (bytesLength?: number) => Uint8Array } = {}
|
|
1190
|
+
): ECDH {
|
|
1191
|
+
const { Fn } = Point;
|
|
1192
|
+
const randomBytes_ = ecdhOpts.randomBytes || randomBytesWeb;
|
|
1193
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
|
|
1258
1194
|
|
|
1259
|
-
function
|
|
1260
|
-
|
|
1261
|
-
|
|
1195
|
+
function isValidSecretKey(secretKey: PrivKey) {
|
|
1196
|
+
try {
|
|
1197
|
+
return !!_normFnElement(Fn, secretKey);
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1262
1201
|
}
|
|
1263
1202
|
|
|
1264
|
-
function
|
|
1265
|
-
|
|
1203
|
+
function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
|
|
1204
|
+
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
1205
|
+
try {
|
|
1206
|
+
const l = publicKey.length;
|
|
1207
|
+
if (isCompressed === true && l !== comp) return false;
|
|
1208
|
+
if (isCompressed === false && l !== publicKeyUncompressed) return false;
|
|
1209
|
+
return !!Point.fromBytes(publicKey);
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1266
1213
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Produces cryptographically secure secret key from random of size
|
|
1217
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1218
|
+
*/
|
|
1219
|
+
function randomSecretKey(seed = randomBytes_(lengths.seed)): Uint8Array {
|
|
1220
|
+
return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1225
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
1226
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1227
|
+
*/
|
|
1228
|
+
function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
|
|
1229
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function keygen(seed?: Uint8Array) {
|
|
1233
|
+
const secretKey = randomSecretKey(seed);
|
|
1234
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1239
|
+
*/
|
|
1240
|
+
function isProbPub(item: PrivKey | PubKey): boolean | undefined {
|
|
1241
|
+
if (typeof item === 'bigint') return false;
|
|
1242
|
+
if (item instanceof Point) return true;
|
|
1243
|
+
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
1244
|
+
if (Fn.allowedLengths || secretKey === publicKey) return undefined;
|
|
1245
|
+
const l = ensureBytes('key', item).length;
|
|
1246
|
+
return l === publicKey || l === publicKeyUncompressed;
|
|
1270
1247
|
}
|
|
1271
1248
|
|
|
1272
1249
|
/**
|
|
1273
|
-
*
|
|
1250
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1251
|
+
* Computes shared public key from secret key A and public key B.
|
|
1252
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1253
|
+
* Does NOT hash the result.
|
|
1254
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
1255
|
+
* @returns shared public key
|
|
1256
|
+
*/
|
|
1257
|
+
function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
|
|
1258
|
+
if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
|
|
1259
|
+
if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
|
|
1260
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1261
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1262
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const utils = {
|
|
1266
|
+
isValidSecretKey,
|
|
1267
|
+
isValidPublicKey,
|
|
1268
|
+
randomSecretKey,
|
|
1269
|
+
|
|
1270
|
+
// TODO: remove
|
|
1271
|
+
isValidPrivateKey: isValidSecretKey,
|
|
1272
|
+
randomPrivateKey: randomSecretKey,
|
|
1273
|
+
normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
|
|
1274
|
+
precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
|
|
1275
|
+
return point.precompute(windowSize, false);
|
|
1276
|
+
},
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
/**
|
|
1283
|
+
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
1284
|
+
* We need `hash` for 2 features:
|
|
1285
|
+
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
1286
|
+
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
1287
|
+
*
|
|
1288
|
+
* ECDSAOpts are only rarely needed.
|
|
1289
|
+
*
|
|
1290
|
+
* @example
|
|
1291
|
+
* ```js
|
|
1292
|
+
* const p256_Point = weierstrass(...);
|
|
1293
|
+
* const p256_sha256 = ecdsa(p256_Point, sha256);
|
|
1294
|
+
* const p256_sha224 = ecdsa(p256_Point, sha224);
|
|
1295
|
+
* const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
|
|
1296
|
+
* ```
|
|
1297
|
+
*/
|
|
1298
|
+
export function ecdsa(
|
|
1299
|
+
Point: WeierstrassPointCons<bigint>,
|
|
1300
|
+
hash: CHash,
|
|
1301
|
+
ecdsaOpts: ECDSAOpts = {}
|
|
1302
|
+
): ECDSA {
|
|
1303
|
+
ahash(hash);
|
|
1304
|
+
_validateObject(
|
|
1305
|
+
ecdsaOpts,
|
|
1306
|
+
{},
|
|
1307
|
+
{
|
|
1308
|
+
hmac: 'function',
|
|
1309
|
+
lowS: 'boolean',
|
|
1310
|
+
randomBytes: 'function',
|
|
1311
|
+
bits2int: 'function',
|
|
1312
|
+
bits2int_modN: 'function',
|
|
1313
|
+
}
|
|
1314
|
+
);
|
|
1315
|
+
|
|
1316
|
+
const randomBytes = ecdsaOpts.randomBytes || randomBytesWeb;
|
|
1317
|
+
const hmac: HmacFnSync =
|
|
1318
|
+
ecdsaOpts.hmac ||
|
|
1319
|
+
(((key, ...msgs) => nobleHmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1320
|
+
|
|
1321
|
+
const { Fp, Fn } = Point;
|
|
1322
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
1323
|
+
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
1324
|
+
const defaultSigOpts: Required<ECDSASignOpts> = {
|
|
1325
|
+
prehash: false,
|
|
1326
|
+
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
|
|
1327
|
+
format: undefined as any, //'compact' as ECDSASigFormat,
|
|
1328
|
+
extraEntropy: false,
|
|
1329
|
+
};
|
|
1330
|
+
const defaultSigOpts_format = 'compact';
|
|
1331
|
+
|
|
1332
|
+
function isBiggerThanHalfOrder(number: bigint) {
|
|
1333
|
+
const HALF = CURVE_ORDER >> _1n;
|
|
1334
|
+
return number > HALF;
|
|
1335
|
+
}
|
|
1336
|
+
function validateRS(title: string, num: bigint): bigint {
|
|
1337
|
+
if (!Fn.isValidNot0(num))
|
|
1338
|
+
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
1339
|
+
return num;
|
|
1340
|
+
}
|
|
1341
|
+
function validateSigLength(bytes: Uint8Array, format: ECDSASigFormat) {
|
|
1342
|
+
validateSigFormat(format);
|
|
1343
|
+
const size = lengths.signature!;
|
|
1344
|
+
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
1345
|
+
return abytes(bytes, sizer, `${format} signature`);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
|
|
1274
1350
|
*/
|
|
1275
1351
|
class Signature implements ECDSASignature {
|
|
1276
1352
|
readonly r: bigint;
|
|
1277
1353
|
readonly s: bigint;
|
|
1278
1354
|
readonly recovery?: number;
|
|
1279
1355
|
constructor(r: bigint, s: bigint, recovery?: number) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
this.r = r;
|
|
1283
|
-
this.s = s;
|
|
1356
|
+
this.r = validateRS('r', r); // r in [1..N-1];
|
|
1357
|
+
this.s = validateRS('s', s); // s in [1..N-1];
|
|
1284
1358
|
if (recovery != null) this.recovery = recovery;
|
|
1285
1359
|
Object.freeze(this);
|
|
1286
1360
|
}
|
|
1287
1361
|
|
|
1288
|
-
static fromBytes(bytes: Uint8Array, format: ECDSASigFormat =
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
}
|
|
1362
|
+
static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = defaultSigOpts_format): Signature {
|
|
1363
|
+
validateSigLength(bytes, format);
|
|
1364
|
+
let recid: number | undefined;
|
|
1296
1365
|
if (format === 'der') {
|
|
1297
|
-
abytes(bytes);
|
|
1298
|
-
const { r, s } = DER.toSig(bytes);
|
|
1366
|
+
const { r, s } = DER.toSig(abytes(bytes));
|
|
1299
1367
|
return new Signature(r, s);
|
|
1300
1368
|
}
|
|
1301
|
-
|
|
1369
|
+
if (format === 'recovered') {
|
|
1370
|
+
recid = bytes[0];
|
|
1371
|
+
format = 'compact';
|
|
1372
|
+
bytes = bytes.subarray(1);
|
|
1373
|
+
}
|
|
1374
|
+
const L = Fn.BYTES;
|
|
1375
|
+
const r = bytes.subarray(0, L);
|
|
1376
|
+
const s = bytes.subarray(L, L * 2);
|
|
1377
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
1302
1378
|
}
|
|
1303
1379
|
|
|
1304
1380
|
static fromHex(hex: string, format?: ECDSASigFormat) {
|
|
@@ -1309,8 +1385,7 @@ export function ecdsa(
|
|
|
1309
1385
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
1310
1386
|
}
|
|
1311
1387
|
|
|
1312
|
-
|
|
1313
|
-
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
|
1388
|
+
recoverPublicKey(messageHash: Hex): WeierstrassPoint<bigint> {
|
|
1314
1389
|
const FIELD_ORDER = Fp.ORDER;
|
|
1315
1390
|
const { r, s, recovery: rec } = this;
|
|
1316
1391
|
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
|
@@ -1329,9 +1404,9 @@ export function ecdsa(
|
|
|
1329
1404
|
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
1330
1405
|
if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
|
|
1331
1406
|
const x = Fp.toBytes(radj);
|
|
1332
|
-
const R = Point.
|
|
1407
|
+
const R = Point.fromBytes(concatBytes(pprefix((rec & 1) === 0), x));
|
|
1333
1408
|
const ir = Fn.inv(radj); // r^-1
|
|
1334
|
-
const h = bits2int_modN(ensureBytes('msgHash',
|
|
1409
|
+
const h = bits2int_modN(ensureBytes('msgHash', messageHash)); // Truncate hash
|
|
1335
1410
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
1336
1411
|
const u2 = Fn.create(s * ir); // sr^-1
|
|
1337
1412
|
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
@@ -1346,14 +1421,16 @@ export function ecdsa(
|
|
|
1346
1421
|
return isBiggerThanHalfOrder(this.s);
|
|
1347
1422
|
}
|
|
1348
1423
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
toBytes(format: ECDSASigFormat = 'compact') {
|
|
1354
|
-
if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
1424
|
+
toBytes(format: ECDSASigFormat = defaultSigOpts_format) {
|
|
1425
|
+
validateSigFormat(format);
|
|
1355
1426
|
if (format === 'der') return hexToBytes(DER.hexFromSig(this));
|
|
1356
|
-
|
|
1427
|
+
const r = Fn.toBytes(this.r);
|
|
1428
|
+
const s = Fn.toBytes(this.s);
|
|
1429
|
+
if (format === 'recovered') {
|
|
1430
|
+
if (this.recovery == null) throw new Error('recovery bit must be present');
|
|
1431
|
+
return concatBytes(Uint8Array.of(this.recovery), r, s);
|
|
1432
|
+
}
|
|
1433
|
+
return concatBytes(r, s);
|
|
1357
1434
|
}
|
|
1358
1435
|
|
|
1359
1436
|
toHex(format?: ECDSASigFormat) {
|
|
@@ -1368,6 +1445,9 @@ export function ecdsa(
|
|
|
1368
1445
|
static fromDER(hex: Hex) {
|
|
1369
1446
|
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
1370
1447
|
}
|
|
1448
|
+
normalizeS() {
|
|
1449
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1450
|
+
}
|
|
1371
1451
|
toDERRawBytes() {
|
|
1372
1452
|
return this.toBytes('der');
|
|
1373
1453
|
}
|
|
@@ -1383,90 +1463,13 @@ export function ecdsa(
|
|
|
1383
1463
|
}
|
|
1384
1464
|
type RecoveredSignature = Signature & { recovery: number };
|
|
1385
1465
|
|
|
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
|
-
}
|
|
1410
|
-
|
|
1411
|
-
const utils = {
|
|
1412
|
-
isValidSecretKey,
|
|
1413
|
-
isValidPublicKey,
|
|
1414
|
-
randomSecretKey,
|
|
1415
|
-
|
|
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> {
|
|
1421
|
-
return point.precompute(windowSize, false);
|
|
1422
|
-
},
|
|
1423
|
-
};
|
|
1424
|
-
|
|
1425
|
-
/**
|
|
1426
|
-
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1427
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1428
|
-
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1429
|
-
*/
|
|
1430
|
-
function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
|
|
1431
|
-
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
/**
|
|
1435
|
-
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1436
|
-
*/
|
|
1437
|
-
function isProbPub(item: PrivKey | PubKey): boolean | undefined {
|
|
1438
|
-
// TODO: remove
|
|
1439
|
-
if (typeof item === 'bigint') return false;
|
|
1440
|
-
// TODO: remove
|
|
1441
|
-
if (item instanceof Point) return true;
|
|
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;
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
/**
|
|
1448
|
-
* ECDH (Elliptic Curve Diffie Hellman).
|
|
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.
|
|
1451
|
-
* Does NOT hash the result.
|
|
1452
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1453
|
-
* @returns shared public key
|
|
1454
|
-
*/
|
|
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);
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
1466
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1464
1467
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1465
1468
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1466
1469
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1467
1470
|
const bits2int =
|
|
1468
1471
|
ecdsaOpts.bits2int ||
|
|
1469
|
-
function (bytes: Uint8Array): bigint {
|
|
1472
|
+
function bits2int_def(bytes: Uint8Array): bigint {
|
|
1470
1473
|
// Our custom check "just in case", for protection against DoS
|
|
1471
1474
|
if (bytes.length > 8192) throw new Error('input is too large');
|
|
1472
1475
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
@@ -1477,44 +1480,47 @@ export function ecdsa(
|
|
|
1477
1480
|
};
|
|
1478
1481
|
const bits2int_modN =
|
|
1479
1482
|
ecdsaOpts.bits2int_modN ||
|
|
1480
|
-
function (bytes: Uint8Array): bigint {
|
|
1483
|
+
function bits2int_modN_def(bytes: Uint8Array): bigint {
|
|
1481
1484
|
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1482
1485
|
};
|
|
1483
|
-
//
|
|
1486
|
+
// Pads output with zero as per spec
|
|
1484
1487
|
const ORDER_MASK = bitMask(fnBits);
|
|
1485
|
-
/**
|
|
1486
|
-
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
1487
|
-
*/
|
|
1488
|
+
/** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
|
|
1488
1489
|
function int2octets(num: bigint): Uint8Array {
|
|
1489
1490
|
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1490
1491
|
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1491
1492
|
return Fn.toBytes(num);
|
|
1492
1493
|
}
|
|
1493
1494
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1495
|
+
function validateMsgAndHash(message: Uint8Array, prehash: boolean) {
|
|
1496
|
+
abytes(message, undefined, 'message');
|
|
1497
|
+
return prehash ? abytes(hash(message), undefined, 'prehashed message') : message;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/**
|
|
1501
|
+
* Steps A, D of RFC6979 3.2.
|
|
1502
|
+
* Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
1503
|
+
* Used only in sign, not in verify.
|
|
1504
|
+
*
|
|
1505
|
+
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
1506
|
+
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1507
|
+
*/
|
|
1508
|
+
function prepSig(message: Uint8Array, privateKey: PrivKey, opts: ECDSASignOpts) {
|
|
1500
1509
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1501
1510
|
throw new Error('sign() legacy options not supported');
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
msgHash = ensureBytes('msgHash', msgHash);
|
|
1505
|
-
validateSigVerOpts(opts);
|
|
1506
|
-
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
1507
|
-
|
|
1511
|
+
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
1512
|
+
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
1508
1513
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1509
1514
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1510
1515
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1511
|
-
const h1int = bits2int_modN(
|
|
1516
|
+
const h1int = bits2int_modN(message);
|
|
1512
1517
|
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1513
1518
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1514
1519
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1515
|
-
if (
|
|
1520
|
+
if (extraEntropy != null && extraEntropy !== false) {
|
|
1516
1521
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1517
|
-
|
|
1522
|
+
// gen random bytes OR pass as-is
|
|
1523
|
+
const e = extraEntropy === true ? randomBytes(lengths.secretKey) : extraEntropy;
|
|
1518
1524
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
1519
1525
|
}
|
|
1520
1526
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
@@ -1530,7 +1536,7 @@ export function ecdsa(
|
|
|
1530
1536
|
function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
|
|
1531
1537
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1532
1538
|
// Important: all mod() calls here must be done over N
|
|
1533
|
-
const k = bits2int(kBytes); //
|
|
1539
|
+
const k = bits2int(kBytes); // mod n, not mod p
|
|
1534
1540
|
if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
|
|
1535
1541
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
1536
1542
|
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
@@ -1541,122 +1547,102 @@ export function ecdsa(
|
|
|
1541
1547
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
1542
1548
|
let normS = s;
|
|
1543
1549
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
1544
|
-
normS =
|
|
1550
|
+
normS = Fn.neg(s); // if lowS was passed, ensure s is always
|
|
1545
1551
|
recovery ^= 1; // // in the bottom half of N
|
|
1546
1552
|
}
|
|
1547
1553
|
return new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
|
|
1548
1554
|
}
|
|
1549
1555
|
return { seed, k2sig };
|
|
1550
1556
|
}
|
|
1551
|
-
const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1552
|
-
const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1553
1557
|
|
|
1554
1558
|
/**
|
|
1555
1559
|
* Signs message hash with a secret key.
|
|
1560
|
+
*
|
|
1556
1561
|
* ```
|
|
1557
|
-
* sign(m, d
|
|
1562
|
+
* sign(m, d) where
|
|
1563
|
+
* k = rfc6979_hmac_drbg(m, d)
|
|
1558
1564
|
* (x, y) = G × k
|
|
1559
1565
|
* r = x mod n
|
|
1560
|
-
* s = (m + dr)/k mod n
|
|
1566
|
+
* s = (m + dr) / k mod n
|
|
1561
1567
|
* ```
|
|
1562
1568
|
*/
|
|
1563
|
-
function sign(
|
|
1564
|
-
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1569
|
+
function sign(message: Hex, secretKey: PrivKey, opts: ECDSASignOpts = {}): RecoveredSignature {
|
|
1570
|
+
message = ensureBytes('message', message);
|
|
1571
|
+
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1572
|
+
const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac);
|
|
1573
|
+
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1574
|
+
return sig;
|
|
1567
1575
|
}
|
|
1568
1576
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1577
|
+
function tryParsingSig(sg: Hex | SignatureLike) {
|
|
1578
|
+
// Try to deduce format
|
|
1579
|
+
let sig: Signature | undefined = undefined;
|
|
1580
|
+
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1581
|
+
const isObj =
|
|
1582
|
+
!isHex &&
|
|
1583
|
+
sg !== null &&
|
|
1584
|
+
typeof sg === 'object' &&
|
|
1585
|
+
typeof sg.r === 'bigint' &&
|
|
1586
|
+
typeof sg.s === 'bigint';
|
|
1587
|
+
if (!isHex && !isObj)
|
|
1588
|
+
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1589
|
+
if (isObj) {
|
|
1590
|
+
sig = new Signature(sg.r, sg.s);
|
|
1591
|
+
} else if (isHex) {
|
|
1592
|
+
try {
|
|
1593
|
+
sig = Signature.fromBytes(ensureBytes('sig', sg), 'der');
|
|
1594
|
+
} catch (derError) {
|
|
1595
|
+
if (!(derError instanceof DER.Err)) throw derError;
|
|
1596
|
+
}
|
|
1597
|
+
if (!sig) {
|
|
1598
|
+
try {
|
|
1599
|
+
sig = Signature.fromBytes(ensureBytes('sig', sg), 'compact');
|
|
1600
|
+
} catch (error) {
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (!sig) return false;
|
|
1606
|
+
return sig;
|
|
1607
|
+
}
|
|
1571
1608
|
|
|
1572
1609
|
/**
|
|
1573
|
-
* Verifies a signature against message
|
|
1574
|
-
* Rejects lowS signatures by default:
|
|
1575
|
-
*
|
|
1610
|
+
* Verifies a signature against message and public key.
|
|
1611
|
+
* Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
|
|
1612
|
+
* Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
|
|
1576
1613
|
*
|
|
1577
1614
|
* ```
|
|
1578
1615
|
* verify(r, s, h, P) where
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1581
|
-
* R =
|
|
1616
|
+
* u1 = hs^-1 mod n
|
|
1617
|
+
* u2 = rs^-1 mod n
|
|
1618
|
+
* R = u1⋅G + u2⋅P
|
|
1582
1619
|
* mod(R.x, n) == r
|
|
1583
1620
|
* ```
|
|
1584
1621
|
*/
|
|
1585
1622
|
function verify(
|
|
1586
1623
|
signature: Hex | SignatureLike,
|
|
1587
|
-
|
|
1624
|
+
message: Hex,
|
|
1588
1625
|
publicKey: Hex,
|
|
1589
|
-
opts =
|
|
1626
|
+
opts: ECDSAVerifyOpts = {}
|
|
1590
1627
|
): boolean {
|
|
1591
|
-
const
|
|
1592
|
-
msgHash = ensureBytes('msgHash', msgHash);
|
|
1628
|
+
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
1593
1629
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1594
|
-
|
|
1595
|
-
// Verify opts
|
|
1596
|
-
validateSigVerOpts(opts);
|
|
1597
|
-
const { lowS, prehash, format } = opts;
|
|
1598
|
-
|
|
1599
|
-
// TODO: remove
|
|
1630
|
+
message = validateMsgAndHash(ensureBytes('message', message), prehash);
|
|
1600
1631
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
if (
|
|
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');
|
|
1616
|
-
if (isObj) {
|
|
1617
|
-
_sig = new Signature(sg.r, sg.s);
|
|
1618
|
-
} else if (isHex) {
|
|
1619
|
-
// TODO: remove this malleable check
|
|
1620
|
-
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1621
|
-
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1622
|
-
try {
|
|
1623
|
-
_sig = Signature.fromDER(sg);
|
|
1624
|
-
} catch (derError) {
|
|
1625
|
-
if (!(derError instanceof DER.Err)) throw derError;
|
|
1626
|
-
}
|
|
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"');
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
if (!_sig) return false;
|
|
1632
|
+
const sig =
|
|
1633
|
+
format === undefined
|
|
1634
|
+
? tryParsingSig(signature)
|
|
1635
|
+
: Signature.fromBytes(ensureBytes('sig', signature as Hex), format);
|
|
1636
|
+
if (sig === false) return false;
|
|
1649
1637
|
try {
|
|
1650
|
-
P = Point.
|
|
1651
|
-
if (lowS &&
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
const
|
|
1655
|
-
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1656
|
-
const is = Fn.inv(s); // s^-1
|
|
1638
|
+
const P = Point.fromBytes(publicKey);
|
|
1639
|
+
if (lowS && sig.hasHighS()) return false;
|
|
1640
|
+
const { r, s } = sig;
|
|
1641
|
+
const h = bits2int_modN(message); // mod n, not mod p
|
|
1642
|
+
const is = Fn.inv(s); // s^-1 mod n
|
|
1657
1643
|
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1658
1644
|
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1659
|
-
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1645
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
|
|
1660
1646
|
if (R.is0()) return false;
|
|
1661
1647
|
const v = Fn.create(R.x); // v = r.x mod n
|
|
1662
1648
|
return v === r;
|
|
@@ -1665,37 +1651,144 @@ export function ecdsa(
|
|
|
1665
1651
|
}
|
|
1666
1652
|
}
|
|
1667
1653
|
|
|
1668
|
-
function
|
|
1669
|
-
|
|
1670
|
-
|
|
1654
|
+
function recoverPublicKey(
|
|
1655
|
+
signature: Uint8Array,
|
|
1656
|
+
message: Uint8Array,
|
|
1657
|
+
opts: ECDSARecoverOpts = {}
|
|
1658
|
+
): Uint8Array {
|
|
1659
|
+
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
1660
|
+
message = validateMsgAndHash(message, prehash);
|
|
1661
|
+
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
1671
1662
|
}
|
|
1672
1663
|
|
|
1673
1664
|
return Object.freeze({
|
|
1674
1665
|
keygen,
|
|
1675
1666
|
getPublicKey,
|
|
1676
|
-
sign,
|
|
1677
|
-
verify,
|
|
1678
1667
|
getSharedSecret,
|
|
1679
1668
|
utils,
|
|
1669
|
+
lengths,
|
|
1680
1670
|
Point,
|
|
1671
|
+
sign,
|
|
1672
|
+
verify,
|
|
1673
|
+
recoverPublicKey,
|
|
1681
1674
|
Signature,
|
|
1682
|
-
|
|
1675
|
+
hash,
|
|
1683
1676
|
});
|
|
1684
1677
|
}
|
|
1685
1678
|
|
|
1679
|
+
// TODO: remove everything below
|
|
1680
|
+
/** @deprecated */
|
|
1681
|
+
export type SignatureType = ECDSASignature;
|
|
1682
|
+
/** @deprecated */
|
|
1683
|
+
export type RecoveredSignatureType = ECDSASigRecovered;
|
|
1684
|
+
/** @deprecated */
|
|
1685
|
+
export type SignatureLike = { r: bigint; s: bigint };
|
|
1686
|
+
/** @deprecated use `Uint8Array | boolean` */
|
|
1687
|
+
export type Entropy = Hex | boolean;
|
|
1688
|
+
export type BasicWCurve<T> = BasicCurve<T> & {
|
|
1689
|
+
// Params: a, b
|
|
1690
|
+
a: T;
|
|
1691
|
+
b: T;
|
|
1692
|
+
|
|
1693
|
+
// Optional params
|
|
1694
|
+
allowedPrivateKeyLengths?: readonly number[]; // for P521
|
|
1695
|
+
wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
1696
|
+
endo?: EndomorphismOpts;
|
|
1697
|
+
// When a cofactor != 1, there can be an effective methods to:
|
|
1698
|
+
// 1. Determine whether a point is torsion-free
|
|
1699
|
+
isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
1700
|
+
// 2. Clear torsion component
|
|
1701
|
+
clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
1702
|
+
};
|
|
1703
|
+
/** @deprecated use ECDSASignOpts */
|
|
1704
|
+
export type SignOpts = ECDSASignOpts;
|
|
1705
|
+
/** @deprecated use ECDSASignOpts */
|
|
1706
|
+
export type VerOpts = ECDSAVerifyOpts;
|
|
1707
|
+
|
|
1708
|
+
/** @deprecated use WeierstrassPoint */
|
|
1709
|
+
export type ProjPointType<T> = WeierstrassPoint<T>;
|
|
1710
|
+
/** @deprecated use WeierstrassPointCons */
|
|
1711
|
+
export type ProjConstructor<T> = WeierstrassPointCons<T>;
|
|
1712
|
+
|
|
1686
1713
|
// TODO: remove
|
|
1714
|
+
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
1715
|
+
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
|
1716
|
+
toBytes?: (
|
|
1717
|
+
c: WeierstrassPointCons<T>,
|
|
1718
|
+
point: WeierstrassPoint<T>,
|
|
1719
|
+
isCompressed: boolean
|
|
1720
|
+
) => Uint8Array;
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
// LegacyWeierstrassOpts
|
|
1724
|
+
export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
|
|
1725
|
+
|
|
1726
|
+
// LegacyWeierstrass
|
|
1727
|
+
export type CurvePointsRes<T> = {
|
|
1728
|
+
Point: WeierstrassPointCons<T>;
|
|
1729
|
+
|
|
1730
|
+
/** @deprecated use `Point.CURVE()` */
|
|
1731
|
+
CURVE: CurvePointsType<T>;
|
|
1732
|
+
/** @deprecated use `Point` */
|
|
1733
|
+
ProjectivePoint: WeierstrassPointCons<T>;
|
|
1734
|
+
/** @deprecated use `Point.Fn.fromBytes(privateKey)` */
|
|
1735
|
+
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
1736
|
+
/** @deprecated */
|
|
1737
|
+
weierstrassEquation: (x: T) => T;
|
|
1738
|
+
/** @deprecated use `Point.Fn.isValidNot0(num)` */
|
|
1739
|
+
isWithinCurveOrder: (num: bigint) => boolean;
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
// Aliases to legacy types
|
|
1743
|
+
// export type CurveType = LegacyECDSAOpts;
|
|
1744
|
+
// export type CurveFn = LegacyECDSA;
|
|
1745
|
+
// export type CurvePointsRes<T> = LegacyWeierstrass<T>;
|
|
1746
|
+
// export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
|
|
1747
|
+
// export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
|
|
1748
|
+
// export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
|
|
1749
|
+
|
|
1750
|
+
/** @deprecated use `Uint8Array` */
|
|
1751
|
+
export type PubKey = Hex | WeierstrassPoint<bigint>;
|
|
1752
|
+
export type CurveType = BasicWCurve<bigint> & {
|
|
1753
|
+
hash: CHash; // CHash not FHash because we need outputLen for DRBG
|
|
1754
|
+
hmac?: HmacFnSync;
|
|
1755
|
+
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
1756
|
+
lowS?: boolean;
|
|
1757
|
+
bits2int?: (bytes: Uint8Array) => bigint;
|
|
1758
|
+
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
|
1759
|
+
};
|
|
1760
|
+
export type CurveFn = {
|
|
1761
|
+
/** @deprecated use `Point.CURVE()` */
|
|
1762
|
+
CURVE: CurvePointsType<bigint>;
|
|
1763
|
+
keygen: ECDSA['keygen'];
|
|
1764
|
+
getPublicKey: ECDSA['getPublicKey'];
|
|
1765
|
+
getSharedSecret: ECDSA['getSharedSecret'];
|
|
1766
|
+
sign: ECDSA['sign'];
|
|
1767
|
+
verify: ECDSA['verify'];
|
|
1768
|
+
Point: WeierstrassPointCons<bigint>;
|
|
1769
|
+
/** @deprecated use `Point` */
|
|
1770
|
+
ProjectivePoint: WeierstrassPointCons<bigint>;
|
|
1771
|
+
Signature: ECDSASignatureCons;
|
|
1772
|
+
utils: ECDSA['utils'];
|
|
1773
|
+
lengths: ECDSA['lengths'];
|
|
1774
|
+
};
|
|
1775
|
+
/** @deprecated use `weierstrass` in newer releases */
|
|
1776
|
+
export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
|
|
1777
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1778
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1779
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
1780
|
+
}
|
|
1687
1781
|
export type WsPointComposed<T> = {
|
|
1688
1782
|
CURVE: WeierstrassOpts<T>;
|
|
1689
1783
|
curveOpts: WeierstrassExtraOpts<T>;
|
|
1690
1784
|
};
|
|
1691
|
-
// TODO: remove
|
|
1692
1785
|
export type WsComposed = {
|
|
1786
|
+
/** @deprecated use `Point.CURVE()` */
|
|
1693
1787
|
CURVE: WeierstrassOpts<bigint>;
|
|
1694
1788
|
hash: CHash;
|
|
1695
1789
|
curveOpts: WeierstrassExtraOpts<bigint>;
|
|
1696
1790
|
ecdsaOpts: ECDSAOpts;
|
|
1697
1791
|
};
|
|
1698
|
-
// TODO: remove
|
|
1699
1792
|
function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
|
|
1700
1793
|
const CURVE: WeierstrassOpts<T> = {
|
|
1701
1794
|
a: c.a,
|
|
@@ -1713,7 +1806,7 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
|
|
|
1713
1806
|
const Fn = Field(CURVE.n, {
|
|
1714
1807
|
BITS: c.nBitLength,
|
|
1715
1808
|
allowedLengths: allowedLengths,
|
|
1716
|
-
|
|
1809
|
+
modFromBytes: c.wrapPrivateKey,
|
|
1717
1810
|
});
|
|
1718
1811
|
const curveOpts: WeierstrassExtraOpts<T> = {
|
|
1719
1812
|
Fp,
|
|
@@ -1738,13 +1831,23 @@ function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
|
|
|
1738
1831
|
};
|
|
1739
1832
|
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1740
1833
|
}
|
|
1741
|
-
|
|
1834
|
+
export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
|
|
1835
|
+
/**
|
|
1836
|
+
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
1837
|
+
* @returns y²
|
|
1838
|
+
*/
|
|
1839
|
+
function weierstrassEquation(x: T): T {
|
|
1840
|
+
const x2 = Fp.sqr(x); // x * x
|
|
1841
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
1842
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
1843
|
+
}
|
|
1844
|
+
return weierstrassEquation;
|
|
1845
|
+
}
|
|
1742
1846
|
function _weierstrass_new_output_to_legacy<T>(
|
|
1743
1847
|
c: CurvePointsType<T>,
|
|
1744
1848
|
Point: WeierstrassPointCons<T>
|
|
1745
1849
|
): CurvePointsRes<T> {
|
|
1746
1850
|
const { Fp, Fn } = Point;
|
|
1747
|
-
// TODO: remove
|
|
1748
1851
|
function isWithinCurveOrder(num: bigint): boolean {
|
|
1749
1852
|
return inRange(num, _1n, Fn.ORDER);
|
|
1750
1853
|
}
|
|
@@ -1761,11 +1864,11 @@ function _weierstrass_new_output_to_legacy<T>(
|
|
|
1761
1864
|
}
|
|
1762
1865
|
);
|
|
1763
1866
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
return Object.assign({},
|
|
1767
|
-
ProjectivePoint:
|
|
1768
|
-
CURVE: c,
|
|
1867
|
+
function _ecdsa_new_output_to_legacy(c: CurveType, _ecdsa: ECDSA): CurveFn {
|
|
1868
|
+
const Point = _ecdsa.Point;
|
|
1869
|
+
return Object.assign({}, _ecdsa, {
|
|
1870
|
+
ProjectivePoint: Point,
|
|
1871
|
+
CURVE: Object.assign({}, c, nLength(Point.Fn.ORDER, Point.Fn.BITS)),
|
|
1769
1872
|
});
|
|
1770
1873
|
}
|
|
1771
1874
|
|