@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
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
|
-
import type { CHash } from '../utils.ts';
|
|
8
|
+
import type { CHash, TArg, TRet } from '../utils.ts';
|
|
9
9
|
import {
|
|
10
10
|
abytes,
|
|
11
11
|
asafenumber,
|
|
12
12
|
asciiToBytes,
|
|
13
13
|
bytesToNumberBE,
|
|
14
|
+
copyBytes,
|
|
14
15
|
concatBytes,
|
|
15
16
|
isBytes,
|
|
16
17
|
validateObject,
|
|
@@ -18,7 +19,17 @@ import {
|
|
|
18
19
|
import type { AffinePoint, PC_ANY, PC_F, PC_P } from './curve.ts';
|
|
19
20
|
import { FpInvertBatch, mod, type IField } from './modular.ts';
|
|
20
21
|
|
|
22
|
+
/** ASCII domain-separation tag or raw bytes. */
|
|
21
23
|
export type AsciiOrBytes = string | Uint8Array;
|
|
24
|
+
type H2CDefaults = {
|
|
25
|
+
DST: AsciiOrBytes;
|
|
26
|
+
expand: 'xmd' | 'xof';
|
|
27
|
+
hash: CHash;
|
|
28
|
+
p: bigint;
|
|
29
|
+
m: number;
|
|
30
|
+
k: number;
|
|
31
|
+
encodeDST?: AsciiOrBytes;
|
|
32
|
+
};
|
|
22
33
|
|
|
23
34
|
/**
|
|
24
35
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
|
@@ -29,83 +40,154 @@ export type AsciiOrBytes = string | Uint8Array;
|
|
|
29
40
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
|
30
41
|
*/
|
|
31
42
|
export type H2COpts = {
|
|
43
|
+
/** Domain separation tag. */
|
|
32
44
|
DST: AsciiOrBytes;
|
|
45
|
+
/** Expander family used by RFC 9380. */
|
|
33
46
|
expand: 'xmd' | 'xof';
|
|
47
|
+
/** Hash or XOF implementation used by the expander. */
|
|
34
48
|
hash: CHash;
|
|
49
|
+
/** Base-field characteristic. */
|
|
35
50
|
p: bigint;
|
|
51
|
+
/** Extension degree (`1` for prime fields). */
|
|
36
52
|
m: number;
|
|
53
|
+
/** Target security level in bits. */
|
|
37
54
|
k: number;
|
|
38
55
|
};
|
|
56
|
+
/** Hash-only subset of RFC 9380 options used by per-call overrides. */
|
|
39
57
|
export type H2CHashOpts = {
|
|
58
|
+
/** Expander family used by RFC 9380. */
|
|
40
59
|
expand: 'xmd' | 'xof';
|
|
60
|
+
/** Hash or XOF implementation used by the expander. */
|
|
41
61
|
hash: CHash;
|
|
42
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Map one hash-to-field output tuple onto affine curve coordinates.
|
|
65
|
+
* Implementations receive the validated scalar tuple by reference for performance and MUST treat it
|
|
66
|
+
* as read-only. Callers that need scratch space should copy before mutating.
|
|
67
|
+
* @param scalar - Field-element tuple produced by `hash_to_field`.
|
|
68
|
+
* @returns Affine point before subgroup clearing.
|
|
69
|
+
*/
|
|
43
70
|
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|
44
71
|
|
|
45
72
|
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
|
46
73
|
// (changing DST is ok!)
|
|
47
|
-
|
|
74
|
+
/** Per-call override for the domain-separation tag. */
|
|
75
|
+
export type H2CDSTOpts = {
|
|
76
|
+
/** Domain-separation tag override. */
|
|
77
|
+
DST: AsciiOrBytes;
|
|
78
|
+
};
|
|
79
|
+
/** Base hash-to-curve helpers shared by `hashToCurve` and `encodeToCurve`. */
|
|
48
80
|
export type H2CHasherBase<PC extends PC_ANY> = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Hash arbitrary bytes to one curve point.
|
|
83
|
+
* @param msg - Input message bytes.
|
|
84
|
+
* @param options - Optional domain-separation override. See {@link H2CDSTOpts}.
|
|
85
|
+
* @returns Curve point after hash-to-curve.
|
|
86
|
+
*/
|
|
87
|
+
hashToCurve(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): PC_P<PC>;
|
|
88
|
+
/**
|
|
89
|
+
* Hash arbitrary bytes to one scalar.
|
|
90
|
+
* @param msg - Input message bytes.
|
|
91
|
+
* @param options - Optional domain-separation override. See {@link H2CDSTOpts}.
|
|
92
|
+
* @returns Scalar reduced into the target field.
|
|
93
|
+
*/
|
|
94
|
+
hashToScalar(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): bigint;
|
|
95
|
+
/**
|
|
96
|
+
* Derive one curve point from non-uniform bytes without the random-oracle
|
|
97
|
+
* guarantees of `hashToCurve`.
|
|
98
|
+
* Accepts the same arguments as `hashToCurve`, but runs the encode-to-curve
|
|
99
|
+
* path instead of the random-oracle construction.
|
|
100
|
+
*/
|
|
101
|
+
deriveToCurve?(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): PC_P<PC>;
|
|
102
|
+
/** Point constructor for the target curve. */
|
|
52
103
|
Point: PC;
|
|
53
104
|
};
|
|
54
105
|
/**
|
|
55
|
-
* RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
|
|
106
|
+
* RFC 9380 methods, with cofactor clearing. See {@link https://www.rfc-editor.org/rfc/rfc9380#section-3 | RFC 9380 section 3}.
|
|
56
107
|
*
|
|
57
108
|
* * hashToCurve: `map(hash(input))`, encodes RANDOM bytes to curve (WITH hashing)
|
|
58
109
|
* * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
|
|
59
110
|
* * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
|
|
60
111
|
*/
|
|
61
112
|
export type H2CHasher<PC extends PC_ANY> = H2CHasherBase<PC> & {
|
|
62
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Encode non-uniform bytes to one curve point.
|
|
115
|
+
* @param msg - Input message bytes.
|
|
116
|
+
* @param options - Optional domain-separation override. See {@link H2CDSTOpts}.
|
|
117
|
+
* @returns Curve point after encode-to-curve.
|
|
118
|
+
*/
|
|
119
|
+
encodeToCurve(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): PC_P<PC>;
|
|
120
|
+
/** Deterministic map from `hash_to_field` tuples into affine coordinates. */
|
|
63
121
|
mapToCurve: MapToCurve<PC_F<PC>>;
|
|
64
|
-
|
|
122
|
+
/** Default RFC 9380 options captured by this hasher bundle. */
|
|
123
|
+
defaults: H2CDefaults;
|
|
65
124
|
};
|
|
66
125
|
|
|
67
126
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
|
68
127
|
const os2ip = bytesToNumberBE;
|
|
69
128
|
|
|
70
|
-
// Integer to Octet Stream (numberToBytesBE)
|
|
71
|
-
function i2osp(value: number, length: number): Uint8Array {
|
|
129
|
+
// Integer to Octet Stream (numberToBytesBE).
|
|
130
|
+
function i2osp(value: number, length: number): TRet<Uint8Array> {
|
|
72
131
|
asafenumber(value);
|
|
73
132
|
asafenumber(length);
|
|
74
|
-
|
|
133
|
+
// This helper stays on the JS bitwise/u32 fast-path. Callers that need wider encodings should
|
|
134
|
+
// use bigint + numberToBytesBE instead of routing large widths through this small helper.
|
|
135
|
+
if (length < 0 || length > 4) throw new Error('invalid I2OSP length: ' + length);
|
|
136
|
+
if (value < 0 || value > 2 ** (8 * length) - 1) throw new Error('invalid I2OSP input: ' + value);
|
|
75
137
|
const res = Array.from({ length }).fill(0) as number[];
|
|
76
138
|
for (let i = length - 1; i >= 0; i--) {
|
|
77
139
|
res[i] = value & 0xff;
|
|
78
140
|
value >>>= 8;
|
|
79
141
|
}
|
|
80
|
-
return new Uint8Array(res)
|
|
142
|
+
return new Uint8Array(res) as TRet<Uint8Array>;
|
|
81
143
|
}
|
|
82
144
|
|
|
83
|
-
|
|
145
|
+
// RFC 9380 only applies strxor() to equal-length strings; callers must preserve that invariant.
|
|
146
|
+
function strxor(a: TArg<Uint8Array>, b: TArg<Uint8Array>): TRet<Uint8Array> {
|
|
84
147
|
const arr = new Uint8Array(a.length);
|
|
85
148
|
for (let i = 0; i < a.length; i++) {
|
|
86
149
|
arr[i] = a[i] ^ b[i];
|
|
87
150
|
}
|
|
88
|
-
return arr
|
|
151
|
+
return arr as TRet<Uint8Array>;
|
|
89
152
|
}
|
|
90
153
|
|
|
91
154
|
// User can always use utf8 if they want, by passing Uint8Array.
|
|
92
155
|
// If string is passed, we treat it as ASCII: other formats are likely a mistake.
|
|
93
|
-
function normDST(DST: AsciiOrBytes): Uint8Array {
|
|
156
|
+
function normDST(DST: TArg<AsciiOrBytes>): TRet<Uint8Array> {
|
|
94
157
|
if (!isBytes(DST) && typeof DST !== 'string')
|
|
95
158
|
throw new Error('DST must be Uint8Array or ascii string');
|
|
96
|
-
|
|
159
|
+
const dst = typeof DST === 'string' ? asciiToBytes(DST) : DST;
|
|
160
|
+
// RFC 9380 §3.1 requirement 2: tags "MUST have nonzero length".
|
|
161
|
+
if (dst.length === 0) throw new Error('DST must be non-empty');
|
|
162
|
+
return dst as TRet<Uint8Array>;
|
|
97
163
|
}
|
|
98
164
|
|
|
99
165
|
/**
|
|
100
|
-
* Produces a uniformly random byte string using a cryptographic hash
|
|
101
|
-
*
|
|
166
|
+
* Produces a uniformly random byte string using a cryptographic hash
|
|
167
|
+
* function H that outputs b bits.
|
|
168
|
+
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1 | RFC 9380 section 5.3.1}.
|
|
169
|
+
* @param msg - Input message.
|
|
170
|
+
* @param DST - Domain separation tag. This helper normalizes DST, rejects empty DSTs, and
|
|
171
|
+
* oversize-hashes DST when needed.
|
|
172
|
+
* @param lenInBytes - Output length.
|
|
173
|
+
* @param H - Hash function.
|
|
174
|
+
* @returns Uniform byte string.
|
|
175
|
+
* @throws If the message, DST, hash, or output length is invalid. {@link Error}
|
|
176
|
+
* @example
|
|
177
|
+
* Expand one message into uniform bytes with the XMD construction.
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* import { expand_message_xmd } from '@noble/curves/abstract/hash-to-curve.js';
|
|
181
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
182
|
+
* const uniform = expand_message_xmd(new TextEncoder().encode('hello noble'), 'DST', 32, sha256);
|
|
183
|
+
* ```
|
|
102
184
|
*/
|
|
103
185
|
export function expand_message_xmd(
|
|
104
|
-
msg: Uint8Array
|
|
105
|
-
DST: AsciiOrBytes
|
|
186
|
+
msg: TArg<Uint8Array>,
|
|
187
|
+
DST: TArg<AsciiOrBytes>,
|
|
106
188
|
lenInBytes: number,
|
|
107
|
-
H: CHash
|
|
108
|
-
): Uint8Array {
|
|
189
|
+
H: TArg<CHash>
|
|
190
|
+
): TRet<Uint8Array> {
|
|
109
191
|
abytes(msg);
|
|
110
192
|
asafenumber(lenInBytes);
|
|
111
193
|
DST = normDST(DST);
|
|
@@ -115,12 +197,15 @@ export function expand_message_xmd(
|
|
|
115
197
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
|
116
198
|
if (lenInBytes > 65535 || ell > 255) throw new Error('expand_message_xmd: invalid lenInBytes');
|
|
117
199
|
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
|
118
|
-
const Z_pad =
|
|
200
|
+
const Z_pad = new Uint8Array(r_in_bytes); // RFC 9380: Z_pad = I2OSP(0, s_in_bytes)
|
|
119
201
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
|
120
202
|
const b = new Array<Uint8Array>(ell);
|
|
121
203
|
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
|
122
204
|
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
|
123
|
-
|
|
205
|
+
// `b[0]` already stores RFC `b_1`, so only derive `b_2..b_ell` here. The old `<= ell`
|
|
206
|
+
// loop computed one extra tail block, which was usually sliced away but broke at max `ell=255`
|
|
207
|
+
// by reaching `I2OSP(256, 1)`.
|
|
208
|
+
for (let i = 1; i < ell; i++) {
|
|
124
209
|
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
|
|
125
210
|
b[i] = H(concatBytes(...args));
|
|
126
211
|
}
|
|
@@ -133,20 +218,42 @@ export function expand_message_xmd(
|
|
|
133
218
|
* 1. The collision resistance of H MUST be at least k bits.
|
|
134
219
|
* 2. H MUST be an XOF that has been proved indifferentiable from
|
|
135
220
|
* a random oracle under a reasonable cryptographic assumption.
|
|
136
|
-
*
|
|
221
|
+
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2 | RFC 9380 section 5.3.2}.
|
|
222
|
+
* @param msg - Input message.
|
|
223
|
+
* @param DST - Domain separation tag. This helper normalizes DST, rejects empty DSTs, and
|
|
224
|
+
* oversize-hashes DST when needed.
|
|
225
|
+
* @param lenInBytes - Output length.
|
|
226
|
+
* @param k - Target security level.
|
|
227
|
+
* @param H - XOF hash function.
|
|
228
|
+
* @returns Uniform byte string.
|
|
229
|
+
* @throws If the message, DST, XOF, or output length is invalid. {@link Error}
|
|
230
|
+
* @example
|
|
231
|
+
* Expand one message into uniform bytes with the XOF construction.
|
|
232
|
+
*
|
|
233
|
+
* ```ts
|
|
234
|
+
* import { expand_message_xof } from '@noble/curves/abstract/hash-to-curve.js';
|
|
235
|
+
* import { shake256 } from '@noble/hashes/sha3.js';
|
|
236
|
+
* const uniform = expand_message_xof(
|
|
237
|
+
* new TextEncoder().encode('hello noble'),
|
|
238
|
+
* 'DST',
|
|
239
|
+
* 32,
|
|
240
|
+
* 128,
|
|
241
|
+
* shake256
|
|
242
|
+
* );
|
|
243
|
+
* ```
|
|
137
244
|
*/
|
|
138
245
|
export function expand_message_xof(
|
|
139
|
-
msg: Uint8Array
|
|
140
|
-
DST: AsciiOrBytes
|
|
246
|
+
msg: TArg<Uint8Array>,
|
|
247
|
+
DST: TArg<AsciiOrBytes>,
|
|
141
248
|
lenInBytes: number,
|
|
142
249
|
k: number,
|
|
143
|
-
H: CHash
|
|
144
|
-
): Uint8Array {
|
|
250
|
+
H: TArg<CHash>
|
|
251
|
+
): TRet<Uint8Array> {
|
|
145
252
|
abytes(msg);
|
|
146
253
|
asafenumber(lenInBytes);
|
|
147
254
|
DST = normDST(DST);
|
|
148
255
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
|
149
|
-
// DST = H(
|
|
256
|
+
// RFC 9380 §5.3.3: DST = H("H2C-OVERSIZE-DST-" || a_very_long_DST, ceil(2 * k / 8)).
|
|
150
257
|
if (DST.length > 255) {
|
|
151
258
|
const dkLen = Math.ceil((2 * k) / 8);
|
|
152
259
|
DST = H.create({ dkLen }).update(asciiToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
|
@@ -166,13 +273,33 @@ export function expand_message_xof(
|
|
|
166
273
|
|
|
167
274
|
/**
|
|
168
275
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
169
|
-
*
|
|
170
|
-
* @param msg
|
|
171
|
-
* @param count
|
|
172
|
-
* @param options
|
|
173
|
-
* @returns [u_0, ..., u_(count - 1)]
|
|
276
|
+
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.2 | RFC 9380 section 5.2}.
|
|
277
|
+
* @param msg - Input message bytes.
|
|
278
|
+
* @param count - Number of field elements to derive. Must be `>= 1`.
|
|
279
|
+
* @param options - RFC 9380 options. See {@link H2COpts}. `m` must be `>= 1`.
|
|
280
|
+
* @returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
|
281
|
+
* @throws If the expander choice or RFC 9380 options are invalid. {@link Error}
|
|
282
|
+
* @example
|
|
283
|
+
* Hash one message into field elements before mapping it onto a curve.
|
|
284
|
+
*
|
|
285
|
+
* ```ts
|
|
286
|
+
* import { hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
|
|
287
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
288
|
+
* const scalars = hash_to_field(new TextEncoder().encode('hello noble'), 2, {
|
|
289
|
+
* DST: 'DST',
|
|
290
|
+
* p: 17n,
|
|
291
|
+
* m: 1,
|
|
292
|
+
* k: 128,
|
|
293
|
+
* expand: 'xmd',
|
|
294
|
+
* hash: sha256,
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
174
297
|
*/
|
|
175
|
-
export function hash_to_field(
|
|
298
|
+
export function hash_to_field(
|
|
299
|
+
msg: TArg<Uint8Array>,
|
|
300
|
+
count: number,
|
|
301
|
+
options: TArg<H2COpts>
|
|
302
|
+
): bigint[][] {
|
|
176
303
|
validateObject(options, {
|
|
177
304
|
p: 'bigint',
|
|
178
305
|
m: 'number',
|
|
@@ -183,6 +310,10 @@ export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts):
|
|
|
183
310
|
asafenumber(hash.outputLen, 'valid hash');
|
|
184
311
|
abytes(msg);
|
|
185
312
|
asafenumber(count);
|
|
313
|
+
// RFC 9380 §5.2 defines hash_to_field over a list of one or more field elements and requires
|
|
314
|
+
// extension degree `m >= 1`; rejecting here avoids degenerate `[]` / `[[]]` helper outputs.
|
|
315
|
+
if (count < 1) throw new Error('hash_to_field: expected count >= 1');
|
|
316
|
+
if (m < 1) throw new Error('hash_to_field: expected m >= 1');
|
|
186
317
|
const log2p = p.toString(2).length;
|
|
187
318
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
|
188
319
|
const len_in_bytes = count * m * L;
|
|
@@ -212,6 +343,21 @@ export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts):
|
|
|
212
343
|
|
|
213
344
|
type XY<T> = (x: T, y: T) => { x: T; y: T };
|
|
214
345
|
type XYRatio<T> = [T[], T[], T[], T[]]; // xn/xd, yn/yd
|
|
346
|
+
/**
|
|
347
|
+
* @param field - Field implementation.
|
|
348
|
+
* @param map - Isogeny coefficients.
|
|
349
|
+
* @returns Isogeny mapping helper.
|
|
350
|
+
* @example
|
|
351
|
+
* Build one rational isogeny map, then apply it to affine x/y coordinates.
|
|
352
|
+
*
|
|
353
|
+
* ```ts
|
|
354
|
+
* import { isogenyMap } from '@noble/curves/abstract/hash-to-curve.js';
|
|
355
|
+
* import { Field } from '@noble/curves/abstract/modular.js';
|
|
356
|
+
* const Fp = Field(17n);
|
|
357
|
+
* const iso = isogenyMap(Fp, [[0n, 1n], [1n], [1n], [1n]]);
|
|
358
|
+
* const point = iso(3n, 5n);
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
215
361
|
export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): XY<T> {
|
|
216
362
|
// Make same order as in spec
|
|
217
363
|
const coeff = map.map((i) => Array.from(i).reverse());
|
|
@@ -219,10 +365,11 @@ export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): X
|
|
|
219
365
|
const [xn, xd, yn, yd] = coeff.map((val) =>
|
|
220
366
|
val.reduce((acc, i) => field.add(field.mul(acc, x), i))
|
|
221
367
|
);
|
|
222
|
-
// 6.6.3
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
368
|
+
// RFC 9380 §6.6.3 / Appendix E: denominator-zero exceptional cases must
|
|
369
|
+
// return the identity on E.
|
|
370
|
+
// Shipped Weierstrass consumers encode that affine identity as all-zero
|
|
371
|
+
// coordinates, so `passZero=true` intentionally collapses zero
|
|
372
|
+
// denominators to `{ x: 0, y: 0 }`.
|
|
226
373
|
const [xd_inv, yd_inv] = FpInvertBatch(field, [xd, yd], true);
|
|
227
374
|
x = field.mul(xn, xd_inv); // xNum / xDen
|
|
228
375
|
y = field.mul(y, field.mul(yn, yd_inv)); // y * (yNum / yDev)
|
|
@@ -230,39 +377,90 @@ export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): X
|
|
|
230
377
|
};
|
|
231
378
|
}
|
|
232
379
|
|
|
233
|
-
|
|
380
|
+
// Keep the shared DST removable when the selected bundle never hashes to scalar.
|
|
381
|
+
// Callers that need protocol-specific scalar domain separation must override this generic default.
|
|
382
|
+
// RFC 9497 §§4.1-4.5 use this ASCII prefix before appending the ciphersuite context string.
|
|
383
|
+
// Export a string instead of mutable bytes so callers cannot poison default hash-to-scalar behavior
|
|
384
|
+
// by mutating a shared Uint8Array in place.
|
|
385
|
+
export const _DST_scalar = 'HashToScalar-' as const;
|
|
234
386
|
|
|
235
|
-
/**
|
|
387
|
+
/**
|
|
388
|
+
* Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}.
|
|
389
|
+
* @param Point - Point constructor.
|
|
390
|
+
* @param mapToCurve - Map-to-curve function.
|
|
391
|
+
* @param defaults - Default hash-to-curve options. This object is frozen in place and reused as
|
|
392
|
+
* the shared defaults bundle for the returned helpers.
|
|
393
|
+
* @returns Hash-to-curve helper namespace.
|
|
394
|
+
* @throws If the map-to-curve callback or default hash-to-curve options are invalid. {@link Error}
|
|
395
|
+
* @example
|
|
396
|
+
* Bundle hash-to-curve, hash-to-scalar, and encode-to-curve helpers for one curve.
|
|
397
|
+
*
|
|
398
|
+
* ```ts
|
|
399
|
+
* import { createHasher } from '@noble/curves/abstract/hash-to-curve.js';
|
|
400
|
+
* import { p256 } from '@noble/curves/nist.js';
|
|
401
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
402
|
+
* const hasher = createHasher(p256.Point, () => p256.Point.BASE.toAffine(), {
|
|
403
|
+
* DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
|
404
|
+
* encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
|
405
|
+
* p: p256.Point.Fp.ORDER,
|
|
406
|
+
* m: 1,
|
|
407
|
+
* k: 128,
|
|
408
|
+
* expand: 'xmd',
|
|
409
|
+
* hash: sha256,
|
|
410
|
+
* });
|
|
411
|
+
* const point = hasher.encodeToCurve(new TextEncoder().encode('hello noble'));
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
236
414
|
export function createHasher<PC extends PC_ANY>(
|
|
237
415
|
Point: PC,
|
|
238
416
|
mapToCurve: MapToCurve<PC_F<PC>>,
|
|
239
|
-
defaults: H2COpts & { encodeDST?: AsciiOrBytes }
|
|
417
|
+
defaults: TArg<H2COpts & { encodeDST?: AsciiOrBytes }>
|
|
240
418
|
): H2CHasher<PC> {
|
|
241
419
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
|
420
|
+
// `Point` is intentionally not shape-validated eagerly here: point constructors vary across
|
|
421
|
+
// curve families, so this helper only checks the hooks it can validate cheaply. Misconfigured
|
|
422
|
+
// suites fail later when hashing first touches Point.fromAffine / Point.ZERO / clearCofactor().
|
|
423
|
+
const snapshot = (src: TArg<H2COpts & { encodeDST?: AsciiOrBytes }>): TRet<H2CDefaults> =>
|
|
424
|
+
Object.freeze({
|
|
425
|
+
...src,
|
|
426
|
+
DST: isBytes(src.DST) ? copyBytes(src.DST) : src.DST,
|
|
427
|
+
...(src.encodeDST === undefined
|
|
428
|
+
? {}
|
|
429
|
+
: { encodeDST: isBytes(src.encodeDST) ? copyBytes(src.encodeDST) : src.encodeDST }),
|
|
430
|
+
}) as TRet<H2CDefaults>;
|
|
431
|
+
// Keep one private defaults snapshot for actual hashing and expose fresh
|
|
432
|
+
// detached snapshots via the public getter.
|
|
433
|
+
// Otherwise a caller could mutate `hasher.defaults.DST` in place and poison
|
|
434
|
+
// the singleton hasher for every other consumer in the same process.
|
|
435
|
+
const safeDefaults = snapshot(defaults);
|
|
242
436
|
function map(num: bigint[]): PC_P<PC> {
|
|
243
437
|
return Point.fromAffine(mapToCurve(num)) as PC_P<PC>;
|
|
244
438
|
}
|
|
245
439
|
function clear(initial: PC_P<PC>): PC_P<PC> {
|
|
246
440
|
const P = initial.clearCofactor();
|
|
247
|
-
|
|
441
|
+
// Keep ZERO as the algebraic cofactor-clearing result here; strict public point-validity
|
|
442
|
+
// surfaces may still reject it later, but createHasher.clear() itself is not that boundary.
|
|
443
|
+
if (P.equals(Point.ZERO)) return Point.ZERO as PC_P<PC>;
|
|
248
444
|
P.assertValidity();
|
|
249
445
|
return P as PC_P<PC>;
|
|
250
446
|
}
|
|
251
447
|
|
|
252
|
-
return {
|
|
253
|
-
defaults
|
|
448
|
+
return Object.freeze({
|
|
449
|
+
get defaults() {
|
|
450
|
+
return snapshot(safeDefaults);
|
|
451
|
+
},
|
|
254
452
|
Point,
|
|
255
453
|
|
|
256
|
-
hashToCurve(msg: Uint8Array
|
|
257
|
-
const opts = Object.assign({},
|
|
454
|
+
hashToCurve(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): PC_P<PC> {
|
|
455
|
+
const opts = Object.assign({}, safeDefaults, options);
|
|
258
456
|
const u = hash_to_field(msg, 2, opts);
|
|
259
457
|
const u0 = map(u[0]);
|
|
260
458
|
const u1 = map(u[1]);
|
|
261
459
|
return clear(u0.add(u1) as PC_P<PC>);
|
|
262
460
|
},
|
|
263
|
-
encodeToCurve(msg: Uint8Array
|
|
264
|
-
const optsDst =
|
|
265
|
-
const opts = Object.assign({},
|
|
461
|
+
encodeToCurve(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): PC_P<PC> {
|
|
462
|
+
const optsDst = safeDefaults.encodeDST ? { DST: safeDefaults.encodeDST } : {};
|
|
463
|
+
const opts = Object.assign({}, safeDefaults, optsDst, options);
|
|
266
464
|
const u = hash_to_field(msg, 1, opts);
|
|
267
465
|
const u0 = map(u[0]);
|
|
268
466
|
return clear(u0);
|
|
@@ -270,7 +468,7 @@ export function createHasher<PC extends PC_ANY>(
|
|
|
270
468
|
/** See {@link H2CHasher} */
|
|
271
469
|
mapToCurve(scalars: bigint | bigint[]): PC_P<PC> {
|
|
272
470
|
// Curves with m=1 accept only single scalar
|
|
273
|
-
if (
|
|
471
|
+
if (safeDefaults.m === 1) {
|
|
274
472
|
if (typeof scalars !== 'bigint') throw new Error('expected bigint (m=1)');
|
|
275
473
|
return clear(map([scalars]));
|
|
276
474
|
}
|
|
@@ -281,12 +479,13 @@ export function createHasher<PC extends PC_ANY>(
|
|
|
281
479
|
},
|
|
282
480
|
|
|
283
481
|
// hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
|
|
284
|
-
// RFC 9380, draft-irtf-cfrg-bbs-signatures-08
|
|
285
|
-
|
|
482
|
+
// RFC 9380, draft-irtf-cfrg-bbs-signatures-08. Default scalar DST is the shared generic
|
|
483
|
+
// `HashToScalar-` prefix above unless the caller overrides it per invocation.
|
|
484
|
+
hashToScalar(msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>): bigint {
|
|
286
485
|
// @ts-ignore
|
|
287
486
|
const N = Point.Fn.ORDER;
|
|
288
|
-
const opts = Object.assign({},
|
|
487
|
+
const opts = Object.assign({}, safeDefaults, { p: N, m: 1, DST: _DST_scalar }, options);
|
|
289
488
|
return hash_to_field(msg, 1, opts)[0][0];
|
|
290
489
|
},
|
|
291
|
-
};
|
|
490
|
+
});
|
|
292
491
|
}
|