@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
package/abstract/weierstrass.js
CHANGED
|
@@ -27,17 +27,22 @@
|
|
|
27
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
28
28
|
import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
|
|
29
29
|
import { ahash } from '@noble/hashes/utils.js';
|
|
30
|
-
import { abool, abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, hexToBytes, isBytes,
|
|
30
|
+
import { abignumber, abool, abytes, aInRange, asafenumber, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, hexToBytes, isBytes, numberToHexUnpadded, validateObject, randomBytes as wcRandomBytes, } from "../utils.js";
|
|
31
31
|
import { createCurveFields, createKeygen, mulEndoUnsafe, negateCt, normalizeZ, wNAF, } from "./curve.js";
|
|
32
|
-
import { FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
|
|
33
|
-
// We construct basis
|
|
32
|
+
import { FpInvertBatch, FpIsSquare, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
|
|
33
|
+
// We construct the basis so `den` is always positive and equals `n`,
|
|
34
|
+
// but the `num` sign depends on the basis, not on the secret value.
|
|
35
|
+
// Exact half-way cases round away from zero, which keeps the split symmetric
|
|
36
|
+
// around the reduced-basis boundaries used by endomorphism decomposition.
|
|
34
37
|
const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n) / den;
|
|
35
|
-
/**
|
|
36
|
-
* Splits scalar for GLV endomorphism.
|
|
37
|
-
*/
|
|
38
|
+
/** Splits scalar for GLV endomorphism. */
|
|
38
39
|
export function _splitEndoScalar(k, basis, n) {
|
|
39
40
|
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
|
|
40
41
|
// Since part can be negative, we need to do this on point.
|
|
42
|
+
// Callers must provide a reduced GLV basis whose vectors satisfy
|
|
43
|
+
// `a + b * lambda ≡ 0 (mod n)`; this helper only sees the basis and `n`.
|
|
44
|
+
// Reject unreduced scalars instead of silently treating them mod n.
|
|
45
|
+
aInRange('scalar', k, _0n, n);
|
|
41
46
|
// TODO: verifyScalar function which consumes lambda
|
|
42
47
|
const [[a1, b1], [a2, b2]] = basis;
|
|
43
48
|
const c1 = divNearest(b2 * k, n);
|
|
@@ -53,10 +58,11 @@ export function _splitEndoScalar(k, basis, n) {
|
|
|
53
58
|
if (k2neg)
|
|
54
59
|
k2 = -k2;
|
|
55
60
|
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
56
|
-
// This should only happen on wrong
|
|
61
|
+
// This should only happen on wrong bases.
|
|
62
|
+
// Also, the math inside is complex enough that this guard is worth keeping.
|
|
57
63
|
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
|
|
58
64
|
if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
|
|
59
|
-
throw new Error('splitScalar (endomorphism): failed
|
|
65
|
+
throw new Error('splitScalar (endomorphism): failed for k');
|
|
60
66
|
}
|
|
61
67
|
return { k1neg, k1, k2neg, k2 };
|
|
62
68
|
}
|
|
@@ -66,7 +72,11 @@ function validateSigFormat(format) {
|
|
|
66
72
|
return format;
|
|
67
73
|
}
|
|
68
74
|
function validateSigOpts(opts, def) {
|
|
75
|
+
validateObject(opts);
|
|
69
76
|
const optsn = {};
|
|
77
|
+
// Normalize only the declared option subset from `def`; unknown keys are
|
|
78
|
+
// intentionally ignored so shared / superset option bags stay valid here too.
|
|
79
|
+
// `extraEntropy` stays an opaque payload until the signing path consumes it.
|
|
70
80
|
for (let optName of Object.keys(def)) {
|
|
71
81
|
// @ts-ignore
|
|
72
82
|
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
@@ -77,6 +87,15 @@ function validateSigOpts(opts, def) {
|
|
|
77
87
|
validateSigFormat(optsn.format);
|
|
78
88
|
return optsn;
|
|
79
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* @param m - Error message.
|
|
92
|
+
* @example
|
|
93
|
+
* Throw a DER-specific error when signature parsing encounters invalid bytes.
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* new DERErr('bad der');
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
80
99
|
export class DERErr extends Error {
|
|
81
100
|
constructor(m = '') {
|
|
82
101
|
super(m);
|
|
@@ -87,7 +106,14 @@ export class DERErr extends Error {
|
|
|
87
106
|
*
|
|
88
107
|
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
|
89
108
|
*
|
|
90
|
-
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der
|
|
109
|
+
* Docs: {@link https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ | Let's Encrypt ASN.1 guide} and
|
|
110
|
+
* {@link https://luca.ntop.org/Teaching/Appunti/asn1.html | Luca Deri's ASN.1 notes}.
|
|
111
|
+
* @example
|
|
112
|
+
* ASN.1 DER encoding utilities.
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* const der = DER.hexFromSig({ r: 1n, s: 2n });
|
|
116
|
+
* ```
|
|
91
117
|
*/
|
|
92
118
|
export const DER = {
|
|
93
119
|
// asn.1 DER encoding utils
|
|
@@ -96,8 +122,13 @@ export const DER = {
|
|
|
96
122
|
_tlv: {
|
|
97
123
|
encode: (tag, data) => {
|
|
98
124
|
const { Err: E } = DER;
|
|
99
|
-
|
|
125
|
+
asafenumber(tag, 'tag');
|
|
126
|
+
if (tag < 0 || tag > 255)
|
|
100
127
|
throw new E('tlv.encode: wrong tag');
|
|
128
|
+
if (typeof data !== 'string')
|
|
129
|
+
throw new TypeError('"data" expected string, got type=' + typeof data);
|
|
130
|
+
// Internal helper: callers hand this already-validated hex payload, so we only enforce
|
|
131
|
+
// byte alignment here instead of re-validating every nibble.
|
|
101
132
|
if (data.length & 1)
|
|
102
133
|
throw new E('tlv.encode: unpadded data');
|
|
103
134
|
const dataLen = data.length / 2;
|
|
@@ -112,13 +143,15 @@ export const DER = {
|
|
|
112
143
|
// v - value, l - left bytes (unparsed)
|
|
113
144
|
decode(tag, data) {
|
|
114
145
|
const { Err: E } = DER;
|
|
146
|
+
data = abytes(data, undefined, 'DER data');
|
|
115
147
|
let pos = 0;
|
|
116
|
-
if (tag < 0 || tag >
|
|
148
|
+
if (tag < 0 || tag > 255)
|
|
117
149
|
throw new E('tlv.encode: wrong tag');
|
|
118
150
|
if (data.length < 2 || data[pos++] !== tag)
|
|
119
151
|
throw new E('tlv.decode: wrong tlv');
|
|
120
152
|
const first = data[pos++];
|
|
121
|
-
|
|
153
|
+
// First bit of first length byte is the short/long form flag.
|
|
154
|
+
const isLong = !!(first & 0b1000_0000);
|
|
122
155
|
let length = 0;
|
|
123
156
|
if (!isLong)
|
|
124
157
|
length = first;
|
|
@@ -127,8 +160,9 @@ export const DER = {
|
|
|
127
160
|
const lenLen = first & 0b0111_1111;
|
|
128
161
|
if (!lenLen)
|
|
129
162
|
throw new E('tlv.decode(long): indefinite length not supported');
|
|
163
|
+
// This would overflow u32 in JS.
|
|
130
164
|
if (lenLen > 4)
|
|
131
|
-
throw new E('tlv.decode(long): byte length is too big');
|
|
165
|
+
throw new E('tlv.decode(long): byte length is too big');
|
|
132
166
|
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|
133
167
|
if (lengthBytes.length !== lenLen)
|
|
134
168
|
throw new E('tlv.decode: length bytes not complete');
|
|
@@ -153,6 +187,7 @@ export const DER = {
|
|
|
153
187
|
_int: {
|
|
154
188
|
encode(num) {
|
|
155
189
|
const { Err: E } = DER;
|
|
190
|
+
abignumber(num);
|
|
156
191
|
if (num < _0n)
|
|
157
192
|
throw new E('integer: negative integers are not allowed');
|
|
158
193
|
let hex = numberToHexUnpadded(num);
|
|
@@ -165,9 +200,12 @@ export const DER = {
|
|
|
165
200
|
},
|
|
166
201
|
decode(data) {
|
|
167
202
|
const { Err: E } = DER;
|
|
203
|
+
if (data.length < 1)
|
|
204
|
+
throw new E('invalid signature integer: empty');
|
|
168
205
|
if (data[0] & 0b1000_0000)
|
|
169
206
|
throw new E('invalid signature integer: negative');
|
|
170
|
-
|
|
207
|
+
// Single-byte zero `00` is the canonical DER INTEGER encoding for zero.
|
|
208
|
+
if (data.length > 1 && data[0] === 0x00 && !(data[1] & 0b1000_0000))
|
|
171
209
|
throw new E('invalid signature integer: unnecessary leading zero');
|
|
172
210
|
return bytesToNumberBE(data);
|
|
173
211
|
},
|
|
@@ -193,31 +231,41 @@ export const DER = {
|
|
|
193
231
|
return tlv.encode(0x30, seq);
|
|
194
232
|
},
|
|
195
233
|
};
|
|
234
|
+
Object.freeze(DER._tlv);
|
|
235
|
+
Object.freeze(DER._int);
|
|
236
|
+
Object.freeze(DER);
|
|
196
237
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
197
238
|
// prettier-ignore
|
|
198
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
239
|
+
const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4);
|
|
199
240
|
/**
|
|
200
241
|
* Creates weierstrass Point constructor, based on specified curve options.
|
|
201
242
|
*
|
|
202
243
|
* See {@link WeierstrassOpts}.
|
|
244
|
+
* @param params - Curve parameters. See {@link WeierstrassOpts}.
|
|
245
|
+
* @param extraOpts - Optional helpers and overrides. See {@link WeierstrassExtraOpts}.
|
|
246
|
+
* @returns Weierstrass point constructor.
|
|
247
|
+
* @throws If the curve parameters, overrides, or point codecs are invalid. {@link Error}
|
|
203
248
|
*
|
|
204
249
|
* @example
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
250
|
+
* Construct a point type from explicit Weierstrass curve parameters.
|
|
251
|
+
*
|
|
252
|
+
* ```js
|
|
253
|
+
* const opts = {
|
|
254
|
+
* p: 0xfffffffffffffffffffffffffffffffeffffac73n,
|
|
255
|
+
* n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
|
|
256
|
+
* h: 1n,
|
|
257
|
+
* a: 0n,
|
|
258
|
+
* b: 7n,
|
|
259
|
+
* Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
|
|
260
|
+
* Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
|
|
261
|
+
* };
|
|
262
|
+
* const secp160k1_Point = weierstrass(opts);
|
|
263
|
+
* ```
|
|
217
264
|
*/
|
|
218
265
|
export function weierstrass(params, extraOpts = {}) {
|
|
219
266
|
const validated = createCurveFields('weierstrass', params, extraOpts);
|
|
220
|
-
const
|
|
267
|
+
const Fp = validated.Fp;
|
|
268
|
+
const Fn = validated.Fn;
|
|
221
269
|
let CURVE = validated.CURVE;
|
|
222
270
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
223
271
|
validateObject(extraOpts, {}, {
|
|
@@ -228,7 +276,9 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
228
276
|
toBytes: 'function',
|
|
229
277
|
endo: 'object',
|
|
230
278
|
});
|
|
231
|
-
|
|
279
|
+
// Snapshot constructor-time flags whose later mutation would otherwise change
|
|
280
|
+
// validity semantics of an already-built point type.
|
|
281
|
+
const { endo, allowInfinityPoint } = extraOpts;
|
|
232
282
|
if (endo) {
|
|
233
283
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
234
284
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
@@ -242,6 +292,10 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
242
292
|
}
|
|
243
293
|
// Implements IEEE P1363 point encoding
|
|
244
294
|
function pointToBytes(_c, point, isCompressed) {
|
|
295
|
+
// SEC 1 v2.0 §2.3.3 encodes infinity as the single octet 0x00. Only curves
|
|
296
|
+
// that opt into infinity as a public point value should expose that byte form.
|
|
297
|
+
if (allowInfinityPoint && point.is0())
|
|
298
|
+
return Uint8Array.of(0);
|
|
245
299
|
const { x, y } = point.toAffine();
|
|
246
300
|
const bx = Fp.toBytes(x);
|
|
247
301
|
abool(isCompressed, 'isCompressed');
|
|
@@ -260,6 +314,13 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
260
314
|
const length = bytes.length;
|
|
261
315
|
const head = bytes[0];
|
|
262
316
|
const tail = bytes.subarray(1);
|
|
317
|
+
if (allowInfinityPoint && length === 1 && head === 0x00)
|
|
318
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
319
|
+
// SEC 1 v2.0 §2.3.4 decodes 0x00 as infinity, but §3.2.2 public-key validation
|
|
320
|
+
// rejects infinity. We therefore keep 0x00 rejected by default because callers
|
|
321
|
+
// reuse this parser as the strict public-key boundary, and only admit it when
|
|
322
|
+
// the curve explicitly opts into infinity as a public point value. secp256k1
|
|
323
|
+
// crosstests show OpenSSL raw point codecs accept 0x00 too.
|
|
263
324
|
// No actual validation is done here: use .assertValidity()
|
|
264
325
|
if (length === comp && (head === 0x02 || head === 0x03)) {
|
|
265
326
|
const x = Fp.fromBytes(tail);
|
|
@@ -294,8 +355,8 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
294
355
|
throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
|
|
295
356
|
}
|
|
296
357
|
}
|
|
297
|
-
const encodePoint = extraOpts.toBytes
|
|
298
|
-
const decodePoint = extraOpts.fromBytes
|
|
358
|
+
const encodePoint = extraOpts.toBytes === undefined ? pointToBytes : extraOpts.toBytes;
|
|
359
|
+
const decodePoint = extraOpts.fromBytes === undefined ? pointFromBytes : extraOpts.fromBytes;
|
|
299
360
|
function weierstrassEquation(x) {
|
|
300
361
|
const x2 = Fp.sqr(x); // x * x
|
|
301
362
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
@@ -308,7 +369,8 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
308
369
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
309
370
|
return Fp.eql(left, right);
|
|
310
371
|
}
|
|
311
|
-
//
|
|
372
|
+
// Keep constructor-time generator validation cheap: callers are responsible for supplying the
|
|
373
|
+
// correct prime-order base point, while eager subgroup checks here would slow heavy module imports.
|
|
312
374
|
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
313
375
|
if (!isValidXY(CURVE.Gx, CURVE.Gy))
|
|
314
376
|
throw new Error('bad curve params: generator point');
|
|
@@ -333,50 +395,6 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
333
395
|
throw new Error('no endo');
|
|
334
396
|
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|
335
397
|
}
|
|
336
|
-
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
337
|
-
// Converts Projective point to affine (x, y) coordinates.
|
|
338
|
-
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
339
|
-
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
340
|
-
const toAffineMemo = memoized((p, iz) => {
|
|
341
|
-
const { X, Y, Z } = p;
|
|
342
|
-
// Fast-path for normalized points
|
|
343
|
-
if (Fp.eql(Z, Fp.ONE))
|
|
344
|
-
return { x: X, y: Y };
|
|
345
|
-
const is0 = p.is0();
|
|
346
|
-
// If invZ was 0, we return zero point. However we still want to execute
|
|
347
|
-
// all operations, so we replace invZ with a random number, 1.
|
|
348
|
-
if (iz == null)
|
|
349
|
-
iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
350
|
-
const x = Fp.mul(X, iz);
|
|
351
|
-
const y = Fp.mul(Y, iz);
|
|
352
|
-
const zz = Fp.mul(Z, iz);
|
|
353
|
-
if (is0)
|
|
354
|
-
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
355
|
-
if (!Fp.eql(zz, Fp.ONE))
|
|
356
|
-
throw new Error('invZ was invalid');
|
|
357
|
-
return { x, y };
|
|
358
|
-
});
|
|
359
|
-
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
360
|
-
// Otherwise true will be return
|
|
361
|
-
const assertValidMemo = memoized((p) => {
|
|
362
|
-
if (p.is0()) {
|
|
363
|
-
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
364
|
-
// In BLS, ZERO can be serialized, so we allow it.
|
|
365
|
-
// (0, 0, 0) is invalid representation of ZERO.
|
|
366
|
-
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|
367
|
-
return;
|
|
368
|
-
throw new Error('bad point: ZERO');
|
|
369
|
-
}
|
|
370
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
371
|
-
const { x, y } = p.toAffine();
|
|
372
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
373
|
-
throw new Error('bad point: x or y not field elements');
|
|
374
|
-
if (!isValidXY(x, y))
|
|
375
|
-
throw new Error('bad point: equation left != right');
|
|
376
|
-
if (!p.isTorsionFree())
|
|
377
|
-
throw new Error('bad point: not in prime-order subgroup');
|
|
378
|
-
return true;
|
|
379
|
-
});
|
|
380
398
|
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
381
399
|
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
382
400
|
k1p = negateCt(k1neg, k1p);
|
|
@@ -403,6 +421,9 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
403
421
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
404
422
|
constructor(X, Y, Z) {
|
|
405
423
|
this.X = acoord('x', X);
|
|
424
|
+
// This is not just about ZERO / infinity: ambient curves can have real
|
|
425
|
+
// finite points with y=0. Those points are 2-torsion, so they cannot lie
|
|
426
|
+
// in the odd prime-order subgroups this point type is meant to represent.
|
|
406
427
|
this.Y = acoord('y', Y, true);
|
|
407
428
|
this.Z = acoord('z', Z);
|
|
408
429
|
Object.freeze(this);
|
|
@@ -439,7 +460,7 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
439
460
|
/**
|
|
440
461
|
*
|
|
441
462
|
* @param windowSize
|
|
442
|
-
* @param isLazy true will defer table computation until the first multiplication
|
|
463
|
+
* @param isLazy - true will defer table computation until the first multiplication
|
|
443
464
|
* @returns
|
|
444
465
|
*/
|
|
445
466
|
precompute(windowSize = 8, isLazy = true) {
|
|
@@ -451,7 +472,24 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
451
472
|
// TODO: return `this`
|
|
452
473
|
/** A point on curve is valid if it conforms to equation. */
|
|
453
474
|
assertValidity() {
|
|
454
|
-
|
|
475
|
+
const p = this;
|
|
476
|
+
if (p.is0()) {
|
|
477
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
478
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
479
|
+
// Keep the accepted infinity encoding canonical: projective-equivalent (X, Y, 0) points
|
|
480
|
+
// like (1, 1, 0) compare equal to ZERO, but only (0, 1, 0) should pass this guard.
|
|
481
|
+
if (extraOpts.allowInfinityPoint && Fp.is0(p.X) && Fp.eql(p.Y, Fp.ONE) && Fp.is0(p.Z))
|
|
482
|
+
return;
|
|
483
|
+
throw new Error('bad point: ZERO');
|
|
484
|
+
}
|
|
485
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
486
|
+
const { x, y } = p.toAffine();
|
|
487
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
488
|
+
throw new Error('bad point: x or y not field elements');
|
|
489
|
+
if (!isValidXY(x, y))
|
|
490
|
+
throw new Error('bad point: equation left != right');
|
|
491
|
+
if (!p.isTorsionFree())
|
|
492
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
455
493
|
}
|
|
456
494
|
hasEvenY() {
|
|
457
495
|
const { y } = this.toAffine();
|
|
@@ -568,6 +606,9 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
568
606
|
return new Point(X3, Y3, Z3);
|
|
569
607
|
}
|
|
570
608
|
subtract(other) {
|
|
609
|
+
// Validate before calling `negate()` so wrong inputs fail with the point guard
|
|
610
|
+
// instead of leaking a foreign `negate()` error.
|
|
611
|
+
aprjpoint(other);
|
|
571
612
|
return this.add(other.negate());
|
|
572
613
|
}
|
|
573
614
|
is0() {
|
|
@@ -579,13 +620,16 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
579
620
|
* but takes 2x longer to generate and consumes 2x memory.
|
|
580
621
|
* Uses precomputes when available.
|
|
581
622
|
* Uses endomorphism for Koblitz curves.
|
|
582
|
-
* @param scalar by which the point would be multiplied
|
|
623
|
+
* @param scalar - by which the point would be multiplied
|
|
583
624
|
* @returns New point
|
|
584
625
|
*/
|
|
585
626
|
multiply(scalar) {
|
|
586
627
|
const { endo } = extraOpts;
|
|
628
|
+
// Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.
|
|
629
|
+
// In key/signature-style callers, those values usually mean broken hash/scalar plumbing,
|
|
630
|
+
// and failing closed is safer than silently producing the identity point.
|
|
587
631
|
if (!Fn.isValidNot0(scalar))
|
|
588
|
-
throw new
|
|
632
|
+
throw new RangeError('invalid scalar: out of range'); // 0 is invalid
|
|
589
633
|
let point, fake; // Fake point is used to const-time mult
|
|
590
634
|
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
591
635
|
/** See docs for {@link EndomorphismOpts} */
|
|
@@ -609,11 +653,14 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
609
653
|
* It's faster, but should only be used when you don't care about
|
|
610
654
|
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
611
655
|
*/
|
|
612
|
-
multiplyUnsafe(
|
|
656
|
+
multiplyUnsafe(scalar) {
|
|
613
657
|
const { endo } = extraOpts;
|
|
614
658
|
const p = this;
|
|
659
|
+
const sc = scalar;
|
|
660
|
+
// Public-scalar callers may need 0, but n and larger values stay rejected here too.
|
|
661
|
+
// Reducing them mod n would turn bad caller input into an accidental identity point.
|
|
615
662
|
if (!Fn.isValid(sc))
|
|
616
|
-
throw new
|
|
663
|
+
throw new RangeError('invalid scalar: out of range'); // 0 is valid
|
|
617
664
|
if (sc === _0n || p.is0())
|
|
618
665
|
return Point.ZERO; // 0
|
|
619
666
|
if (sc === _1n)
|
|
@@ -633,10 +680,29 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
633
680
|
}
|
|
634
681
|
/**
|
|
635
682
|
* Converts Projective point to affine (x, y) coordinates.
|
|
636
|
-
*
|
|
683
|
+
* (X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
684
|
+
* @param invertedZ - Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
637
685
|
*/
|
|
638
686
|
toAffine(invertedZ) {
|
|
639
|
-
|
|
687
|
+
const p = this;
|
|
688
|
+
let iz = invertedZ;
|
|
689
|
+
const { X, Y, Z } = p;
|
|
690
|
+
// Fast-path for normalized points
|
|
691
|
+
if (Fp.eql(Z, Fp.ONE))
|
|
692
|
+
return { x: X, y: Y };
|
|
693
|
+
const is0 = p.is0();
|
|
694
|
+
// If invZ was 0, we return zero point. However we still want to execute
|
|
695
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
696
|
+
if (iz == null)
|
|
697
|
+
iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
698
|
+
const x = Fp.mul(X, iz);
|
|
699
|
+
const y = Fp.mul(Y, iz);
|
|
700
|
+
const zz = Fp.mul(Z, iz);
|
|
701
|
+
if (is0)
|
|
702
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
703
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
704
|
+
throw new Error('invZ was invalid');
|
|
705
|
+
return { x, y };
|
|
640
706
|
}
|
|
641
707
|
/**
|
|
642
708
|
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
@@ -656,14 +722,20 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
656
722
|
return this; // Fast-path
|
|
657
723
|
if (clearCofactor)
|
|
658
724
|
return clearCofactor(Point, this);
|
|
725
|
+
// Default fallback assumes the cofactor fits the usual subgroup-scalar
|
|
726
|
+
// multiplyUnsafe() contract. Curves with larger / structured cofactors
|
|
727
|
+
// should define a clearCofactor override anyway (e.g. psi/Frobenius maps).
|
|
659
728
|
return this.multiplyUnsafe(cofactor);
|
|
660
729
|
}
|
|
661
730
|
isSmallOrder() {
|
|
662
|
-
|
|
663
|
-
|
|
731
|
+
if (cofactor === _1n)
|
|
732
|
+
return this.is0(); // Fast-path
|
|
733
|
+
return this.clearCofactor().is0();
|
|
664
734
|
}
|
|
665
735
|
toBytes(isCompressed = true) {
|
|
666
736
|
abool(isCompressed, 'isCompressed');
|
|
737
|
+
// Same policy as pointFromBytes(): keep ZERO out of the default byte surface because
|
|
738
|
+
// callers use these encodings as public keys, where SEC 1 validation rejects infinity.
|
|
667
739
|
this.assertValidity();
|
|
668
740
|
return encodePoint(Point, this, isCompressed);
|
|
669
741
|
}
|
|
@@ -676,7 +748,12 @@ export function weierstrass(params, extraOpts = {}) {
|
|
|
676
748
|
}
|
|
677
749
|
const bits = Fn.BITS;
|
|
678
750
|
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
679
|
-
|
|
751
|
+
// Tiny toy curves can have scalar fields narrower than 8 bits. Skip the
|
|
752
|
+
// eager W=8 cache there instead of rejecting an otherwise valid constructor.
|
|
753
|
+
if (bits >= 8)
|
|
754
|
+
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
755
|
+
Object.freeze(Point.prototype);
|
|
756
|
+
Object.freeze(Point);
|
|
680
757
|
return Point;
|
|
681
758
|
}
|
|
682
759
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
@@ -688,13 +765,26 @@ function pprefix(hasEvenY) {
|
|
|
688
765
|
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
689
766
|
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
690
767
|
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
691
|
-
*
|
|
692
|
-
* @param
|
|
693
|
-
* @
|
|
768
|
+
* RFC 9380 expects callers to provide `v != 0`; this helper does not enforce it.
|
|
769
|
+
* @param Fp - Field implementation.
|
|
770
|
+
* @param Z - Simplified SWU map parameter.
|
|
771
|
+
* @returns Square-root ratio helper.
|
|
772
|
+
* @example
|
|
773
|
+
* Build the square-root ratio helper used by SWU map implementations.
|
|
774
|
+
*
|
|
775
|
+
* ```ts
|
|
776
|
+
* import { SWUFpSqrtRatio } from '@noble/curves/abstract/weierstrass.js';
|
|
777
|
+
* import { Field } from '@noble/curves/abstract/modular.js';
|
|
778
|
+
* const Fp = Field(17n);
|
|
779
|
+
* const sqrtRatio = SWUFpSqrtRatio(Fp, 3n);
|
|
780
|
+
* const out = sqrtRatio(4n, 1n);
|
|
781
|
+
* ```
|
|
694
782
|
*/
|
|
695
783
|
export function SWUFpSqrtRatio(Fp, Z) {
|
|
784
|
+
// Fail with the usual field-shape error before touching pow/cmov on malformed field shims.
|
|
785
|
+
const F = validateField(Fp);
|
|
696
786
|
// Generic implementation
|
|
697
|
-
const q =
|
|
787
|
+
const q = F.ORDER;
|
|
698
788
|
let l = _0n;
|
|
699
789
|
for (let o = q - _1n; o % _2n === _0n; o /= _2n)
|
|
700
790
|
l += _1n;
|
|
@@ -707,54 +797,60 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
707
797
|
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
708
798
|
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
709
799
|
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
710
|
-
const c6 =
|
|
711
|
-
const c7 =
|
|
800
|
+
const c6 = F.pow(Z, c2); // 6. c6 = Z^c2
|
|
801
|
+
const c7 = F.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
802
|
+
// RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) only for v != 0.
|
|
803
|
+
// We keep v=0 on the regular result path with isValid=false instead of
|
|
804
|
+
// throwing so the helper stays closer to the RFC's fixed control flow.
|
|
712
805
|
let sqrtRatio = (u, v) => {
|
|
713
806
|
let tv1 = c6; // 1. tv1 = c6
|
|
714
|
-
let tv2 =
|
|
715
|
-
let tv3 =
|
|
716
|
-
tv3 =
|
|
717
|
-
let tv5 =
|
|
718
|
-
tv5 =
|
|
719
|
-
tv5 =
|
|
720
|
-
tv2 =
|
|
721
|
-
tv3 =
|
|
722
|
-
let tv4 =
|
|
723
|
-
tv5 =
|
|
724
|
-
let isQR =
|
|
725
|
-
tv2 =
|
|
726
|
-
tv5 =
|
|
727
|
-
tv3 =
|
|
728
|
-
tv4 =
|
|
807
|
+
let tv2 = F.pow(v, c4); // 2. tv2 = v^c4
|
|
808
|
+
let tv3 = F.sqr(tv2); // 3. tv3 = tv2^2
|
|
809
|
+
tv3 = F.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
810
|
+
let tv5 = F.mul(u, tv3); // 5. tv5 = u * tv3
|
|
811
|
+
tv5 = F.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
812
|
+
tv5 = F.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
813
|
+
tv2 = F.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
814
|
+
tv3 = F.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
815
|
+
let tv4 = F.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
816
|
+
tv5 = F.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
817
|
+
let isQR = F.eql(tv5, F.ONE); // 12. isQR = tv5 == 1
|
|
818
|
+
tv2 = F.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
819
|
+
tv5 = F.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
820
|
+
tv3 = F.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
821
|
+
tv4 = F.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
729
822
|
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
730
823
|
for (let i = c1; i > _1n; i--) {
|
|
731
824
|
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
732
825
|
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
733
|
-
let tvv5 =
|
|
734
|
-
const e1 =
|
|
735
|
-
tv2 =
|
|
736
|
-
tv1 =
|
|
737
|
-
tvv5 =
|
|
738
|
-
tv3 =
|
|
739
|
-
tv4 =
|
|
826
|
+
let tvv5 = F.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
827
|
+
const e1 = F.eql(tvv5, F.ONE); // 21. e1 = tv5 == 1
|
|
828
|
+
tv2 = F.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
829
|
+
tv1 = F.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
830
|
+
tvv5 = F.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
831
|
+
tv3 = F.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
832
|
+
tv4 = F.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
740
833
|
}
|
|
741
|
-
|
|
834
|
+
// RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) for v != 0.
|
|
835
|
+
// When u = 0 and v != 0, u / v = 0 is square and the computed root is
|
|
836
|
+
// still 0, so widen only the final flag and keep the full control flow.
|
|
837
|
+
return { isValid: !F.is0(v) && (isQR || F.is0(u)), value: tv3 };
|
|
742
838
|
};
|
|
743
|
-
if (
|
|
839
|
+
if (F.ORDER % _4n === _3n) {
|
|
744
840
|
// sqrt_ratio_3mod4(u, v)
|
|
745
|
-
const c1 = (
|
|
746
|
-
const c2 =
|
|
841
|
+
const c1 = (F.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
842
|
+
const c2 = F.sqrt(F.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
747
843
|
sqrtRatio = (u, v) => {
|
|
748
|
-
let tv1 =
|
|
749
|
-
const tv2 =
|
|
750
|
-
tv1 =
|
|
751
|
-
let y1 =
|
|
752
|
-
y1 =
|
|
753
|
-
const y2 =
|
|
754
|
-
const tv3 =
|
|
755
|
-
const isQR =
|
|
756
|
-
let y =
|
|
757
|
-
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
844
|
+
let tv1 = F.sqr(v); // 1. tv1 = v^2
|
|
845
|
+
const tv2 = F.mul(u, v); // 2. tv2 = u * v
|
|
846
|
+
tv1 = F.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
847
|
+
let y1 = F.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
848
|
+
y1 = F.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
849
|
+
const y2 = F.mul(y1, c2); // 6. y2 = y1 * c2
|
|
850
|
+
const tv3 = F.mul(F.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
851
|
+
const isQR = F.eql(tv3, u); // 9. isQR = tv3 == u
|
|
852
|
+
let y = F.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
853
|
+
return { isValid: !F.is0(v) && isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
758
854
|
};
|
|
759
855
|
}
|
|
760
856
|
// No curves uses that
|
|
@@ -763,47 +859,82 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
763
859
|
}
|
|
764
860
|
/**
|
|
765
861
|
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
766
|
-
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
862
|
+
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2 | RFC 9380 section 6.6.2}.
|
|
863
|
+
* @param Fp - Field implementation.
|
|
864
|
+
* @param opts - SWU parameters:
|
|
865
|
+
* - `A`: Curve parameter `A`.
|
|
866
|
+
* - `B`: Curve parameter `B`.
|
|
867
|
+
* - `Z`: Simplified SWU map parameter.
|
|
868
|
+
* @returns Deterministic map-to-curve function.
|
|
869
|
+
* @throws If the SWU parameters are invalid or the field lacks the required helpers. {@link Error}
|
|
870
|
+
* @example
|
|
871
|
+
* Map one field element to a Weierstrass curve point with the SWU recipe.
|
|
872
|
+
*
|
|
873
|
+
* ```ts
|
|
874
|
+
* import { mapToCurveSimpleSWU } from '@noble/curves/abstract/weierstrass.js';
|
|
875
|
+
* import { Field } from '@noble/curves/abstract/modular.js';
|
|
876
|
+
* const Fp = Field(17n);
|
|
877
|
+
* const map = mapToCurveSimpleSWU(Fp, { A: 1n, B: 2n, Z: 3n });
|
|
878
|
+
* const point = map(5n);
|
|
879
|
+
* ```
|
|
767
880
|
*/
|
|
768
881
|
export function mapToCurveSimpleSWU(Fp, opts) {
|
|
769
|
-
validateField(Fp);
|
|
882
|
+
const F = validateField(Fp);
|
|
770
883
|
const { A, B, Z } = opts;
|
|
771
|
-
if (!
|
|
884
|
+
if (!F.isValidNot0(A) || !F.isValidNot0(B) || !F.isValid(Z))
|
|
772
885
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
773
|
-
|
|
774
|
-
|
|
886
|
+
// RFC 9380 §6.6.2 and Appendix H.2 require:
|
|
887
|
+
// 1. Z is non-square in F
|
|
888
|
+
// 2. Z != -1 in F
|
|
889
|
+
// 3. g(x) - Z is irreducible over F
|
|
890
|
+
// 4. g(B / (Z * A)) is square in F
|
|
891
|
+
// We can enforce 1, 2, and 4 with the current field API.
|
|
892
|
+
// Criterion 3 is not checked here because generic `IField<T>` does not expose
|
|
893
|
+
// polynomial-ring / irreducibility operations, and this helper is used for
|
|
894
|
+
// both prime and extension fields.
|
|
895
|
+
if (F.eql(Z, F.neg(F.ONE)) || FpIsSquare(F, Z))
|
|
896
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
897
|
+
// RFC 9380 Appendix H.2 criterion 4: g(B / (Z * A)) is square in F.
|
|
898
|
+
// x = B / (Z * A)
|
|
899
|
+
const x = F.mul(B, F.inv(F.mul(Z, A)));
|
|
900
|
+
// g(x) = x^3 + A*x + B
|
|
901
|
+
const gx = F.add(F.add(F.mul(F.sqr(x), x), F.mul(A, x)), B);
|
|
902
|
+
if (!FpIsSquare(F, gx))
|
|
903
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
904
|
+
const sqrtRatio = SWUFpSqrtRatio(F, Z);
|
|
905
|
+
if (!F.isOdd)
|
|
775
906
|
throw new Error('Field does not have .isOdd()');
|
|
776
907
|
// Input: u, an element of F.
|
|
777
908
|
// Output: (x, y), a point on E.
|
|
778
909
|
return (u) => {
|
|
779
910
|
// prettier-ignore
|
|
780
911
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
781
|
-
tv1 =
|
|
782
|
-
tv1 =
|
|
783
|
-
tv2 =
|
|
784
|
-
tv2 =
|
|
785
|
-
tv3 =
|
|
786
|
-
tv3 =
|
|
787
|
-
tv4 =
|
|
788
|
-
tv4 =
|
|
789
|
-
tv2 =
|
|
790
|
-
tv6 =
|
|
791
|
-
tv5 =
|
|
792
|
-
tv2 =
|
|
793
|
-
tv2 =
|
|
794
|
-
tv6 =
|
|
795
|
-
tv5 =
|
|
796
|
-
tv2 =
|
|
797
|
-
x =
|
|
912
|
+
tv1 = F.sqr(u); // 1. tv1 = u^2
|
|
913
|
+
tv1 = F.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
914
|
+
tv2 = F.sqr(tv1); // 3. tv2 = tv1^2
|
|
915
|
+
tv2 = F.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
916
|
+
tv3 = F.add(tv2, F.ONE); // 5. tv3 = tv2 + 1
|
|
917
|
+
tv3 = F.mul(tv3, B); // 6. tv3 = B * tv3
|
|
918
|
+
tv4 = F.cmov(Z, F.neg(tv2), !F.eql(tv2, F.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
919
|
+
tv4 = F.mul(tv4, A); // 8. tv4 = A * tv4
|
|
920
|
+
tv2 = F.sqr(tv3); // 9. tv2 = tv3^2
|
|
921
|
+
tv6 = F.sqr(tv4); // 10. tv6 = tv4^2
|
|
922
|
+
tv5 = F.mul(tv6, A); // 11. tv5 = A * tv6
|
|
923
|
+
tv2 = F.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
924
|
+
tv2 = F.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
925
|
+
tv6 = F.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
926
|
+
tv5 = F.mul(tv6, B); // 15. tv5 = B * tv6
|
|
927
|
+
tv2 = F.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
928
|
+
x = F.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
798
929
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
799
|
-
y =
|
|
800
|
-
y =
|
|
801
|
-
x =
|
|
802
|
-
y =
|
|
803
|
-
const e1 =
|
|
804
|
-
y =
|
|
805
|
-
const tv4_inv = FpInvertBatch(
|
|
806
|
-
x =
|
|
930
|
+
y = F.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
931
|
+
y = F.mul(y, value); // 20. y = y * y1
|
|
932
|
+
x = F.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
933
|
+
y = F.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
934
|
+
const e1 = F.isOdd(u) === F.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
935
|
+
y = F.cmov(F.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
936
|
+
const tv4_inv = FpInvertBatch(F, [tv4], true)[0];
|
|
937
|
+
x = F.mul(x, tv4_inv); // 25. x = x / tv4
|
|
807
938
|
return { x, y };
|
|
808
939
|
};
|
|
809
940
|
}
|
|
@@ -813,17 +944,37 @@ function getWLengths(Fp, Fn) {
|
|
|
813
944
|
publicKey: 1 + Fp.BYTES,
|
|
814
945
|
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|
815
946
|
publicKeyHasPrefix: true,
|
|
947
|
+
// Raw compact `(r || s)` signature width; DER and recovered signatures use
|
|
948
|
+
// different lengths outside this helper.
|
|
816
949
|
signature: 2 * Fn.BYTES,
|
|
817
950
|
};
|
|
818
951
|
}
|
|
819
952
|
/**
|
|
820
953
|
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
821
954
|
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
955
|
+
* @param Point - Weierstrass point constructor.
|
|
956
|
+
* @param ecdhOpts - Optional randomness helpers:
|
|
957
|
+
* - `randomBytes` (optional): Optional RNG override.
|
|
958
|
+
* @returns ECDH helper namespace.
|
|
959
|
+
* @example
|
|
960
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
961
|
+
*
|
|
962
|
+
* ```ts
|
|
963
|
+
* import { ecdh } from '@noble/curves/abstract/weierstrass.js';
|
|
964
|
+
* import { p256 } from '@noble/curves/nist.js';
|
|
965
|
+
* const dh = ecdh(p256.Point);
|
|
966
|
+
* const alice = dh.keygen();
|
|
967
|
+
* const shared = dh.getSharedSecret(alice.secretKey, alice.publicKey);
|
|
968
|
+
* ```
|
|
822
969
|
*/
|
|
823
970
|
export function ecdh(Point, ecdhOpts = {}) {
|
|
824
971
|
const { Fn } = Point;
|
|
825
|
-
const randomBytes_ = ecdhOpts.randomBytes
|
|
826
|
-
|
|
972
|
+
const randomBytes_ = ecdhOpts.randomBytes === undefined ? wcRandomBytes : ecdhOpts.randomBytes;
|
|
973
|
+
// Keep the advertised seed length aligned with mapHashToField(), which keeps a hard 16-byte
|
|
974
|
+
// minimum even on toy curves.
|
|
975
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), {
|
|
976
|
+
seed: Math.max(getMinHashLength(Fn.ORDER), 16),
|
|
977
|
+
});
|
|
827
978
|
function isValidSecretKey(secretKey) {
|
|
828
979
|
try {
|
|
829
980
|
const num = Fn.fromBytes(secretKey);
|
|
@@ -851,12 +1002,13 @@ export function ecdh(Point, ecdhOpts = {}) {
|
|
|
851
1002
|
* Produces cryptographically secure secret key from random of size
|
|
852
1003
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
853
1004
|
*/
|
|
854
|
-
function randomSecretKey(seed
|
|
1005
|
+
function randomSecretKey(seed) {
|
|
1006
|
+
seed = seed === undefined ? randomBytes_(lengths.seed) : seed;
|
|
855
1007
|
return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
856
1008
|
}
|
|
857
1009
|
/**
|
|
858
1010
|
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
859
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1011
|
+
* @param isCompressed - whether to return compact (default), or full key
|
|
860
1012
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
861
1013
|
*/
|
|
862
1014
|
function getPublicKey(secretKey, isCompressed = true) {
|
|
@@ -867,20 +1019,28 @@ export function ecdh(Point, ecdhOpts = {}) {
|
|
|
867
1019
|
*/
|
|
868
1020
|
function isProbPub(item) {
|
|
869
1021
|
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
1022
|
+
const allowedLengths = Fn._lengths;
|
|
870
1023
|
if (!isBytes(item))
|
|
871
1024
|
return undefined;
|
|
872
|
-
if (('_lengths' in Fn && Fn._lengths) || secretKey === publicKey)
|
|
873
|
-
return undefined;
|
|
874
1025
|
const l = abytes(item, undefined, 'key').length;
|
|
875
|
-
|
|
1026
|
+
const isPub = l === publicKey || l === publicKeyUncompressed;
|
|
1027
|
+
const isSec = l === secretKey || !!allowedLengths?.includes(l);
|
|
1028
|
+
// P-521 accepts both 65- and 66-byte secret keys, so overlapping lengths stay ambiguous.
|
|
1029
|
+
if (isPub && isSec)
|
|
1030
|
+
return undefined;
|
|
1031
|
+
return isPub;
|
|
876
1032
|
}
|
|
877
1033
|
/**
|
|
878
1034
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
879
|
-
* Computes shared
|
|
1035
|
+
* Computes encoded shared point from secret key A and public key B.
|
|
880
1036
|
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
881
|
-
* Does NOT hash the result
|
|
882
|
-
*
|
|
883
|
-
*
|
|
1037
|
+
* Does NOT hash the result or expose the SEC 1 x-coordinate-only `z`.
|
|
1038
|
+
* Returns the encoded shared point on purpose: callers that need `x_P`
|
|
1039
|
+
* can derive it from the encoded point, but `x_P` alone cannot recover the
|
|
1040
|
+
* point/parity back.
|
|
1041
|
+
* This helper only exposes the fully validated public-key path, not cofactor DH.
|
|
1042
|
+
* @param isCompressed - whether to return compact (default), or full key
|
|
1043
|
+
* @returns shared point encoding
|
|
884
1044
|
*/
|
|
885
1045
|
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
886
1046
|
if (isProbPub(secretKeyA) === true)
|
|
@@ -897,25 +1057,41 @@ export function ecdh(Point, ecdhOpts = {}) {
|
|
|
897
1057
|
randomSecretKey,
|
|
898
1058
|
};
|
|
899
1059
|
const keygen = createKeygen(randomSecretKey, getPublicKey);
|
|
1060
|
+
Object.freeze(utils);
|
|
1061
|
+
Object.freeze(lengths);
|
|
900
1062
|
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
901
1063
|
}
|
|
902
1064
|
/**
|
|
903
1065
|
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
904
1066
|
*
|
|
905
|
-
* @param Point created using {@link weierstrass} function
|
|
906
|
-
* @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
907
|
-
* @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
|
|
1067
|
+
* @param Point - created using {@link weierstrass} function
|
|
1068
|
+
* @param hash - used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
1069
|
+
* @param ecdsaOpts - rarely needed, see {@link ECDSAOpts}:
|
|
1070
|
+
* - `lowS`: Default low-S policy.
|
|
1071
|
+
* - `hmac`: HMAC implementation used by RFC6979 DRBG.
|
|
1072
|
+
* - `randomBytes`: Optional RNG override.
|
|
1073
|
+
* - `bits2int`: Optional hash-to-int conversion override.
|
|
1074
|
+
* - `bits2int_modN`: Optional hash-to-int-mod-n conversion override.
|
|
908
1075
|
*
|
|
1076
|
+
* @returns ECDSA helper namespace.
|
|
909
1077
|
* @example
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
1078
|
+
* Create an ECDSA signer/verifier bundle for one curve implementation.
|
|
1079
|
+
*
|
|
1080
|
+
* ```ts
|
|
1081
|
+
* import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
1082
|
+
* import { p256 } from '@noble/curves/nist.js';
|
|
1083
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
1084
|
+
* const p256ecdsa = ecdsa(p256.Point, sha256);
|
|
1085
|
+
* const { secretKey, publicKey } = p256ecdsa.keygen();
|
|
1086
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
1087
|
+
* const sig = p256ecdsa.sign(msg, secretKey);
|
|
1088
|
+
* const isValid = p256ecdsa.verify(sig, msg, publicKey);
|
|
915
1089
|
* ```
|
|
916
1090
|
*/
|
|
917
1091
|
export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
918
|
-
|
|
1092
|
+
// Custom hash / bits2int hooks are treated as pure functions over validated caller-owned bytes.
|
|
1093
|
+
const hash_ = hash;
|
|
1094
|
+
ahash(hash_);
|
|
919
1095
|
validateObject(ecdsaOpts, {}, {
|
|
920
1096
|
hmac: 'function',
|
|
921
1097
|
lowS: 'boolean',
|
|
@@ -924,8 +1100,10 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
924
1100
|
bits2int_modN: 'function',
|
|
925
1101
|
});
|
|
926
1102
|
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|
927
|
-
const randomBytes = ecdsaOpts.randomBytes
|
|
928
|
-
const hmac = ecdsaOpts.hmac
|
|
1103
|
+
const randomBytes = ecdsaOpts.randomBytes === undefined ? wcRandomBytes : ecdsaOpts.randomBytes;
|
|
1104
|
+
const hmac = ecdsaOpts.hmac === undefined
|
|
1105
|
+
? (key, msg) => nobleHmac(hash_, key, msg)
|
|
1106
|
+
: ecdsaOpts.hmac;
|
|
929
1107
|
const { Fp, Fn } = Point;
|
|
930
1108
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
931
1109
|
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
@@ -935,7 +1113,11 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
935
1113
|
format: 'compact',
|
|
936
1114
|
extraEntropy: false,
|
|
937
1115
|
};
|
|
938
|
-
|
|
1116
|
+
// SEC 1 4.1.6 public-key recovery tries x = r + jn for j = 0..h. Our recovered-signature
|
|
1117
|
+
// format only stores one overflow bit, so it can only distinguish q.x = r from q.x = r + n.
|
|
1118
|
+
// A third lift would have the form q.x = r + 2n. Since valid ECDSA r is in 1..n-1, the
|
|
1119
|
+
// smallest such lift is 1 + 2n, not 2n.
|
|
1120
|
+
const hasLargeRecoveryLifts = CURVE_ORDER * _2n + _1n < Fp.ORDER;
|
|
939
1121
|
function isBiggerThanHalfOrder(number) {
|
|
940
1122
|
const HALF = CURVE_ORDER >> _1n;
|
|
941
1123
|
return number > HALF;
|
|
@@ -945,16 +1127,15 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
945
1127
|
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
946
1128
|
return num;
|
|
947
1129
|
}
|
|
948
|
-
function
|
|
949
|
-
// ECDSA recovery
|
|
950
|
-
//
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
// r+n*i would need to be done instead where i is unknown.
|
|
1130
|
+
function assertRecoverableCurve() {
|
|
1131
|
+
// ECDSA recovery only supports curves where the current recovery id can distinguish
|
|
1132
|
+
// q.x = r and q.x = r + n; larger lifts may need additional `r + n*i` branches.
|
|
1133
|
+
// SEC 1 4.1.6 recovers candidates via x = r + jn, but this format only encodes j = 0 or 1.
|
|
1134
|
+
// The next possible candidate is q.x = r + 2n, and its smallest valid value is 1 + 2n.
|
|
954
1135
|
// To easily get i, we either need to:
|
|
955
1136
|
// a. increase amount of valid recid values (4, 5...); OR
|
|
956
|
-
// b. prohibit
|
|
957
|
-
if (
|
|
1137
|
+
// b. prohibit recovered signatures for those curves.
|
|
1138
|
+
if (hasLargeRecoveryLifts)
|
|
958
1139
|
throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
|
|
959
1140
|
}
|
|
960
1141
|
function validateSigLength(bytes, format) {
|
|
@@ -974,7 +1155,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
974
1155
|
this.r = validateRS('r', r); // r in [1..N-1];
|
|
975
1156
|
this.s = validateRS('s', s); // s in [1..N-1];
|
|
976
1157
|
if (recovery != null) {
|
|
977
|
-
|
|
1158
|
+
assertRecoverableCurve();
|
|
978
1159
|
if (![0, 1, 2, 3].includes(recovery))
|
|
979
1160
|
throw new Error('invalid recovery id');
|
|
980
1161
|
this.recovery = recovery;
|
|
@@ -1010,6 +1191,8 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1010
1191
|
addRecoveryBit(recovery) {
|
|
1011
1192
|
return new Signature(this.r, this.s, recovery);
|
|
1012
1193
|
}
|
|
1194
|
+
// Unlike the top-level helper below, this method expects a digest that has
|
|
1195
|
+
// already been hashed to the curve's message representative.
|
|
1013
1196
|
recoverPublicKey(messageHash) {
|
|
1014
1197
|
const { r, s } = this;
|
|
1015
1198
|
const recovery = this.assertRecovery();
|
|
@@ -1041,7 +1224,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1041
1224
|
const rb = Fn.toBytes(r);
|
|
1042
1225
|
const sb = Fn.toBytes(s);
|
|
1043
1226
|
if (format === 'recovered') {
|
|
1044
|
-
|
|
1227
|
+
assertRecoverableCurve();
|
|
1045
1228
|
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
|
|
1046
1229
|
}
|
|
1047
1230
|
return concatBytes(rb, sb);
|
|
@@ -1050,12 +1233,14 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1050
1233
|
return bytesToHex(this.toBytes(format));
|
|
1051
1234
|
}
|
|
1052
1235
|
}
|
|
1236
|
+
Object.freeze(Signature.prototype);
|
|
1237
|
+
Object.freeze(Signature);
|
|
1053
1238
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1054
1239
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1055
1240
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1056
1241
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1057
|
-
const bits2int = ecdsaOpts.bits2int
|
|
1058
|
-
function bits2int_def(bytes) {
|
|
1242
|
+
const bits2int = ecdsaOpts.bits2int === undefined
|
|
1243
|
+
? function bits2int_def(bytes) {
|
|
1059
1244
|
// Our custom check "just in case", for protection against DoS
|
|
1060
1245
|
if (bytes.length > 8192)
|
|
1061
1246
|
throw new Error('input is too large');
|
|
@@ -1064,22 +1249,23 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1064
1249
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1065
1250
|
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
1066
1251
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1252
|
+
}
|
|
1253
|
+
: ecdsaOpts.bits2int;
|
|
1254
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN === undefined
|
|
1255
|
+
? function bits2int_modN_def(bytes) {
|
|
1070
1256
|
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1257
|
+
}
|
|
1258
|
+
: ecdsaOpts.bits2int_modN;
|
|
1073
1259
|
const ORDER_MASK = bitMask(fnBits);
|
|
1260
|
+
// Pads output with zero as per spec.
|
|
1074
1261
|
/** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
|
|
1075
1262
|
function int2octets(num) {
|
|
1076
|
-
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1077
1263
|
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1078
1264
|
return Fn.toBytes(num);
|
|
1079
1265
|
}
|
|
1080
1266
|
function validateMsgAndHash(message, prehash) {
|
|
1081
1267
|
abytes(message, undefined, 'message');
|
|
1082
|
-
return prehash ? abytes(
|
|
1268
|
+
return (prehash ? abytes(hash_(message), undefined, 'prehashed message') : message);
|
|
1083
1269
|
}
|
|
1084
1270
|
/**
|
|
1085
1271
|
* Steps A, D of RFC6979 3.2.
|
|
@@ -1137,12 +1323,14 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1137
1323
|
normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
|
|
1138
1324
|
recovery ^= 1;
|
|
1139
1325
|
}
|
|
1140
|
-
return new Signature(r, normS,
|
|
1326
|
+
return new Signature(r, normS, hasLargeRecoveryLifts ? undefined : recovery);
|
|
1141
1327
|
}
|
|
1142
1328
|
return { seed, k2sig };
|
|
1143
1329
|
}
|
|
1144
1330
|
/**
|
|
1145
|
-
* Signs message hash with a secret key.
|
|
1331
|
+
* Signs a message or message hash with a secret key.
|
|
1332
|
+
* With the default `prehash: true`, raw message bytes are hashed internally;
|
|
1333
|
+
* only `{ prehash: false }` expects a caller-supplied digest.
|
|
1146
1334
|
*
|
|
1147
1335
|
* ```
|
|
1148
1336
|
* sign(m, d) where
|
|
@@ -1154,7 +1342,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1154
1342
|
*/
|
|
1155
1343
|
function sign(message, secretKey, opts = {}) {
|
|
1156
1344
|
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1157
|
-
const drbg = createHmacDrbg(
|
|
1345
|
+
const drbg = createHmacDrbg(hash_.outputLen, Fn.BYTES, hmac);
|
|
1158
1346
|
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1159
1347
|
return sig.toBytes(opts.format);
|
|
1160
1348
|
}
|
|
@@ -1201,6 +1389,8 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1201
1389
|
}
|
|
1202
1390
|
}
|
|
1203
1391
|
function recoverPublicKey(signature, message, opts = {}) {
|
|
1392
|
+
// Top-level recovery mirrors `sign()` / `verify()`: it hashes raw message
|
|
1393
|
+
// bytes first unless the caller passes `{ prehash: false }`.
|
|
1204
1394
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
1205
1395
|
message = validateMsgAndHash(message, prehash);
|
|
1206
1396
|
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
@@ -1216,7 +1406,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1216
1406
|
verify,
|
|
1217
1407
|
recoverPublicKey,
|
|
1218
1408
|
Signature,
|
|
1219
|
-
hash,
|
|
1409
|
+
hash: hash_,
|
|
1220
1410
|
});
|
|
1221
1411
|
}
|
|
1222
1412
|
//# sourceMappingURL=weierstrass.js.map
|