@noble/curves 0.5.2 → 0.6.1

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.
Files changed (61) hide show
  1. package/README.md +115 -41
  2. package/lib/_shortw_utils.d.ts +13 -24
  3. package/lib/abstract/bls.d.ts +39 -32
  4. package/lib/abstract/bls.js +74 -73
  5. package/lib/abstract/{group.d.ts → curve.d.ts} +30 -1
  6. package/lib/abstract/{group.js → curve.js} +33 -2
  7. package/lib/abstract/edwards.d.ts +30 -72
  8. package/lib/abstract/edwards.js +206 -389
  9. package/lib/abstract/hash-to-curve.d.ts +25 -6
  10. package/lib/abstract/hash-to-curve.js +40 -12
  11. package/lib/abstract/modular.d.ts +21 -8
  12. package/lib/abstract/modular.js +72 -48
  13. package/lib/abstract/montgomery.js +23 -68
  14. package/lib/abstract/poseidon.d.ts +29 -0
  15. package/lib/abstract/poseidon.js +115 -0
  16. package/lib/abstract/utils.d.ts +9 -37
  17. package/lib/abstract/utils.js +61 -87
  18. package/lib/abstract/weierstrass.d.ts +58 -81
  19. package/lib/abstract/weierstrass.js +485 -679
  20. package/lib/bls12-381.js +63 -58
  21. package/lib/bn.js +1 -1
  22. package/lib/ed25519.d.ts +7 -5
  23. package/lib/ed25519.js +82 -79
  24. package/lib/ed448.d.ts +3 -0
  25. package/lib/ed448.js +86 -83
  26. package/lib/esm/abstract/bls.js +75 -74
  27. package/lib/esm/abstract/{group.js → curve.js} +31 -1
  28. package/lib/esm/abstract/edwards.js +204 -387
  29. package/lib/esm/abstract/hash-to-curve.js +38 -11
  30. package/lib/esm/abstract/modular.js +69 -47
  31. package/lib/esm/abstract/montgomery.js +24 -69
  32. package/lib/esm/abstract/poseidon.js +109 -0
  33. package/lib/esm/abstract/utils.js +58 -82
  34. package/lib/esm/abstract/weierstrass.js +484 -678
  35. package/lib/esm/bls12-381.js +75 -70
  36. package/lib/esm/bn.js +1 -1
  37. package/lib/esm/ed25519.js +80 -78
  38. package/lib/esm/ed448.js +84 -82
  39. package/lib/esm/jubjub.js +1 -1
  40. package/lib/esm/p224.js +1 -1
  41. package/lib/esm/p256.js +11 -9
  42. package/lib/esm/p384.js +11 -9
  43. package/lib/esm/p521.js +12 -23
  44. package/lib/esm/secp256k1.js +124 -162
  45. package/lib/esm/stark.js +105 -41
  46. package/lib/jubjub.d.ts +2 -2
  47. package/lib/jubjub.js +1 -1
  48. package/lib/p192.d.ts +26 -48
  49. package/lib/p224.d.ts +26 -48
  50. package/lib/p224.js +1 -1
  51. package/lib/p256.d.ts +29 -48
  52. package/lib/p256.js +13 -10
  53. package/lib/p384.d.ts +29 -48
  54. package/lib/p384.js +13 -10
  55. package/lib/p521.d.ts +37 -57
  56. package/lib/p521.js +14 -24
  57. package/lib/secp256k1.d.ts +37 -46
  58. package/lib/secp256k1.js +124 -162
  59. package/lib/stark.d.ts +39 -22
  60. package/lib/stark.js +108 -41
  61. package/package.json +15 -10
package/lib/stark.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.keccak = exports.computeHashOnElements = exports.hashChain = exports.pedersen = exports.getAccountPath = exports.ethSigToPrivate = exports.getStarkKey = exports.grindKey = exports.numberToHexEth = exports.strip0x = exports.bytesToHexEth = exports.verify = exports.sign = exports.getSharedSecret = exports.getPublicKey = exports.ProjectivePoint = exports.Signature = exports.Point = exports.CURVE = exports.utils = exports.starkCurve = void 0;
3
+ exports.poseidonHash = exports.poseidonSmall = exports.poseidonCreate = exports.poseidonBasic = exports._poseidonMDS = exports.Fp251 = exports.Fp253 = exports.keccak = exports.computeHashOnElements = exports.hashChain = exports.pedersen = exports.getAccountPath = exports.ethSigToPrivate = exports.getStarkKey = exports.grindKey = exports.numberToHexEth = exports.strip0x = exports.bytesToHexEth = exports.verify = exports.sign = exports.getSharedSecret = exports.getPublicKey = exports.ProjectivePoint = exports.Signature = exports.CURVE = exports.utils = exports.starkCurve = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
5
  const sha3_1 = require("@noble/hashes/sha3");
