@noble/curves 2.0.0 → 2.2.0
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 +214 -122
- package/abstract/bls.d.ts +299 -16
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +89 -24
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +274 -27
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +177 -23
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +166 -30
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +221 -86
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +327 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +155 -12
- package/abstract/fft.js.map +1 -1
- package/abstract/frost.d.ts +293 -0
- package/abstract/frost.d.ts.map +1 -0
- package/abstract/frost.js +704 -0
- package/abstract/frost.js.map +1 -0
- package/abstract/hash-to-curve.d.ts +173 -24
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +170 -31
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +429 -37
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +414 -119
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +83 -12
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +32 -7
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +164 -91
- package/abstract/oprf.d.ts.map +1 -1
- package/abstract/oprf.js +88 -29
- package/abstract/oprf.js.map +1 -1
- package/abstract/poseidon.d.ts +138 -7
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +178 -15
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +122 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +323 -139
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +339 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +395 -205
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +16 -2
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +199 -209
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +11 -2
- package/bn254.d.ts.map +1 -1
- package/bn254.js +93 -38
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +135 -14
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +207 -41
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +108 -14
- package/ed448.d.ts.map +1 -1
- package/ed448.js +194 -42
- package/ed448.js.map +1 -1
- package/index.js +7 -1
- package/index.js.map +1 -1
- package/misc.d.ts +106 -7
- package/misc.d.ts.map +1 -1
- package/misc.js +141 -32
- package/misc.js.map +1 -1
- package/nist.d.ts +112 -11
- package/nist.d.ts.map +1 -1
- package/nist.js +139 -17
- package/nist.js.map +1 -1
- package/package.json +34 -6
- package/secp256k1.d.ts +92 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +211 -28
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +356 -69
- package/src/abstract/curve.ts +327 -44
- package/src/abstract/edwards.ts +367 -143
- package/src/abstract/fft.ts +371 -36
- package/src/abstract/frost.ts +1092 -0
- package/src/abstract/hash-to-curve.ts +255 -56
- package/src/abstract/modular.ts +591 -144
- package/src/abstract/montgomery.ts +114 -30
- package/src/abstract/oprf.ts +383 -194
- package/src/abstract/poseidon.ts +235 -35
- package/src/abstract/tower.ts +428 -159
- package/src/abstract/weierstrass.ts +710 -312
- package/src/bls12-381.ts +239 -236
- package/src/bn254.ts +107 -46
- package/src/ed25519.ts +234 -56
- package/src/ed448.ts +227 -57
- package/src/index.ts +7 -1
- package/src/misc.ts +154 -35
- package/src/nist.ts +143 -20
- package/src/secp256k1.ts +284 -41
- package/src/utils.ts +583 -81
- package/src/webcrypto.ts +302 -73
- package/utils.d.ts +457 -24
- package/utils.d.ts.map +1 -1
- package/utils.js +410 -53
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +167 -25
- package/webcrypto.d.ts.map +1 -1
- package/webcrypto.js +165 -58
- package/webcrypto.js.map +1 -1
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
|
|
29
29
|
import { ahash } from '@noble/hashes/utils.js';
|
|
30
30
|
import {
|
|
31
|
+
abignumber,
|
|
31
32
|
abool,
|
|
32
33
|
abytes,
|
|
33
34
|
aInRange,
|
|
35
|
+
asafenumber,
|
|
34
36
|
bitLen,
|
|
35
37
|
bitMask,
|
|
36
38
|
bytesToHex,
|
|
@@ -39,12 +41,14 @@ import {
|
|
|
39
41
|
createHmacDrbg,
|
|
40
42
|
hexToBytes,
|
|
41
43
|
isBytes,
|
|
42
|
-
memoized,
|
|
43
44
|
numberToHexUnpadded,
|
|
44
45
|
validateObject,
|
|
45
46
|
randomBytes as wcRandomBytes,
|
|
46
47
|
type CHash,
|
|
48
|
+
type HmacFn,
|
|
47
49
|
type Signer,
|
|
50
|
+
type TArg,
|
|
51
|
+
type TRet,
|
|
48
52
|
} from '../utils.ts';
|
|
49
53
|
import {
|
|
50
54
|
createCurveFields,
|
|
@@ -60,12 +64,14 @@ import {
|
|
|
60
64
|
} from './curve.ts';
|
|
61
65
|
import {
|
|
62
66
|
FpInvertBatch,
|
|
67
|
+
FpIsSquare,
|
|
63
68
|
getMinHashLength,
|
|
64
69
|
mapHashToField,
|
|
65
70
|
validateField,
|
|
66
71
|
type IField,
|
|
67
72
|
} from './modular.ts';
|
|
68
73
|
|
|
74
|
+
/** Shared affine point shape used by Weierstrass helpers. */
|
|
69
75
|
export type { AffinePoint };
|
|
70
76
|
|
|
71
77
|
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
|
@@ -90,24 +96,45 @@ type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
|
|
90
96
|
* Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
|
|
91
97
|
*
|
|
92
98
|
* Check out `test/misc/endomorphism.js` and
|
|
93
|
-
*
|
|
99
|
+
* {@link https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 | this endomorphism gist}.
|
|
94
100
|
*/
|
|
95
101
|
export type EndomorphismOpts = {
|
|
102
|
+
/** Cube root of unity used by the GLV endomorphism. */
|
|
96
103
|
beta: bigint;
|
|
104
|
+
/** Reduced lattice basis used for scalar splitting. */
|
|
97
105
|
basises?: EndoBasis;
|
|
106
|
+
/**
|
|
107
|
+
* Optional custom scalar-splitting helper.
|
|
108
|
+
* Receives one scalar and returns two half-sized scalar components.
|
|
109
|
+
*/
|
|
98
110
|
splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
99
111
|
};
|
|
100
|
-
// We construct basis
|
|
112
|
+
// We construct the basis so `den` is always positive and equals `n`,
|
|
113
|
+
// but the `num` sign depends on the basis, not on the secret value.
|
|
114
|
+
// Exact half-way cases round away from zero, which keeps the split symmetric
|
|
115
|
+
// around the reduced-basis boundaries used by endomorphism decomposition.
|
|
101
116
|
const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
/** Two half-sized scalar components returned by endomorphism splitting. */
|
|
119
|
+
export type ScalarEndoParts = {
|
|
120
|
+
/** Whether the first split scalar should be negated. */
|
|
121
|
+
k1neg: boolean;
|
|
122
|
+
/** Absolute value of the first split scalar. */
|
|
123
|
+
k1: bigint;
|
|
124
|
+
/** Whether the second split scalar should be negated. */
|
|
125
|
+
k2neg: boolean;
|
|
126
|
+
/** Absolute value of the second split scalar. */
|
|
127
|
+
k2: bigint;
|
|
128
|
+
};
|
|
104
129
|
|
|
105
|
-
/**
|
|
106
|
-
* Splits scalar for GLV endomorphism.
|
|
107
|
-
*/
|
|
130
|
+
/** Splits scalar for GLV endomorphism. */
|
|
108
131
|
export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts {
|
|
109
132
|
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
|
|
110
133
|
// Since part can be negative, we need to do this on point.
|
|
134
|
+
// Callers must provide a reduced GLV basis whose vectors satisfy
|
|
135
|
+
// `a + b * lambda ≡ 0 (mod n)`; this helper only sees the basis and `n`.
|
|
136
|
+
// Reject unreduced scalars instead of silently treating them mod n.
|
|
137
|
+
aInRange('scalar', k, _0n, n);
|
|
111
138
|
// TODO: verifyScalar function which consumes lambda
|
|
112
139
|
const [[a1, b1], [a2, b2]] = basis;
|
|
113
140
|
const c1 = divNearest(b2 * k, n);
|
|
@@ -121,10 +148,11 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
|
|
|
121
148
|
if (k1neg) k1 = -k1;
|
|
122
149
|
if (k2neg) k2 = -k2;
|
|
123
150
|
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
124
|
-
// This should only happen on wrong
|
|
151
|
+
// This should only happen on wrong bases.
|
|
152
|
+
// Also, the math inside is complex enough that this guard is worth keeping.
|
|
125
153
|
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
|
|
126
154
|
if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
|
|
127
|
-
throw new Error('splitScalar (endomorphism): failed
|
|
155
|
+
throw new Error('splitScalar (endomorphism): failed for k');
|
|
128
156
|
}
|
|
129
157
|
return { k1neg, k1, k2neg, k2 };
|
|
130
158
|
}
|
|
@@ -143,7 +171,7 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
|
|
|
143
171
|
* * `false` means "disable extra entropy, use purely deterministic k"
|
|
144
172
|
* * `Uint8Array` passed means "incorporate following data into k generation"
|
|
145
173
|
*
|
|
146
|
-
* https://paulmillr.com/posts/deterministic-signatures/
|
|
174
|
+
* See {@link https://paulmillr.com/posts/deterministic-signatures/ | deterministic signatures}.
|
|
147
175
|
*/
|
|
148
176
|
export type ECDSAExtraEntropy = boolean | Uint8Array;
|
|
149
177
|
/**
|
|
@@ -157,36 +185,45 @@ export type ECDSASignatureFormat = 'compact' | 'recovered' | 'der';
|
|
|
157
185
|
* When a custom hash is used, it must be set to `false`.
|
|
158
186
|
*/
|
|
159
187
|
export type ECDSARecoverOpts = {
|
|
188
|
+
/** Whether to hash the message before signature recovery. */
|
|
160
189
|
prehash?: boolean;
|
|
161
190
|
};
|
|
162
191
|
/**
|
|
163
192
|
* - `prehash`: (default: true) indicates whether to do sha256(message).
|
|
164
193
|
* When a custom hash is used, it must be set to `false`.
|
|
165
|
-
* - `lowS`: (default: true) prohibits signatures
|
|
194
|
+
* - `lowS`: (default: true) prohibits signatures with `sig.s >= CURVE.n/2n`.
|
|
166
195
|
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
|
|
167
196
|
* which is default openssl behavior.
|
|
168
197
|
* Non-malleable signatures can still be successfully verified in openssl.
|
|
169
198
|
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
|
|
170
199
|
*/
|
|
171
200
|
export type ECDSAVerifyOpts = {
|
|
201
|
+
/** Whether to hash the message before verification. */
|
|
172
202
|
prehash?: boolean;
|
|
203
|
+
/** Whether to reject high-S signatures. */
|
|
173
204
|
lowS?: boolean;
|
|
205
|
+
/** Signature encoding to accept. */
|
|
174
206
|
format?: ECDSASignatureFormat;
|
|
175
207
|
};
|
|
176
208
|
/**
|
|
177
209
|
* - `prehash`: (default: true) indicates whether to do sha256(message).
|
|
178
210
|
* When a custom hash is used, it must be set to `false`.
|
|
179
|
-
* - `lowS`: (default: true) prohibits signatures
|
|
211
|
+
* - `lowS`: (default: true) prohibits signatures with `sig.s >= CURVE.n/2n`.
|
|
180
212
|
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
|
|
181
213
|
* which is default openssl behavior.
|
|
182
214
|
* Non-malleable signatures can still be successfully verified in openssl.
|
|
183
215
|
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
|
|
184
|
-
* - `extraEntropy`: (default: false) creates
|
|
216
|
+
* - `extraEntropy`: (default: false) creates signatures with increased
|
|
217
|
+
* security, see {@link ECDSAExtraEntropy}
|
|
185
218
|
*/
|
|
186
219
|
export type ECDSASignOpts = {
|
|
220
|
+
/** Whether to hash the message before signing. */
|
|
187
221
|
prehash?: boolean;
|
|
222
|
+
/** Whether to normalize signatures into the low-S half-order. */
|
|
188
223
|
lowS?: boolean;
|
|
224
|
+
/** Signature encoding to produce. */
|
|
189
225
|
format?: ECDSASignatureFormat;
|
|
226
|
+
/** Optional hedging input for deterministic k generation. */
|
|
190
227
|
extraEntropy?: ECDSAExtraEntropy;
|
|
191
228
|
};
|
|
192
229
|
|
|
@@ -199,19 +236,23 @@ function validateSigFormat(format: string): ECDSASignatureFormat {
|
|
|
199
236
|
function validateSigOpts<T extends ECDSASignOpts, D extends Required<ECDSASignOpts>>(
|
|
200
237
|
opts: T,
|
|
201
238
|
def: D
|
|
202
|
-
):
|
|
203
|
-
|
|
204
|
-
|
|
239
|
+
): D {
|
|
240
|
+
validateObject(opts);
|
|
241
|
+
const optsn = {} as D;
|
|
242
|
+
// Normalize only the declared option subset from `def`; unknown keys are
|
|
243
|
+
// intentionally ignored so shared / superset option bags stay valid here too.
|
|
244
|
+
// `extraEntropy` stays an opaque payload until the signing path consumes it.
|
|
245
|
+
for (let optName of Object.keys(def) as (keyof D)[]) {
|
|
205
246
|
// @ts-ignore
|
|
206
247
|
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
207
248
|
}
|
|
208
249
|
abool(optsn.lowS!, 'lowS');
|
|
209
250
|
abool(optsn.prehash!, 'prehash');
|
|
210
251
|
if (optsn.format !== undefined) validateSigFormat(optsn.format);
|
|
211
|
-
return optsn
|
|
252
|
+
return optsn;
|
|
212
253
|
}
|
|
213
254
|
|
|
214
|
-
/**
|
|
255
|
+
/** Projective XYZ point used by short Weierstrass curves. */
|
|
215
256
|
export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
|
|
216
257
|
/** projective X coordinate. Different from affine x. */
|
|
217
258
|
readonly X: T;
|
|
@@ -223,15 +264,28 @@ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>>
|
|
|
223
264
|
get x(): T;
|
|
224
265
|
/** affine y coordinate. Different from projective Y. */
|
|
225
266
|
get y(): T;
|
|
226
|
-
/**
|
|
227
|
-
|
|
267
|
+
/**
|
|
268
|
+
* Encode the point into compressed or uncompressed SEC1 bytes.
|
|
269
|
+
* @param isCompressed - Whether to use the compressed form.
|
|
270
|
+
* @returns Encoded point bytes.
|
|
271
|
+
*/
|
|
272
|
+
toBytes(isCompressed?: boolean): TRet<Uint8Array>;
|
|
273
|
+
/**
|
|
274
|
+
* Encode the point into compressed or uncompressed SEC1 hex.
|
|
275
|
+
* @param isCompressed - Whether to use the compressed form.
|
|
276
|
+
* @returns Encoded point hex.
|
|
277
|
+
*/
|
|
228
278
|
toHex(isCompressed?: boolean): string;
|
|
229
279
|
}
|
|
230
280
|
|
|
231
|
-
/**
|
|
281
|
+
/** Constructor and metadata helpers for Weierstrass points. */
|
|
232
282
|
export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint<T>> {
|
|
233
283
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
234
284
|
new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
|
|
285
|
+
/**
|
|
286
|
+
* Return the curve parameters captured by this point constructor.
|
|
287
|
+
* @returns Curve parameters.
|
|
288
|
+
*/
|
|
235
289
|
CURVE(): WeierstrassOpts<T>;
|
|
236
290
|
}
|
|
237
291
|
|
|
@@ -247,67 +301,116 @@ export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint
|
|
|
247
301
|
* * Gy: y coordinate of generator point
|
|
248
302
|
*/
|
|
249
303
|
export type WeierstrassOpts<T> = Readonly<{
|
|
304
|
+
/** Base-field modulus. */
|
|
250
305
|
p: bigint;
|
|
306
|
+
/** Prime subgroup order. */
|
|
251
307
|
n: bigint;
|
|
308
|
+
/** Curve cofactor. */
|
|
252
309
|
h: bigint;
|
|
310
|
+
/** Weierstrass curve parameter `a`. */
|
|
253
311
|
a: T;
|
|
312
|
+
/** Weierstrass curve parameter `b`. */
|
|
254
313
|
b: T;
|
|
314
|
+
/** Generator x coordinate. */
|
|
255
315
|
Gx: T;
|
|
316
|
+
/** Generator y coordinate. */
|
|
256
317
|
Gy: T;
|
|
257
318
|
}>;
|
|
258
319
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Optional helpers and overrides for a Weierstrass point constructor.
|
|
322
|
+
*
|
|
323
|
+
* When a cofactor != 1, there can be effective methods to:
|
|
324
|
+
* 1. Determine whether a point is torsion-free
|
|
325
|
+
* 2. Clear torsion component
|
|
326
|
+
*/
|
|
262
327
|
export type WeierstrassExtraOpts<T> = Partial<{
|
|
328
|
+
/** Optional base-field override. */
|
|
263
329
|
Fp: IField<T>;
|
|
330
|
+
/** Optional scalar-field override. */
|
|
264
331
|
Fn: IField<bigint>;
|
|
332
|
+
/** Whether the point constructor accepts infinity points. */
|
|
265
333
|
allowInfinityPoint: boolean;
|
|
334
|
+
/** Optional GLV endomorphism data. */
|
|
266
335
|
endo: EndomorphismOpts;
|
|
336
|
+
/** Optional torsion-check override. */
|
|
267
337
|
isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
|
338
|
+
/** Optional cofactor-clearing override. */
|
|
268
339
|
clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
|
269
|
-
|
|
340
|
+
/** Optional custom point decoder. */
|
|
341
|
+
fromBytes: (bytes: TArg<Uint8Array>) => AffinePoint<T>;
|
|
342
|
+
/** Optional custom point encoder. */
|
|
270
343
|
toBytes: (
|
|
271
344
|
c: WeierstrassPointCons<T>,
|
|
272
345
|
point: WeierstrassPoint<T>,
|
|
273
346
|
isCompressed: boolean
|
|
274
|
-
) => Uint8Array
|
|
347
|
+
) => TRet<Uint8Array>;
|
|
275
348
|
}>;
|
|
276
349
|
|
|
277
350
|
/**
|
|
278
351
|
* Options for ECDSA signatures over a Weierstrass curve.
|
|
279
352
|
*
|
|
280
|
-
* * lowS: (default: true) whether produced
|
|
353
|
+
* * lowS: (default: true) whether produced or verified signatures occupy the
|
|
354
|
+
* low half of `ecdsaOpts.n`. Prevents malleability.
|
|
281
355
|
* * hmac: (default: noble-hashes hmac) function, would be used to init hmac-drbg for k generation.
|
|
282
356
|
* * randomBytes: (default: webcrypto os-level CSPRNG) custom method for fetching secure randomness.
|
|
283
|
-
* * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves
|
|
357
|
+
* * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves. Custom hooks are
|
|
358
|
+
* treated as pure functions over validated bytes and MUST NOT mutate caller-owned buffers or
|
|
359
|
+
* closure-captured option bags. `bits2int_modN` must also return a canonical scalar in
|
|
360
|
+
* `[0..Point.Fn.ORDER-1]`.
|
|
284
361
|
*/
|
|
285
362
|
export type ECDSAOpts = Partial<{
|
|
363
|
+
/** Default low-S policy for this ECDSA instance. */
|
|
286
364
|
lowS: boolean;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
365
|
+
/** HMAC implementation used by RFC6979 DRBG. */
|
|
366
|
+
hmac: HmacFn;
|
|
367
|
+
/** RNG override used by helper constructors. */
|
|
368
|
+
randomBytes: (bytesLength?: number) => TRet<Uint8Array>;
|
|
369
|
+
/** Hash-to-integer conversion override. */
|
|
370
|
+
bits2int: (bytes: TArg<Uint8Array>) => bigint;
|
|
371
|
+
/** Hash-to-integer-mod-n conversion override. Returns a canonical scalar in `[0..Fn.ORDER-1]`. */
|
|
372
|
+
bits2int_modN: (bytes: TArg<Uint8Array>) => bigint;
|
|
291
373
|
}>;
|
|
292
374
|
|
|
293
|
-
/**
|
|
294
|
-
* Elliptic Curve Diffie-Hellman interface.
|
|
295
|
-
* Provides keygen, secret-to-public conversion, calculating shared secrets.
|
|
296
|
-
*/
|
|
375
|
+
/** Elliptic Curve Diffie-Hellman helper namespace. */
|
|
297
376
|
export interface ECDH {
|
|
298
|
-
|
|
299
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Generate a secret/public key pair.
|
|
379
|
+
* @param seed - Optional seed material.
|
|
380
|
+
* @returns Secret/public key pair.
|
|
381
|
+
*/
|
|
382
|
+
keygen: (seed?: TArg<Uint8Array>) => { secretKey: TRet<Uint8Array>; publicKey: TRet<Uint8Array> };
|
|
383
|
+
/**
|
|
384
|
+
* Derive the public key from a secret key.
|
|
385
|
+
* @param secretKey - Secret key bytes.
|
|
386
|
+
* @param isCompressed - Whether to emit compressed SEC1 bytes.
|
|
387
|
+
* @returns Encoded public key.
|
|
388
|
+
*/
|
|
389
|
+
getPublicKey: (secretKey: TArg<Uint8Array>, isCompressed?: boolean) => TRet<Uint8Array>;
|
|
390
|
+
/**
|
|
391
|
+
* Compute the shared secret point from a secret key and peer public key.
|
|
392
|
+
* @param secretKeyA - Local secret key bytes.
|
|
393
|
+
* @param publicKeyB - Peer public key bytes.
|
|
394
|
+
* @param isCompressed - Whether to emit compressed SEC1 bytes.
|
|
395
|
+
* @returns Encoded shared point.
|
|
396
|
+
*/
|
|
300
397
|
getSharedSecret: (
|
|
301
|
-
secretKeyA: Uint8Array
|
|
302
|
-
publicKeyB: Uint8Array
|
|
398
|
+
secretKeyA: TArg<Uint8Array>,
|
|
399
|
+
publicKeyB: TArg<Uint8Array>,
|
|
303
400
|
isCompressed?: boolean
|
|
304
|
-
) => Uint8Array
|
|
401
|
+
) => TRet<Uint8Array>;
|
|
402
|
+
/** Point constructor used by this ECDH instance. */
|
|
305
403
|
Point: WeierstrassPointCons<bigint>;
|
|
404
|
+
/** Validation and random-key helpers. */
|
|
306
405
|
utils: {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
406
|
+
/** Check whether a secret key has the expected encoding. */
|
|
407
|
+
isValidSecretKey: (secretKey: TArg<Uint8Array>) => boolean;
|
|
408
|
+
/** Check whether a public key decodes to a valid point. */
|
|
409
|
+
isValidPublicKey: (publicKey: TArg<Uint8Array>, isCompressed?: boolean) => boolean;
|
|
410
|
+
/** Generate a valid random secret key. */
|
|
411
|
+
randomSecretKey: (seed?: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
310
412
|
};
|
|
413
|
+
/** Byte lengths for keys and signatures exposed by this curve. */
|
|
311
414
|
lengths: CurveLengths;
|
|
312
415
|
}
|
|
313
416
|
|
|
@@ -316,39 +419,119 @@ export interface ECDH {
|
|
|
316
419
|
* Only supported for prime fields, not Fp2 (extension fields).
|
|
317
420
|
*/
|
|
318
421
|
export interface ECDSA extends ECDH {
|
|
319
|
-
|
|
422
|
+
/**
|
|
423
|
+
* Sign a message with the given secret key.
|
|
424
|
+
* @param message - Message bytes.
|
|
425
|
+
* @param secretKey - Secret key bytes.
|
|
426
|
+
* @param opts - Optional signing tweaks. See {@link ECDSASignOpts}.
|
|
427
|
+
* @returns Encoded signature bytes.
|
|
428
|
+
*/
|
|
429
|
+
sign: (
|
|
430
|
+
message: TArg<Uint8Array>,
|
|
431
|
+
secretKey: TArg<Uint8Array>,
|
|
432
|
+
opts?: TArg<ECDSASignOpts>
|
|
433
|
+
) => TRet<Uint8Array>;
|
|
434
|
+
/**
|
|
435
|
+
* Verify a signature against a message and public key.
|
|
436
|
+
* @param signature - Encoded signature bytes.
|
|
437
|
+
* @param message - Message bytes.
|
|
438
|
+
* @param publicKey - Encoded public key.
|
|
439
|
+
* @param opts - Optional verification tweaks. See {@link ECDSAVerifyOpts}.
|
|
440
|
+
* @returns Whether the signature is valid.
|
|
441
|
+
*/
|
|
320
442
|
verify: (
|
|
321
|
-
signature: Uint8Array
|
|
322
|
-
message: Uint8Array
|
|
323
|
-
publicKey: Uint8Array
|
|
324
|
-
opts?: ECDSAVerifyOpts
|
|
443
|
+
signature: TArg<Uint8Array>,
|
|
444
|
+
message: TArg<Uint8Array>,
|
|
445
|
+
publicKey: TArg<Uint8Array>,
|
|
446
|
+
opts?: TArg<ECDSAVerifyOpts>
|
|
325
447
|
) => boolean;
|
|
326
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Recover the public key encoded into a recoverable signature.
|
|
450
|
+
* @param signature - Recoverable signature bytes.
|
|
451
|
+
* @param message - Message bytes.
|
|
452
|
+
* @param opts - Optional recovery tweaks. See {@link ECDSARecoverOpts}.
|
|
453
|
+
* @returns Encoded recovered public key.
|
|
454
|
+
*/
|
|
455
|
+
recoverPublicKey(
|
|
456
|
+
signature: TArg<Uint8Array>,
|
|
457
|
+
message: TArg<Uint8Array>,
|
|
458
|
+
opts?: TArg<ECDSARecoverOpts>
|
|
459
|
+
): TRet<Uint8Array>;
|
|
460
|
+
/** Signature constructor and parser helpers. */
|
|
327
461
|
Signature: ECDSASignatureCons;
|
|
328
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* @param m - Error message.
|
|
465
|
+
* @example
|
|
466
|
+
* Throw a DER-specific error when signature parsing encounters invalid bytes.
|
|
467
|
+
*
|
|
468
|
+
* ```ts
|
|
469
|
+
* new DERErr('bad der');
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
329
472
|
export class DERErr extends Error {
|
|
330
473
|
constructor(m = '') {
|
|
331
474
|
super(m);
|
|
332
475
|
}
|
|
333
476
|
}
|
|
477
|
+
/** DER helper namespace used by ECDSA signature parsing and encoding. */
|
|
334
478
|
export type IDER = {
|
|
335
479
|
// asn.1 DER encoding utils
|
|
480
|
+
/**
|
|
481
|
+
* DER-specific error constructor.
|
|
482
|
+
* @param m - Error message.
|
|
483
|
+
* @returns DER-specific error instance.
|
|
484
|
+
*/
|
|
336
485
|
Err: typeof DERErr;
|
|
337
486
|
// Basic building block is TLV (Tag-Length-Value)
|
|
487
|
+
/** Low-level tag-length-value helpers used by DER encoders. */
|
|
338
488
|
_tlv: {
|
|
489
|
+
/**
|
|
490
|
+
* Encode one TLV record.
|
|
491
|
+
* @param tag - ASN.1 tag byte.
|
|
492
|
+
* @param data - Hex-encoded value payload.
|
|
493
|
+
* @returns Encoded TLV string.
|
|
494
|
+
*/
|
|
339
495
|
encode: (tag: number, data: string) => string;
|
|
340
496
|
// v - value, l - left bytes (unparsed)
|
|
341
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Decode one TLV record and return the value plus leftover bytes.
|
|
499
|
+
* @param tag - Expected ASN.1 tag byte.
|
|
500
|
+
* @param data - Remaining DER bytes.
|
|
501
|
+
* @returns Parsed value plus leftover bytes.
|
|
502
|
+
*/
|
|
503
|
+
decode(tag: number, data: TArg<Uint8Array>): TRet<{ v: Uint8Array; l: Uint8Array }>;
|
|
342
504
|
};
|
|
343
505
|
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
344
506
|
// since we always use positive integers here. It must always be empty:
|
|
345
507
|
// - add zero byte if exists
|
|
346
508
|
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|
509
|
+
/** Positive-integer DER helpers used by ECDSA signature encoding. */
|
|
347
510
|
_int: {
|
|
511
|
+
/**
|
|
512
|
+
* Encode one positive bigint as a DER INTEGER.
|
|
513
|
+
* @param num - Positive integer to encode.
|
|
514
|
+
* @returns Encoded DER INTEGER.
|
|
515
|
+
*/
|
|
348
516
|
encode(num: bigint): string;
|
|
349
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Decode one DER INTEGER into a bigint.
|
|
519
|
+
* @param data - DER INTEGER bytes.
|
|
520
|
+
* @returns Decoded bigint.
|
|
521
|
+
*/
|
|
522
|
+
decode(data: TArg<Uint8Array>): bigint;
|
|
350
523
|
};
|
|
351
|
-
|
|
524
|
+
/**
|
|
525
|
+
* Parse a DER signature into `{ r, s }`.
|
|
526
|
+
* @param bytes - DER signature bytes.
|
|
527
|
+
* @returns Parsed signature components.
|
|
528
|
+
*/
|
|
529
|
+
toSig(bytes: TArg<Uint8Array>): { r: bigint; s: bigint };
|
|
530
|
+
/**
|
|
531
|
+
* Encode `{ r, s }` as a DER signature.
|
|
532
|
+
* @param sig - Signature components.
|
|
533
|
+
* @returns DER-encoded signature hex.
|
|
534
|
+
*/
|
|
352
535
|
hexFromSig(sig: { r: bigint; s: bigint }): string;
|
|
353
536
|
};
|
|
354
537
|
/**
|
|
@@ -356,7 +539,14 @@ export type IDER = {
|
|
|
356
539
|
*
|
|
357
540
|
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
|
358
541
|
*
|
|
359
|
-
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der
|
|
542
|
+
* Docs: {@link https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ | Let's Encrypt ASN.1 guide} and
|
|
543
|
+
* {@link https://luca.ntop.org/Teaching/Appunti/asn1.html | Luca Deri's ASN.1 notes}.
|
|
544
|
+
* @example
|
|
545
|
+
* ASN.1 DER encoding utilities.
|
|
546
|
+
*
|
|
547
|
+
* ```ts
|
|
548
|
+
* const der = DER.hexFromSig({ r: 1n, s: 2n });
|
|
549
|
+
* ```
|
|
360
550
|
*/
|
|
361
551
|
export const DER: IDER = {
|
|
362
552
|
// asn.1 DER encoding utils
|
|
@@ -365,7 +555,12 @@ export const DER: IDER = {
|
|
|
365
555
|
_tlv: {
|
|
366
556
|
encode: (tag: number, data: string): string => {
|
|
367
557
|
const { Err: E } = DER;
|
|
368
|
-
|
|
558
|
+
asafenumber(tag, 'tag');
|
|
559
|
+
if (tag < 0 || tag > 255) throw new E('tlv.encode: wrong tag');
|
|
560
|
+
if (typeof data !== 'string')
|
|
561
|
+
throw new TypeError('"data" expected string, got type=' + typeof data);
|
|
562
|
+
// Internal helper: callers hand this already-validated hex payload, so we only enforce
|
|
563
|
+
// byte alignment here instead of re-validating every nibble.
|
|
369
564
|
if (data.length & 1) throw new E('tlv.encode: unpadded data');
|
|
370
565
|
const dataLen = data.length / 2;
|
|
371
566
|
const len = numberToHexUnpadded(dataLen);
|
|
@@ -376,20 +571,23 @@ export const DER: IDER = {
|
|
|
376
571
|
return t + lenLen + len + data;
|
|
377
572
|
},
|
|
378
573
|
// v - value, l - left bytes (unparsed)
|
|
379
|
-
decode(tag: number, data: Uint8Array): { v: Uint8Array; l: Uint8Array } {
|
|
574
|
+
decode(tag: number, data: TArg<Uint8Array>): TRet<{ v: Uint8Array; l: Uint8Array }> {
|
|
380
575
|
const { Err: E } = DER;
|
|
576
|
+
data = abytes(data, undefined, 'DER data');
|
|
381
577
|
let pos = 0;
|
|
382
|
-
if (tag < 0 || tag >
|
|
578
|
+
if (tag < 0 || tag > 255) throw new E('tlv.encode: wrong tag');
|
|
383
579
|
if (data.length < 2 || data[pos++] !== tag) throw new E('tlv.decode: wrong tlv');
|
|
384
580
|
const first = data[pos++];
|
|
385
|
-
|
|
581
|
+
// First bit of first length byte is the short/long form flag.
|
|
582
|
+
const isLong = !!(first & 0b1000_0000);
|
|
386
583
|
let length = 0;
|
|
387
584
|
if (!isLong) length = first;
|
|
388
585
|
else {
|
|
389
586
|
// Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
|
|
390
587
|
const lenLen = first & 0b0111_1111;
|
|
391
588
|
if (!lenLen) throw new E('tlv.decode(long): indefinite length not supported');
|
|
392
|
-
|
|
589
|
+
// This would overflow u32 in JS.
|
|
590
|
+
if (lenLen > 4) throw new E('tlv.decode(long): byte length is too big');
|
|
393
591
|
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|
394
592
|
if (lengthBytes.length !== lenLen) throw new E('tlv.decode: length bytes not complete');
|
|
395
593
|
if (lengthBytes[0] === 0) throw new E('tlv.decode(long): zero leftmost byte');
|
|
@@ -399,7 +597,7 @@ export const DER: IDER = {
|
|
|
399
597
|
}
|
|
400
598
|
const v = data.subarray(pos, pos + length);
|
|
401
599
|
if (v.length !== length) throw new E('tlv.decode: wrong value length');
|
|
402
|
-
return { v, l: data.subarray(pos + length) };
|
|
600
|
+
return { v, l: data.subarray(pos + length) } as TRet<{ v: Uint8Array; l: Uint8Array }>;
|
|
403
601
|
},
|
|
404
602
|
},
|
|
405
603
|
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
@@ -409,6 +607,7 @@ export const DER: IDER = {
|
|
|
409
607
|
_int: {
|
|
410
608
|
encode(num: bigint): string {
|
|
411
609
|
const { Err: E } = DER;
|
|
610
|
+
abignumber(num);
|
|
412
611
|
if (num < _0n) throw new E('integer: negative integers are not allowed');
|
|
413
612
|
let hex = numberToHexUnpadded(num);
|
|
414
613
|
// Pad with zero byte if negative flag is present
|
|
@@ -416,15 +615,17 @@ export const DER: IDER = {
|
|
|
416
615
|
if (hex.length & 1) throw new E('unexpected DER parsing assertion: unpadded hex');
|
|
417
616
|
return hex;
|
|
418
617
|
},
|
|
419
|
-
decode(data: Uint8Array): bigint {
|
|
618
|
+
decode(data: TArg<Uint8Array>): bigint {
|
|
420
619
|
const { Err: E } = DER;
|
|
620
|
+
if (data.length < 1) throw new E('invalid signature integer: empty');
|
|
421
621
|
if (data[0] & 0b1000_0000) throw new E('invalid signature integer: negative');
|
|
422
|
-
|
|
622
|
+
// Single-byte zero `00` is the canonical DER INTEGER encoding for zero.
|
|
623
|
+
if (data.length > 1 && data[0] === 0x00 && !(data[1] & 0b1000_0000))
|
|
423
624
|
throw new E('invalid signature integer: unnecessary leading zero');
|
|
424
625
|
return bytesToNumberBE(data);
|
|
425
626
|
},
|
|
426
627
|
},
|
|
427
|
-
toSig(bytes: Uint8Array): { r: bigint; s: bigint } {
|
|
628
|
+
toSig(bytes: TArg<Uint8Array>): { r: bigint; s: bigint } {
|
|
428
629
|
// parse DER signature
|
|
429
630
|
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|
430
631
|
const data = abytes(bytes, undefined, 'signature');
|
|
@@ -443,36 +644,46 @@ export const DER: IDER = {
|
|
|
443
644
|
return tlv.encode(0x30, seq);
|
|
444
645
|
},
|
|
445
646
|
};
|
|
647
|
+
Object.freeze(DER._tlv);
|
|
648
|
+
Object.freeze(DER._int);
|
|
649
|
+
Object.freeze(DER);
|
|
446
650
|
|
|
447
651
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
448
652
|
// prettier-ignore
|
|
449
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
653
|
+
const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4);
|
|
450
654
|
|
|
451
655
|
/**
|
|
452
656
|
* Creates weierstrass Point constructor, based on specified curve options.
|
|
453
657
|
*
|
|
454
658
|
* See {@link WeierstrassOpts}.
|
|
659
|
+
* @param params - Curve parameters. See {@link WeierstrassOpts}.
|
|
660
|
+
* @param extraOpts - Optional helpers and overrides. See {@link WeierstrassExtraOpts}.
|
|
661
|
+
* @returns Weierstrass point constructor.
|
|
662
|
+
* @throws If the curve parameters, overrides, or point codecs are invalid. {@link Error}
|
|
455
663
|
*
|
|
456
664
|
* @example
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
665
|
+
* Construct a point type from explicit Weierstrass curve parameters.
|
|
666
|
+
*
|
|
667
|
+
* ```js
|
|
668
|
+
* const opts = {
|
|
669
|
+
* p: 0xfffffffffffffffffffffffffffffffeffffac73n,
|
|
670
|
+
* n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
|
|
671
|
+
* h: 1n,
|
|
672
|
+
* a: 0n,
|
|
673
|
+
* b: 7n,
|
|
674
|
+
* Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
|
|
675
|
+
* Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
|
|
676
|
+
* };
|
|
677
|
+
* const secp160k1_Point = weierstrass(opts);
|
|
678
|
+
* ```
|
|
469
679
|
*/
|
|
470
680
|
export function weierstrass<T>(
|
|
471
681
|
params: WeierstrassOpts<T>,
|
|
472
682
|
extraOpts: WeierstrassExtraOpts<T> = {}
|
|
473
683
|
): WeierstrassPointCons<T> {
|
|
474
684
|
const validated = createCurveFields('weierstrass', params, extraOpts);
|
|
475
|
-
const
|
|
685
|
+
const Fp = validated.Fp as IField<T>;
|
|
686
|
+
const Fn = validated.Fn as IField<bigint>;
|
|
476
687
|
let CURVE = validated.CURVE as WeierstrassOpts<T>;
|
|
477
688
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
478
689
|
validateObject(
|
|
@@ -488,7 +699,9 @@ export function weierstrass<T>(
|
|
|
488
699
|
}
|
|
489
700
|
);
|
|
490
701
|
|
|
491
|
-
|
|
702
|
+
// Snapshot constructor-time flags whose later mutation would otherwise change
|
|
703
|
+
// validity semantics of an already-built point type.
|
|
704
|
+
const { endo, allowInfinityPoint } = extraOpts;
|
|
492
705
|
if (endo) {
|
|
493
706
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
494
707
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
@@ -496,7 +709,7 @@ export function weierstrass<T>(
|
|
|
496
709
|
}
|
|
497
710
|
}
|
|
498
711
|
|
|
499
|
-
const lengths = getWLengths(Fp
|
|
712
|
+
const lengths = getWLengths(Fp as TArg<IField<T>>, Fn);
|
|
500
713
|
|
|
501
714
|
function assertCompressionIsSupported() {
|
|
502
715
|
if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
@@ -507,24 +720,33 @@ export function weierstrass<T>(
|
|
|
507
720
|
_c: WeierstrassPointCons<T>,
|
|
508
721
|
point: WeierstrassPoint<T>,
|
|
509
722
|
isCompressed: boolean
|
|
510
|
-
): Uint8Array {
|
|
723
|
+
): TRet<Uint8Array> {
|
|
724
|
+
// SEC 1 v2.0 §2.3.3 encodes infinity as the single octet 0x00. Only curves
|
|
725
|
+
// that opt into infinity as a public point value should expose that byte form.
|
|
726
|
+
if (allowInfinityPoint && point.is0()) return Uint8Array.of(0) as TRet<Uint8Array>;
|
|
511
727
|
const { x, y } = point.toAffine();
|
|
512
728
|
const bx = Fp.toBytes(x);
|
|
513
729
|
abool(isCompressed, 'isCompressed');
|
|
514
730
|
if (isCompressed) {
|
|
515
731
|
assertCompressionIsSupported();
|
|
516
732
|
const hasEvenY = !Fp.isOdd!(y);
|
|
517
|
-
return concatBytes(pprefix(hasEvenY), bx)
|
|
733
|
+
return concatBytes(pprefix(hasEvenY), bx) as TRet<Uint8Array>;
|
|
518
734
|
} else {
|
|
519
|
-
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y))
|
|
735
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y)) as TRet<Uint8Array>;
|
|
520
736
|
}
|
|
521
737
|
}
|
|
522
|
-
function pointFromBytes(bytes: Uint8Array) {
|
|
738
|
+
function pointFromBytes(bytes: TArg<Uint8Array>) {
|
|
523
739
|
abytes(bytes, undefined, 'Point');
|
|
524
740
|
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
525
741
|
const length = bytes.length;
|
|
526
742
|
const head = bytes[0];
|
|
527
743
|
const tail = bytes.subarray(1);
|
|
744
|
+
if (allowInfinityPoint && length === 1 && head === 0x00) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
745
|
+
// SEC 1 v2.0 §2.3.4 decodes 0x00 as infinity, but §3.2.2 public-key validation
|
|
746
|
+
// rejects infinity. We therefore keep 0x00 rejected by default because callers
|
|
747
|
+
// reuse this parser as the strict public-key boundary, and only admit it when
|
|
748
|
+
// the curve explicitly opts into infinity as a public point value. secp256k1
|
|
749
|
+
// crosstests show OpenSSL raw point codecs accept 0x00 too.
|
|
528
750
|
// No actual validation is done here: use .assertValidity()
|
|
529
751
|
if (length === comp && (head === 0x02 || head === 0x03)) {
|
|
530
752
|
const x = Fp.fromBytes(tail);
|
|
@@ -556,8 +778,8 @@ export function weierstrass<T>(
|
|
|
556
778
|
}
|
|
557
779
|
}
|
|
558
780
|
|
|
559
|
-
const encodePoint = extraOpts.toBytes
|
|
560
|
-
const decodePoint = extraOpts.fromBytes
|
|
781
|
+
const encodePoint = extraOpts.toBytes === undefined ? pointToBytes : extraOpts.toBytes;
|
|
782
|
+
const decodePoint = extraOpts.fromBytes === undefined ? pointFromBytes : extraOpts.fromBytes;
|
|
561
783
|
function weierstrassEquation(x: T): T {
|
|
562
784
|
const x2 = Fp.sqr(x); // x * x
|
|
563
785
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
@@ -572,7 +794,8 @@ export function weierstrass<T>(
|
|
|
572
794
|
return Fp.eql(left, right);
|
|
573
795
|
}
|
|
574
796
|
|
|
575
|
-
//
|
|
797
|
+
// Keep constructor-time generator validation cheap: callers are responsible for supplying the
|
|
798
|
+
// correct prime-order base point, while eager subgroup checks here would slow heavy module imports.
|
|
576
799
|
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
577
800
|
if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error('bad curve params: generator point');
|
|
578
801
|
|
|
@@ -588,7 +811,7 @@ export function weierstrass<T>(
|
|
|
588
811
|
return n;
|
|
589
812
|
}
|
|
590
813
|
|
|
591
|
-
function aprjpoint(other: unknown) {
|
|
814
|
+
function aprjpoint(other: unknown): asserts other is Point {
|
|
592
815
|
if (!(other instanceof Point)) throw new Error('Weierstrass Point expected');
|
|
593
816
|
}
|
|
594
817
|
|
|
@@ -597,44 +820,6 @@ export function weierstrass<T>(
|
|
|
597
820
|
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|
598
821
|
}
|
|
599
822
|
|
|
600
|
-
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
601
|
-
|
|
602
|
-
// Converts Projective point to affine (x, y) coordinates.
|
|
603
|
-
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
604
|
-
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
605
|
-
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
|
|
606
|
-
const { X, Y, Z } = p;
|
|
607
|
-
// Fast-path for normalized points
|
|
608
|
-
if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
|
|
609
|
-
const is0 = p.is0();
|
|
610
|
-
// If invZ was 0, we return zero point. However we still want to execute
|
|
611
|
-
// all operations, so we replace invZ with a random number, 1.
|
|
612
|
-
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
613
|
-
const x = Fp.mul(X, iz);
|
|
614
|
-
const y = Fp.mul(Y, iz);
|
|
615
|
-
const zz = Fp.mul(Z, iz);
|
|
616
|
-
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
617
|
-
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
618
|
-
return { x, y };
|
|
619
|
-
});
|
|
620
|
-
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
621
|
-
// Otherwise true will be return
|
|
622
|
-
const assertValidMemo = memoized((p: Point) => {
|
|
623
|
-
if (p.is0()) {
|
|
624
|
-
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
625
|
-
// In BLS, ZERO can be serialized, so we allow it.
|
|
626
|
-
// (0, 0, 0) is invalid representation of ZERO.
|
|
627
|
-
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
|
|
628
|
-
throw new Error('bad point: ZERO');
|
|
629
|
-
}
|
|
630
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
631
|
-
const { x, y } = p.toAffine();
|
|
632
|
-
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
|
|
633
|
-
if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
|
|
634
|
-
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
635
|
-
return true;
|
|
636
|
-
});
|
|
637
|
-
|
|
638
823
|
function finishEndo(
|
|
639
824
|
endoBeta: EndomorphismOpts['beta'],
|
|
640
825
|
k1p: Point,
|
|
@@ -670,6 +855,9 @@ export function weierstrass<T>(
|
|
|
670
855
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
671
856
|
constructor(X: T, Y: T, Z: T) {
|
|
672
857
|
this.X = acoord('x', X);
|
|
858
|
+
// This is not just about ZERO / infinity: ambient curves can have real
|
|
859
|
+
// finite points with y=0. Those points are 2-torsion, so they cannot lie
|
|
860
|
+
// in the odd prime-order subgroups this point type is meant to represent.
|
|
673
861
|
this.Y = acoord('y', Y, true);
|
|
674
862
|
this.Z = acoord('z', Z);
|
|
675
863
|
Object.freeze(this);
|
|
@@ -689,7 +877,7 @@ export function weierstrass<T>(
|
|
|
689
877
|
return new Point(x, y, Fp.ONE);
|
|
690
878
|
}
|
|
691
879
|
|
|
692
|
-
static fromBytes(bytes: Uint8Array): Point {
|
|
880
|
+
static fromBytes(bytes: TArg<Uint8Array>): Point {
|
|
693
881
|
const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, 'point')));
|
|
694
882
|
P.assertValidity();
|
|
695
883
|
return P;
|
|
@@ -709,7 +897,7 @@ export function weierstrass<T>(
|
|
|
709
897
|
/**
|
|
710
898
|
*
|
|
711
899
|
* @param windowSize
|
|
712
|
-
* @param isLazy true will defer table computation until the first multiplication
|
|
900
|
+
* @param isLazy - true will defer table computation until the first multiplication
|
|
713
901
|
* @returns
|
|
714
902
|
*/
|
|
715
903
|
precompute(windowSize: number = 8, isLazy = true): Point {
|
|
@@ -721,7 +909,21 @@ export function weierstrass<T>(
|
|
|
721
909
|
// TODO: return `this`
|
|
722
910
|
/** A point on curve is valid if it conforms to equation. */
|
|
723
911
|
assertValidity(): void {
|
|
724
|
-
|
|
912
|
+
const p = this;
|
|
913
|
+
if (p.is0()) {
|
|
914
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
915
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
916
|
+
// Keep the accepted infinity encoding canonical: projective-equivalent (X, Y, 0) points
|
|
917
|
+
// like (1, 1, 0) compare equal to ZERO, but only (0, 1, 0) should pass this guard.
|
|
918
|
+
if (extraOpts.allowInfinityPoint && Fp.is0(p.X) && Fp.eql(p.Y, Fp.ONE) && Fp.is0(p.Z))
|
|
919
|
+
return;
|
|
920
|
+
throw new Error('bad point: ZERO');
|
|
921
|
+
}
|
|
922
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
923
|
+
const { x, y } = p.toAffine();
|
|
924
|
+
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
|
|
925
|
+
if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
|
|
926
|
+
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
725
927
|
}
|
|
726
928
|
|
|
727
929
|
hasEvenY(): boolean {
|
|
@@ -731,7 +933,7 @@ export function weierstrass<T>(
|
|
|
731
933
|
}
|
|
732
934
|
|
|
733
935
|
/** Compare one point to another. */
|
|
734
|
-
equals(other:
|
|
936
|
+
equals(other: WeierstrassPoint<T>): boolean {
|
|
735
937
|
aprjpoint(other);
|
|
736
938
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
737
939
|
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
@@ -792,7 +994,7 @@ export function weierstrass<T>(
|
|
|
792
994
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
793
995
|
// https://eprint.iacr.org/2015/1060, algorithm 1
|
|
794
996
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
795
|
-
add(other:
|
|
997
|
+
add(other: WeierstrassPoint<T>): Point {
|
|
796
998
|
aprjpoint(other);
|
|
797
999
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
798
1000
|
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
@@ -842,7 +1044,10 @@ export function weierstrass<T>(
|
|
|
842
1044
|
return new Point(X3, Y3, Z3);
|
|
843
1045
|
}
|
|
844
1046
|
|
|
845
|
-
subtract(other:
|
|
1047
|
+
subtract(other: WeierstrassPoint<T>) {
|
|
1048
|
+
// Validate before calling `negate()` so wrong inputs fail with the point guard
|
|
1049
|
+
// instead of leaking a foreign `negate()` error.
|
|
1050
|
+
aprjpoint(other);
|
|
846
1051
|
return this.add(other.negate());
|
|
847
1052
|
}
|
|
848
1053
|
|
|
@@ -856,12 +1061,15 @@ export function weierstrass<T>(
|
|
|
856
1061
|
* but takes 2x longer to generate and consumes 2x memory.
|
|
857
1062
|
* Uses precomputes when available.
|
|
858
1063
|
* Uses endomorphism for Koblitz curves.
|
|
859
|
-
* @param scalar by which the point would be multiplied
|
|
1064
|
+
* @param scalar - by which the point would be multiplied
|
|
860
1065
|
* @returns New point
|
|
861
1066
|
*/
|
|
862
1067
|
multiply(scalar: bigint): Point {
|
|
863
1068
|
const { endo } = extraOpts;
|
|
864
|
-
|
|
1069
|
+
// Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.
|
|
1070
|
+
// In key/signature-style callers, those values usually mean broken hash/scalar plumbing,
|
|
1071
|
+
// and failing closed is safer than silently producing the identity point.
|
|
1072
|
+
if (!Fn.isValidNot0(scalar)) throw new RangeError('invalid scalar: out of range'); // 0 is invalid
|
|
865
1073
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
866
1074
|
const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
867
1075
|
/** See docs for {@link EndomorphismOpts} */
|
|
@@ -885,10 +1093,13 @@ export function weierstrass<T>(
|
|
|
885
1093
|
* It's faster, but should only be used when you don't care about
|
|
886
1094
|
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
887
1095
|
*/
|
|
888
|
-
multiplyUnsafe(
|
|
1096
|
+
multiplyUnsafe(scalar: bigint): Point {
|
|
889
1097
|
const { endo } = extraOpts;
|
|
890
1098
|
const p = this as Point;
|
|
891
|
-
|
|
1099
|
+
const sc = scalar;
|
|
1100
|
+
// Public-scalar callers may need 0, but n and larger values stay rejected here too.
|
|
1101
|
+
// Reducing them mod n would turn bad caller input into an accidental identity point.
|
|
1102
|
+
if (!Fn.isValid(sc)) throw new RangeError('invalid scalar: out of range'); // 0 is valid
|
|
892
1103
|
if (sc === _0n || p.is0()) return Point.ZERO; // 0
|
|
893
1104
|
if (sc === _1n) return p; // 1
|
|
894
1105
|
if (wnaf.hasCache(this)) return this.multiply(sc); // precomputes
|
|
@@ -905,10 +1116,25 @@ export function weierstrass<T>(
|
|
|
905
1116
|
|
|
906
1117
|
/**
|
|
907
1118
|
* Converts Projective point to affine (x, y) coordinates.
|
|
908
|
-
*
|
|
1119
|
+
* (X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
1120
|
+
* @param invertedZ - Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
909
1121
|
*/
|
|
910
1122
|
toAffine(invertedZ?: T): AffinePoint<T> {
|
|
911
|
-
|
|
1123
|
+
const p = this;
|
|
1124
|
+
let iz = invertedZ;
|
|
1125
|
+
const { X, Y, Z } = p;
|
|
1126
|
+
// Fast-path for normalized points
|
|
1127
|
+
if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
|
|
1128
|
+
const is0 = p.is0();
|
|
1129
|
+
// If invZ was 0, we return zero point. However we still want to execute
|
|
1130
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
1131
|
+
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
1132
|
+
const x = Fp.mul(X, iz);
|
|
1133
|
+
const y = Fp.mul(Y, iz);
|
|
1134
|
+
const zz = Fp.mul(Z, iz);
|
|
1135
|
+
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
1136
|
+
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
1137
|
+
return { x, y };
|
|
912
1138
|
}
|
|
913
1139
|
|
|
914
1140
|
/**
|
|
@@ -926,16 +1152,21 @@ export function weierstrass<T>(
|
|
|
926
1152
|
const { clearCofactor } = extraOpts;
|
|
927
1153
|
if (cofactor === _1n) return this; // Fast-path
|
|
928
1154
|
if (clearCofactor) return clearCofactor(Point, this) as Point;
|
|
1155
|
+
// Default fallback assumes the cofactor fits the usual subgroup-scalar
|
|
1156
|
+
// multiplyUnsafe() contract. Curves with larger / structured cofactors
|
|
1157
|
+
// should define a clearCofactor override anyway (e.g. psi/Frobenius maps).
|
|
929
1158
|
return this.multiplyUnsafe(cofactor);
|
|
930
1159
|
}
|
|
931
1160
|
|
|
932
1161
|
isSmallOrder(): boolean {
|
|
933
|
-
|
|
934
|
-
return this.
|
|
1162
|
+
if (cofactor === _1n) return this.is0(); // Fast-path
|
|
1163
|
+
return this.clearCofactor().is0();
|
|
935
1164
|
}
|
|
936
1165
|
|
|
937
|
-
toBytes(isCompressed = true): Uint8Array {
|
|
1166
|
+
toBytes(isCompressed = true): TRet<Uint8Array> {
|
|
938
1167
|
abool(isCompressed, 'isCompressed');
|
|
1168
|
+
// Same policy as pointFromBytes(): keep ZERO out of the default byte surface because
|
|
1169
|
+
// callers use these encodings as public keys, where SEC 1 validation rejects infinity.
|
|
939
1170
|
this.assertValidity();
|
|
940
1171
|
return encodePoint(Point, this, isCompressed);
|
|
941
1172
|
}
|
|
@@ -950,31 +1181,75 @@ export function weierstrass<T>(
|
|
|
950
1181
|
}
|
|
951
1182
|
const bits = Fn.BITS;
|
|
952
1183
|
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
953
|
-
|
|
1184
|
+
// Tiny toy curves can have scalar fields narrower than 8 bits. Skip the
|
|
1185
|
+
// eager W=8 cache there instead of rejecting an otherwise valid constructor.
|
|
1186
|
+
if (bits >= 8) Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1187
|
+
Object.freeze(Point.prototype);
|
|
1188
|
+
Object.freeze(Point);
|
|
954
1189
|
return Point;
|
|
955
1190
|
}
|
|
956
1191
|
|
|
957
|
-
/**
|
|
1192
|
+
/** Parsed ECDSA signature with helpers for recovery and re-encoding. */
|
|
958
1193
|
export interface ECDSASignature {
|
|
1194
|
+
/** Signature component `r`. */
|
|
959
1195
|
readonly r: bigint;
|
|
1196
|
+
/** Signature component `s`. */
|
|
960
1197
|
readonly s: bigint;
|
|
1198
|
+
/** Optional recovery bit for recoverable signatures. */
|
|
961
1199
|
readonly recovery?: number;
|
|
1200
|
+
/**
|
|
1201
|
+
* Return a copy of the signature with a recovery bit attached.
|
|
1202
|
+
* @param recovery - Recovery bit to attach.
|
|
1203
|
+
* @returns Signature with an attached recovery bit.
|
|
1204
|
+
*/
|
|
962
1205
|
addRecoveryBit(recovery: number): ECDSASignature & { readonly recovery: number };
|
|
1206
|
+
/**
|
|
1207
|
+
* Check whether the signature uses the high-S half-order.
|
|
1208
|
+
* @returns Whether the signature uses the high-S half-order.
|
|
1209
|
+
*/
|
|
963
1210
|
hasHighS(): boolean;
|
|
964
|
-
|
|
965
|
-
|
|
1211
|
+
/**
|
|
1212
|
+
* Recover the public key from the hashed message and recovery bit.
|
|
1213
|
+
* @param messageHash - Hashed message bytes.
|
|
1214
|
+
* @returns Recovered public-key point.
|
|
1215
|
+
*/
|
|
1216
|
+
recoverPublicKey(messageHash: TArg<Uint8Array>): WeierstrassPoint<bigint>;
|
|
1217
|
+
/**
|
|
1218
|
+
* Encode the signature into bytes.
|
|
1219
|
+
* @param format - Signature encoding to produce.
|
|
1220
|
+
* @returns Encoded signature bytes.
|
|
1221
|
+
*/
|
|
1222
|
+
toBytes(format?: string): TRet<Uint8Array>;
|
|
1223
|
+
/**
|
|
1224
|
+
* Encode the signature into hex.
|
|
1225
|
+
* @param format - Signature encoding to produce.
|
|
1226
|
+
* @returns Encoded signature hex.
|
|
1227
|
+
*/
|
|
966
1228
|
toHex(format?: string): string;
|
|
967
1229
|
}
|
|
968
|
-
/**
|
|
1230
|
+
/** Constructor and decoding helpers for ECDSA signatures. */
|
|
969
1231
|
export type ECDSASignatureCons = {
|
|
1232
|
+
/** Create a signature from `r`, `s`, and an optional recovery bit. */
|
|
970
1233
|
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
|
|
971
|
-
|
|
1234
|
+
/**
|
|
1235
|
+
* Decode a signature from bytes.
|
|
1236
|
+
* @param bytes - Encoded signature bytes.
|
|
1237
|
+
* @param format - Signature encoding to parse.
|
|
1238
|
+
* @returns Parsed signature.
|
|
1239
|
+
*/
|
|
1240
|
+
fromBytes(bytes: TArg<Uint8Array>, format?: ECDSASignatureFormat): ECDSASignature;
|
|
1241
|
+
/**
|
|
1242
|
+
* Decode a signature from hex.
|
|
1243
|
+
* @param hex - Encoded signature hex.
|
|
1244
|
+
* @param format - Signature encoding to parse.
|
|
1245
|
+
* @returns Parsed signature.
|
|
1246
|
+
*/
|
|
972
1247
|
fromHex(hex: string, format?: ECDSASignatureFormat): ECDSASignature;
|
|
973
1248
|
};
|
|
974
1249
|
|
|
975
1250
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
976
|
-
function pprefix(hasEvenY: boolean): Uint8Array {
|
|
977
|
-
return Uint8Array.of(hasEvenY ? 0x02 : 0x03)
|
|
1251
|
+
function pprefix(hasEvenY: boolean): TRet<Uint8Array> {
|
|
1252
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03) as TRet<Uint8Array>;
|
|
978
1253
|
}
|
|
979
1254
|
|
|
980
1255
|
/**
|
|
@@ -982,16 +1257,29 @@ function pprefix(hasEvenY: boolean): Uint8Array {
|
|
|
982
1257
|
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
983
1258
|
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
984
1259
|
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
985
|
-
*
|
|
986
|
-
* @param
|
|
987
|
-
* @
|
|
1260
|
+
* RFC 9380 expects callers to provide `v != 0`; this helper does not enforce it.
|
|
1261
|
+
* @param Fp - Field implementation.
|
|
1262
|
+
* @param Z - Simplified SWU map parameter.
|
|
1263
|
+
* @returns Square-root ratio helper.
|
|
1264
|
+
* @example
|
|
1265
|
+
* Build the square-root ratio helper used by SWU map implementations.
|
|
1266
|
+
*
|
|
1267
|
+
* ```ts
|
|
1268
|
+
* import { SWUFpSqrtRatio } from '@noble/curves/abstract/weierstrass.js';
|
|
1269
|
+
* import { Field } from '@noble/curves/abstract/modular.js';
|
|
1270
|
+
* const Fp = Field(17n);
|
|
1271
|
+
* const sqrtRatio = SWUFpSqrtRatio(Fp, 3n);
|
|
1272
|
+
* const out = sqrtRatio(4n, 1n);
|
|
1273
|
+
* ```
|
|
988
1274
|
*/
|
|
989
1275
|
export function SWUFpSqrtRatio<T>(
|
|
990
|
-
Fp: IField<T
|
|
1276
|
+
Fp: TArg<IField<T>>,
|
|
991
1277
|
Z: T
|
|
992
1278
|
): (u: T, v: T) => { isValid: boolean; value: T } {
|
|
1279
|
+
// Fail with the usual field-shape error before touching pow/cmov on malformed field shims.
|
|
1280
|
+
const F = validateField(Fp as IField<T>) as IField<T>;
|
|
993
1281
|
// Generic implementation
|
|
994
|
-
const q =
|
|
1282
|
+
const q = F.ORDER;
|
|
995
1283
|
let l = _0n;
|
|
996
1284
|
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
|
997
1285
|
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
@@ -1003,54 +1291,60 @@ export function SWUFpSqrtRatio<T>(
|
|
|
1003
1291
|
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1004
1292
|
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1005
1293
|
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1006
|
-
const c6 =
|
|
1007
|
-
const c7 =
|
|
1294
|
+
const c6 = F.pow(Z, c2); // 6. c6 = Z^c2
|
|
1295
|
+
const c7 = F.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1296
|
+
// RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) only for v != 0.
|
|
1297
|
+
// We keep v=0 on the regular result path with isValid=false instead of
|
|
1298
|
+
// throwing so the helper stays closer to the RFC's fixed control flow.
|
|
1008
1299
|
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1009
1300
|
let tv1 = c6; // 1. tv1 = c6
|
|
1010
|
-
let tv2 =
|
|
1011
|
-
let tv3 =
|
|
1012
|
-
tv3 =
|
|
1013
|
-
let tv5 =
|
|
1014
|
-
tv5 =
|
|
1015
|
-
tv5 =
|
|
1016
|
-
tv2 =
|
|
1017
|
-
tv3 =
|
|
1018
|
-
let tv4 =
|
|
1019
|
-
tv5 =
|
|
1020
|
-
let isQR =
|
|
1021
|
-
tv2 =
|
|
1022
|
-
tv5 =
|
|
1023
|
-
tv3 =
|
|
1024
|
-
tv4 =
|
|
1301
|
+
let tv2 = F.pow(v, c4); // 2. tv2 = v^c4
|
|
1302
|
+
let tv3 = F.sqr(tv2); // 3. tv3 = tv2^2
|
|
1303
|
+
tv3 = F.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1304
|
+
let tv5 = F.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1305
|
+
tv5 = F.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1306
|
+
tv5 = F.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1307
|
+
tv2 = F.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1308
|
+
tv3 = F.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1309
|
+
let tv4 = F.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1310
|
+
tv5 = F.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1311
|
+
let isQR = F.eql(tv5, F.ONE); // 12. isQR = tv5 == 1
|
|
1312
|
+
tv2 = F.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1313
|
+
tv5 = F.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1314
|
+
tv3 = F.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1315
|
+
tv4 = F.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1025
1316
|
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1026
1317
|
for (let i = c1; i > _1n; i--) {
|
|
1027
1318
|
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1028
1319
|
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1029
|
-
let tvv5 =
|
|
1030
|
-
const e1 =
|
|
1031
|
-
tv2 =
|
|
1032
|
-
tv1 =
|
|
1033
|
-
tvv5 =
|
|
1034
|
-
tv3 =
|
|
1035
|
-
tv4 =
|
|
1320
|
+
let tvv5 = F.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1321
|
+
const e1 = F.eql(tvv5, F.ONE); // 21. e1 = tv5 == 1
|
|
1322
|
+
tv2 = F.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1323
|
+
tv1 = F.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1324
|
+
tvv5 = F.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1325
|
+
tv3 = F.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1326
|
+
tv4 = F.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1036
1327
|
}
|
|
1037
|
-
|
|
1328
|
+
// RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) for v != 0.
|
|
1329
|
+
// When u = 0 and v != 0, u / v = 0 is square and the computed root is
|
|
1330
|
+
// still 0, so widen only the final flag and keep the full control flow.
|
|
1331
|
+
return { isValid: !F.is0(v) && (isQR || F.is0(u)), value: tv3 };
|
|
1038
1332
|
};
|
|
1039
|
-
if (
|
|
1333
|
+
if (F.ORDER % _4n === _3n) {
|
|
1040
1334
|
// sqrt_ratio_3mod4(u, v)
|
|
1041
|
-
const c1 = (
|
|
1042
|
-
const c2 =
|
|
1335
|
+
const c1 = (F.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1336
|
+
const c2 = F.sqrt(F.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1043
1337
|
sqrtRatio = (u: T, v: T) => {
|
|
1044
|
-
let tv1 =
|
|
1045
|
-
const tv2 =
|
|
1046
|
-
tv1 =
|
|
1047
|
-
let y1 =
|
|
1048
|
-
y1 =
|
|
1049
|
-
const y2 =
|
|
1050
|
-
const tv3 =
|
|
1051
|
-
const isQR =
|
|
1052
|
-
let y =
|
|
1053
|
-
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1338
|
+
let tv1 = F.sqr(v); // 1. tv1 = v^2
|
|
1339
|
+
const tv2 = F.mul(u, v); // 2. tv2 = u * v
|
|
1340
|
+
tv1 = F.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1341
|
+
let y1 = F.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1342
|
+
y1 = F.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1343
|
+
const y2 = F.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1344
|
+
const tv3 = F.mul(F.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1345
|
+
const isQR = F.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1346
|
+
let y = F.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1347
|
+
return { isValid: !F.is0(v) && isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1054
1348
|
};
|
|
1055
1349
|
}
|
|
1056
1350
|
// No curves uses that
|
|
@@ -1059,63 +1353,99 @@ export function SWUFpSqrtRatio<T>(
|
|
|
1059
1353
|
}
|
|
1060
1354
|
/**
|
|
1061
1355
|
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1062
|
-
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1356
|
+
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2 | RFC 9380 section 6.6.2}.
|
|
1357
|
+
* @param Fp - Field implementation.
|
|
1358
|
+
* @param opts - SWU parameters:
|
|
1359
|
+
* - `A`: Curve parameter `A`.
|
|
1360
|
+
* - `B`: Curve parameter `B`.
|
|
1361
|
+
* - `Z`: Simplified SWU map parameter.
|
|
1362
|
+
* @returns Deterministic map-to-curve function.
|
|
1363
|
+
* @throws If the SWU parameters are invalid or the field lacks the required helpers. {@link Error}
|
|
1364
|
+
* @example
|
|
1365
|
+
* Map one field element to a Weierstrass curve point with the SWU recipe.
|
|
1366
|
+
*
|
|
1367
|
+
* ```ts
|
|
1368
|
+
* import { mapToCurveSimpleSWU } from '@noble/curves/abstract/weierstrass.js';
|
|
1369
|
+
* import { Field } from '@noble/curves/abstract/modular.js';
|
|
1370
|
+
* const Fp = Field(17n);
|
|
1371
|
+
* const map = mapToCurveSimpleSWU(Fp, { A: 1n, B: 2n, Z: 3n });
|
|
1372
|
+
* const point = map(5n);
|
|
1373
|
+
* ```
|
|
1063
1374
|
*/
|
|
1064
1375
|
export function mapToCurveSimpleSWU<T>(
|
|
1065
|
-
Fp: IField<T
|
|
1376
|
+
Fp: TArg<IField<T>>,
|
|
1066
1377
|
opts: {
|
|
1067
1378
|
A: T;
|
|
1068
1379
|
B: T;
|
|
1069
1380
|
Z: T;
|
|
1070
1381
|
}
|
|
1071
1382
|
): (u: T) => { x: T; y: T } {
|
|
1072
|
-
validateField(Fp)
|
|
1383
|
+
const F = validateField(Fp as IField<T>) as IField<T>;
|
|
1073
1384
|
const { A, B, Z } = opts;
|
|
1074
|
-
if (!
|
|
1385
|
+
if (!F.isValidNot0(A) || !F.isValidNot0(B) || !F.isValid(Z))
|
|
1075
1386
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1076
|
-
|
|
1077
|
-
|
|
1387
|
+
// RFC 9380 §6.6.2 and Appendix H.2 require:
|
|
1388
|
+
// 1. Z is non-square in F
|
|
1389
|
+
// 2. Z != -1 in F
|
|
1390
|
+
// 3. g(x) - Z is irreducible over F
|
|
1391
|
+
// 4. g(B / (Z * A)) is square in F
|
|
1392
|
+
// We can enforce 1, 2, and 4 with the current field API.
|
|
1393
|
+
// Criterion 3 is not checked here because generic `IField<T>` does not expose
|
|
1394
|
+
// polynomial-ring / irreducibility operations, and this helper is used for
|
|
1395
|
+
// both prime and extension fields.
|
|
1396
|
+
if (F.eql(Z, F.neg(F.ONE)) || FpIsSquare(F, Z))
|
|
1397
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1398
|
+
// RFC 9380 Appendix H.2 criterion 4: g(B / (Z * A)) is square in F.
|
|
1399
|
+
// x = B / (Z * A)
|
|
1400
|
+
const x = F.mul(B, F.inv(F.mul(Z, A)));
|
|
1401
|
+
// g(x) = x^3 + A*x + B
|
|
1402
|
+
const gx = F.add(F.add(F.mul(F.sqr(x), x), F.mul(A, x)), B);
|
|
1403
|
+
if (!FpIsSquare(F, gx)) throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1404
|
+
const sqrtRatio = SWUFpSqrtRatio(F, Z);
|
|
1405
|
+
if (!F.isOdd) throw new Error('Field does not have .isOdd()');
|
|
1078
1406
|
// Input: u, an element of F.
|
|
1079
1407
|
// Output: (x, y), a point on E.
|
|
1080
1408
|
return (u: T): { x: T; y: T } => {
|
|
1081
1409
|
// prettier-ignore
|
|
1082
1410
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1083
|
-
tv1 =
|
|
1084
|
-
tv1 =
|
|
1085
|
-
tv2 =
|
|
1086
|
-
tv2 =
|
|
1087
|
-
tv3 =
|
|
1088
|
-
tv3 =
|
|
1089
|
-
tv4 =
|
|
1090
|
-
tv4 =
|
|
1091
|
-
tv2 =
|
|
1092
|
-
tv6 =
|
|
1093
|
-
tv5 =
|
|
1094
|
-
tv2 =
|
|
1095
|
-
tv2 =
|
|
1096
|
-
tv6 =
|
|
1097
|
-
tv5 =
|
|
1098
|
-
tv2 =
|
|
1099
|
-
x =
|
|
1411
|
+
tv1 = F.sqr(u); // 1. tv1 = u^2
|
|
1412
|
+
tv1 = F.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1413
|
+
tv2 = F.sqr(tv1); // 3. tv2 = tv1^2
|
|
1414
|
+
tv2 = F.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1415
|
+
tv3 = F.add(tv2, F.ONE); // 5. tv3 = tv2 + 1
|
|
1416
|
+
tv3 = F.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1417
|
+
tv4 = F.cmov(Z, F.neg(tv2), !F.eql(tv2, F.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1418
|
+
tv4 = F.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1419
|
+
tv2 = F.sqr(tv3); // 9. tv2 = tv3^2
|
|
1420
|
+
tv6 = F.sqr(tv4); // 10. tv6 = tv4^2
|
|
1421
|
+
tv5 = F.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1422
|
+
tv2 = F.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1423
|
+
tv2 = F.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1424
|
+
tv6 = F.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1425
|
+
tv5 = F.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1426
|
+
tv2 = F.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1427
|
+
x = F.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1100
1428
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1101
|
-
y =
|
|
1102
|
-
y =
|
|
1103
|
-
x =
|
|
1104
|
-
y =
|
|
1105
|
-
const e1 =
|
|
1106
|
-
y =
|
|
1107
|
-
const tv4_inv = FpInvertBatch(
|
|
1108
|
-
x =
|
|
1429
|
+
y = F.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1430
|
+
y = F.mul(y, value); // 20. y = y * y1
|
|
1431
|
+
x = F.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1432
|
+
y = F.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1433
|
+
const e1 = F.isOdd!(u) === F.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1434
|
+
y = F.cmov(F.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1435
|
+
const tv4_inv = FpInvertBatch(F, [tv4], true)[0];
|
|
1436
|
+
x = F.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1109
1437
|
return { x, y };
|
|
1110
1438
|
};
|
|
1111
1439
|
}
|
|
1112
1440
|
|
|
1113
|
-
function getWLengths<T>(Fp: IField<T
|
|
1441
|
+
function getWLengths<T>(Fp: TArg<IField<T>>, Fn: TArg<IField<bigint>>) {
|
|
1114
1442
|
return {
|
|
1115
1443
|
secretKey: Fn.BYTES,
|
|
1116
1444
|
publicKey: 1 + Fp.BYTES,
|
|
1117
1445
|
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|
1118
1446
|
publicKeyHasPrefix: true,
|
|
1447
|
+
// Raw compact `(r || s)` signature width; DER and recovered signatures use
|
|
1448
|
+
// different lengths outside this helper.
|
|
1119
1449
|
signature: 2 * Fn.BYTES,
|
|
1120
1450
|
};
|
|
1121
1451
|
}
|
|
@@ -1123,16 +1453,34 @@ function getWLengths<T>(Fp: IField<T>, Fn: IField<bigint>) {
|
|
|
1123
1453
|
/**
|
|
1124
1454
|
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
1125
1455
|
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
1456
|
+
* @param Point - Weierstrass point constructor.
|
|
1457
|
+
* @param ecdhOpts - Optional randomness helpers:
|
|
1458
|
+
* - `randomBytes` (optional): Optional RNG override.
|
|
1459
|
+
* @returns ECDH helper namespace.
|
|
1460
|
+
* @example
|
|
1461
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
1462
|
+
*
|
|
1463
|
+
* ```ts
|
|
1464
|
+
* import { ecdh } from '@noble/curves/abstract/weierstrass.js';
|
|
1465
|
+
* import { p256 } from '@noble/curves/nist.js';
|
|
1466
|
+
* const dh = ecdh(p256.Point);
|
|
1467
|
+
* const alice = dh.keygen();
|
|
1468
|
+
* const shared = dh.getSharedSecret(alice.secretKey, alice.publicKey);
|
|
1469
|
+
* ```
|
|
1126
1470
|
*/
|
|
1127
1471
|
export function ecdh(
|
|
1128
1472
|
Point: WeierstrassPointCons<bigint>,
|
|
1129
|
-
ecdhOpts: { randomBytes?: (bytesLength?: number) => Uint8Array } = {}
|
|
1473
|
+
ecdhOpts: TArg<{ randomBytes?: (bytesLength?: number) => TRet<Uint8Array> }> = {}
|
|
1130
1474
|
): ECDH {
|
|
1131
1475
|
const { Fn } = Point;
|
|
1132
|
-
const randomBytes_ = ecdhOpts.randomBytes
|
|
1133
|
-
|
|
1476
|
+
const randomBytes_ = ecdhOpts.randomBytes === undefined ? wcRandomBytes : ecdhOpts.randomBytes;
|
|
1477
|
+
// Keep the advertised seed length aligned with mapHashToField(), which keeps a hard 16-byte
|
|
1478
|
+
// minimum even on toy curves.
|
|
1479
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), {
|
|
1480
|
+
seed: Math.max(getMinHashLength(Fn.ORDER), 16),
|
|
1481
|
+
});
|
|
1134
1482
|
|
|
1135
|
-
function isValidSecretKey(secretKey: Uint8Array) {
|
|
1483
|
+
function isValidSecretKey(secretKey: TArg<Uint8Array>) {
|
|
1136
1484
|
try {
|
|
1137
1485
|
const num = Fn.fromBytes(secretKey);
|
|
1138
1486
|
return Fn.isValidNot0(num);
|
|
@@ -1141,7 +1489,7 @@ export function ecdh(
|
|
|
1141
1489
|
}
|
|
1142
1490
|
}
|
|
1143
1491
|
|
|
1144
|
-
function isValidPublicKey(publicKey: Uint8Array
|
|
1492
|
+
function isValidPublicKey(publicKey: TArg<Uint8Array>, isCompressed?: boolean): boolean {
|
|
1145
1493
|
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
1146
1494
|
try {
|
|
1147
1495
|
const l = publicKey.length;
|
|
@@ -1157,43 +1505,52 @@ export function ecdh(
|
|
|
1157
1505
|
* Produces cryptographically secure secret key from random of size
|
|
1158
1506
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1159
1507
|
*/
|
|
1160
|
-
function randomSecretKey(seed
|
|
1161
|
-
|
|
1508
|
+
function randomSecretKey(seed?: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
1509
|
+
seed = seed === undefined ? randomBytes_(lengths.seed) : seed;
|
|
1510
|
+
return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER) as TRet<Uint8Array>;
|
|
1162
1511
|
}
|
|
1163
1512
|
|
|
1164
1513
|
/**
|
|
1165
1514
|
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1166
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1515
|
+
* @param isCompressed - whether to return compact (default), or full key
|
|
1167
1516
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1168
1517
|
*/
|
|
1169
|
-
function getPublicKey(secretKey: Uint8Array
|
|
1518
|
+
function getPublicKey(secretKey: TArg<Uint8Array>, isCompressed = true): TRet<Uint8Array> {
|
|
1170
1519
|
return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
|
|
1171
1520
|
}
|
|
1172
1521
|
|
|
1173
1522
|
/**
|
|
1174
1523
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1175
1524
|
*/
|
|
1176
|
-
function isProbPub(item: Uint8Array): boolean | undefined {
|
|
1525
|
+
function isProbPub(item: TArg<Uint8Array>): boolean | undefined {
|
|
1177
1526
|
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
1527
|
+
const allowedLengths = (Fn as { _lengths?: readonly number[] })._lengths;
|
|
1178
1528
|
if (!isBytes(item)) return undefined;
|
|
1179
|
-
if (('_lengths' in Fn && Fn._lengths) || secretKey === publicKey) return undefined;
|
|
1180
1529
|
const l = abytes(item, undefined, 'key').length;
|
|
1181
|
-
|
|
1530
|
+
const isPub = l === publicKey || l === publicKeyUncompressed;
|
|
1531
|
+
const isSec = l === secretKey || !!allowedLengths?.includes(l);
|
|
1532
|
+
// P-521 accepts both 65- and 66-byte secret keys, so overlapping lengths stay ambiguous.
|
|
1533
|
+
if (isPub && isSec) return undefined;
|
|
1534
|
+
return isPub;
|
|
1182
1535
|
}
|
|
1183
1536
|
|
|
1184
1537
|
/**
|
|
1185
1538
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1186
|
-
* Computes shared
|
|
1539
|
+
* Computes encoded shared point from secret key A and public key B.
|
|
1187
1540
|
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1188
|
-
* Does NOT hash the result
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1541
|
+
* Does NOT hash the result or expose the SEC 1 x-coordinate-only `z`.
|
|
1542
|
+
* Returns the encoded shared point on purpose: callers that need `x_P`
|
|
1543
|
+
* can derive it from the encoded point, but `x_P` alone cannot recover the
|
|
1544
|
+
* point/parity back.
|
|
1545
|
+
* This helper only exposes the fully validated public-key path, not cofactor DH.
|
|
1546
|
+
* @param isCompressed - whether to return compact (default), or full key
|
|
1547
|
+
* @returns shared point encoding
|
|
1191
1548
|
*/
|
|
1192
1549
|
function getSharedSecret(
|
|
1193
|
-
secretKeyA: Uint8Array
|
|
1194
|
-
publicKeyB: Uint8Array
|
|
1550
|
+
secretKeyA: TArg<Uint8Array>,
|
|
1551
|
+
publicKeyB: TArg<Uint8Array>,
|
|
1195
1552
|
isCompressed = true
|
|
1196
|
-
): Uint8Array {
|
|
1553
|
+
): TRet<Uint8Array> {
|
|
1197
1554
|
if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
|
|
1198
1555
|
if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
|
|
1199
1556
|
const s = Fn.fromBytes(secretKeyA);
|
|
@@ -1207,6 +1564,8 @@ export function ecdh(
|
|
|
1207
1564
|
randomSecretKey,
|
|
1208
1565
|
};
|
|
1209
1566
|
const keygen = createKeygen(randomSecretKey, getPublicKey);
|
|
1567
|
+
Object.freeze(utils);
|
|
1568
|
+
Object.freeze(lengths);
|
|
1210
1569
|
|
|
1211
1570
|
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
1212
1571
|
}
|
|
@@ -1214,24 +1573,38 @@ export function ecdh(
|
|
|
1214
1573
|
/**
|
|
1215
1574
|
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
1216
1575
|
*
|
|
1217
|
-
* @param Point created using {@link weierstrass} function
|
|
1218
|
-
* @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
1219
|
-
* @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
|
|
1576
|
+
* @param Point - created using {@link weierstrass} function
|
|
1577
|
+
* @param hash - used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
1578
|
+
* @param ecdsaOpts - rarely needed, see {@link ECDSAOpts}:
|
|
1579
|
+
* - `lowS`: Default low-S policy.
|
|
1580
|
+
* - `hmac`: HMAC implementation used by RFC6979 DRBG.
|
|
1581
|
+
* - `randomBytes`: Optional RNG override.
|
|
1582
|
+
* - `bits2int`: Optional hash-to-int conversion override.
|
|
1583
|
+
* - `bits2int_modN`: Optional hash-to-int-mod-n conversion override.
|
|
1220
1584
|
*
|
|
1585
|
+
* @returns ECDSA helper namespace.
|
|
1221
1586
|
* @example
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1587
|
+
* Create an ECDSA signer/verifier bundle for one curve implementation.
|
|
1588
|
+
*
|
|
1589
|
+
* ```ts
|
|
1590
|
+
* import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
1591
|
+
* import { p256 } from '@noble/curves/nist.js';
|
|
1592
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
1593
|
+
* const p256ecdsa = ecdsa(p256.Point, sha256);
|
|
1594
|
+
* const { secretKey, publicKey } = p256ecdsa.keygen();
|
|
1595
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
1596
|
+
* const sig = p256ecdsa.sign(msg, secretKey);
|
|
1597
|
+
* const isValid = p256ecdsa.verify(sig, msg, publicKey);
|
|
1227
1598
|
* ```
|
|
1228
1599
|
*/
|
|
1229
1600
|
export function ecdsa(
|
|
1230
1601
|
Point: WeierstrassPointCons<bigint>,
|
|
1231
|
-
hash: CHash
|
|
1232
|
-
ecdsaOpts: ECDSAOpts = {}
|
|
1602
|
+
hash: TArg<CHash>,
|
|
1603
|
+
ecdsaOpts: TArg<ECDSAOpts> = {}
|
|
1233
1604
|
): ECDSA {
|
|
1234
|
-
|
|
1605
|
+
// Custom hash / bits2int hooks are treated as pure functions over validated caller-owned bytes.
|
|
1606
|
+
const hash_ = hash as CHash;
|
|
1607
|
+
ahash(hash_);
|
|
1235
1608
|
validateObject(
|
|
1236
1609
|
ecdsaOpts,
|
|
1237
1610
|
{},
|
|
@@ -1244,8 +1617,11 @@ export function ecdsa(
|
|
|
1244
1617
|
}
|
|
1245
1618
|
);
|
|
1246
1619
|
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|
1247
|
-
const randomBytes = ecdsaOpts.randomBytes
|
|
1248
|
-
const hmac =
|
|
1620
|
+
const randomBytes = ecdsaOpts.randomBytes === undefined ? wcRandomBytes : ecdsaOpts.randomBytes;
|
|
1621
|
+
const hmac =
|
|
1622
|
+
ecdsaOpts.hmac === undefined
|
|
1623
|
+
? (key: TArg<Uint8Array>, msg: TArg<Uint8Array>) => nobleHmac(hash_, key, msg)
|
|
1624
|
+
: (ecdsaOpts.hmac as HmacFn);
|
|
1249
1625
|
|
|
1250
1626
|
const { Fp, Fn } = Point;
|
|
1251
1627
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
@@ -1256,7 +1632,11 @@ export function ecdsa(
|
|
|
1256
1632
|
format: 'compact' as ECDSASignatureFormat,
|
|
1257
1633
|
extraEntropy: false,
|
|
1258
1634
|
};
|
|
1259
|
-
|
|
1635
|
+
// SEC 1 4.1.6 public-key recovery tries x = r + jn for j = 0..h. Our recovered-signature
|
|
1636
|
+
// format only stores one overflow bit, so it can only distinguish q.x = r from q.x = r + n.
|
|
1637
|
+
// A third lift would have the form q.x = r + 2n. Since valid ECDSA r is in 1..n-1, the
|
|
1638
|
+
// smallest such lift is 1 + 2n, not 2n.
|
|
1639
|
+
const hasLargeRecoveryLifts = CURVE_ORDER * _2n + _1n < Fp.ORDER;
|
|
1260
1640
|
|
|
1261
1641
|
function isBiggerThanHalfOrder(number: bigint) {
|
|
1262
1642
|
const HALF = CURVE_ORDER >> _1n;
|
|
@@ -1267,19 +1647,18 @@ export function ecdsa(
|
|
|
1267
1647
|
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
1268
1648
|
return num;
|
|
1269
1649
|
}
|
|
1270
|
-
function
|
|
1271
|
-
// ECDSA recovery
|
|
1272
|
-
//
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
1275
|
-
// r+n*i would need to be done instead where i is unknown.
|
|
1650
|
+
function assertRecoverableCurve(): void {
|
|
1651
|
+
// ECDSA recovery only supports curves where the current recovery id can distinguish
|
|
1652
|
+
// q.x = r and q.x = r + n; larger lifts may need additional `r + n*i` branches.
|
|
1653
|
+
// SEC 1 4.1.6 recovers candidates via x = r + jn, but this format only encodes j = 0 or 1.
|
|
1654
|
+
// The next possible candidate is q.x = r + 2n, and its smallest valid value is 1 + 2n.
|
|
1276
1655
|
// To easily get i, we either need to:
|
|
1277
1656
|
// a. increase amount of valid recid values (4, 5...); OR
|
|
1278
|
-
// b. prohibit
|
|
1279
|
-
if (
|
|
1657
|
+
// b. prohibit recovered signatures for those curves.
|
|
1658
|
+
if (hasLargeRecoveryLifts)
|
|
1280
1659
|
throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
|
|
1281
1660
|
}
|
|
1282
|
-
function validateSigLength(bytes: Uint8Array
|
|
1661
|
+
function validateSigLength(bytes: TArg<Uint8Array>, format: ECDSASignatureFormat) {
|
|
1283
1662
|
validateSigFormat(format);
|
|
1284
1663
|
const size = lengths.signature!;
|
|
1285
1664
|
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
@@ -1298,7 +1677,7 @@ export function ecdsa(
|
|
|
1298
1677
|
this.r = validateRS('r', r); // r in [1..N-1];
|
|
1299
1678
|
this.s = validateRS('s', s); // s in [1..N-1];
|
|
1300
1679
|
if (recovery != null) {
|
|
1301
|
-
|
|
1680
|
+
assertRecoverableCurve();
|
|
1302
1681
|
if (![0, 1, 2, 3].includes(recovery)) throw new Error('invalid recovery id');
|
|
1303
1682
|
this.recovery = recovery;
|
|
1304
1683
|
}
|
|
@@ -1306,7 +1685,7 @@ export function ecdsa(
|
|
|
1306
1685
|
}
|
|
1307
1686
|
|
|
1308
1687
|
static fromBytes(
|
|
1309
|
-
bytes: Uint8Array
|
|
1688
|
+
bytes: TArg<Uint8Array>,
|
|
1310
1689
|
format: ECDSASignatureFormat = defaultSigOpts.format
|
|
1311
1690
|
): Signature {
|
|
1312
1691
|
validateSigLength(bytes, format);
|
|
@@ -1340,7 +1719,9 @@ export function ecdsa(
|
|
|
1340
1719
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
1341
1720
|
}
|
|
1342
1721
|
|
|
1343
|
-
|
|
1722
|
+
// Unlike the top-level helper below, this method expects a digest that has
|
|
1723
|
+
// already been hashed to the curve's message representative.
|
|
1724
|
+
recoverPublicKey(messageHash: TArg<Uint8Array>): WeierstrassPoint<bigint> {
|
|
1344
1725
|
const { r, s } = this;
|
|
1345
1726
|
const recovery = this.assertRecovery();
|
|
1346
1727
|
const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;
|
|
@@ -1363,17 +1744,17 @@ export function ecdsa(
|
|
|
1363
1744
|
return isBiggerThanHalfOrder(this.s);
|
|
1364
1745
|
}
|
|
1365
1746
|
|
|
1366
|
-
toBytes(format: ECDSASignatureFormat = defaultSigOpts.format) {
|
|
1747
|
+
toBytes(format: ECDSASignatureFormat = defaultSigOpts.format): TRet<Uint8Array> {
|
|
1367
1748
|
validateSigFormat(format);
|
|
1368
|
-
if (format === 'der') return hexToBytes(DER.hexFromSig(this))
|
|
1749
|
+
if (format === 'der') return hexToBytes(DER.hexFromSig(this)) as TRet<Uint8Array>;
|
|
1369
1750
|
const { r, s } = this;
|
|
1370
1751
|
const rb = Fn.toBytes(r);
|
|
1371
1752
|
const sb = Fn.toBytes(s);
|
|
1372
1753
|
if (format === 'recovered') {
|
|
1373
|
-
|
|
1374
|
-
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb)
|
|
1754
|
+
assertRecoverableCurve();
|
|
1755
|
+
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb) as TRet<Uint8Array>;
|
|
1375
1756
|
}
|
|
1376
|
-
return concatBytes(rb, sb)
|
|
1757
|
+
return concatBytes(rb, sb) as TRet<Uint8Array>;
|
|
1377
1758
|
}
|
|
1378
1759
|
|
|
1379
1760
|
toHex(format?: ECDSASignatureFormat) {
|
|
@@ -1381,39 +1762,44 @@ export function ecdsa(
|
|
|
1381
1762
|
}
|
|
1382
1763
|
}
|
|
1383
1764
|
type RecoveredSignature = Signature & { recovery: number };
|
|
1765
|
+
Object.freeze(Signature.prototype);
|
|
1766
|
+
Object.freeze(Signature);
|
|
1384
1767
|
|
|
1385
1768
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1386
1769
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1387
1770
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1388
1771
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1389
|
-
const bits2int =
|
|
1390
|
-
ecdsaOpts.bits2int
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1772
|
+
const bits2int: (bytes: TArg<Uint8Array>) => bigint =
|
|
1773
|
+
ecdsaOpts.bits2int === undefined
|
|
1774
|
+
? function bits2int_def(bytes: TArg<Uint8Array>): bigint {
|
|
1775
|
+
// Our custom check "just in case", for protection against DoS
|
|
1776
|
+
if (bytes.length > 8192) throw new Error('input is too large');
|
|
1777
|
+
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
1778
|
+
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
1779
|
+
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1780
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
1781
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1782
|
+
}
|
|
1783
|
+
: (ecdsaOpts.bits2int as (bytes: TArg<Uint8Array>) => bigint);
|
|
1784
|
+
const bits2int_modN: (bytes: TArg<Uint8Array>) => bigint =
|
|
1785
|
+
ecdsaOpts.bits2int_modN === undefined
|
|
1786
|
+
? function bits2int_modN_def(bytes: TArg<Uint8Array>): bigint {
|
|
1787
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1788
|
+
}
|
|
1789
|
+
: (ecdsaOpts.bits2int_modN as (bytes: TArg<Uint8Array>) => bigint);
|
|
1406
1790
|
const ORDER_MASK = bitMask(fnBits);
|
|
1791
|
+
// Pads output with zero as per spec.
|
|
1407
1792
|
/** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
|
|
1408
|
-
function int2octets(num: bigint): Uint8Array {
|
|
1409
|
-
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1793
|
+
function int2octets(num: bigint): TRet<Uint8Array> {
|
|
1410
1794
|
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1411
|
-
return Fn.toBytes(num)
|
|
1795
|
+
return Fn.toBytes(num) as TRet<Uint8Array>;
|
|
1412
1796
|
}
|
|
1413
1797
|
|
|
1414
|
-
function validateMsgAndHash(message: Uint8Array
|
|
1798
|
+
function validateMsgAndHash(message: TArg<Uint8Array>, prehash: boolean): TRet<Uint8Array> {
|
|
1415
1799
|
abytes(message, undefined, 'message');
|
|
1416
|
-
return
|
|
1800
|
+
return (
|
|
1801
|
+
prehash ? abytes(hash_(message), undefined, 'prehashed message') : message
|
|
1802
|
+
) as TRet<Uint8Array>;
|
|
1417
1803
|
}
|
|
1418
1804
|
|
|
1419
1805
|
/**
|
|
@@ -1424,7 +1810,11 @@ export function ecdsa(
|
|
|
1424
1810
|
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
1425
1811
|
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1426
1812
|
*/
|
|
1427
|
-
function prepSig(
|
|
1813
|
+
function prepSig(
|
|
1814
|
+
message: TArg<Uint8Array>,
|
|
1815
|
+
secretKey: TArg<Uint8Array>,
|
|
1816
|
+
opts: TArg<ECDSASignOpts>
|
|
1817
|
+
) {
|
|
1428
1818
|
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
1429
1819
|
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
1430
1820
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
@@ -1433,7 +1823,7 @@ export function ecdsa(
|
|
|
1433
1823
|
const h1int = bits2int_modN(message);
|
|
1434
1824
|
const d = Fn.fromBytes(secretKey); // validate secret key, convert to bigint
|
|
1435
1825
|
if (!Fn.isValidNot0(d)) throw new Error('invalid private key');
|
|
1436
|
-
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1826
|
+
const seedArgs: TArg<Uint8Array>[] = [int2octets(d), int2octets(h1int)];
|
|
1437
1827
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1438
1828
|
if (extraEntropy != null && extraEntropy !== false) {
|
|
1439
1829
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
@@ -1441,7 +1831,7 @@ export function ecdsa(
|
|
|
1441
1831
|
const e = extraEntropy === true ? randomBytes(lengths.secretKey) : extraEntropy;
|
|
1442
1832
|
seedArgs.push(abytes(e, undefined, 'extraEntropy')); // check for being bytes
|
|
1443
1833
|
}
|
|
1444
|
-
const seed = concatBytes(...seedArgs)
|
|
1834
|
+
const seed = concatBytes(...seedArgs) as TRet<Uint8Array>; // Step D of RFC6979 3.2
|
|
1445
1835
|
const m = h1int; // no need to call bits2int second time here, it is inside truncateHash!
|
|
1446
1836
|
// Converts signature params into point w r/s, checks result for validity.
|
|
1447
1837
|
// To transform k => Signature:
|
|
@@ -1451,7 +1841,7 @@ export function ecdsa(
|
|
|
1451
1841
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1452
1842
|
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1453
1843
|
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1454
|
-
function k2sig(kBytes: Uint8Array): Signature | undefined {
|
|
1844
|
+
function k2sig(kBytes: TArg<Uint8Array>): Signature | undefined {
|
|
1455
1845
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1456
1846
|
// Important: all mod() calls here must be done over N
|
|
1457
1847
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
@@ -1468,13 +1858,15 @@ export function ecdsa(
|
|
|
1468
1858
|
normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
|
|
1469
1859
|
recovery ^= 1;
|
|
1470
1860
|
}
|
|
1471
|
-
return new Signature(r, normS,
|
|
1861
|
+
return new Signature(r, normS, hasLargeRecoveryLifts ? undefined : recovery);
|
|
1472
1862
|
}
|
|
1473
1863
|
return { seed, k2sig };
|
|
1474
1864
|
}
|
|
1475
1865
|
|
|
1476
1866
|
/**
|
|
1477
|
-
* Signs message hash with a secret key.
|
|
1867
|
+
* Signs a message or message hash with a secret key.
|
|
1868
|
+
* With the default `prehash: true`, raw message bytes are hashed internally;
|
|
1869
|
+
* only `{ prehash: false }` expects a caller-supplied digest.
|
|
1478
1870
|
*
|
|
1479
1871
|
* ```
|
|
1480
1872
|
* sign(m, d) where
|
|
@@ -1484,9 +1876,13 @@ export function ecdsa(
|
|
|
1484
1876
|
* s = (m + dr) / k mod n
|
|
1485
1877
|
* ```
|
|
1486
1878
|
*/
|
|
1487
|
-
function sign(
|
|
1879
|
+
function sign(
|
|
1880
|
+
message: TArg<Uint8Array>,
|
|
1881
|
+
secretKey: TArg<Uint8Array>,
|
|
1882
|
+
opts: TArg<ECDSASignOpts> = {}
|
|
1883
|
+
): TRet<Uint8Array> {
|
|
1488
1884
|
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1489
|
-
const drbg = createHmacDrbg<Signature>(
|
|
1885
|
+
const drbg = createHmacDrbg<Signature>(hash_.outputLen, Fn.BYTES, hmac);
|
|
1490
1886
|
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1491
1887
|
return sig.toBytes(opts.format);
|
|
1492
1888
|
}
|
|
@@ -1505,10 +1901,10 @@ export function ecdsa(
|
|
|
1505
1901
|
* ```
|
|
1506
1902
|
*/
|
|
1507
1903
|
function verify(
|
|
1508
|
-
signature: Uint8Array
|
|
1509
|
-
message: Uint8Array
|
|
1510
|
-
publicKey: Uint8Array
|
|
1511
|
-
opts: ECDSAVerifyOpts = {}
|
|
1904
|
+
signature: TArg<Uint8Array>,
|
|
1905
|
+
message: TArg<Uint8Array>,
|
|
1906
|
+
publicKey: TArg<Uint8Array>,
|
|
1907
|
+
opts: TArg<ECDSAVerifyOpts> = {}
|
|
1512
1908
|
): boolean {
|
|
1513
1909
|
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
1514
1910
|
publicKey = abytes(publicKey, undefined, 'publicKey');
|
|
@@ -1537,10 +1933,12 @@ export function ecdsa(
|
|
|
1537
1933
|
}
|
|
1538
1934
|
|
|
1539
1935
|
function recoverPublicKey(
|
|
1540
|
-
signature: Uint8Array
|
|
1541
|
-
message: Uint8Array
|
|
1542
|
-
opts: ECDSARecoverOpts = {}
|
|
1543
|
-
): Uint8Array {
|
|
1936
|
+
signature: TArg<Uint8Array>,
|
|
1937
|
+
message: TArg<Uint8Array>,
|
|
1938
|
+
opts: TArg<ECDSARecoverOpts> = {}
|
|
1939
|
+
): TRet<Uint8Array> {
|
|
1940
|
+
// Top-level recovery mirrors `sign()` / `verify()`: it hashes raw message
|
|
1941
|
+
// bytes first unless the caller passes `{ prehash: false }`.
|
|
1544
1942
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
1545
1943
|
message = validateMsgAndHash(message, prehash);
|
|
1546
1944
|
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
@@ -1557,6 +1955,6 @@ export function ecdsa(
|
|
|
1557
1955
|
verify,
|
|
1558
1956
|
recoverPublicKey,
|
|
1559
1957
|
Signature,
|
|
1560
|
-
hash,
|
|
1958
|
+
hash: hash_,
|
|
1561
1959
|
}) satisfies Signer;
|
|
1562
1960
|
}
|