@noble/curves 2.0.1 → 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 +82 -22
- 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 +322 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +154 -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 +125 -14
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +202 -40
- 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 +11 -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 +350 -67
- package/src/abstract/curve.ts +327 -44
- package/src/abstract/edwards.ts +367 -143
- package/src/abstract/fft.ts +369 -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 +227 -55
- 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/src/abstract/edwards.ts
CHANGED
|
@@ -9,18 +9,20 @@ import {
|
|
|
9
9
|
abool,
|
|
10
10
|
abytes,
|
|
11
11
|
aInRange,
|
|
12
|
+
asafenumber,
|
|
12
13
|
bytesToHex,
|
|
13
14
|
bytesToNumberLE,
|
|
14
15
|
concatBytes,
|
|
15
16
|
copyBytes,
|
|
16
17
|
hexToBytes,
|
|
17
18
|
isBytes,
|
|
18
|
-
memoized,
|
|
19
19
|
notImplemented,
|
|
20
20
|
validateObject,
|
|
21
21
|
randomBytes as wcRandomBytes,
|
|
22
22
|
type FHash,
|
|
23
23
|
type Signer,
|
|
24
|
+
type TArg,
|
|
25
|
+
type TRet,
|
|
24
26
|
} from '../utils.ts';
|
|
25
27
|
import {
|
|
26
28
|
createCurveFields,
|
|
@@ -36,9 +38,9 @@ import { type IField } from './modular.ts';
|
|
|
36
38
|
|
|
37
39
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
38
40
|
// prettier-ignore
|
|
39
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
|
41
|
+
const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _8n = /* @__PURE__ */ BigInt(8);
|
|
40
42
|
|
|
41
|
-
/**
|
|
43
|
+
/** Extended Edwards point with X/Y/Z/T coordinates. */
|
|
42
44
|
export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
|
|
43
45
|
/** extended X coordinate. Different from affine x. */
|
|
44
46
|
readonly X: bigint;
|
|
@@ -49,11 +51,28 @@ export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
|
|
|
49
51
|
/** extended T coordinate */
|
|
50
52
|
readonly T: bigint;
|
|
51
53
|
}
|
|
52
|
-
/**
|
|
54
|
+
/** Constructor and decoding helpers for extended Edwards points. */
|
|
53
55
|
export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
|
|
56
|
+
/** Create a point from extended X/Y/Z/T coordinates without validation. */
|
|
54
57
|
new (X: bigint, Y: bigint, Z: bigint, T: bigint): EdwardsPoint;
|
|
58
|
+
/**
|
|
59
|
+
* Return the curve parameters used by this point constructor.
|
|
60
|
+
* @returns Curve parameters.
|
|
61
|
+
*/
|
|
55
62
|
CURVE(): EdwardsOpts;
|
|
63
|
+
/**
|
|
64
|
+
* Decode a point from bytes, optionally using ZIP-215 rules.
|
|
65
|
+
* @param bytes - Encoded point bytes.
|
|
66
|
+
* @param zip215 - Whether to accept ZIP-215 encodings.
|
|
67
|
+
* @returns Decoded Edwards point.
|
|
68
|
+
*/
|
|
56
69
|
fromBytes(bytes: Uint8Array, zip215?: boolean): EdwardsPoint;
|
|
70
|
+
/**
|
|
71
|
+
* Decode a point from hex, optionally using ZIP-215 rules.
|
|
72
|
+
* @param hex - Encoded point hex.
|
|
73
|
+
* @param zip215 - Whether to accept ZIP-215 encodings.
|
|
74
|
+
* @returns Decoded Edwards point.
|
|
75
|
+
*/
|
|
57
76
|
fromHex(hex: string, zip215?: boolean): EdwardsPoint;
|
|
58
77
|
}
|
|
59
78
|
|
|
@@ -69,12 +88,19 @@ export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
|
|
|
69
88
|
* * Gy: y coordinate of generator point
|
|
70
89
|
*/
|
|
71
90
|
export type EdwardsOpts = Readonly<{
|
|
91
|
+
/** Base-field modulus. */
|
|
72
92
|
p: bigint;
|
|
93
|
+
/** Prime subgroup order. */
|
|
73
94
|
n: bigint;
|
|
95
|
+
/** Curve cofactor. */
|
|
74
96
|
h: bigint;
|
|
97
|
+
/** Edwards curve parameter `a`. */
|
|
75
98
|
a: bigint;
|
|
99
|
+
/** Edwards curve parameter `d`. */
|
|
76
100
|
d: bigint;
|
|
101
|
+
/** Generator x coordinate. */
|
|
77
102
|
Gx: bigint;
|
|
103
|
+
/** Generator y coordinate. */
|
|
78
104
|
Gy: bigint;
|
|
79
105
|
}>;
|
|
80
106
|
|
|
@@ -86,9 +112,13 @@ export type EdwardsOpts = Readonly<{
|
|
|
86
112
|
* * uvRatio: helper function for decompression, calculating √(u/v)
|
|
87
113
|
*/
|
|
88
114
|
export type EdwardsExtraOpts = Partial<{
|
|
115
|
+
/** Optional base-field override. */
|
|
89
116
|
Fp: IField<bigint>;
|
|
117
|
+
/** Optional scalar-field override. */
|
|
90
118
|
Fn: IField<bigint>;
|
|
119
|
+
/** Whether field encodings are little-endian. */
|
|
91
120
|
FpFnLE: boolean;
|
|
121
|
+
/** Square-root ratio helper used during point decompression. */
|
|
92
122
|
uvRatio: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
|
|
93
123
|
}>;
|
|
94
124
|
|
|
@@ -103,37 +133,79 @@ export type EdwardsExtraOpts = Partial<{
|
|
|
103
133
|
* * randomBytes: function generating random bytes, used for randomSecretKey
|
|
104
134
|
*/
|
|
105
135
|
export type EdDSAOpts = Partial<{
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
/** Clamp or otherwise normalize secret-scalar bytes before reducing mod `n`. */
|
|
137
|
+
adjustScalarBytes: (bytes: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
138
|
+
/** Domain-separation helper for contexts and prehash mode. */
|
|
139
|
+
domain: (data: TArg<Uint8Array>, ctx: TArg<Uint8Array>, phflag: boolean) => TRet<Uint8Array>;
|
|
140
|
+
/** Optional hash-to-curve mapper for protocols like Ristretto hash-to-group. */
|
|
108
141
|
mapToCurve: (scalar: bigint[]) => AffinePoint<bigint>;
|
|
142
|
+
/** Optional prehash function used before signing or verifying messages. */
|
|
109
143
|
prehash: FHash;
|
|
110
|
-
|
|
144
|
+
/** Default verification decoding policy. ZIP-215 is more permissive than RFC 8032 / NIST. */
|
|
145
|
+
zip215: boolean;
|
|
146
|
+
/** RNG override used by helper constructors. */
|
|
147
|
+
randomBytes: (bytesLength?: number) => TRet<Uint8Array>;
|
|
111
148
|
}>;
|
|
112
149
|
|
|
113
150
|
/**
|
|
114
|
-
* EdDSA (Edwards Digital Signature algorithm)
|
|
115
|
-
*
|
|
116
|
-
* Allows to create and verify signatures, create public and secret keys.
|
|
151
|
+
* EdDSA (Edwards Digital Signature algorithm) helper namespace.
|
|
152
|
+
* Allows creating and verifying signatures, and deriving public keys.
|
|
117
153
|
*/
|
|
118
154
|
export interface EdDSA {
|
|
119
|
-
|
|
120
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Generate a secret/public key pair.
|
|
157
|
+
* @param seed - Optional seed material.
|
|
158
|
+
* @returns Secret/public key pair.
|
|
159
|
+
*/
|
|
160
|
+
keygen: (seed?: TArg<Uint8Array>) => { secretKey: TRet<Uint8Array>; publicKey: TRet<Uint8Array> };
|
|
161
|
+
/**
|
|
162
|
+
* Derive the public key from a secret key.
|
|
163
|
+
* @param secretKey - Secret key bytes.
|
|
164
|
+
* @returns Encoded public key.
|
|
165
|
+
*/
|
|
166
|
+
getPublicKey: (secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
167
|
+
/**
|
|
168
|
+
* Sign a message with an EdDSA secret key.
|
|
169
|
+
* @param message - Message bytes.
|
|
170
|
+
* @param secretKey - Secret key bytes.
|
|
171
|
+
* @param options - Optional signature tweaks:
|
|
172
|
+
* - `context` (optional): Domain-separation context for Ed25519ctx/Ed448.
|
|
173
|
+
* @returns Encoded signature bytes.
|
|
174
|
+
*/
|
|
121
175
|
sign: (
|
|
122
|
-
message: Uint8Array
|
|
123
|
-
secretKey: Uint8Array
|
|
124
|
-
options?: { context?: Uint8Array }
|
|
125
|
-
) => Uint8Array
|
|
176
|
+
message: TArg<Uint8Array>,
|
|
177
|
+
secretKey: TArg<Uint8Array>,
|
|
178
|
+
options?: TArg<{ context?: Uint8Array }>
|
|
179
|
+
) => TRet<Uint8Array>;
|
|
180
|
+
/**
|
|
181
|
+
* Verify a signature against a message and public key.
|
|
182
|
+
* @param sig - Encoded signature bytes.
|
|
183
|
+
* @param message - Message bytes.
|
|
184
|
+
* @param publicKey - Encoded public key.
|
|
185
|
+
* @param options - Optional verification tweaks:
|
|
186
|
+
* - `context` (optional): Domain-separation context for Ed25519ctx/Ed448.
|
|
187
|
+
* - `zip215` (optional): Whether to accept ZIP-215 encodings.
|
|
188
|
+
* @returns Whether the signature is valid.
|
|
189
|
+
*/
|
|
126
190
|
verify: (
|
|
127
|
-
sig: Uint8Array
|
|
128
|
-
message: Uint8Array
|
|
129
|
-
publicKey: Uint8Array
|
|
130
|
-
options?: { context?: Uint8Array; zip215
|
|
191
|
+
sig: TArg<Uint8Array>,
|
|
192
|
+
message: TArg<Uint8Array>,
|
|
193
|
+
publicKey: TArg<Uint8Array>,
|
|
194
|
+
options?: TArg<{ context?: Uint8Array; zip215?: boolean }>
|
|
131
195
|
) => boolean;
|
|
196
|
+
/** Point constructor used by this signature scheme. */
|
|
132
197
|
Point: EdwardsPointCons;
|
|
198
|
+
/** Helper utilities for key validation and Montgomery conversion. */
|
|
133
199
|
utils: {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Generate a valid random secret key.
|
|
202
|
+
* Optional seed bytes are only length-checked and returned unchanged.
|
|
203
|
+
*/
|
|
204
|
+
randomSecretKey: (seed?: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
205
|
+
/** Check whether a secret key has the expected encoding. */
|
|
206
|
+
isValidSecretKey: (secretKey: TArg<Uint8Array>) => boolean;
|
|
207
|
+
/** Check whether a public key decodes to a valid point. */
|
|
208
|
+
isValidPublicKey: (publicKey: TArg<Uint8Array>, zip215?: boolean) => boolean;
|
|
137
209
|
|
|
138
210
|
/**
|
|
139
211
|
* Converts ed public key to x public key.
|
|
@@ -144,6 +216,8 @@ export interface EdDSA {
|
|
|
144
216
|
* accepts inputs on the quadratic twist, which can't be moved to ed25519
|
|
145
217
|
*
|
|
146
218
|
* @example
|
|
219
|
+
* Converts ed public key to x public key.
|
|
220
|
+
*
|
|
147
221
|
* ```js
|
|
148
222
|
* const someonesPub_ed = ed25519.getPublicKey(ed25519.utils.randomSecretKey());
|
|
149
223
|
* const someonesPub = ed25519.utils.toMontgomery(someonesPub);
|
|
@@ -151,10 +225,12 @@ export interface EdDSA {
|
|
|
151
225
|
* const shared = x25519.getSharedSecret(aPriv, someonesPub)
|
|
152
226
|
* ```
|
|
153
227
|
*/
|
|
154
|
-
toMontgomery: (publicKey: Uint8Array) => Uint8Array
|
|
228
|
+
toMontgomery: (publicKey: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
155
229
|
/**
|
|
156
230
|
* Converts ed secret key to x secret key.
|
|
157
231
|
* @example
|
|
232
|
+
* Converts ed secret key to x secret key.
|
|
233
|
+
*
|
|
158
234
|
* ```js
|
|
159
235
|
* const someonesPub = x25519.getPublicKey(x25519.utils.randomSecretKey());
|
|
160
236
|
* const aPriv_ed = ed25519.utils.randomSecretKey();
|
|
@@ -162,19 +238,23 @@ export interface EdDSA {
|
|
|
162
238
|
* const shared = x25519.getSharedSecret(aPriv, someonesPub)
|
|
163
239
|
* ```
|
|
164
240
|
*/
|
|
165
|
-
toMontgomerySecret: (secretKey: Uint8Array) => Uint8Array
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
241
|
+
toMontgomerySecret: (secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
|
|
242
|
+
/** Return the expanded private key components used by RFC8032 signing. */
|
|
243
|
+
getExtendedPublicKey: (key: TArg<Uint8Array>) => {
|
|
244
|
+
head: TRet<Uint8Array>;
|
|
245
|
+
prefix: TRet<Uint8Array>;
|
|
169
246
|
scalar: bigint;
|
|
170
247
|
point: EdwardsPoint;
|
|
171
|
-
pointBytes: Uint8Array
|
|
248
|
+
pointBytes: TRet<Uint8Array>;
|
|
172
249
|
};
|
|
173
250
|
};
|
|
251
|
+
/** Byte lengths for keys and signatures exposed by this scheme. */
|
|
174
252
|
lengths: CurveLengths;
|
|
175
253
|
}
|
|
176
254
|
|
|
177
|
-
|
|
255
|
+
// Affine Edwards-equation check only; this does not prove subgroup membership, canonical
|
|
256
|
+
// encoding, prime-order base-point requirements, or identity exclusion.
|
|
257
|
+
function isEdValidXY(Fp: TArg<IField<bigint>>, CURVE: EdwardsOpts, x: bigint, y: bigint): boolean {
|
|
178
258
|
const x2 = Fp.sqr(x);
|
|
179
259
|
const y2 = Fp.sqr(y);
|
|
180
260
|
const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
|
|
@@ -182,12 +262,37 @@ function isEdValidXY(Fp: IField<bigint>, CURVE: EdwardsOpts, x: bigint, y: bigin
|
|
|
182
262
|
return Fp.eql(left, right);
|
|
183
263
|
}
|
|
184
264
|
|
|
185
|
-
|
|
186
|
-
|
|
265
|
+
/**
|
|
266
|
+
* @param params - Curve parameters. See {@link EdwardsOpts}.
|
|
267
|
+
* @param extraOpts - Optional helpers and overrides. See {@link EdwardsExtraOpts}.
|
|
268
|
+
* @returns Edwards point constructor. Generator validation here only checks
|
|
269
|
+
* that `(Gx, Gy)` satisfies the affine Edwards equation.
|
|
270
|
+
* RFC 8032 base-point constraints like `B != (0,1)` and `[L]B = 0`
|
|
271
|
+
* are left to the caller's chosen parameters, since eager subgroup
|
|
272
|
+
* validation here adds about 10-15ms to heavyweight imports like ed448.
|
|
273
|
+
* The returned constructor also eagerly marks `Point.BASE` for W=8
|
|
274
|
+
* precompute caching. Some code paths still assume
|
|
275
|
+
* `Fp.BYTES === Fn.BYTES`, so mismatched byte lengths are not fully audited here.
|
|
276
|
+
* @throws If the curve parameters or Edwards overrides are invalid. {@link Error}
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* import { edwards } from '@noble/curves/abstract/edwards.js';
|
|
280
|
+
* import { jubjub } from '@noble/curves/misc.js';
|
|
281
|
+
* // Build a point constructor from explicit curve parameters, then use its base point.
|
|
282
|
+
* const Point = edwards(jubjub.Point.CURVE());
|
|
283
|
+
* Point.BASE.toHex();
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
export function edwards(
|
|
287
|
+
params: TArg<EdwardsOpts>,
|
|
288
|
+
extraOpts: TArg<EdwardsExtraOpts> = {}
|
|
289
|
+
): EdwardsPointCons {
|
|
290
|
+
const opts = extraOpts as EdwardsExtraOpts;
|
|
291
|
+
const validated = createCurveFields('edwards', params as EdwardsOpts, opts, opts.FpFnLE);
|
|
187
292
|
const { Fp, Fn } = validated;
|
|
188
293
|
let CURVE = validated.CURVE as EdwardsOpts;
|
|
189
294
|
const { h: cofactor } = CURVE;
|
|
190
|
-
validateObject(
|
|
295
|
+
validateObject(opts, {}, { uvRatio: 'function' });
|
|
191
296
|
|
|
192
297
|
// Important:
|
|
193
298
|
// There are some places where Fp.BYTES is used instead of nByteLength.
|
|
@@ -198,14 +303,15 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
198
303
|
|
|
199
304
|
// sqrt(u/v)
|
|
200
305
|
const uvRatio =
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
306
|
+
opts.uvRatio === undefined
|
|
307
|
+
? (u: bigint, v: bigint) => {
|
|
308
|
+
try {
|
|
309
|
+
return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
|
|
310
|
+
} catch (e) {
|
|
311
|
+
return { isValid: false, value: _0n };
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
: opts.uvRatio;
|
|
209
315
|
|
|
210
316
|
// Validate whether the passed curve params are valid.
|
|
211
317
|
// equation ax² + y² = 1 + dx²y² should work for generator point.
|
|
@@ -225,39 +331,6 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
225
331
|
function aedpoint(other: unknown) {
|
|
226
332
|
if (!(other instanceof Point)) throw new Error('EdwardsPoint expected');
|
|
227
333
|
}
|
|
228
|
-
// Converts Extended point to default (x, y) coordinates.
|
|
229
|
-
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
230
|
-
const toAffineMemo = memoized((p: Point, iz?: bigint): AffinePoint<bigint> => {
|
|
231
|
-
const { X, Y, Z } = p;
|
|
232
|
-
const is0 = p.is0();
|
|
233
|
-
if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
|
|
234
|
-
const x = modP(X * iz);
|
|
235
|
-
const y = modP(Y * iz);
|
|
236
|
-
const zz = Fp.mul(Z, iz);
|
|
237
|
-
if (is0) return { x: _0n, y: _1n };
|
|
238
|
-
if (zz !== _1n) throw new Error('invZ was invalid');
|
|
239
|
-
return { x, y };
|
|
240
|
-
});
|
|
241
|
-
const assertValidMemo = memoized((p: Point) => {
|
|
242
|
-
const { a, d } = CURVE;
|
|
243
|
-
if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
244
|
-
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
245
|
-
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
246
|
-
const { X, Y, Z, T } = p;
|
|
247
|
-
const X2 = modP(X * X); // X²
|
|
248
|
-
const Y2 = modP(Y * Y); // Y²
|
|
249
|
-
const Z2 = modP(Z * Z); // Z²
|
|
250
|
-
const Z4 = modP(Z2 * Z2); // Z⁴
|
|
251
|
-
const aX2 = modP(X2 * a); // aX²
|
|
252
|
-
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
|
253
|
-
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
|
254
|
-
if (left !== right) throw new Error('bad point: equation left != right (1)');
|
|
255
|
-
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
256
|
-
const XY = modP(X * Y);
|
|
257
|
-
const ZT = modP(Z * T);
|
|
258
|
-
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
|
|
259
|
-
return true;
|
|
260
|
-
});
|
|
261
334
|
|
|
262
335
|
// Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
|
|
263
336
|
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
|
@@ -288,6 +361,11 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
288
361
|
return CURVE;
|
|
289
362
|
}
|
|
290
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Create one extended Edwards point from affine coordinates.
|
|
366
|
+
* Does NOT validate that the point is on-curve or torsion-free.
|
|
367
|
+
* Use `.assertValidity()` on adversarial inputs.
|
|
368
|
+
*/
|
|
291
369
|
static fromAffine(p: AffinePoint<bigint>): Point {
|
|
292
370
|
if (p instanceof Point) throw new Error('extended point not allowed');
|
|
293
371
|
const { x, y } = p || {};
|
|
@@ -349,7 +427,28 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
349
427
|
|
|
350
428
|
// Useful in fromAffine() - not for fromBytes(), which always created valid points.
|
|
351
429
|
assertValidity(): void {
|
|
352
|
-
|
|
430
|
+
const p = this;
|
|
431
|
+
const { a, d } = CURVE;
|
|
432
|
+
// Keep generic Edwards validation fail-closed on the neutral point.
|
|
433
|
+
// Even though ZERO is algebraically valid and can roundtrip through encodings, higher-level
|
|
434
|
+
// callers often reach it only through broken hash/scalar plumbing; rejecting it here avoids
|
|
435
|
+
// silently treating that degenerate state as an ordinary public point.
|
|
436
|
+
if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
437
|
+
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
438
|
+
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
439
|
+
const { X, Y, Z, T } = p;
|
|
440
|
+
const X2 = modP(X * X); // X²
|
|
441
|
+
const Y2 = modP(Y * Y); // Y²
|
|
442
|
+
const Z2 = modP(Z * Z); // Z²
|
|
443
|
+
const Z4 = modP(Z2 * Z2); // Z⁴
|
|
444
|
+
const aX2 = modP(X2 * a); // aX²
|
|
445
|
+
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
|
446
|
+
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
|
447
|
+
if (left !== right) throw new Error('bad point: equation left != right (1)');
|
|
448
|
+
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
449
|
+
const XY = modP(X * Y);
|
|
450
|
+
const ZT = modP(Z * T);
|
|
451
|
+
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
|
|
353
452
|
}
|
|
354
453
|
|
|
355
454
|
// Compare one point to another.
|
|
@@ -419,13 +518,20 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
419
518
|
}
|
|
420
519
|
|
|
421
520
|
subtract(other: Point): Point {
|
|
521
|
+
// Validate before calling `negate()` so wrong inputs fail with the point guard
|
|
522
|
+
// instead of leaking a foreign `negate()` error.
|
|
523
|
+
aedpoint(other);
|
|
422
524
|
return this.add(other.negate());
|
|
423
525
|
}
|
|
424
526
|
|
|
425
527
|
// Constant-time multiplication.
|
|
426
528
|
multiply(scalar: bigint): Point {
|
|
427
529
|
// 1 <= scalar < L
|
|
428
|
-
|
|
530
|
+
// Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.
|
|
531
|
+
// In keygen/signing-style callers, those values usually mean broken hash/scalar plumbing,
|
|
532
|
+
// and failing closed is safer than silently producing the identity point.
|
|
533
|
+
if (!Fn.isValidNot0(scalar))
|
|
534
|
+
throw new RangeError('invalid scalar: expected 1 <= sc < curve.n');
|
|
429
535
|
const { p, f } = wnaf.cached(this, scalar, (p) => normalizeZ(Point, p));
|
|
430
536
|
return normalizeZ(Point, [p, f])[0];
|
|
431
537
|
}
|
|
@@ -433,22 +539,22 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
433
539
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
434
540
|
// It's faster, but should only be used when you don't care about
|
|
435
541
|
// an exposed private key e.g. sig verification.
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
multiplyUnsafe(scalar: bigint
|
|
542
|
+
// Keeps the same subgroup-scalar contract: 0 is allowed for public-scalar callers, but
|
|
543
|
+
// n and larger values are rejected instead of being reduced mod n to the identity point.
|
|
544
|
+
multiplyUnsafe(scalar: bigint): Point {
|
|
439
545
|
// 0 <= scalar < L
|
|
440
|
-
if (!Fn.isValid(scalar)) throw new
|
|
546
|
+
if (!Fn.isValid(scalar)) throw new RangeError('invalid scalar: expected 0 <= sc < curve.n');
|
|
441
547
|
if (scalar === _0n) return Point.ZERO;
|
|
442
548
|
if (this.is0() || scalar === _1n) return this;
|
|
443
|
-
return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p)
|
|
549
|
+
return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p));
|
|
444
550
|
}
|
|
445
551
|
|
|
446
552
|
// Checks if point is of small order.
|
|
447
553
|
// If you add something to small order point, you will have "dirty"
|
|
448
554
|
// point with torsion component.
|
|
449
|
-
//
|
|
555
|
+
// Clears cofactor and checks if the result is 0.
|
|
450
556
|
isSmallOrder(): boolean {
|
|
451
|
-
return this.
|
|
557
|
+
return this.clearCofactor().is0();
|
|
452
558
|
}
|
|
453
559
|
|
|
454
560
|
// Multiplies point by curve order and checks if the result is 0.
|
|
@@ -460,7 +566,17 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
460
566
|
// Converts Extended point to default (x, y) coordinates.
|
|
461
567
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
462
568
|
toAffine(invertedZ?: bigint): AffinePoint<bigint> {
|
|
463
|
-
|
|
569
|
+
const p = this;
|
|
570
|
+
let iz = invertedZ;
|
|
571
|
+
const { X, Y, Z } = p;
|
|
572
|
+
const is0 = p.is0();
|
|
573
|
+
if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
|
|
574
|
+
const x = modP(X * iz);
|
|
575
|
+
const y = modP(Y * iz);
|
|
576
|
+
const zz = Fp.mul(Z, iz);
|
|
577
|
+
if (is0) return { x: _0n, y: _1n };
|
|
578
|
+
if (zz !== _1n) throw new Error('invZ was invalid');
|
|
579
|
+
return { x, y };
|
|
464
580
|
}
|
|
465
581
|
|
|
466
582
|
clearCofactor(): Point {
|
|
@@ -486,14 +602,37 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
|
|
|
486
602
|
}
|
|
487
603
|
}
|
|
488
604
|
const wnaf = new wNAF(Point, Fn.BITS);
|
|
489
|
-
|
|
605
|
+
// Keep constructor work cheap: subgroup/generator validation belongs to the caller's curve
|
|
606
|
+
// parameters, and doing the extra checks here adds about 10-15ms to heavy module imports.
|
|
607
|
+
// Callers that construct custom curves are responsible for supplying the correct base point.
|
|
608
|
+
// try {
|
|
609
|
+
// Point.BASE.assertValidity();
|
|
610
|
+
// if (!Point.BASE.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
611
|
+
// } catch {
|
|
612
|
+
// throw new Error('bad curve params: generator point');
|
|
613
|
+
// }
|
|
614
|
+
// Tiny toy curves can have scalar fields narrower than 8 bits. Skip the
|
|
615
|
+
// eager W=8 cache there instead of rejecting an otherwise valid constructor.
|
|
616
|
+
if (Fn.BITS >= 8) Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
617
|
+
Object.freeze(Point.prototype);
|
|
618
|
+
Object.freeze(Point);
|
|
490
619
|
return Point;
|
|
491
620
|
}
|
|
492
621
|
|
|
493
622
|
/**
|
|
494
623
|
* Base class for prime-order points like Ristretto255 and Decaf448.
|
|
495
624
|
* These points eliminate cofactor issues by representing equivalence classes
|
|
496
|
-
* of Edwards curve points.
|
|
625
|
+
* of Edwards curve points. Multiple Edwards representatives can describe the
|
|
626
|
+
* same abstract wrapper element, so wrapper validity is not the same thing as
|
|
627
|
+
* the hidden representative being torsion-free.
|
|
628
|
+
* @param ep - Backing Edwards point.
|
|
629
|
+
* @example
|
|
630
|
+
* Base class for prime-order points like Ristretto255 and Decaf448.
|
|
631
|
+
*
|
|
632
|
+
* ```ts
|
|
633
|
+
* import { ristretto255 } from '@noble/curves/ed25519.js';
|
|
634
|
+
* const point = ristretto255.Point.BASE.multiply(2n);
|
|
635
|
+
* ```
|
|
497
636
|
*/
|
|
498
637
|
export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
499
638
|
implements CurvePoint<bigint, T>
|
|
@@ -505,6 +644,11 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
|
505
644
|
|
|
506
645
|
protected readonly ep: EdwardsPoint;
|
|
507
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Wrap one internal Edwards representative directly.
|
|
649
|
+
* This is not a canonical encoding boundary: alternate Edwards
|
|
650
|
+
* representatives may still describe the same abstract wrapper element.
|
|
651
|
+
*/
|
|
508
652
|
constructor(ep: EdwardsPoint) {
|
|
509
653
|
this.ep = ep;
|
|
510
654
|
}
|
|
@@ -531,14 +675,24 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
|
531
675
|
|
|
532
676
|
// Common implementations
|
|
533
677
|
clearCofactor(): T {
|
|
534
|
-
// no-op for prime-order
|
|
678
|
+
// no-op for the abstract prime-order wrapper group; this is about the
|
|
679
|
+
// wrapper element, not the hidden Edwards representative.
|
|
535
680
|
return this as any;
|
|
536
681
|
}
|
|
537
682
|
|
|
538
683
|
assertValidity(): void {
|
|
684
|
+
// Keep wrapper validity at the abstract-group boundary. Canonical decode
|
|
685
|
+
// may choose Edwards representatives that differ by small torsion, so
|
|
686
|
+
// checking `this.ep.isTorsionFree()` here would reject valid wrapper points.
|
|
539
687
|
this.ep.assertValidity();
|
|
540
688
|
}
|
|
541
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Return affine coordinates of the current internal Edwards representative.
|
|
692
|
+
* This is a convenience helper, not a canonical Ristretto/Decaf encoding.
|
|
693
|
+
* Equal abstract elements may expose different `x` / `y`; use
|
|
694
|
+
* `toBytes()` / `fromBytes()` for canonical roundtrips.
|
|
695
|
+
*/
|
|
542
696
|
toAffine(invertedZ?: bigint): AffinePoint<bigint> {
|
|
543
697
|
return this.ep.toAffine(invertedZ);
|
|
544
698
|
}
|
|
@@ -552,6 +706,8 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
|
552
706
|
}
|
|
553
707
|
|
|
554
708
|
isTorsionFree(): boolean {
|
|
709
|
+
// Abstract Ristretto/Decaf elements are already prime-order even when the
|
|
710
|
+
// hidden Edwards representative is not torsion-free.
|
|
555
711
|
return true;
|
|
556
712
|
}
|
|
557
713
|
|
|
@@ -586,7 +742,10 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
|
586
742
|
}
|
|
587
743
|
|
|
588
744
|
precompute(windowSize?: number, isLazy?: boolean): T {
|
|
589
|
-
|
|
745
|
+
this.ep.precompute(windowSize, isLazy);
|
|
746
|
+
// Keep the wrapper identity stable like the backing Edwards API instead of
|
|
747
|
+
// allocating a fresh wrapper around the same cached point.
|
|
748
|
+
return this as unknown as T;
|
|
590
749
|
}
|
|
591
750
|
|
|
592
751
|
// Helper methods
|
|
@@ -597,103 +756,156 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
|
|
|
597
756
|
|
|
598
757
|
/**
|
|
599
758
|
* Initializes EdDSA signatures over given Edwards curve.
|
|
759
|
+
* @param Point - Edwards point constructor.
|
|
760
|
+
* @param cHash - Hash function.
|
|
761
|
+
* @param eddsaOpts - Optional signature helpers. See {@link EdDSAOpts}.
|
|
762
|
+
* @returns EdDSA helper namespace.
|
|
763
|
+
* @throws If the hash function, options, or derived point operations are invalid. {@link Error}
|
|
764
|
+
* @example
|
|
765
|
+
* Initializes EdDSA signatures over given Edwards curve.
|
|
766
|
+
*
|
|
767
|
+
* ```ts
|
|
768
|
+
* import { eddsa } from '@noble/curves/abstract/edwards.js';
|
|
769
|
+
* import { jubjub } from '@noble/curves/misc.js';
|
|
770
|
+
* import { sha512 } from '@noble/hashes/sha2.js';
|
|
771
|
+
* const sigs = eddsa(jubjub.Point, sha512);
|
|
772
|
+
* const { secretKey, publicKey } = sigs.keygen();
|
|
773
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
774
|
+
* const sig = sigs.sign(msg, secretKey);
|
|
775
|
+
* const isValid = sigs.verify(sig, msg, publicKey);
|
|
776
|
+
* ```
|
|
600
777
|
*/
|
|
601
|
-
export function eddsa(
|
|
778
|
+
export function eddsa(
|
|
779
|
+
Point: EdwardsPointCons,
|
|
780
|
+
cHash: TArg<FHash>,
|
|
781
|
+
eddsaOpts: TArg<EdDSAOpts> = {}
|
|
782
|
+
): EdDSA {
|
|
602
783
|
if (typeof cHash !== 'function') throw new Error('"hash" function param is required');
|
|
784
|
+
const hash = cHash as FHash;
|
|
785
|
+
const opts = eddsaOpts as EdDSAOpts;
|
|
603
786
|
validateObject(
|
|
604
|
-
|
|
787
|
+
opts,
|
|
605
788
|
{},
|
|
606
789
|
{
|
|
607
790
|
adjustScalarBytes: 'function',
|
|
608
791
|
randomBytes: 'function',
|
|
609
792
|
domain: 'function',
|
|
610
793
|
prehash: 'function',
|
|
794
|
+
zip215: 'boolean',
|
|
611
795
|
mapToCurve: 'function',
|
|
612
796
|
}
|
|
613
797
|
);
|
|
614
798
|
|
|
615
|
-
const { prehash } =
|
|
799
|
+
const { prehash } = opts;
|
|
616
800
|
const { BASE, Fp, Fn } = Point;
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
|
|
801
|
+
const outputLen = (hash as FHash & { outputLen?: number }).outputLen;
|
|
802
|
+
const expectedLen = 2 * Fp.BYTES;
|
|
803
|
+
// When hash metadata is available, reject incompatible EdDSA wrappers at construction time
|
|
804
|
+
// instead of deferring the mismatch until the first keygen/sign call.
|
|
805
|
+
if (outputLen !== undefined) {
|
|
806
|
+
asafenumber(outputLen, 'hash.outputLen');
|
|
807
|
+
if (outputLen !== expectedLen)
|
|
808
|
+
throw new Error(`hash.outputLen must be ${expectedLen}, got ${outputLen}`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const randomBytes = opts.randomBytes === undefined ? wcRandomBytes : opts.randomBytes;
|
|
812
|
+
const adjustScalarBytes =
|
|
813
|
+
opts.adjustScalarBytes === undefined
|
|
814
|
+
? (bytes: TArg<Uint8Array>) => bytes as TRet<Uint8Array>
|
|
815
|
+
: opts.adjustScalarBytes;
|
|
620
816
|
const domain =
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
817
|
+
opts.domain === undefined
|
|
818
|
+
? (data: TArg<Uint8Array>, ctx: TArg<Uint8Array>, phflag: boolean) => {
|
|
819
|
+
abool(phflag, 'phflag');
|
|
820
|
+
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
|
821
|
+
return data as TRet<Uint8Array>;
|
|
822
|
+
}
|
|
823
|
+
: opts.domain; // NOOP
|
|
824
|
+
|
|
825
|
+
// Parse an EdDSA digest as a little-endian integer and reduce it modulo the scalar field order.
|
|
826
|
+
function modN_LE(hash: TArg<Uint8Array>): bigint {
|
|
630
827
|
return Fn.create(bytesToNumberLE(hash)); // Not Fn.fromBytes: it has length limit
|
|
631
828
|
}
|
|
632
829
|
|
|
633
830
|
// Get the hashed private scalar per RFC8032 5.1.5
|
|
634
|
-
function getPrivateScalar(key: Uint8Array) {
|
|
831
|
+
function getPrivateScalar(key: TArg<Uint8Array>) {
|
|
635
832
|
const len = lengths.secretKey;
|
|
636
833
|
abytes(key, lengths.secretKey, 'secretKey');
|
|
637
834
|
// Hash private key with curve's hash function to produce uniformingly random input
|
|
638
835
|
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
|
639
|
-
const hashed = abytes(
|
|
836
|
+
const hashed = abytes(hash(key), 2 * len, 'hashedSecretKey');
|
|
837
|
+
// Slice before clamping so in-place adjustors don't corrupt the prefix half.
|
|
640
838
|
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
|
641
|
-
const prefix = hashed.slice(len, 2 * len)
|
|
839
|
+
const prefix = hashed.slice(len, 2 * len) as TRet<Uint8Array>; // second half is called key prefix (5.1.6)
|
|
642
840
|
const scalar = modN_LE(head); // The actual private scalar
|
|
643
841
|
return { head, prefix, scalar };
|
|
644
842
|
}
|
|
645
843
|
|
|
646
|
-
/** Convenience method that creates public key from scalar. RFC8032 5.1.5
|
|
647
|
-
|
|
844
|
+
/** Convenience method that creates public key from scalar. RFC8032 5.1.5
|
|
845
|
+
* Also exposes the derived scalar/prefix tuple and point form reused by sign().
|
|
846
|
+
*/
|
|
847
|
+
function getExtendedPublicKey(secretKey: TArg<Uint8Array>) {
|
|
648
848
|
const { head, prefix, scalar } = getPrivateScalar(secretKey);
|
|
649
849
|
const point = BASE.multiply(scalar); // Point on Edwards curve aka public key
|
|
650
|
-
const pointBytes = point.toBytes()
|
|
850
|
+
const pointBytes = point.toBytes() as TRet<Uint8Array>;
|
|
651
851
|
return { head, prefix, scalar, point, pointBytes };
|
|
652
852
|
}
|
|
653
853
|
|
|
654
854
|
/** Calculates EdDSA pub key. RFC8032 5.1.5. */
|
|
655
|
-
function getPublicKey(secretKey: Uint8Array): Uint8Array {
|
|
855
|
+
function getPublicKey(secretKey: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
656
856
|
return getExtendedPublicKey(secretKey).pointBytes;
|
|
657
857
|
}
|
|
658
858
|
|
|
659
|
-
//
|
|
660
|
-
function hashDomainToScalar(
|
|
859
|
+
// Hash domain-separated chunks into a little-endian scalar modulo the group order.
|
|
860
|
+
function hashDomainToScalar(
|
|
861
|
+
context: TArg<Uint8Array> = Uint8Array.of(),
|
|
862
|
+
...msgs: TArg<Uint8Array[]>
|
|
863
|
+
) {
|
|
661
864
|
const msg = concatBytes(...msgs);
|
|
662
|
-
return modN_LE(
|
|
865
|
+
return modN_LE(hash(domain(msg, abytes(context, undefined, 'context'), !!prehash)));
|
|
663
866
|
}
|
|
664
867
|
|
|
665
868
|
/** Signs message with secret key. RFC8032 5.1.6 */
|
|
666
869
|
function sign(
|
|
667
|
-
msg: Uint8Array
|
|
668
|
-
secretKey: Uint8Array
|
|
669
|
-
options: { context?: Uint8Array } = {}
|
|
670
|
-
): Uint8Array {
|
|
870
|
+
msg: TArg<Uint8Array>,
|
|
871
|
+
secretKey: TArg<Uint8Array>,
|
|
872
|
+
options: TArg<{ context?: Uint8Array }> = {}
|
|
873
|
+
): TRet<Uint8Array> {
|
|
671
874
|
msg = abytes(msg, undefined, 'message');
|
|
672
875
|
if (prehash) msg = prehash(msg); // for ed25519ph etc.
|
|
673
876
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
|
|
674
877
|
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
|
878
|
+
// RFC 8032 5.1.6 allows r mod L = 0, and SUPERCOP ref10 accepts the resulting identity-point
|
|
879
|
+
// signature.
|
|
880
|
+
// We intentionally keep the safe multiply() rejection here so a miswired all-zero hash provider
|
|
881
|
+
// fails loudly instead of silently producing a degenerate signature.
|
|
675
882
|
const R = BASE.multiply(r).toBytes(); // R = rG
|
|
676
883
|
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
|
677
884
|
const s = Fn.create(r + k * scalar); // S = (r + k * s) mod L
|
|
678
885
|
if (!Fn.isValid(s)) throw new Error('sign failed: invalid s'); // 0 <= s < L
|
|
679
886
|
const rs = concatBytes(R, Fn.toBytes(s));
|
|
680
|
-
return abytes(rs, lengths.signature, 'result')
|
|
887
|
+
return abytes(rs, lengths.signature, 'result') as TRet<Uint8Array>;
|
|
681
888
|
}
|
|
682
889
|
|
|
683
|
-
//
|
|
684
|
-
|
|
890
|
+
// Keep the shared helper strict by default: RFC 8032 / NIST-style wrappers should reject
|
|
891
|
+
// non-canonical encodings unless they explicitly opt into ZIP-215's more permissive decode rules.
|
|
892
|
+
const verifyOpts: TArg<{ context?: Uint8Array; zip215?: boolean }> = {
|
|
893
|
+
zip215: opts.zip215,
|
|
894
|
+
};
|
|
685
895
|
|
|
686
896
|
/**
|
|
687
|
-
* Verifies EdDSA signature against message and public key.
|
|
688
|
-
*
|
|
897
|
+
* Verifies EdDSA signature against message and public key. RFC 8032 §§5.1.7 and 5.2.7.
|
|
898
|
+
* A cofactored verification equation is checked.
|
|
689
899
|
*/
|
|
690
900
|
function verify(
|
|
691
|
-
sig: Uint8Array
|
|
692
|
-
msg: Uint8Array
|
|
693
|
-
publicKey: Uint8Array
|
|
901
|
+
sig: TArg<Uint8Array>,
|
|
902
|
+
msg: TArg<Uint8Array>,
|
|
903
|
+
publicKey: TArg<Uint8Array>,
|
|
694
904
|
options = verifyOpts
|
|
695
905
|
): boolean {
|
|
696
|
-
|
|
906
|
+
// Preserve the wrapper-selected default for `{}` / `{ zip215: undefined }`, not just omitted opts.
|
|
907
|
+
const { context } = options;
|
|
908
|
+
const zip215 = options.zip215 === undefined ? !!verifyOpts.zip215 : options.zip215;
|
|
697
909
|
const len = lengths.signature;
|
|
698
910
|
sig = abytes(sig, len, 'signature');
|
|
699
911
|
msg = abytes(msg, undefined, 'message');
|
|
@@ -706,7 +918,8 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
|
|
|
706
918
|
const s = bytesToNumberLE(sig.subarray(mid, len));
|
|
707
919
|
let A, R, SB;
|
|
708
920
|
try {
|
|
709
|
-
//
|
|
921
|
+
// ZIP-215 is more permissive than RFC 8032 / NIST186-5. Use it only for wrappers that
|
|
922
|
+
// explicitly want consensus-style unreduced encoding acceptance.
|
|
710
923
|
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
711
924
|
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
712
925
|
A = Point.fromBytes(publicKey, zip215);
|
|
@@ -715,12 +928,19 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
|
|
|
715
928
|
} catch (error) {
|
|
716
929
|
return false;
|
|
717
930
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
931
|
+
// RFC 8032 §§5.1.7/5.2.7 and FIPS 186-5 §§7.7.2/7.8.2 only decode A' and check the cofactored
|
|
932
|
+
// verification equation; they do not add a separate low-order-public-key rejection here.
|
|
933
|
+
// Strict mode still rejects small-order A' intentionally for SBS-style non-repudiation and to
|
|
934
|
+
// avoid ambiguous verification outcomes where unusual low-order keys can make distinct
|
|
935
|
+
// key/signature/message combinations verify.
|
|
936
|
+
if (!zip215 && A.isSmallOrder()) return false;
|
|
937
|
+
|
|
938
|
+
// ZIP-215 accepts noncanonical / unreduced point encodings, so the challenge hash must use the
|
|
939
|
+
// exact signature/public-key bytes rather than canonicalized re-encodings of the decoded points.
|
|
940
|
+
const k = hashDomainToScalar(context, r, publicKey, msg);
|
|
721
941
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
722
|
-
//
|
|
723
|
-
// [
|
|
942
|
+
// Check the cofactored verification equation via the curve cofactor h.
|
|
943
|
+
// [h][S]B = [h]R + [h][k]A'
|
|
724
944
|
return RkA.subtract(SB).clearCofactor().is0();
|
|
725
945
|
}
|
|
726
946
|
|
|
@@ -731,17 +951,19 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
|
|
|
731
951
|
signature: 2 * _size,
|
|
732
952
|
seed: _size,
|
|
733
953
|
};
|
|
734
|
-
function randomSecretKey(seed
|
|
735
|
-
|
|
954
|
+
function randomSecretKey(seed?: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
955
|
+
seed = seed === undefined ? randomBytes(lengths.seed) : seed;
|
|
956
|
+
return abytes(seed, lengths.seed, 'seed') as TRet<Uint8Array>;
|
|
736
957
|
}
|
|
737
958
|
|
|
738
|
-
function isValidSecretKey(key: Uint8Array): boolean {
|
|
739
|
-
return isBytes(key) && key.length ===
|
|
959
|
+
function isValidSecretKey(key: TArg<Uint8Array>): boolean {
|
|
960
|
+
return isBytes(key) && key.length === lengths.secretKey;
|
|
740
961
|
}
|
|
741
962
|
|
|
742
|
-
function isValidPublicKey(key: Uint8Array
|
|
963
|
+
function isValidPublicKey(key: TArg<Uint8Array>, zip215?: boolean): boolean {
|
|
743
964
|
try {
|
|
744
|
-
|
|
965
|
+
// Preserve the wrapper-selected default for omitted / `undefined` ZIP-215 flags here too.
|
|
966
|
+
return !!Point.fromBytes(key, zip215 === undefined ? verifyOpts.zip215 : zip215);
|
|
745
967
|
} catch (error) {
|
|
746
968
|
return false;
|
|
747
969
|
}
|
|
@@ -761,21 +983,23 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
|
|
|
761
983
|
* - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
|
762
984
|
* - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
|
763
985
|
*/
|
|
764
|
-
toMontgomery(publicKey: Uint8Array): Uint8Array {
|
|
986
|
+
toMontgomery(publicKey: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
765
987
|
const { y } = Point.fromBytes(publicKey);
|
|
766
988
|
const size = lengths.publicKey;
|
|
767
989
|
const is25519 = size === 32;
|
|
768
990
|
if (!is25519 && size !== 57) throw new Error('only defined for 25519 and 448');
|
|
769
991
|
const u = is25519 ? Fp.div(_1n + y, _1n - y) : Fp.div(y - _1n, y + _1n);
|
|
770
|
-
return Fp.toBytes(u)
|
|
992
|
+
return Fp.toBytes(u) as TRet<Uint8Array>;
|
|
771
993
|
},
|
|
772
|
-
toMontgomerySecret(secretKey: Uint8Array): Uint8Array {
|
|
994
|
+
toMontgomerySecret(secretKey: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
773
995
|
const size = lengths.secretKey;
|
|
774
996
|
abytes(secretKey, size);
|
|
775
|
-
const hashed =
|
|
776
|
-
return adjustScalarBytes(hashed).subarray(0, size)
|
|
997
|
+
const hashed = hash(secretKey.subarray(0, size));
|
|
998
|
+
return adjustScalarBytes(hashed).subarray(0, size) as TRet<Uint8Array>;
|
|
777
999
|
},
|
|
778
1000
|
};
|
|
1001
|
+
Object.freeze(lengths);
|
|
1002
|
+
Object.freeze(utils);
|
|
779
1003
|
|
|
780
1004
|
return Object.freeze({
|
|
781
1005
|
keygen: createKeygen(randomSecretKey, getPublicKey),
|