@noble/curves 1.9.1 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -25
- package/_shortw_utils.d.ts +7 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +2 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +60 -24
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +158 -109
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +44 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +86 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +112 -25
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +138 -102
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +25 -11
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +17 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +24 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +49 -20
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -4
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +20 -46
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +132 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +462 -398
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +8 -5
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +67 -54
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +10 -6
- package/ed448.d.ts.map +1 -1
- package/ed448.js +80 -57
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +7 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +2 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +60 -24
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +158 -109
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +44 -9
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +83 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +112 -25
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +138 -104
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +25 -11
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +17 -14
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +24 -11
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +48 -19
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -4
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +20 -46
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +132 -76
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +460 -400
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +8 -5
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +62 -49
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +10 -6
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +74 -51
- package/esm/ed448.js.map +1 -1
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -26
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +86 -97
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +3 -3
- package/esm/p384.d.ts +3 -3
- package/esm/p521.d.ts +3 -3
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +43 -40
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +86 -97
- package/nist.js.map +1 -1
- package/p256.d.ts +3 -3
- package/p384.d.ts +3 -3
- package/p521.d.ts +3 -3
- package/package.json +14 -5
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +46 -43
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +5 -15
- package/src/abstract/bls.ts +260 -145
- package/src/abstract/curve.ts +115 -13
- package/src/abstract/edwards.ts +279 -138
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +49 -28
- package/src/abstract/montgomery.ts +9 -7
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +36 -67
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +700 -453
- package/src/bls12-381.ts +540 -489
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +80 -64
- package/src/ed448.ts +129 -92
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -127
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/secp256k1.ts +58 -46
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
3
3
|
*
|
|
4
|
-
* ### Parameters
|
|
5
|
-
*
|
|
6
|
-
* To initialize a weierstrass curve, one needs to pass following params:
|
|
7
|
-
*
|
|
8
|
-
* * a: formula param
|
|
9
|
-
* * b: formula param
|
|
10
|
-
* * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
|
|
11
|
-
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
12
|
-
* * Gx: Base point (x, y) aka generator point. Gx = x coordinate
|
|
13
|
-
* * Gy: ...y coordinate
|
|
14
|
-
* * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
|
|
15
|
-
* * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
|
|
16
|
-
*
|
|
17
4
|
* ### Design rationale for types
|
|
18
5
|
*
|
|
19
6
|
* * Interaction between classes from different curves should fail:
|
|
@@ -38,29 +25,52 @@
|
|
|
38
25
|
* @module
|
|
39
26
|
*/
|
|
40
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
41
|
-
|
|
28
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
42
29
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
_validateObject,
|
|
31
|
+
abool,
|
|
32
|
+
abytes,
|
|
33
|
+
aInRange,
|
|
34
|
+
bitMask,
|
|
35
|
+
bytesToHex,
|
|
36
|
+
bytesToNumberBE,
|
|
37
|
+
concatBytes,
|
|
38
|
+
createHmacDrbg,
|
|
39
|
+
ensureBytes,
|
|
40
|
+
hexToBytes,
|
|
41
|
+
inRange,
|
|
42
|
+
isBytes,
|
|
43
|
+
memoized,
|
|
44
|
+
numberToHexUnpadded,
|
|
45
|
+
randomBytes,
|
|
46
|
+
type CHash,
|
|
47
|
+
type Hex,
|
|
48
|
+
type PrivKey,
|
|
49
|
+
} from '../utils.ts';
|
|
50
|
+
import {
|
|
51
|
+
_createCurveFields,
|
|
52
|
+
mulEndoUnsafe,
|
|
53
|
+
negateCt,
|
|
54
|
+
normalizeZ,
|
|
55
|
+
pippenger,
|
|
56
|
+
wNAF,
|
|
57
|
+
type AffinePoint,
|
|
58
|
+
type BasicCurve,
|
|
59
|
+
type Group,
|
|
60
|
+
type GroupConstructor,
|
|
45
61
|
} from './curve.ts';
|
|
46
|
-
// prettier-ignore
|
|
47
62
|
import {
|
|
48
63
|
Field,
|
|
49
64
|
FpInvertBatch,
|
|
50
|
-
getMinHashLength,
|
|
51
|
-
|
|
65
|
+
getMinHashLength,
|
|
66
|
+
mapHashToField,
|
|
67
|
+
validateField,
|
|
68
|
+
type IField,
|
|
69
|
+
type NLength,
|
|
52
70
|
} from './modular.ts';
|
|
53
|
-
// prettier-ignore
|
|
54
|
-
import {
|
|
55
|
-
aInRange, abool,
|
|
56
|
-
bitMask,
|
|
57
|
-
bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
|
|
58
|
-
inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject,
|
|
59
|
-
type CHash, type Hex, type PrivKey
|
|
60
|
-
} from './utils.ts';
|
|
61
71
|
|
|
62
72
|
export type { AffinePoint };
|
|
63
|
-
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
73
|
+
export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
64
74
|
/**
|
|
65
75
|
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
|
|
66
76
|
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
|
|
@@ -106,36 +116,70 @@ export type BasicWCurve<T> = BasicCurve<T> & {
|
|
|
106
116
|
|
|
107
117
|
export type Entropy = Hex | boolean;
|
|
108
118
|
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
|
|
109
|
-
export type VerOpts = {
|
|
119
|
+
export type VerOpts = {
|
|
120
|
+
lowS?: boolean;
|
|
121
|
+
prehash?: boolean;
|
|
122
|
+
format?: 'compact' | 'der' | 'js' | undefined;
|
|
123
|
+
};
|
|
110
124
|
|
|
111
125
|
function validateSigVerOpts(opts: SignOpts | VerOpts) {
|
|
112
126
|
if (opts.lowS !== undefined) abool('lowS', opts.lowS);
|
|
113
127
|
if (opts.prehash !== undefined) abool('prehash', opts.prehash);
|
|
114
128
|
}
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
/** Instance methods for 3D XYZ points. */
|
|
117
131
|
export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|
132
|
+
/** projective x coordinate. Note: different from .x */
|
|
118
133
|
readonly px: T;
|
|
134
|
+
/** projective y coordinate. Note: different from .y */
|
|
119
135
|
readonly py: T;
|
|
136
|
+
/** projective z coordinate */
|
|
120
137
|
readonly pz: T;
|
|
138
|
+
/** affine x coordinate */
|
|
121
139
|
get x(): T;
|
|
140
|
+
/** affine y coordinate */
|
|
122
141
|
get y(): T;
|
|
123
|
-
toAffine(iz?: T): AffinePoint<T>;
|
|
124
|
-
toHex(isCompressed?: boolean): string;
|
|
125
|
-
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
126
|
-
|
|
127
142
|
assertValidity(): void;
|
|
128
|
-
|
|
143
|
+
clearCofactor(): ProjPointType<T>;
|
|
144
|
+
is0(): boolean;
|
|
145
|
+
isTorsionFree(): boolean;
|
|
129
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
|
+
/** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
|
|
161
|
+
toBytes(isCompressed?: boolean): Uint8Array;
|
|
162
|
+
toHex(isCompressed?: boolean): string;
|
|
163
|
+
|
|
164
|
+
/** @deprecated use `toBytes` */
|
|
165
|
+
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
166
|
+
/** @deprecated use `multiplyUnsafe` */
|
|
130
167
|
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
/** @deprecated use `p.y % 2n === 0n` */
|
|
169
|
+
hasEvenY(): boolean;
|
|
170
|
+
/** @deprecated use `p.precompute(windowSize)` */
|
|
133
171
|
_setWindowSize(windowSize: number): void;
|
|
134
172
|
}
|
|
135
|
-
|
|
173
|
+
|
|
174
|
+
/** Static methods for 3D XYZ points. */
|
|
136
175
|
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|
176
|
+
Fp: IField<T>;
|
|
177
|
+
Fn: IField<bigint>;
|
|
178
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
137
179
|
new (x: T, y: T, z: T): ProjPointType<T>;
|
|
180
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
138
181
|
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
|
182
|
+
fromBytes(encodedPoint: Uint8Array): ProjPointType<T>;
|
|
139
183
|
fromHex(hex: Hex): ProjPointType<T>;
|
|
140
184
|
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
|
|
141
185
|
normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
|
|
@@ -143,57 +187,104 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|
|
143
187
|
}
|
|
144
188
|
|
|
145
189
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
146
|
-
// Bytes
|
|
147
190
|
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
|
148
191
|
toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
|
|
149
192
|
};
|
|
150
193
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
>;
|
|
154
|
-
|
|
155
|
-
function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLength<T> {
|
|
156
|
-
const opts = validateBasic(curve);
|
|
157
|
-
validateObject(
|
|
158
|
-
opts,
|
|
159
|
-
{
|
|
160
|
-
a: 'field',
|
|
161
|
-
b: 'field',
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
allowInfinityPoint: 'boolean',
|
|
165
|
-
allowedPrivateKeyLengths: 'array',
|
|
166
|
-
clearCofactor: 'function',
|
|
167
|
-
fromBytes: 'function',
|
|
168
|
-
isTorsionFree: 'function',
|
|
169
|
-
toBytes: 'function',
|
|
170
|
-
wrapPrivateKey: 'boolean',
|
|
171
|
-
}
|
|
172
|
-
);
|
|
173
|
-
const { endo, Fp, a } = opts;
|
|
174
|
-
if (endo) {
|
|
175
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
176
|
-
throw new Error('invalid endo: CURVE.a must be 0');
|
|
177
|
-
}
|
|
178
|
-
if (
|
|
179
|
-
typeof endo !== 'object' ||
|
|
180
|
-
typeof endo.beta !== 'bigint' ||
|
|
181
|
-
typeof endo.splitScalar !== 'function'
|
|
182
|
-
) {
|
|
183
|
-
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return Object.freeze({ ...opts } as const);
|
|
187
|
-
}
|
|
194
|
+
// LegacyWeierstrassOpts
|
|
195
|
+
export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
|
|
188
196
|
|
|
197
|
+
// LegacyWeierstrass
|
|
189
198
|
export type CurvePointsRes<T> = {
|
|
190
|
-
CURVE
|
|
199
|
+
/** @deprecated import individual CURVE params */
|
|
200
|
+
CURVE: CurvePointsType<T>;
|
|
201
|
+
Point: ProjConstructor<T>;
|
|
202
|
+
/** @deprecated use `Point` */
|
|
191
203
|
ProjectivePoint: ProjConstructor<T>;
|
|
204
|
+
/** @deprecated */
|
|
192
205
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
206
|
+
/** @deprecated */
|
|
193
207
|
weierstrassEquation: (x: T) => T;
|
|
208
|
+
/** @deprecated use `Point.Fn.isValidNot0(num)` */
|
|
194
209
|
isWithinCurveOrder: (num: bigint) => boolean;
|
|
195
210
|
};
|
|
196
211
|
|
|
212
|
+
// Aliases to legacy types
|
|
213
|
+
// export type CurveType = LegacyECDSAOpts;
|
|
214
|
+
// export type CurveFn = LegacyECDSA;
|
|
215
|
+
// export type CurvePointsRes<T> = LegacyWeierstrass<T>;
|
|
216
|
+
// export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
|
|
217
|
+
// export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
|
|
218
|
+
// export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Weierstrass curve options.
|
|
222
|
+
*
|
|
223
|
+
* * p: prime characteristic (order) of finite field, in which arithmetics is done
|
|
224
|
+
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
225
|
+
* * h: cofactor, usually 1. h*n is group order; n is subgroup order
|
|
226
|
+
* * a: formula param, must be in field of p
|
|
227
|
+
* * b: formula param, must be in field of p
|
|
228
|
+
* * Gx: x coordinate of generator point a.k.a. base point
|
|
229
|
+
* * Gy: y coordinate of generator point
|
|
230
|
+
*/
|
|
231
|
+
export type WeierstrassOpts<T> = Readonly<{
|
|
232
|
+
p: bigint;
|
|
233
|
+
n: bigint;
|
|
234
|
+
h: bigint;
|
|
235
|
+
a: T;
|
|
236
|
+
b: T;
|
|
237
|
+
Gx: T;
|
|
238
|
+
Gy: T;
|
|
239
|
+
}>;
|
|
240
|
+
|
|
241
|
+
// When a cofactor != 1, there can be an effective methods to:
|
|
242
|
+
// 1. Determine whether a point is torsion-free
|
|
243
|
+
// 2. Clear torsion component
|
|
244
|
+
// wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
245
|
+
export type WeierstrassExtraOpts<T> = Partial<{
|
|
246
|
+
Fp: IField<T>;
|
|
247
|
+
Fn: IField<bigint>;
|
|
248
|
+
// TODO: remove
|
|
249
|
+
allowedPrivateKeyLengths: readonly number[]; // for P521
|
|
250
|
+
allowInfinityPoint: boolean;
|
|
251
|
+
endo: EndomorphismOpts;
|
|
252
|
+
wrapPrivateKey: boolean;
|
|
253
|
+
isTorsionFree: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
|
|
254
|
+
clearCofactor: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
|
|
255
|
+
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
|
256
|
+
toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
|
|
257
|
+
}>;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Options for ECDSA signatures over a Weierstrass curve.
|
|
261
|
+
*/
|
|
262
|
+
export type ECDSAOpts = {
|
|
263
|
+
hash: CHash;
|
|
264
|
+
hmac?: HmacFnSync;
|
|
265
|
+
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
266
|
+
lowS?: boolean;
|
|
267
|
+
bits2int?: (bytes: Uint8Array) => bigint;
|
|
268
|
+
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
|
|
272
|
+
export interface ECDSA {
|
|
273
|
+
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
274
|
+
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
275
|
+
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
|
|
276
|
+
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
|
277
|
+
Point: ProjConstructor<bigint>;
|
|
278
|
+
Signature: SignatureConstructor;
|
|
279
|
+
utils: {
|
|
280
|
+
isValidPrivateKey(privateKey: PrivKey): boolean;
|
|
281
|
+
randomPrivateKey: () => Uint8Array;
|
|
282
|
+
// TODO: deprecate those two
|
|
283
|
+
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
284
|
+
/** @deprecated */
|
|
285
|
+
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
197
288
|
export class DERErr extends Error {
|
|
198
289
|
constructor(m = '') {
|
|
199
290
|
super(m);
|
|
@@ -312,47 +403,155 @@ export const DER: IDER = {
|
|
|
312
403
|
},
|
|
313
404
|
};
|
|
314
405
|
|
|
315
|
-
function numToSizedHex(num: bigint, size: number): string {
|
|
316
|
-
return bytesToHex(numberToBytesBE(num, size));
|
|
317
|
-
}
|
|
318
|
-
|
|
319
406
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
320
407
|
// prettier-ignore
|
|
321
408
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
322
409
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
326
|
-
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
327
|
-
|
|
328
|
-
const toBytes =
|
|
329
|
-
CURVE.toBytes ||
|
|
330
|
-
((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
|
|
331
|
-
const a = point.toAffine();
|
|
332
|
-
return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
333
|
-
});
|
|
334
|
-
const fromBytes =
|
|
335
|
-
CURVE.fromBytes ||
|
|
336
|
-
((bytes: Uint8Array) => {
|
|
337
|
-
// const head = bytes[0];
|
|
338
|
-
const tail = bytes.subarray(1);
|
|
339
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
340
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
341
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
342
|
-
return { x, y };
|
|
343
|
-
});
|
|
344
|
-
|
|
410
|
+
// TODO: remove
|
|
411
|
+
export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
|
|
345
412
|
/**
|
|
346
413
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
347
414
|
* @returns y²
|
|
348
415
|
*/
|
|
349
416
|
function weierstrassEquation(x: T): T {
|
|
350
|
-
const { a, b } = CURVE;
|
|
351
417
|
const x2 = Fp.sqr(x); // x * x
|
|
352
418
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
353
419
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
354
420
|
}
|
|
421
|
+
return weierstrassEquation;
|
|
422
|
+
}
|
|
423
|
+
export function _legacyHelperNormPriv(
|
|
424
|
+
Fn: IField<bigint>,
|
|
425
|
+
allowedPrivateKeyLengths?: readonly number[],
|
|
426
|
+
wrapPrivateKey?: boolean
|
|
427
|
+
): (key: PrivKey) => bigint {
|
|
428
|
+
const { BYTES: expected } = Fn;
|
|
429
|
+
// Validates if priv key is valid and converts it to bigint.
|
|
430
|
+
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
|
431
|
+
let num: bigint;
|
|
432
|
+
if (typeof key === 'bigint') {
|
|
433
|
+
num = key;
|
|
434
|
+
} else {
|
|
435
|
+
let bytes = ensureBytes('private key', key);
|
|
436
|
+
if (allowedPrivateKeyLengths) {
|
|
437
|
+
if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
|
|
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
|
+
}
|
|
450
|
+
}
|
|
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
|
+
}
|
|
455
|
+
return normPrivateKeyToScalar;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export function weierstrassN<T>(
|
|
459
|
+
CURVE: WeierstrassOpts<T>,
|
|
460
|
+
curveOpts: WeierstrassExtraOpts<T> = {}
|
|
461
|
+
): ProjConstructor<T> {
|
|
462
|
+
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
463
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
464
|
+
_validateObject(
|
|
465
|
+
curveOpts,
|
|
466
|
+
{},
|
|
467
|
+
{
|
|
468
|
+
allowInfinityPoint: 'boolean',
|
|
469
|
+
clearCofactor: 'function',
|
|
470
|
+
isTorsionFree: 'function',
|
|
471
|
+
fromBytes: 'function',
|
|
472
|
+
toBytes: 'function',
|
|
473
|
+
endo: 'object',
|
|
474
|
+
wrapPrivateKey: 'boolean',
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
const { endo } = curveOpts;
|
|
479
|
+
if (endo) {
|
|
480
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
481
|
+
if (
|
|
482
|
+
!Fp.is0(CURVE.a) ||
|
|
483
|
+
typeof endo.beta !== 'bigint' ||
|
|
484
|
+
typeof endo.splitScalar !== 'function'
|
|
485
|
+
) {
|
|
486
|
+
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
487
|
+
}
|
|
488
|
+
}
|
|
355
489
|
|
|
490
|
+
function assertCompressionIsSupported() {
|
|
491
|
+
if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Implements IEEE P1363 point encoding
|
|
495
|
+
function pointToBytes(
|
|
496
|
+
_c: ProjConstructor<T>,
|
|
497
|
+
point: ProjPointType<T>,
|
|
498
|
+
isCompressed: boolean
|
|
499
|
+
): Uint8Array {
|
|
500
|
+
const { x, y } = point.toAffine();
|
|
501
|
+
const bx = Fp.toBytes(x);
|
|
502
|
+
abool('isCompressed', isCompressed);
|
|
503
|
+
if (isCompressed) {
|
|
504
|
+
assertCompressionIsSupported();
|
|
505
|
+
const hasEvenY = !Fp.isOdd!(y);
|
|
506
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
507
|
+
} else {
|
|
508
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function pointFromBytes(bytes: Uint8Array) {
|
|
512
|
+
abytes(bytes);
|
|
513
|
+
const L = Fp.BYTES;
|
|
514
|
+
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
515
|
+
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
516
|
+
const length = bytes.length;
|
|
517
|
+
const head = bytes[0];
|
|
518
|
+
const tail = bytes.subarray(1);
|
|
519
|
+
// No actual validation is done here: use .assertValidity()
|
|
520
|
+
if (length === LC && (head === 0x02 || head === 0x03)) {
|
|
521
|
+
const x = Fp.fromBytes(tail);
|
|
522
|
+
if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
|
|
523
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
524
|
+
let y: T;
|
|
525
|
+
try {
|
|
526
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
527
|
+
} catch (sqrtError) {
|
|
528
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
529
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
530
|
+
}
|
|
531
|
+
assertCompressionIsSupported();
|
|
532
|
+
const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
|
|
533
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
534
|
+
if (isHeadOdd !== isYOdd) y = Fp.neg(y);
|
|
535
|
+
return { x, y };
|
|
536
|
+
} else if (length === LU && head === 0x04) {
|
|
537
|
+
// TODO: more checks
|
|
538
|
+
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
|
|
539
|
+
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
|
|
540
|
+
if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
|
|
541
|
+
return { x, y };
|
|
542
|
+
} else {
|
|
543
|
+
throw new Error(
|
|
544
|
+
`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const toBytes = curveOpts.toBytes || pointToBytes;
|
|
550
|
+
const fromBytes = curveOpts.fromBytes || pointFromBytes;
|
|
551
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
|
|
552
|
+
|
|
553
|
+
// TODO: move top-level
|
|
554
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
356
555
|
function isValidXY(x: T, y: T): boolean {
|
|
357
556
|
const left = Fp.sqr(y); // y²
|
|
358
557
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
@@ -369,35 +568,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
369
568
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
370
569
|
if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
|
|
371
570
|
|
|
372
|
-
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
// Validates if priv key is valid and converts it to bigint.
|
|
377
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
378
|
-
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
|
379
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
380
|
-
if (lengths && typeof key !== 'bigint') {
|
|
381
|
-
if (isBytes(key)) key = bytesToHex(key);
|
|
382
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
383
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
384
|
-
throw new Error('invalid private key');
|
|
385
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
386
|
-
}
|
|
387
|
-
let num: bigint;
|
|
388
|
-
try {
|
|
389
|
-
num =
|
|
390
|
-
typeof key === 'bigint'
|
|
391
|
-
? key
|
|
392
|
-
: bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
|
393
|
-
} catch (error) {
|
|
394
|
-
throw new Error(
|
|
395
|
-
'invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
if (wrapPrivateKey) num = mod(num, N); // disabled by default, enabled for BLS
|
|
399
|
-
aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
400
|
-
return num;
|
|
571
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
572
|
+
function acoord(title: string, n: T, banZero = false) {
|
|
573
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n))) throw new Error(`bad point coordinate ${title}`);
|
|
574
|
+
return n;
|
|
401
575
|
}
|
|
402
576
|
|
|
403
577
|
function aprjpoint(other: unknown) {
|
|
@@ -431,21 +605,33 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
431
605
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
432
606
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
433
607
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
434
|
-
if (
|
|
608
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.py)) return;
|
|
435
609
|
throw new Error('bad point: ZERO');
|
|
436
610
|
}
|
|
437
611
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
438
612
|
const { x, y } = p.toAffine();
|
|
439
|
-
|
|
440
|
-
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
|
|
613
|
+
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
|
|
441
614
|
if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
|
|
442
615
|
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
443
616
|
return true;
|
|
444
617
|
});
|
|
445
618
|
|
|
619
|
+
function finishEndo(
|
|
620
|
+
endoBeta: EndomorphismOpts['beta'],
|
|
621
|
+
k1p: Point,
|
|
622
|
+
k2p: Point,
|
|
623
|
+
k1neg: boolean,
|
|
624
|
+
k2neg: boolean
|
|
625
|
+
) {
|
|
626
|
+
k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
|
|
627
|
+
k1p = negateCt(k1neg, k1p);
|
|
628
|
+
k2p = negateCt(k2neg, k2p);
|
|
629
|
+
return k1p.add(k2p);
|
|
630
|
+
}
|
|
631
|
+
|
|
446
632
|
/**
|
|
447
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
448
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
633
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
634
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
449
635
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
450
636
|
*/
|
|
451
637
|
class Point implements ProjPointType<T> {
|
|
@@ -453,29 +639,29 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
453
639
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
454
640
|
// zero / infinity / identity point
|
|
455
641
|
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
642
|
+
// fields
|
|
643
|
+
static readonly Fp = Fp;
|
|
644
|
+
static readonly Fn = Fn;
|
|
645
|
+
|
|
456
646
|
readonly px: T;
|
|
457
647
|
readonly py: T;
|
|
458
648
|
readonly pz: T;
|
|
459
649
|
|
|
650
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
460
651
|
constructor(px: T, py: T, pz: T) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
this.px = px;
|
|
465
|
-
this.py = py;
|
|
466
|
-
this.pz = pz;
|
|
652
|
+
this.px = acoord('x', px);
|
|
653
|
+
this.py = acoord('y', py, true);
|
|
654
|
+
this.pz = acoord('z', pz);
|
|
467
655
|
Object.freeze(this);
|
|
468
656
|
}
|
|
469
657
|
|
|
470
|
-
|
|
471
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
658
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
472
659
|
static fromAffine(p: AffinePoint<T>): Point {
|
|
473
660
|
const { x, y } = p || {};
|
|
474
661
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
|
|
475
662
|
if (p instanceof Point) throw new Error('projective point not allowed');
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (is0(x) && is0(y)) return Point.ZERO;
|
|
663
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
664
|
+
if (Fp.is0(x) && Fp.is0(y)) return Point.ZERO;
|
|
479
665
|
return new Point(x, y, Fp.ONE);
|
|
480
666
|
}
|
|
481
667
|
|
|
@@ -486,59 +672,67 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
486
672
|
return this.toAffine().y;
|
|
487
673
|
}
|
|
488
674
|
|
|
489
|
-
/**
|
|
490
|
-
* Takes a bunch of Projective Points but executes only one
|
|
491
|
-
* inversion on all of them. Inversion is very slow operation,
|
|
492
|
-
* so this improves performance massively.
|
|
493
|
-
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
494
|
-
*/
|
|
495
675
|
static normalizeZ(points: Point[]): Point[] {
|
|
496
|
-
|
|
497
|
-
Fp,
|
|
498
|
-
points.map((p) => p.pz)
|
|
499
|
-
);
|
|
500
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
676
|
+
return normalizeZ(Point, 'pz', points);
|
|
501
677
|
}
|
|
502
678
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
679
|
+
static fromBytes(bytes: Uint8Array): Point {
|
|
680
|
+
abytes(bytes);
|
|
681
|
+
return Point.fromHex(bytes);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
507
685
|
static fromHex(hex: Hex): Point {
|
|
508
686
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
509
687
|
P.assertValidity();
|
|
510
688
|
return P;
|
|
511
689
|
}
|
|
512
690
|
|
|
513
|
-
|
|
691
|
+
/** Multiplies generator point by privateKey. */
|
|
514
692
|
static fromPrivateKey(privateKey: PrivKey) {
|
|
693
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(
|
|
694
|
+
Fn,
|
|
695
|
+
curveOpts.allowedPrivateKeyLengths,
|
|
696
|
+
curveOpts.wrapPrivateKey
|
|
697
|
+
);
|
|
515
698
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
516
699
|
}
|
|
517
700
|
|
|
518
|
-
|
|
701
|
+
/** Multiscalar Multiplication */
|
|
519
702
|
static msm(points: Point[], scalars: bigint[]): Point {
|
|
520
703
|
return pippenger(Point, Fn, points, scalars);
|
|
521
704
|
}
|
|
522
705
|
|
|
523
|
-
|
|
524
|
-
|
|
706
|
+
/**
|
|
707
|
+
*
|
|
708
|
+
* @param windowSize
|
|
709
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
710
|
+
* @returns
|
|
711
|
+
*/
|
|
712
|
+
precompute(windowSize: number = 8, isLazy = true): Point {
|
|
525
713
|
wnaf.setWindowSize(this, windowSize);
|
|
714
|
+
if (!isLazy) this.multiply(_3n); // random number
|
|
715
|
+
return this;
|
|
526
716
|
}
|
|
527
717
|
|
|
528
|
-
|
|
718
|
+
/** "Private method", don't use it directly */
|
|
719
|
+
_setWindowSize(windowSize: number) {
|
|
720
|
+
this.precompute(windowSize);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// TODO: return `this`
|
|
724
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
529
725
|
assertValidity(): void {
|
|
530
726
|
assertValidMemo(this);
|
|
531
727
|
}
|
|
532
728
|
|
|
533
729
|
hasEvenY(): boolean {
|
|
534
730
|
const { y } = this.toAffine();
|
|
535
|
-
if (Fp.isOdd)
|
|
536
|
-
|
|
731
|
+
if (!Fp.isOdd) throw new Error("Field doesn't support isOdd");
|
|
732
|
+
return !Fp.isOdd(y);
|
|
537
733
|
}
|
|
538
734
|
|
|
539
|
-
/**
|
|
540
|
-
* Compare one point to another.
|
|
541
|
-
*/
|
|
735
|
+
/** Compare one point to another. */
|
|
542
736
|
equals(other: Point): boolean {
|
|
543
737
|
aprjpoint(other);
|
|
544
738
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
@@ -548,9 +742,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
548
742
|
return U1 && U2;
|
|
549
743
|
}
|
|
550
744
|
|
|
551
|
-
/**
|
|
552
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
553
|
-
*/
|
|
745
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
554
746
|
negate(): Point {
|
|
555
747
|
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
556
748
|
}
|
|
@@ -656,49 +848,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
656
848
|
return this.add(other.negate());
|
|
657
849
|
}
|
|
658
850
|
|
|
659
|
-
is0() {
|
|
851
|
+
is0(): boolean {
|
|
660
852
|
return this.equals(Point.ZERO);
|
|
661
853
|
}
|
|
662
854
|
|
|
663
|
-
private wNAF(n: bigint): { p: Point; f: Point } {
|
|
664
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
669
|
-
* It's faster, but should only be used when you don't care about
|
|
670
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
671
|
-
*/
|
|
672
|
-
multiplyUnsafe(sc: bigint): Point {
|
|
673
|
-
const { endo, n: N } = CURVE;
|
|
674
|
-
aInRange('scalar', sc, _0n, N);
|
|
675
|
-
const I = Point.ZERO;
|
|
676
|
-
if (sc === _0n) return I;
|
|
677
|
-
if (this.is0() || sc === _1n) return this;
|
|
678
|
-
|
|
679
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
680
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
681
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
682
|
-
|
|
683
|
-
// Case c: endomorphism
|
|
684
|
-
/** See docs for {@link EndomorphismOpts} */
|
|
685
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
686
|
-
let k1p = I;
|
|
687
|
-
let k2p = I;
|
|
688
|
-
let d: Point = this;
|
|
689
|
-
while (k1 > _0n || k2 > _0n) {
|
|
690
|
-
if (k1 & _1n) k1p = k1p.add(d);
|
|
691
|
-
if (k2 & _1n) k2p = k2p.add(d);
|
|
692
|
-
d = d.double();
|
|
693
|
-
k1 >>= _1n;
|
|
694
|
-
k2 >>= _1n;
|
|
695
|
-
}
|
|
696
|
-
if (k1neg) k1p = k1p.negate();
|
|
697
|
-
if (k2neg) k2p = k2p.negate();
|
|
698
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
699
|
-
return k1p.add(k2p);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
855
|
/**
|
|
703
856
|
* Constant time multiplication.
|
|
704
857
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -709,21 +862,19 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
709
862
|
* @returns New point
|
|
710
863
|
*/
|
|
711
864
|
multiply(scalar: bigint): Point {
|
|
712
|
-
const { endo
|
|
713
|
-
|
|
865
|
+
const { endo } = curveOpts;
|
|
866
|
+
if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
714
867
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
868
|
+
const mul = (n: bigint) => wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
715
869
|
/** See docs for {@link EndomorphismOpts} */
|
|
716
870
|
if (endo) {
|
|
717
871
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
723
|
-
point = k1p.add(k2p);
|
|
724
|
-
fake = f1p.add(f2p);
|
|
872
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
873
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
874
|
+
fake = k1f.add(k2f);
|
|
875
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
725
876
|
} else {
|
|
726
|
-
const { p, f } =
|
|
877
|
+
const { p, f } = mul(scalar);
|
|
727
878
|
point = p;
|
|
728
879
|
fake = f;
|
|
729
880
|
}
|
|
@@ -732,60 +883,88 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
732
883
|
}
|
|
733
884
|
|
|
734
885
|
/**
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
* @returns non-zero affine point
|
|
886
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
887
|
+
* It's faster, but should only be used when you don't care about
|
|
888
|
+
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
739
889
|
*/
|
|
890
|
+
multiplyUnsafe(sc: bigint): Point {
|
|
891
|
+
const { endo } = curveOpts;
|
|
892
|
+
const p = this;
|
|
893
|
+
if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
894
|
+
if (sc === _0n || p.is0()) return Point.ZERO;
|
|
895
|
+
if (sc === _1n) return p; // fast-path
|
|
896
|
+
if (wnaf.hasPrecomputes(this)) return this.multiply(sc);
|
|
897
|
+
if (endo) {
|
|
898
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
899
|
+
// `wNAFCachedUnsafe` is 30% slower
|
|
900
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
|
|
901
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
902
|
+
} else {
|
|
903
|
+
return wnaf.wNAFCachedUnsafe(p, sc);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
740
907
|
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
|
741
|
-
const
|
|
742
|
-
const mul = (
|
|
743
|
-
P: Point,
|
|
744
|
-
a: bigint // Select faster multiply() method
|
|
745
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
746
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
908
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
747
909
|
return sum.is0() ? undefined : sum;
|
|
748
910
|
}
|
|
749
911
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
912
|
+
/**
|
|
913
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
914
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
915
|
+
*/
|
|
916
|
+
toAffine(invertedZ?: T): AffinePoint<T> {
|
|
917
|
+
return toAffineMemo(this, invertedZ);
|
|
755
918
|
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
922
|
+
* Always torsion-free for cofactor=1 curves.
|
|
923
|
+
*/
|
|
756
924
|
isTorsionFree(): boolean {
|
|
757
|
-
const {
|
|
758
|
-
if (cofactor === _1n) return true;
|
|
925
|
+
const { isTorsionFree } = curveOpts;
|
|
926
|
+
if (cofactor === _1n) return true;
|
|
759
927
|
if (isTorsionFree) return isTorsionFree(Point, this);
|
|
760
|
-
|
|
928
|
+
return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
|
|
761
929
|
}
|
|
930
|
+
|
|
762
931
|
clearCofactor(): Point {
|
|
763
|
-
const {
|
|
932
|
+
const { clearCofactor } = curveOpts;
|
|
764
933
|
if (cofactor === _1n) return this; // Fast-path
|
|
765
934
|
if (clearCofactor) return clearCofactor(Point, this) as Point;
|
|
766
|
-
return this.multiplyUnsafe(
|
|
935
|
+
return this.multiplyUnsafe(cofactor);
|
|
767
936
|
}
|
|
768
937
|
|
|
769
|
-
|
|
938
|
+
toBytes(isCompressed = true): Uint8Array {
|
|
770
939
|
abool('isCompressed', isCompressed);
|
|
771
940
|
this.assertValidity();
|
|
772
941
|
return toBytes(Point, this, isCompressed);
|
|
773
942
|
}
|
|
774
943
|
|
|
944
|
+
/** @deprecated use `toBytes` */
|
|
945
|
+
toRawBytes(isCompressed = true): Uint8Array {
|
|
946
|
+
return this.toBytes(isCompressed);
|
|
947
|
+
}
|
|
948
|
+
|
|
775
949
|
toHex(isCompressed = true): string {
|
|
776
|
-
|
|
777
|
-
|
|
950
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
toString() {
|
|
954
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
778
955
|
}
|
|
779
956
|
}
|
|
780
|
-
const
|
|
781
|
-
const wnaf = wNAF(Point, endo ? Math.ceil(
|
|
782
|
-
return
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
};
|
|
957
|
+
const bits = Fn.BITS;
|
|
958
|
+
const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
959
|
+
return Point;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// _legacyWeierstrass
|
|
963
|
+
/** @deprecated use `weierstrassN` */
|
|
964
|
+
export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
|
|
965
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
966
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
967
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
789
968
|
}
|
|
790
969
|
|
|
791
970
|
// Instance
|
|
@@ -800,57 +979,44 @@ export interface SignatureType {
|
|
|
800
979
|
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
|
801
980
|
toCompactRawBytes(): Uint8Array;
|
|
802
981
|
toCompactHex(): string;
|
|
803
|
-
toDERRawBytes(
|
|
804
|
-
toDERHex(
|
|
982
|
+
toDERRawBytes(): Uint8Array;
|
|
983
|
+
toDERHex(): string;
|
|
984
|
+
// toBytes(format?: string): Uint8Array;
|
|
805
985
|
}
|
|
806
986
|
export type RecoveredSignatureType = SignatureType & {
|
|
807
987
|
readonly recovery: number;
|
|
808
988
|
};
|
|
809
989
|
// Static methods
|
|
810
990
|
export type SignatureConstructor = {
|
|
811
|
-
new (r: bigint, s: bigint): SignatureType;
|
|
991
|
+
new (r: bigint, s: bigint, recovery?: number): SignatureType;
|
|
812
992
|
fromCompact(hex: Hex): SignatureType;
|
|
813
993
|
fromDER(hex: Hex): SignatureType;
|
|
814
994
|
};
|
|
815
|
-
type SignatureLike = { r: bigint; s: bigint };
|
|
816
|
-
|
|
995
|
+
export type SignatureLike = { r: bigint; s: bigint };
|
|
817
996
|
export type PubKey = Hex | ProjPointType<bigint>;
|
|
818
997
|
|
|
819
998
|
export type CurveType = BasicWCurve<bigint> & {
|
|
820
999
|
hash: CHash; // CHash not FHash because we need outputLen for DRBG
|
|
821
|
-
hmac
|
|
822
|
-
randomBytes
|
|
1000
|
+
hmac?: HmacFnSync;
|
|
1001
|
+
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
823
1002
|
lowS?: boolean;
|
|
824
1003
|
bits2int?: (bytes: Uint8Array) => bigint;
|
|
825
1004
|
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
|
826
1005
|
};
|
|
827
1006
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const opts = validateBasic(curve);
|
|
832
|
-
validateObject(
|
|
833
|
-
opts,
|
|
834
|
-
{
|
|
835
|
-
hash: 'hash',
|
|
836
|
-
hmac: 'function',
|
|
837
|
-
randomBytes: 'function',
|
|
838
|
-
},
|
|
839
|
-
{
|
|
840
|
-
bits2int: 'function',
|
|
841
|
-
bits2int_modN: 'function',
|
|
842
|
-
lowS: 'boolean',
|
|
843
|
-
}
|
|
844
|
-
);
|
|
845
|
-
return Object.freeze({ lowS: true, ...opts } as const);
|
|
1007
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
1008
|
+
function pprefix(hasEvenY: boolean): Uint8Array {
|
|
1009
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
846
1010
|
}
|
|
847
1011
|
|
|
848
1012
|
export type CurveFn = {
|
|
849
|
-
CURVE:
|
|
1013
|
+
CURVE: CurvePointsType<bigint>;
|
|
850
1014
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
851
1015
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
852
1016
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
|
|
853
1017
|
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
|
1018
|
+
Point: ProjConstructor<bigint>;
|
|
1019
|
+
/** @deprecated use `Point` */
|
|
854
1020
|
ProjectivePoint: ProjConstructor<bigint>;
|
|
855
1021
|
Signature: SignatureConstructor;
|
|
856
1022
|
utils: {
|
|
@@ -861,78 +1027,30 @@ export type CurveFn = {
|
|
|
861
1027
|
};
|
|
862
1028
|
};
|
|
863
1029
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
function invN(a: bigint) {
|
|
881
|
-
return invert(a, CURVE_ORDER);
|
|
882
|
-
}
|
|
1030
|
+
export function ecdsa(
|
|
1031
|
+
Point: ProjConstructor<bigint>,
|
|
1032
|
+
ecdsaOpts: ECDSAOpts,
|
|
1033
|
+
curveOpts: WeierstrassExtraOpts<bigint> = {}
|
|
1034
|
+
): ECDSA {
|
|
1035
|
+
_validateObject(
|
|
1036
|
+
ecdsaOpts,
|
|
1037
|
+
{ hash: 'function' },
|
|
1038
|
+
{
|
|
1039
|
+
hmac: 'function',
|
|
1040
|
+
lowS: 'boolean',
|
|
1041
|
+
randomBytes: 'function',
|
|
1042
|
+
bits2int: 'function',
|
|
1043
|
+
bits2int_modN: 'function',
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
883
1046
|
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
} =
|
|
890
|
-
|
|
891
|
-
toBytes(_c, point, isCompressed: boolean): Uint8Array {
|
|
892
|
-
const a = point.toAffine();
|
|
893
|
-
const x = Fp.toBytes(a.x);
|
|
894
|
-
const cat = concatBytes;
|
|
895
|
-
abool('isCompressed', isCompressed);
|
|
896
|
-
if (isCompressed) {
|
|
897
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
898
|
-
} else {
|
|
899
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
900
|
-
}
|
|
901
|
-
},
|
|
902
|
-
fromBytes(bytes: Uint8Array) {
|
|
903
|
-
const len = bytes.length;
|
|
904
|
-
const head = bytes[0];
|
|
905
|
-
const tail = bytes.subarray(1);
|
|
906
|
-
// this.assertValidity() is done inside of fromHex
|
|
907
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
908
|
-
const x = bytesToNumberBE(tail);
|
|
909
|
-
if (!inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
|
|
910
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
911
|
-
let y: bigint;
|
|
912
|
-
try {
|
|
913
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
914
|
-
} catch (sqrtError) {
|
|
915
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
916
|
-
throw new Error('Point is not on curve' + suffix);
|
|
917
|
-
}
|
|
918
|
-
const isYOdd = (y & _1n) === _1n;
|
|
919
|
-
// ECDSA
|
|
920
|
-
const isHeadOdd = (head & 1) === 1;
|
|
921
|
-
if (isHeadOdd !== isYOdd) y = Fp.neg(y);
|
|
922
|
-
return { x, y };
|
|
923
|
-
} else if (len === uncompressedLen && head === 0x04) {
|
|
924
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
925
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
926
|
-
return { x, y };
|
|
927
|
-
} else {
|
|
928
|
-
const cl = compressedLen;
|
|
929
|
-
const ul = uncompressedLen;
|
|
930
|
-
throw new Error(
|
|
931
|
-
'invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
},
|
|
935
|
-
});
|
|
1047
|
+
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
1048
|
+
const hmac_: HmacFnSync =
|
|
1049
|
+
ecdsaOpts.hmac ||
|
|
1050
|
+
(((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1051
|
+
|
|
1052
|
+
const { Fp, Fn } = Point;
|
|
1053
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
936
1054
|
|
|
937
1055
|
function isBiggerThanHalfOrder(number: bigint) {
|
|
938
1056
|
const HALF = CURVE_ORDER >> _1n;
|
|
@@ -940,10 +1058,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
940
1058
|
}
|
|
941
1059
|
|
|
942
1060
|
function normalizeS(s: bigint) {
|
|
943
|
-
return isBiggerThanHalfOrder(s) ?
|
|
1061
|
+
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
1062
|
+
}
|
|
1063
|
+
function aValidRS(title: string, num: bigint) {
|
|
1064
|
+
if (!Fn.isValidNot0(num))
|
|
1065
|
+
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
|
|
944
1066
|
}
|
|
945
|
-
// slice bytes num
|
|
946
|
-
const slcNum = (b: Uint8Array, from: number, to: number) => bytesToNumberBE(b.slice(from, to));
|
|
947
1067
|
|
|
948
1068
|
/**
|
|
949
1069
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
@@ -953,8 +1073,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
953
1073
|
readonly s: bigint;
|
|
954
1074
|
readonly recovery?: number;
|
|
955
1075
|
constructor(r: bigint, s: bigint, recovery?: number) {
|
|
956
|
-
|
|
957
|
-
|
|
1076
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
1077
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
958
1078
|
this.r = r;
|
|
959
1079
|
this.s = s;
|
|
960
1080
|
if (recovery != null) this.recovery = recovery;
|
|
@@ -963,9 +1083,9 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
963
1083
|
|
|
964
1084
|
// pair (bytes of r, bytes of s)
|
|
965
1085
|
static fromCompact(hex: Hex) {
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
return new Signature(
|
|
1086
|
+
const L = Fn.BYTES;
|
|
1087
|
+
const b = ensureBytes('compactSignature', hex, L * 2);
|
|
1088
|
+
return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
|
|
969
1089
|
}
|
|
970
1090
|
|
|
971
1091
|
// DER encoded ECDSA signature
|
|
@@ -985,19 +1105,34 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
985
1105
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
986
1106
|
}
|
|
987
1107
|
|
|
1108
|
+
// ProjPointType<bigint>
|
|
988
1109
|
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
|
1110
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
989
1111
|
const { r, s, recovery: rec } = this;
|
|
990
|
-
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
991
1112
|
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1113
|
+
|
|
1114
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
1115
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
1116
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
1117
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
1118
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
1119
|
+
// To easily get i, we either need to:
|
|
1120
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
1121
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
1122
|
+
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
1123
|
+
if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
|
|
1124
|
+
|
|
1125
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
1126
|
+
if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
|
|
1127
|
+
const x = Fp.toBytes(radj);
|
|
1128
|
+
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
|
|
1129
|
+
const ir = Fn.inv(radj); // r^-1
|
|
1130
|
+
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
1131
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
1132
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
1133
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
1134
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
1135
|
+
if (Q.is0()) throw new Error('point at infinify');
|
|
1001
1136
|
Q.assertValidity();
|
|
1002
1137
|
return Q;
|
|
1003
1138
|
}
|
|
@@ -1008,28 +1143,39 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1008
1143
|
}
|
|
1009
1144
|
|
|
1010
1145
|
normalizeS() {
|
|
1011
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
1146
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
toBytes(format: 'compact' | 'der') {
|
|
1150
|
+
if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
1151
|
+
if (format === 'der') return hexToBytes(DER.hexFromSig(this));
|
|
1152
|
+
throw new Error('invalid format');
|
|
1012
1153
|
}
|
|
1013
1154
|
|
|
1014
1155
|
// DER-encoded
|
|
1015
1156
|
toDERRawBytes() {
|
|
1016
|
-
return
|
|
1157
|
+
return this.toBytes('der');
|
|
1017
1158
|
}
|
|
1018
1159
|
toDERHex() {
|
|
1019
|
-
return
|
|
1160
|
+
return bytesToHex(this.toBytes('der'));
|
|
1020
1161
|
}
|
|
1021
1162
|
|
|
1022
1163
|
// padded bytes of r, then padded bytes of s
|
|
1023
1164
|
toCompactRawBytes() {
|
|
1024
|
-
return
|
|
1165
|
+
return this.toBytes('compact');
|
|
1025
1166
|
}
|
|
1026
1167
|
toCompactHex() {
|
|
1027
|
-
|
|
1028
|
-
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
1168
|
+
return bytesToHex(this.toBytes('compact'));
|
|
1029
1169
|
}
|
|
1030
1170
|
}
|
|
1031
1171
|
type RecoveredSignature = Signature & { recovery: number };
|
|
1032
1172
|
|
|
1173
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(
|
|
1174
|
+
Fn,
|
|
1175
|
+
curveOpts.allowedPrivateKeyLengths,
|
|
1176
|
+
curveOpts.wrapPrivateKey
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1033
1179
|
const utils = {
|
|
1034
1180
|
isValidPrivateKey(privateKey: PrivKey) {
|
|
1035
1181
|
try {
|
|
@@ -1046,22 +1192,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1046
1192
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1047
1193
|
*/
|
|
1048
1194
|
randomPrivateKey: (): Uint8Array => {
|
|
1049
|
-
const
|
|
1050
|
-
return mapHashToField(
|
|
1195
|
+
const n = CURVE_ORDER;
|
|
1196
|
+
return mapHashToField(randomBytes_(getMinHashLength(n)), n);
|
|
1051
1197
|
},
|
|
1052
1198
|
|
|
1053
|
-
/**
|
|
1054
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
1055
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
1056
|
-
* @returns cached point
|
|
1057
|
-
* @example
|
|
1058
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
1059
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
1060
|
-
*/
|
|
1061
1199
|
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
|
1062
|
-
point.
|
|
1063
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
1064
|
-
return point;
|
|
1200
|
+
return point.precompute(windowSize, false);
|
|
1065
1201
|
},
|
|
1066
1202
|
};
|
|
1067
1203
|
|
|
@@ -1072,7 +1208,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1072
1208
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1073
1209
|
*/
|
|
1074
1210
|
function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
|
|
1075
|
-
return Point.fromPrivateKey(privateKey).
|
|
1211
|
+
return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
|
|
1076
1212
|
}
|
|
1077
1213
|
|
|
1078
1214
|
/**
|
|
@@ -1082,14 +1218,14 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1082
1218
|
if (typeof item === 'bigint') return false;
|
|
1083
1219
|
if (item instanceof Point) return true;
|
|
1084
1220
|
const arr = ensureBytes('key', item);
|
|
1085
|
-
const
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1088
|
-
const
|
|
1089
|
-
if (
|
|
1221
|
+
const length = arr.length;
|
|
1222
|
+
const L = Fp.BYTES;
|
|
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) {
|
|
1090
1226
|
return undefined;
|
|
1091
1227
|
} else {
|
|
1092
|
-
return
|
|
1228
|
+
return length === LC || length === LU;
|
|
1093
1229
|
}
|
|
1094
1230
|
}
|
|
1095
1231
|
|
|
@@ -1107,7 +1243,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1107
1243
|
if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
|
|
1108
1244
|
if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
|
|
1109
1245
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
1110
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).
|
|
1246
|
+
return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
|
|
1111
1247
|
}
|
|
1112
1248
|
|
|
1113
1249
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
@@ -1115,30 +1251,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1115
1251
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1116
1252
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1117
1253
|
const bits2int =
|
|
1118
|
-
|
|
1254
|
+
ecdsaOpts.bits2int ||
|
|
1119
1255
|
function (bytes: Uint8Array): bigint {
|
|
1120
1256
|
// Our custom check "just in case", for protection against DoS
|
|
1121
1257
|
if (bytes.length > 8192) throw new Error('input is too large');
|
|
1122
1258
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
1123
1259
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
1124
1260
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1125
|
-
const delta = bytes.length * 8 -
|
|
1261
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
1126
1262
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1127
1263
|
};
|
|
1128
1264
|
const bits2int_modN =
|
|
1129
|
-
|
|
1265
|
+
ecdsaOpts.bits2int_modN ||
|
|
1130
1266
|
function (bytes: Uint8Array): bigint {
|
|
1131
|
-
return
|
|
1267
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1132
1268
|
};
|
|
1133
1269
|
// NOTE: pads output with zero as per spec
|
|
1134
|
-
const ORDER_MASK = bitMask(
|
|
1270
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
1135
1271
|
/**
|
|
1136
1272
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
1137
1273
|
*/
|
|
1138
1274
|
function int2octets(num: bigint): Uint8Array {
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
return
|
|
1275
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1276
|
+
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1277
|
+
return Fn.toBytes(num);
|
|
1142
1278
|
}
|
|
1143
1279
|
|
|
1144
1280
|
// Steps A, D of RFC6979 3.2
|
|
@@ -1149,7 +1285,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1149
1285
|
function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
|
|
1150
1286
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1151
1287
|
throw new Error('sign() legacy options not supported');
|
|
1152
|
-
const { hash
|
|
1288
|
+
const { hash } = ecdsaOpts;
|
|
1153
1289
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
1154
1290
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
1155
1291
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
@@ -1157,7 +1293,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1157
1293
|
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
1158
1294
|
|
|
1159
1295
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1160
|
-
// with
|
|
1296
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1161
1297
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1162
1298
|
const h1int = bits2int_modN(msgHash);
|
|
1163
1299
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
|
@@ -1165,24 +1301,25 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1165
1301
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1166
1302
|
if (ent != null && ent !== false) {
|
|
1167
1303
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1168
|
-
const e = ent === true ?
|
|
1304
|
+
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
1169
1305
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
1170
1306
|
}
|
|
1171
1307
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
1172
1308
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
1173
1309
|
// Converts signature params into point w r/s, checks result for validity.
|
|
1310
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1311
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1312
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1174
1313
|
function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
|
|
1175
1314
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1315
|
+
// Important: all mod() calls here must be done over N
|
|
1176
1316
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
1177
|
-
if (!
|
|
1178
|
-
const ik =
|
|
1317
|
+
if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
|
|
1318
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
1179
1319
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
1180
|
-
const r =
|
|
1320
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
1181
1321
|
if (r === _0n) return;
|
|
1182
|
-
|
|
1183
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1184
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1185
|
-
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
|
1322
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
1186
1323
|
if (s === _0n) return;
|
|
1187
1324
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
1188
1325
|
let normS = s;
|
|
@@ -1194,8 +1331,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1194
1331
|
}
|
|
1195
1332
|
return { seed, k2sig };
|
|
1196
1333
|
}
|
|
1197
|
-
const defaultSigOpts: SignOpts = { lowS:
|
|
1198
|
-
const defaultVerOpts: VerOpts = { lowS:
|
|
1334
|
+
const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1335
|
+
const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1199
1336
|
|
|
1200
1337
|
/**
|
|
1201
1338
|
* Signs message hash with a private key.
|
|
@@ -1212,14 +1349,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1212
1349
|
*/
|
|
1213
1350
|
function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
|
|
1214
1351
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1215
|
-
const
|
|
1216
|
-
const drbg = createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
1352
|
+
const drbg = createHmacDrbg<RecoveredSignature>(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
|
|
1217
1353
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1218
1354
|
}
|
|
1219
1355
|
|
|
1220
1356
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1221
|
-
Point.BASE.
|
|
1222
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1357
|
+
Point.BASE.precompute(8);
|
|
1223
1358
|
|
|
1224
1359
|
/**
|
|
1225
1360
|
* Verifies a signature against message hash and public key.
|
|
@@ -1243,13 +1378,16 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1243
1378
|
const sg = signature;
|
|
1244
1379
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1245
1380
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1246
|
-
const { lowS, prehash, format } = opts;
|
|
1247
1381
|
|
|
1248
|
-
// Verify opts
|
|
1382
|
+
// Verify opts
|
|
1249
1383
|
validateSigVerOpts(opts);
|
|
1384
|
+
const { lowS, prehash, format } = opts;
|
|
1385
|
+
|
|
1386
|
+
// TODO: remove
|
|
1250
1387
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
|
1251
|
-
|
|
1252
|
-
|
|
1388
|
+
|
|
1389
|
+
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
1390
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1253
1391
|
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1254
1392
|
const isObj =
|
|
1255
1393
|
!isHex &&
|
|
@@ -1260,14 +1398,31 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1260
1398
|
typeof sg.s === 'bigint';
|
|
1261
1399
|
if (!isHex && !isObj)
|
|
1262
1400
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1263
|
-
|
|
1264
1401
|
let _sig: Signature | undefined = undefined;
|
|
1265
1402
|
let P: ProjPointType<bigint>;
|
|
1403
|
+
|
|
1404
|
+
// deduce signature format
|
|
1266
1405
|
try {
|
|
1267
|
-
if (
|
|
1406
|
+
// if (format === 'js') {
|
|
1407
|
+
// if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
|
|
1408
|
+
// } else if (format === 'compact') {
|
|
1409
|
+
// _sig = Signature.fromCompact(sg);
|
|
1410
|
+
// } else if (format === 'der') {
|
|
1411
|
+
// _sig = Signature.fromDER(sg);
|
|
1412
|
+
// } else {
|
|
1413
|
+
// throw new Error('invalid format');
|
|
1414
|
+
// }
|
|
1415
|
+
if (isObj) {
|
|
1416
|
+
if (format === undefined || format === 'js') {
|
|
1417
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1418
|
+
} else {
|
|
1419
|
+
throw new Error('invalid format');
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1268
1422
|
if (isHex) {
|
|
1269
|
-
//
|
|
1270
|
-
//
|
|
1423
|
+
// TODO: remove this malleable check
|
|
1424
|
+
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1425
|
+
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1271
1426
|
try {
|
|
1272
1427
|
if (format !== 'compact') _sig = Signature.fromDER(sg);
|
|
1273
1428
|
} catch (derError) {
|
|
@@ -1281,27 +1436,118 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1281
1436
|
}
|
|
1282
1437
|
if (!_sig) return false;
|
|
1283
1438
|
if (lowS && _sig.hasHighS()) return false;
|
|
1284
|
-
|
|
1439
|
+
// todo: optional.hash => hash
|
|
1440
|
+
if (prehash) msgHash = ecdsaOpts.hash(msgHash);
|
|
1285
1441
|
const { r, s } = _sig;
|
|
1286
1442
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1287
|
-
const is =
|
|
1288
|
-
const u1 =
|
|
1289
|
-
const u2 =
|
|
1290
|
-
const R = Point.BASE.
|
|
1291
|
-
if (
|
|
1292
|
-
const v =
|
|
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
|
|
1293
1449
|
return v === r;
|
|
1294
1450
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1451
|
+
// TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
|
|
1452
|
+
// const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
|
|
1453
|
+
return Object.freeze({
|
|
1297
1454
|
getPublicKey,
|
|
1298
1455
|
getSharedSecret,
|
|
1299
1456
|
sign,
|
|
1300
1457
|
verify,
|
|
1301
|
-
ProjectivePoint: Point,
|
|
1302
|
-
Signature,
|
|
1303
1458
|
utils,
|
|
1459
|
+
Point,
|
|
1460
|
+
Signature,
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
export type WsPointComposed<T> = {
|
|
1465
|
+
CURVE: WeierstrassOpts<T>;
|
|
1466
|
+
curveOpts: WeierstrassExtraOpts<T>;
|
|
1467
|
+
};
|
|
1468
|
+
export type WsComposed = {
|
|
1469
|
+
CURVE: WeierstrassOpts<bigint>;
|
|
1470
|
+
curveOpts: WeierstrassExtraOpts<bigint>;
|
|
1471
|
+
ecdsaOpts: ECDSAOpts;
|
|
1472
|
+
};
|
|
1473
|
+
function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
|
|
1474
|
+
const CURVE: WeierstrassOpts<T> = {
|
|
1475
|
+
a: c.a,
|
|
1476
|
+
b: c.b,
|
|
1477
|
+
p: c.Fp.ORDER,
|
|
1478
|
+
n: c.n,
|
|
1479
|
+
h: c.h,
|
|
1480
|
+
Gx: c.Gx,
|
|
1481
|
+
Gy: c.Gy,
|
|
1304
1482
|
};
|
|
1483
|
+
const Fp = c.Fp;
|
|
1484
|
+
const Fn = Field(CURVE.n, c.nBitLength);
|
|
1485
|
+
const curveOpts: WeierstrassExtraOpts<T> = {
|
|
1486
|
+
Fp,
|
|
1487
|
+
Fn,
|
|
1488
|
+
allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
|
|
1489
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
1490
|
+
endo: c.endo,
|
|
1491
|
+
wrapPrivateKey: c.wrapPrivateKey,
|
|
1492
|
+
isTorsionFree: c.isTorsionFree,
|
|
1493
|
+
clearCofactor: c.clearCofactor,
|
|
1494
|
+
fromBytes: c.fromBytes,
|
|
1495
|
+
toBytes: c.toBytes,
|
|
1496
|
+
};
|
|
1497
|
+
return { CURVE, curveOpts };
|
|
1498
|
+
}
|
|
1499
|
+
function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
|
|
1500
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1501
|
+
const ecdsaOpts: ECDSAOpts = {
|
|
1502
|
+
hash: c.hash,
|
|
1503
|
+
hmac: c.hmac,
|
|
1504
|
+
randomBytes: c.randomBytes,
|
|
1505
|
+
lowS: c.lowS,
|
|
1506
|
+
bits2int: c.bits2int,
|
|
1507
|
+
bits2int_modN: c.bits2int_modN,
|
|
1508
|
+
};
|
|
1509
|
+
return { CURVE, curveOpts, ecdsaOpts };
|
|
1510
|
+
}
|
|
1511
|
+
function _weierstrass_new_output_to_legacy<T>(
|
|
1512
|
+
c: CurvePointsType<T>,
|
|
1513
|
+
Point: ProjConstructor<T>
|
|
1514
|
+
): CurvePointsRes<T> {
|
|
1515
|
+
const { Fp, Fn } = Point;
|
|
1516
|
+
// TODO: remove
|
|
1517
|
+
function isWithinCurveOrder(num: bigint): boolean {
|
|
1518
|
+
return inRange(num, _1n, Fn.ORDER);
|
|
1519
|
+
}
|
|
1520
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1521
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(
|
|
1522
|
+
Fn,
|
|
1523
|
+
c.allowedPrivateKeyLengths,
|
|
1524
|
+
c.wrapPrivateKey
|
|
1525
|
+
);
|
|
1526
|
+
return Object.assign(
|
|
1527
|
+
{},
|
|
1528
|
+
{
|
|
1529
|
+
CURVE: c,
|
|
1530
|
+
Point: Point,
|
|
1531
|
+
ProjectivePoint: Point,
|
|
1532
|
+
normPrivateKeyToScalar,
|
|
1533
|
+
weierstrassEquation,
|
|
1534
|
+
isWithinCurveOrder,
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
|
|
1539
|
+
return Object.assign({}, ecdsa, {
|
|
1540
|
+
ProjectivePoint: ecdsa.Point,
|
|
1541
|
+
CURVE: c,
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// _ecdsa_legacy
|
|
1546
|
+
export function weierstrass(c: CurveType): CurveFn {
|
|
1547
|
+
const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1548
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1549
|
+
const signs = ecdsa(Point, ecdsaOpts, curveOpts);
|
|
1550
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1305
1551
|
}
|
|
1306
1552
|
|
|
1307
1553
|
/**
|
|
@@ -1397,30 +1643,31 @@ export function mapToCurveSimpleSWU<T>(
|
|
|
1397
1643
|
}
|
|
1398
1644
|
): (u: T) => { x: T; y: T } {
|
|
1399
1645
|
validateField(Fp);
|
|
1400
|
-
|
|
1646
|
+
const { A, B, Z } = opts;
|
|
1647
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1401
1648
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1402
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp,
|
|
1403
|
-
if (!Fp.isOdd) throw new Error('
|
|
1649
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1650
|
+
if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
|
|
1404
1651
|
// Input: u, an element of F.
|
|
1405
1652
|
// Output: (x, y), a point on E.
|
|
1406
1653
|
return (u: T): { x: T; y: T } => {
|
|
1407
1654
|
// prettier-ignore
|
|
1408
1655
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1409
1656
|
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1410
|
-
tv1 = Fp.mul(tv1,
|
|
1657
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1411
1658
|
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1412
1659
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1413
1660
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1414
|
-
tv3 = Fp.mul(tv3,
|
|
1415
|
-
tv4 = Fp.cmov(
|
|
1416
|
-
tv4 = Fp.mul(tv4,
|
|
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
|
|
1417
1664
|
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1418
1665
|
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1419
|
-
tv5 = Fp.mul(tv6,
|
|
1666
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1420
1667
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1421
1668
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1422
1669
|
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1423
|
-
tv5 = Fp.mul(tv6,
|
|
1670
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1424
1671
|
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1425
1672
|
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1426
1673
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|