6
6
  const sha256_1 = require("@noble/hashes/sha256");
@@ -8,10 +8,21 @@ const weierstrass_js_1 = require("./abstract/weierstrass.js");
8
8
  const cutils = require("./abstract/utils.js");
9
9
  const modular_js_1 = require("./abstract/modular.js");
10
10
  const _shortw_utils_js_1 = require("./_shortw_utils.js");
11
+ const poseidon = require("./abstract/poseidon.js");
12
+ const utils_1 = require("@noble/hashes/utils");
11
13
  // Stark-friendly elliptic curve
12
14
  // https://docs.starkware.co/starkex/stark-curve.html
13
15
  const CURVE_N = BigInt('3618502788666131213697322783095070105526743751716087489154079457884512865583');
14
16
  const nBitLength = 252;
17
+ // Copy-pasted from weierstrass.ts
18
+ function bits2int(bytes) {
19
+ const delta = bytes.length * 8 - nBitLength;
20
+ const num = cutils.bytesToNumberBE(bytes);
21
+ return delta > 0 ? num >> BigInt(delta) : num;
22
+ }
23
+ function bits2int_modN(bytes) {
24
+ return (0, modular_js_1.mod)(bits2int(bytes), CURVE_N);
25
+ }
15
26
  exports.starkCurve = (0, weierstrass_js_1.weierstrass)({
16
27
  // Params: a, b
17
28
  a: BigInt(1),
@@ -29,33 +40,28 @@ exports.starkCurve = (0, weierstrass_js_1.weierstrass)({
29
40
  // Default options
30
41
  lowS: false,
31
42
  ...(0, _shortw_utils_js_1.getHash)(sha256_1.sha256),
32
- truncateHash: (hash, truncateOnly = false) => {
33
- // TODO: cleanup, ugly code
34
- // Fix truncation
35
- if (!truncateOnly) {
36
- let hashS = bytesToNumber0x(hash).toString(16);
37
- if (hashS.length === 63) {
38
- hashS += '0';
39
- hash = hexToBytes0x(hashS);
40
- }
43
+ // Custom truncation routines for stark curve
44
+ bits2int: (bytes) => {
45
+ while (bytes[0] === 0)
46
+ bytes = bytes.subarray(1);
47
+ return bits2int(bytes);
48
+ },
49
+ bits2int_modN: (bytes) => {
50
+ let hashS = cutils.bytesToNumberBE(bytes).toString(16);
51
+ if (hashS.length === 63) {
52
+ hashS += '0';
53
+ bytes = hexToBytes0x(hashS);
41
54
  }
42
55
  // Truncate zero bytes on left (compat with elliptic)
43
- while (hash[0] === 0)
44
- hash = hash.subarray(1);
45
- const byteLength = hash.length;
46
- const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
47
- let h = hash.length ? bytesToNumber0x(hash) : 0n;
48
- if (delta > 0)
49
- h = h >> BigInt(delta);
50
- if (!truncateOnly && h >= CURVE_N)
51
- h -= CURVE_N;
52
- return h;
56
+ while (bytes[0] === 0)
57
+ bytes = bytes.subarray(1);
58
+ return bits2int_modN(bytes);
53
59
  },
54
60
  });
55
61
  // Custom Starknet type conversion functions that can handle 0x and unpadded hex
56
62
  function hexToBytes0x(hex) {
57
63
  if (typeof hex !== 'string') {
58
- throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
64
+ throw new Error('hexToBytes: expected string, got ' + typeof hex);
59
65
  }
60
66
  hex = (0, exports.strip0x)(hex);
61
67
  if (hex.length & 1)
@@ -75,7 +81,7 @@ function hexToBytes0x(hex) {
75
81
  }
76
82
  function hexToNumber0x(hex) {
77
83
  if (typeof hex !== 'string') {
78
- throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
84
+ throw new Error('hexToNumber: expected string, got ' + typeof hex);
79
85
  }
80
86
  // Big Endian
81
87
  // TODO: strip vs no strip?
@@ -90,9 +96,9 @@ function ensureBytes0x(hex) {
90
96
  return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
91
97
  }
92
98
  function normalizePrivateKey(privKey) {
93
- return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
99
+ return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0');
94
100
  }
95
- function getPublicKey0x(privKey, isCompressed) {
101
+ function getPublicKey0x(privKey, isCompressed = false) {
96
102
  return exports.starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
97
103
  }
98
104
  exports.getPublicKey = getPublicKey0x;
@@ -111,9 +117,8 @@ function verify0x(signature, msgHash, pubKey) {
111
117
  return exports.starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
112
118
  }
113
119
  exports.verify = verify0x;
114
- const { CURVE, Point, ProjectivePoint, Signature } = exports.starkCurve;
120
+ const { CURVE, ProjectivePoint, Signature } = exports.starkCurve;
115
121
  exports.CURVE = CURVE;
116
- exports.Point = Point;
117
122
  exports.ProjectivePoint = ProjectivePoint;
118
123
  exports.Signature = Signature;
119
124
  exports.utils = exports.starkCurve.utils;
@@ -129,18 +134,17 @@ function hashKeyWithIndex(key, index) {
129
134
  let indexHex = cutils.numberToHexUnpadded(index);
130
135
  if (indexHex.length & 1)
131
136
  indexHex = '0' + indexHex;
132
- return bytesToNumber0x((0, sha256_1.sha256)(cutils.concatBytes(key, hexToBytes0x(indexHex))));
137
+ return sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
133
138
  }
134
139
  function grindKey(seed) {
135
140
  const _seed = ensureBytes0x(seed);
136
141
  const sha256mask = 2n ** 256n;
137
- const Fn = (0, modular_js_1.Fp)(CURVE.n);
138
- const limit = sha256mask - Fn.create(sha256mask);
142
+ const limit = sha256mask - (0, modular_js_1.mod)(sha256mask, CURVE_N);
139
143
  for (let i = 0;; i++) {
140
144
  const key = hashKeyWithIndex(_seed, i);
141
145
  // key should be in [0, limit)
142
146
  if (key < limit)
143
- return Fn.create(key).toString(16);
147
+ return (0, modular_js_1.mod)(key, CURVE_N).toString(16);
144
148
  }
145
149
  }
146
150
  exports.grindKey = grindKey;
@@ -158,22 +162,22 @@ exports.ethSigToPrivate = ethSigToPrivate;
158
162
  const MASK_31 = 2n ** 31n - 1n;
159
163
  const int31 = (n) => Number(n & MASK_31);
160
164
  function getAccountPath(layer, application, ethereumAddress, index) {
161
- const layerNum = int31(bytesToNumber0x((0, sha256_1.sha256)(layer)));
162
- const applicationNum = int31(bytesToNumber0x((0, sha256_1.sha256)(application)));
165
+ const layerNum = int31(sha256Num(layer));
166
+ const applicationNum = int31(sha256Num(application));
163
167
  const eth = hexToNumber0x(ethereumAddress);
164
168
  return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
165
169
  }
166
170
  exports.getAccountPath = getAccountPath;
167
171
  // https://docs.starkware.co/starkex/pedersen-hash-function.html
168
172
  const PEDERSEN_POINTS_AFFINE = [
169
- new Point(2089986280348253421170679821480865132823066470938446095505822317253594081284n, 1713931329540660377023406109199410414810705867260802078187082345529207694986n),
170
- new Point(996781205833008774514500082376783249102396023663454813447423147977397232763n, 1668503676786377725805489344771023921079126552019160156920634619255970485781n),
171
- new Point(2251563274489750535117886426533222435294046428347329203627021249169616184184n, 1798716007562728905295480679789526322175868328062420237419143593021674992973n),
172
- new Point(2138414695194151160943305727036575959195309218611738193261179310511854807447n, 113410276730064486255102093846540133784865286929052426931474106396135072156n),
173
- new Point(2379962749567351885752724891227938183011949129833673362440656643086021394946n, 776496453633298175483985398648758586525933812536653089401905292063708816422n),
173
+ new ProjectivePoint(2089986280348253421170679821480865132823066470938446095505822317253594081284n, 1713931329540660377023406109199410414810705867260802078187082345529207694986n, 1n),
174
+ new ProjectivePoint(996781205833008774514500082376783249102396023663454813447423147977397232763n, 1668503676786377725805489344771023921079126552019160156920634619255970485781n, 1n),
175
+ new ProjectivePoint(2251563274489750535117886426533222435294046428347329203627021249169616184184n, 1798716007562728905295480679789526322175868328062420237419143593021674992973n, 1n),
176
+ new ProjectivePoint(2138414695194151160943305727036575959195309218611738193261179310511854807447n, 113410276730064486255102093846540133784865286929052426931474106396135072156n, 1n),
177
+ new ProjectivePoint(2379962749567351885752724891227938183011949129833673362440656643086021394946n, 776496453633298175483985398648758586525933812536653089401905292063708816422n, 1n),
174
178
  ];
175
179
  // for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
176
- const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
180
+ const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
177
181
  function pedersenPrecompute(p1, p2) {
178
182
  const out = [];
179
183
  let p = p1;
@@ -212,7 +216,7 @@ function pedersenSingle(point, value, constants) {
212
216
  let x = pedersenArg(value);
213
217
  for (let j = 0; j < 252; j++) {
214
218
  const pt = constants[j];
215
- if (pt.x === point.x)
219
+ if (pt.px === point.px)
216
220
  throw new Error('Same point');
217
221
  if ((x & 1n) !== 0n)
218
222
  point = point.add(pt);
@@ -225,7 +229,7 @@ function pedersen(x, y) {
225
229
  let point = PEDERSEN_POINTS[0];
226
230
  point = pedersenSingle(point, x, PEDERSEN_POINTS1);
227
231
  point = pedersenSingle(point, y, PEDERSEN_POINTS2);
228
- return (0, exports.bytesToHexEth)(point.toAffine().toRawBytes(true).slice(1));
232
+ return (0, exports.bytesToHexEth)(point.toRawBytes(true).slice(1));
229
233
  }
230
234
  exports.pedersen = pedersen;
231
235
  function hashChain(data, fn = pedersen) {
@@ -241,6 +245,69 @@ exports.hashChain = hashChain;
241
245
  // Same as hashChain, but computes hash even for single element and order is not revesed
242
246
  const computeHashOnElements = (data, fn = pedersen) => [0, ...data, data.length].reduce((x, y) => fn(x, y));
243
247
  exports.computeHashOnElements = computeHashOnElements;
244
- const MASK_250 = 2n ** 250n - 1n;
248
+ const MASK_250 = cutils.bitMask(250);
245
249
  const keccak = (data) => bytesToNumber0x((0, sha3_1.keccak_256)(data)) & MASK_250;
246
250
  exports.keccak = keccak;
251
+ const sha256Num = (data) => cutils.bytesToNumberBE((0, sha256_1.sha256)(data));
252
+ // Poseidon hash
253
+ exports.Fp253 = (0, modular_js_1.Fp)(BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')); // 2^253 + 2^199 + 1
254
+ exports.Fp251 = (0, modular_js_1.Fp)(BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')); // 2^251 + 17 * 2^192 + 1
255
+ function poseidonRoundConstant(Fp, name, idx) {
256
+ const val = Fp.fromBytes((0, sha256_1.sha256)((0, utils_1.utf8ToBytes)(`${name}${idx}`)));
257
+ return Fp.create(val);
258
+ }
259
+ // NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
260
+ // Please use only if you really know what you doing.
261
+ // https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
262
+ function _poseidonMDS(Fp, name, m, attempt = 0) {
263
+ const x_values = [];
264
+ const y_values = [];
265
+ for (let i = 0; i < m; i++) {
266
+ x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
267
+ y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
268
+ }
269
+ if (new Set([...x_values, ...y_values]).size !== 2 * m)
270
+ throw new Error('X and Y values are not distinct');
271
+ return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
272
+ }
273
+ exports._poseidonMDS = _poseidonMDS;
274
+ const MDS_SMALL = [
275
+ [3, 1, 1],
276
+ [1, -1, 1],
277
+ [1, 1, -2],
278
+ ].map((i) => i.map(BigInt));
279
+ function poseidonBasic(opts, mds) {
280
+ (0, modular_js_1.validateField)(opts.Fp);
281
+ if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
282
+ throw new Error(`Wrong poseidon opts: ${opts}`);
283
+ const m = opts.rate + opts.capacity;
284
+ const rounds = opts.roundsFull + opts.roundsPartial;
285
+ const roundConstants = [];
286
+ for (let i = 0; i < rounds; i++) {
287
+ const row = [];
288
+ for (let j = 0; j < m; j++)
289
+ row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
290
+ roundConstants.push(row);
291
+ }
292
+ return poseidon.poseidon({
293
+ ...opts,
294
+ t: m,
295
+ sboxPower: 3,
296
+ reversePartialPowIdx: true,
297
+ mds,
298
+ roundConstants,
299
+ });
300
+ }
301
+ exports.poseidonBasic = poseidonBasic;
302
+ function poseidonCreate(opts, mdsAttempt = 0) {
303
+ const m = opts.rate + opts.capacity;
304
+ if (!Number.isSafeInteger(mdsAttempt))
305
+ throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
306
+ return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
307
+ }
308
+ exports.poseidonCreate = poseidonCreate;
309
+ exports.poseidonSmall = poseidonBasic({ Fp: exports.Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 }, MDS_SMALL);
310
+ function poseidonHash(x, y, fn = exports.poseidonSmall) {
311
+ return fn([x, y, 2n])[0];
312
+ }
313
+ exports.poseidonHash = poseidonHash;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@noble/curves",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "description": "Minimal, auditable JS implementation of elliptic curve cryptography",
5
5
  "files": [
6
6
  "lib"
7
7
  ],
8
8
  "scripts": {
9
- "bench": "node benchmark/index.js",
9
+ "bench": "cd benchmark; node secp256k1.js; node curves.js; node stark.js; node bls.js",
10
10
  "build": "tsc && tsc -p tsconfig.esm.json",
11
11
  "build:release": "rollup -c rollup.config.js",
12
12
  "lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
@@ -30,9 +30,9 @@
30
30
  "@scure/bip39": "~1.1.0",
31
31
  "@types/node": "18.11.3",
32
32
  "fast-check": "3.0.0",
33
- "micro-bmark": "0.2.0",
34
- "micro-should": "0.3.0",
35
- "prettier": "2.6.2",
33
+ "micro-bmark": "0.3.0",
34
+ "micro-should": "0.4.0",
35
+ "prettier": "2.8.3",
36
36
  "rollup": "2.75.5",
37
37
  "typescript": "4.7.3"
38
38
  },
@@ -73,16 +73,21 @@
73
73
  "import": "./lib/esm/abstract/hash-to-curve.js",
74
74
  "default": "./lib/abstract/hash-to-curve.js"
75
75
  },
76
- "./abstract/group": {
77
- "types": "./lib/abstract/group.d.ts",
78
- "import": "./lib/esm/abstract/group.js",
79
- "default": "./lib/abstract/group.js"
76
+ "./abstract/curve": {
77
+ "types": "./lib/abstract/curve.d.ts",
78
+ "import": "./lib/esm/abstract/curve.js",
79
+ "default": "./lib/abstract/curve.js"
80
80
  },
81
81
  "./abstract/utils": {
82
82
  "types": "./lib/abstract/utils.d.ts",
83
83
  "import": "./lib/esm/abstract/utils.js",
84
84
  "default": "./lib/abstract/utils.js"
85
85
  },
86
+ "./abstract/poseidon": {
87
+ "types": "./lib/abstract/poseidon.d.ts",
88
+ "import": "./lib/esm/abstract/poseidon.js",
89
+ "default": "./lib/abstract/poseidon.js"
90
+ },
86
91
  "./_shortw_utils": {
87
92
  "types": "./lib/_shortw_utils.d.ts",
88
93
  "import": "./lib/esm/_shortw_utils.js",
@@ -189,4 +194,4 @@
189
194
  "url": "https://paulmillr.com/funding/"
190
195
  }
191
196
  ]
192
- }
197
+ }