@noble/curves 0.4.0 → 0.5.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.
Files changed (72) hide show
  1. package/README.md +203 -162
  2. package/lib/_shortw_utils.d.ts +75 -0
  3. package/lib/_shortw_utils.js +20 -0
  4. package/lib/{bls.d.ts → abstract/bls.d.ts} +2 -1
  5. package/lib/{bls.js → abstract/bls.js} +28 -27
  6. package/lib/{edwards.d.ts → abstract/edwards.d.ts} +17 -0
  7. package/lib/{edwards.js → abstract/edwards.js} +45 -4
  8. package/lib/{group.d.ts → abstract/group.d.ts} +2 -1
  9. package/lib/{group.js → abstract/group.js} +4 -3
  10. package/lib/{hashToCurve.d.ts → abstract/hash-to-curve.d.ts} +6 -0
  11. package/lib/{hashToCurve.js → abstract/hash-to-curve.js} +15 -2
  12. package/lib/{modular.d.ts → abstract/modular.d.ts} +10 -4
  13. package/lib/{modular.js → abstract/modular.js} +110 -19
  14. package/lib/{montgomery.d.ts → abstract/montgomery.d.ts} +2 -1
  15. package/lib/{montgomery.js → abstract/montgomery.js} +17 -8
  16. package/lib/{utils.d.ts → abstract/utils.d.ts} +1 -1
  17. package/lib/{utils.js → abstract/utils.js} +1 -1
  18. package/lib/{weierstrass.d.ts → abstract/weierstrass.d.ts} +28 -16
  19. package/lib/{weierstrass.js → abstract/weierstrass.js} +261 -127
  20. package/lib/bls12-381.d.ts +66 -0
  21. package/lib/bls12-381.js +1132 -0
  22. package/lib/bn.d.ts +7 -0
  23. package/lib/bn.js +24 -0
  24. package/lib/ed25519.d.ts +48 -0
  25. package/lib/ed25519.js +322 -0
  26. package/lib/ed448.d.ts +3 -0
  27. package/lib/ed448.js +128 -0
  28. package/lib/esm/_shortw_utils.js +15 -0
  29. package/lib/esm/{bls.js → abstract/bls.js} +25 -24
  30. package/lib/esm/{edwards.js → abstract/edwards.js} +45 -4
  31. package/lib/esm/{group.js → abstract/group.js} +4 -3
  32. package/lib/esm/{hashToCurve.js → abstract/hash-to-curve.js} +13 -1
  33. package/lib/esm/{modular.js → abstract/modular.js} +108 -18
  34. package/lib/esm/{montgomery.js → abstract/montgomery.js} +17 -8
  35. package/lib/esm/{utils.js → abstract/utils.js} +1 -1
  36. package/lib/esm/{weierstrass.js → abstract/weierstrass.js} +255 -123
  37. package/lib/esm/bls12-381.js +1129 -0
  38. package/lib/esm/bn.js +21 -0
  39. package/lib/esm/ed25519.js +318 -0
  40. package/lib/esm/ed448.js +125 -0
  41. package/lib/esm/index.js +2 -0
  42. package/lib/esm/jubjub.js +52 -0
  43. package/lib/esm/p192.js +21 -0
  44. package/lib/esm/p224.js +21 -0
  45. package/lib/esm/p256.js +39 -0
  46. package/lib/esm/p384.js +44 -0
  47. package/lib/esm/p521.js +58 -0
  48. package/lib/esm/pasta.js +29 -0
  49. package/lib/esm/secp256k1.js +290 -0
  50. package/lib/esm/stark.js +222 -0
  51. package/lib/index.d.ts +0 -0
  52. package/lib/index.js +2 -0
  53. package/lib/jubjub.d.ts +7 -0
  54. package/lib/jubjub.js +57 -0
  55. package/lib/p192.d.ts +130 -0
  56. package/lib/p192.js +24 -0
  57. package/lib/p224.d.ts +130 -0
  58. package/lib/p224.js +24 -0
  59. package/lib/p256.d.ts +130 -0
  60. package/lib/p256.js +42 -0
  61. package/lib/p384.d.ts +130 -0
  62. package/lib/p384.js +47 -0
  63. package/lib/p521.d.ts +131 -0
  64. package/lib/p521.js +61 -0
  65. package/lib/pasta.d.ts +4 -0
  66. package/lib/pasta.js +32 -0
  67. package/lib/secp256k1.d.ts +96 -0
  68. package/lib/secp256k1.js +294 -0
  69. package/lib/stark.d.ts +72 -0
  70. package/lib/stark.js +243 -0
  71. package/package.json +146 -50
  72. package/index.js +0 -1
@@ -0,0 +1,58 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { createCurve } from './_shortw_utils.js';
3
+ import { sha512 } from '@noble/hashes/sha512';
4
+ import { bytesToHex } from './abstract/utils.js';
5
+ import { Fp as Field } from './abstract/modular.js';
6
+ import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
7
+ // NIST secp521r1 aka P521
8
+ // Note that it's 521, which differs from 512 of its hash function.
9
+ // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
10
+ // Field over which we'll do calculations; 2n**521n - 1n
11
+ // prettier-ignore
12
+ const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
13
+ const Fp = Field(P);
14
+ const CURVE_A = Fp.create(BigInt('-3'));
15
+ // prettier-ignore
16
+ const CURVE_B = BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00');
17
+ const mapSWU = mapToCurveSimpleSWU(Fp, {
18
+ A: CURVE_A,
19
+ B: CURVE_B,
20
+ Z: Fp.create(BigInt('-4')),
21
+ });
22
+ // prettier-ignore
23
+ export const P521 = createCurve({
24
+ // Params: a, b
25
+ a: CURVE_A,
26
+ b: CURVE_B,
27
+ Fp,
28
+ // Curve order, total count of valid points in the field
29
+ n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
30
+ // Base point (x, y) aka generator point
31
+ Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
32
+ Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
33
+ h: BigInt(1),
34
+ lowS: false,
35
+ // P521 keys could be 130, 131, 132 bytes - which doesn't play nicely.
36
+ // We ensure all keys are 132 bytes.
37
+ // Does not replace validation; invalid keys would still be rejected.
38
+ normalizePrivateKey(key) {
39
+ if (typeof key === 'bigint')
40
+ return key;
41
+ if (key instanceof Uint8Array)
42
+ key = bytesToHex(key);
43
+ if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
44
+ throw new Error('Invalid key');
45
+ }
46
+ return key.padStart(66 * 2, '0');
47
+ },
48
+ mapToCurve: (scalars) => mapSWU(scalars[0]),
49
+ htfDefaults: {
50
+ DST: 'P521_XMD:SHA-512_SSWU_RO_',
51
+ p: Fp.ORDER,
52
+ m: 1,
53
+ k: 256,
54
+ expand: true,
55
+ hash: sha512,
56
+ },
57
+ }, sha512);
58
+ export const secp521r1 = P521;
@@ -0,0 +1,29 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { sha256 } from '@noble/hashes/sha256';
3
+ import { weierstrass } from './abstract/weierstrass.js';
4
+ import { getHash } from './_shortw_utils.js';
5
+ import * as mod from './abstract/modular.js';
6
+ export const p = BigInt('0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001');
7
+ export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001');
8
+ // https://neuromancer.sk/std/other/Pallas
9
+ export const pallas = weierstrass({
10
+ a: BigInt(0),
11
+ b: BigInt(5),
12
+ Fp: mod.Fp(p),
13
+ n: q,
14
+ Gx: mod.mod(BigInt(-1), p),
15
+ Gy: BigInt(2),
16
+ h: BigInt(1),
17
+ ...getHash(sha256),
18
+ });
19
+ // https://neuromancer.sk/std/other/Vesta
20
+ export const vesta = weierstrass({
21
+ a: BigInt(0),
22
+ b: BigInt(5),
23
+ Fp: mod.Fp(q),
24
+ n: p,
25
+ Gx: mod.mod(BigInt(-1), q),
26
+ Gy: BigInt(2),
27
+ h: BigInt(1),
28
+ ...getHash(sha256),
29
+ });
@@ -0,0 +1,290 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { sha256 } from '@noble/hashes/sha256';
3
+ import { Fp as Field, mod, pow2 } from './abstract/modular.js';
4
+ import { createCurve } from './_shortw_utils.js';
5
+ import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
6
+ import { ensureBytes, concatBytes, hexToBytes, bytesToNumberBE, } from './abstract/utils.js';
7
+ import { randomBytes } from '@noble/hashes/utils';
8
+ import { isogenyMap } from './abstract/hash-to-curve.js';
9
+ /**
10
+ * secp256k1 belongs to Koblitz curves: it has
11
+ * efficiently computable Frobenius endomorphism.
12
+ * Endomorphism improves efficiency:
13
+ * Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
14
+ * Should always be used for Projective's double-and-add multiplication.
15
+ * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
16
+ * https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
17
+ */
18
+ const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
19
+ const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
20
+ const _1n = BigInt(1);
21
+ const _2n = BigInt(2);
22
+ const divNearest = (a, b) => (a + b / _2n) / b;
23
+ /**
24
+ * Allows to compute square root √y 2x faster.
25
+ * To calculate √y, we need to exponentiate it to a very big number:
26
+ * `y² = x³ + ax + b; y = y² ^ (p+1)/4`
27
+ * We are unwrapping the loop and multiplying it bit-by-bit.
28
+ * (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
29
+ */
30
+ function sqrtMod(y) {
31
+ const P = secp256k1P;
32
+ // prettier-ignore
33
+ const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
34
+ // prettier-ignore
35
+ const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
36
+ const b2 = (y * y * y) % P; // x^3, 11
37
+ const b3 = (b2 * b2 * y) % P; // x^7
38
+ const b6 = (pow2(b3, _3n, P) * b3) % P;
39
+ const b9 = (pow2(b6, _3n, P) * b3) % P;
40
+ const b11 = (pow2(b9, _2n, P) * b2) % P;
41
+ const b22 = (pow2(b11, _11n, P) * b11) % P;
42
+ const b44 = (pow2(b22, _22n, P) * b22) % P;
43
+ const b88 = (pow2(b44, _44n, P) * b44) % P;
44
+ const b176 = (pow2(b88, _88n, P) * b88) % P;
45
+ const b220 = (pow2(b176, _44n, P) * b44) % P;
46
+ const b223 = (pow2(b220, _3n, P) * b3) % P;
47
+ const t1 = (pow2(b223, _23n, P) * b22) % P;
48
+ const t2 = (pow2(t1, _6n, P) * b2) % P;
49
+ return pow2(t2, _2n, P);
50
+ }
51
+ const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
52
+ const isoMap = isogenyMap(Fp, [
53
+ // xNum
54
+ [
55
+ '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
56
+ '0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
57
+ '0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
58
+ '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
59
+ ],
60
+ // xDen
61
+ [
62
+ '0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
63
+ '0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
64
+ '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
65
+ ],
66
+ // yNum
67
+ [
68
+ '0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
69
+ '0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
70
+ '0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
71
+ '0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
72
+ ],
73
+ // yDen
74
+ [
75
+ '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
76
+ '0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
77
+ '0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
78
+ '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
79
+ ],
80
+ ].map((i) => i.map((j) => BigInt(j))));
81
+ const mapSWU = mapToCurveSimpleSWU(Fp, {
82
+ A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
83
+ B: BigInt('1771'),
84
+ Z: Fp.create(BigInt('-11')),
85
+ });
86
+ export const secp256k1 = createCurve({
87
+ // Params: a, b
88
+ // Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
89
+ a: BigInt(0),
90
+ b: BigInt(7),
91
+ // Field over which we'll do calculations;
92
+ // 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
93
+ Fp,
94
+ // Curve order, total count of valid points in the field
95
+ n: secp256k1N,
96
+ // Base point (x, y) aka generator point
97
+ Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
98
+ Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
99
+ h: BigInt(1),
100
+ // Alllow only low-S signatures by default in sign() and verify()
101
+ lowS: true,
102
+ endo: {
103
+ // Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
104
+ beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
105
+ splitScalar: (k) => {
106
+ const n = secp256k1N;
107
+ const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
108
+ const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
109
+ const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
110
+ const b2 = a1;
111
+ const POW_2_128 = BigInt('0x100000000000000000000000000000000');
112
+ const c1 = divNearest(b2 * k, n);
113
+ const c2 = divNearest(-b1 * k, n);
114
+ let k1 = mod(k - c1 * a1 - c2 * a2, n);
115
+ let k2 = mod(-c1 * b1 - c2 * b2, n);
116
+ const k1neg = k1 > POW_2_128;
117
+ const k2neg = k2 > POW_2_128;
118
+ if (k1neg)
119
+ k1 = n - k1;
120
+ if (k2neg)
121
+ k2 = n - k2;
122
+ if (k1 > POW_2_128 || k2 > POW_2_128) {
123
+ throw new Error('splitScalar: Endomorphism failed, k=' + k);
124
+ }
125
+ return { k1neg, k1, k2neg, k2 };
126
+ },
127
+ },
128
+ mapToCurve: (scalars) => {
129
+ const { x, y } = mapSWU(Fp.create(scalars[0]));
130
+ return isoMap(x, y);
131
+ },
132
+ htfDefaults: {
133
+ DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
134
+ p: Fp.ORDER,
135
+ m: 1,
136
+ k: 128,
137
+ expand: true,
138
+ hash: sha256,
139
+ },
140
+ }, sha256);
141
+ // Schnorr
142
+ const _0n = BigInt(0);
143
+ const numTo32b = secp256k1.utils._bigintToBytes;
144
+ const numTo32bStr = secp256k1.utils._bigintToString;
145
+ const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
146
+ // TODO: export?
147
+ function normalizePublicKey(publicKey) {
148
+ if (publicKey instanceof secp256k1.Point) {
149
+ publicKey.assertValidity();
150
+ return publicKey;
151
+ }
152
+ else {
153
+ const bytes = ensureBytes(publicKey);
154
+ // Schnorr is 32 bytes
155
+ if (bytes.length === 32) {
156
+ const x = bytesToNumberBE(bytes);
157
+ if (!isValidFieldElement(x))
158
+ throw new Error('Point is not on curve');
159
+ const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
160
+ let y = sqrtMod(y2); // y = y² ^ (p+1)/4
161
+ const isYOdd = (y & _1n) === _1n;
162
+ // Schnorr
163
+ if (isYOdd)
164
+ y = secp256k1.CURVE.Fp.negate(y);
165
+ const point = new secp256k1.Point(x, y);
166
+ point.assertValidity();
167
+ return point;
168
+ }
169
+ // Do we need that in schnorr at all?
170
+ return secp256k1.Point.fromHex(publicKey);
171
+ }
172
+ }
173
+ const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
174
+ const isValidFieldElement = secp256k1.utils._isValidFieldElement;
175
+ const TAGS = {
176
+ challenge: 'BIP0340/challenge',
177
+ aux: 'BIP0340/aux',
178
+ nonce: 'BIP0340/nonce',
179
+ };
180
+ /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
181
+ const TAGGED_HASH_PREFIXES = {};
182
+ export function taggedHash(tag, ...messages) {
183
+ let tagP = TAGGED_HASH_PREFIXES[tag];
184
+ if (tagP === undefined) {
185
+ const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
186
+ tagP = concatBytes(tagH, tagH);
187
+ TAGGED_HASH_PREFIXES[tag] = tagP;
188
+ }
189
+ return sha256(concatBytes(tagP, ...messages));
190
+ }
191
+ const toRawX = (point) => point.toRawBytes(true).slice(1);
192
+ // Schnorr signatures are superior to ECDSA from above.
193
+ // Below is Schnorr-specific code as per BIP0340.
194
+ function schnorrChallengeFinalize(ch) {
195
+ return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
196
+ }
197
+ // Do we need this at all for Schnorr?
198
+ class SchnorrSignature {
199
+ constructor(r, s) {
200
+ this.r = r;
201
+ this.s = s;
202
+ this.assertValidity();
203
+ }
204
+ static fromHex(hex) {
205
+ const bytes = ensureBytes(hex);
206
+ if (bytes.length !== 64)
207
+ throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
208
+ const r = bytesToNumberBE(bytes.subarray(0, 32));
209
+ const s = bytesToNumberBE(bytes.subarray(32, 64));
210
+ return new SchnorrSignature(r, s);
211
+ }
212
+ assertValidity() {
213
+ const { r, s } = this;
214
+ if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
215
+ throw new Error('Invalid signature');
216
+ }
217
+ toHex() {
218
+ return numTo32bStr(this.r) + numTo32bStr(this.s);
219
+ }
220
+ toRawBytes() {
221
+ return hexToBytes(this.toHex());
222
+ }
223
+ }
224
+ function schnorrGetScalar(priv) {
225
+ const point = secp256k1.Point.fromPrivateKey(priv);
226
+ const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
227
+ return { point, scalar, x: toRawX(point) };
228
+ }
229
+ /**
230
+ * Synchronously creates Schnorr signature. Improved security: verifies itself before
231
+ * producing an output.
232
+ * @param msg message (not message hash)
233
+ * @param privateKey private key
234
+ * @param auxRand random bytes that would be added to k. Bad RNG won't break it.
235
+ */
236
+ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
237
+ if (message == null)
238
+ throw new TypeError(`sign: Expected valid message, not "${message}"`);
239
+ const m = ensureBytes(message);
240
+ // checks for isWithinCurveOrder
241
+ const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
242
+ const rand = ensureBytes(auxRand);
243
+ if (rand.length !== 32)
244
+ throw new TypeError('sign: Expected 32 bytes of aux randomness');
245
+ const tag = taggedHash;
246
+ const t0h = tag(TAGS.aux, rand);
247
+ const t = numTo32b(d ^ bytesToNumberBE(t0h));
248
+ const k0h = tag(TAGS.nonce, t, px, m);
249
+ const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
250
+ if (k0 === _0n)
251
+ throw new Error('sign: Creation of signature failed. k is zero');
252
+ const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
253
+ const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
254
+ const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
255
+ if (!schnorrVerify(sig, m, px))
256
+ throw new Error('sign: Invalid signature produced');
257
+ return sig;
258
+ }
259
+ /**
260
+ * Verifies Schnorr signature synchronously.
261
+ */
262
+ function schnorrVerify(signature, message, publicKey) {
263
+ try {
264
+ const raw = signature instanceof SchnorrSignature;
265
+ const sig = raw ? signature : SchnorrSignature.fromHex(signature);
266
+ if (raw)
267
+ sig.assertValidity(); // just in case
268
+ const { r, s } = sig;
269
+ const m = ensureBytes(message);
270
+ const P = normalizePublicKey(publicKey);
271
+ const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
272
+ // Finalize
273
+ // R = s⋅G - e⋅P
274
+ // -eP == (n-e)P
275
+ const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, secp256k1.CURVE.n));
276
+ if (!R || !R.hasEvenY() || R.x !== r)
277
+ return false;
278
+ return true;
279
+ }
280
+ catch (error) {
281
+ return false;
282
+ }
283
+ }
284
+ export const schnorr = {
285
+ Signature: SchnorrSignature,
286
+ // Schnorr's pubkey is just `x` of Point (BIP340)
287
+ getPublicKey: (privateKey) => toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
288
+ sign: schnorrSign,
289
+ verify: schnorrVerify,
290
+ };
@@ -0,0 +1,222 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { keccak_256 } from '@noble/hashes/sha3';
3
+ import { sha256 } from '@noble/hashes/sha256';
4
+ import { weierstrass } from './abstract/weierstrass.js';
5
+ import * as cutils from './abstract/utils.js';
6
+ import { Fp } from './abstract/modular.js';
7
+ import { getHash } from './_shortw_utils.js';
8
+ // Stark-friendly elliptic curve
9
+ // https://docs.starkware.co/starkex/stark-curve.html
10
+ const CURVE_N = BigInt('3618502788666131213697322783095070105526743751716087489154079457884512865583');
11
+ const nBitLength = 252;
12
+ export const starkCurve = weierstrass({
13
+ // Params: a, b
14
+ a: BigInt(1),
15
+ b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
16
+ // Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
17
+ // There is no efficient sqrt for field (P%4==1)
18
+ Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
19
+ // Curve order, total count of valid points in the field.
20
+ n: CURVE_N,
21
+ nBitLength: nBitLength,
22
+ // Base point (x, y) aka generator point
23
+ Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
24
+ Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
25
+ h: BigInt(1),
26
+ // Default options
27
+ lowS: false,
28
+ ...getHash(sha256),
29
+ truncateHash: (hash, truncateOnly = false) => {
30
+ // TODO: cleanup, ugly code
31
+ // Fix truncation
32
+ if (!truncateOnly) {
33
+ let hashS = bytesToNumber0x(hash).toString(16);
34
+ if (hashS.length === 63) {
35
+ hashS += '0';
36
+ hash = hexToBytes0x(hashS);
37
+ }
38
+ }
39
+ // Truncate zero bytes on left (compat with elliptic)
40
+ while (hash[0] === 0)
41
+ hash = hash.subarray(1);
42
+ const byteLength = hash.length;
43
+ const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
44
+ let h = hash.length ? bytesToNumber0x(hash) : 0n;
45
+ if (delta > 0)
46
+ h = h >> BigInt(delta);
47
+ if (!truncateOnly && h >= CURVE_N)
48
+ h -= CURVE_N;
49
+ return h;
50
+ },
51
+ });
52
+ // Custom Starknet type conversion functions that can handle 0x and unpadded hex
53
+ function hexToBytes0x(hex) {
54
+ if (typeof hex !== 'string') {
55
+ throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
56
+ }
57
+ hex = strip0x(hex);
58
+ if (hex.length & 1)
59
+ hex = '0' + hex; // padding
60
+ if (hex.length % 2)
61
+ throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
62
+ const array = new Uint8Array(hex.length / 2);
63
+ for (let i = 0; i < array.length; i++) {
64
+ const j = i * 2;
65
+ const hexByte = hex.slice(j, j + 2);
66
+ const byte = Number.parseInt(hexByte, 16);
67
+ if (Number.isNaN(byte) || byte < 0)
68
+ throw new Error('Invalid byte sequence');
69
+ array[i] = byte;
70
+ }
71
+ return array;
72
+ }
73
+ function hexToNumber0x(hex) {
74
+ if (typeof hex !== 'string') {
75
+ throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
76
+ }
77
+ // Big Endian
78
+ // TODO: strip vs no strip?
79
+ return BigInt(`0x${strip0x(hex)}`);
80
+ }
81
+ function bytesToNumber0x(bytes) {
82
+ return hexToNumber0x(cutils.bytesToHex(bytes));
83
+ }
84
+ function ensureBytes0x(hex) {
85
+ // Uint8Array.from() instead of hash.slice() because node.js Buffer
86
+ // is instance of Uint8Array, and its slice() creates **mutable** copy
87
+ return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
88
+ }
89
+ function normalizePrivateKey(privKey) {
90
+ return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
91
+ }
92
+ function getPublicKey0x(privKey, isCompressed) {
93
+ return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
94
+ }
95
+ function getSharedSecret0x(privKeyA, pubKeyB) {
96
+ return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
97
+ }
98
+ function sign0x(msgHash, privKey, opts) {
99
+ if (typeof privKey === 'string')
100
+ privKey = strip0x(privKey).padStart(64, '0');
101
+ return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
102
+ }
103
+ function verify0x(signature, msgHash, pubKey) {
104
+ const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
105
+ return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
106
+ }
107
+ const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
108
+ export const utils = starkCurve.utils;
109
+ export { CURVE, Point, Signature, ProjectivePoint, getPublicKey0x as getPublicKey, getSharedSecret0x as getSharedSecret, sign0x as sign, verify0x as verify, };
110
+ const stripLeadingZeros = (s) => s.replace(/^0+/gm, '');
111
+ export const bytesToHexEth = (uint8a) => `0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
112
+ export const strip0x = (hex) => hex.replace(/^0x/i, '');
113
+ export const numberToHexEth = (num) => `0x${num.toString(16)}`;
114
+ // 1. seed generation
115
+ function hashKeyWithIndex(key, index) {
116
+ let indexHex = cutils.numberToHexUnpadded(index);
117
+ if (indexHex.length & 1)
118
+ indexHex = '0' + indexHex;
119
+ return bytesToNumber0x(sha256(cutils.concatBytes(key, hexToBytes0x(indexHex))));
120
+ }
121
+ export function grindKey(seed) {
122
+ const _seed = ensureBytes0x(seed);
123
+ const sha256mask = 2n ** 256n;
124
+ const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
125
+ for (let i = 0;; i++) {
126
+ const key = hashKeyWithIndex(_seed, i);
127
+ // key should be in [0, limit)
128
+ if (key < limit)
129
+ return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
130
+ }
131
+ }
132
+ export function getStarkKey(privateKey) {
133
+ return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
134
+ }
135
+ export function ethSigToPrivate(signature) {
136
+ signature = strip0x(signature.replace(/^0x/, ''));
137
+ if (signature.length !== 130)
138
+ throw new Error('Wrong ethereum signature');
139
+ return grindKey(signature.substring(0, 64));
140
+ }
141
+ const MASK_31 = 2n ** 31n - 1n;
142
+ const int31 = (n) => Number(n & MASK_31);
143
+ export function getAccountPath(layer, application, ethereumAddress, index) {
144
+ const layerNum = int31(bytesToNumber0x(sha256(layer)));
145
+ const applicationNum = int31(bytesToNumber0x(sha256(application)));
146
+ const eth = hexToNumber0x(ethereumAddress);
147
+ return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
148
+ }
149
+ // https://docs.starkware.co/starkex/pedersen-hash-function.html
150
+ const PEDERSEN_POINTS_AFFINE = [
151
+ new Point(2089986280348253421170679821480865132823066470938446095505822317253594081284n, 1713931329540660377023406109199410414810705867260802078187082345529207694986n),
152
+ new Point(996781205833008774514500082376783249102396023663454813447423147977397232763n, 1668503676786377725805489344771023921079126552019160156920634619255970485781n),
153
+ new Point(2251563274489750535117886426533222435294046428347329203627021249169616184184n, 1798716007562728905295480679789526322175868328062420237419143593021674992973n),
154
+ new Point(2138414695194151160943305727036575959195309218611738193261179310511854807447n, 113410276730064486255102093846540133784865286929052426931474106396135072156n),
155
+ new Point(2379962749567351885752724891227938183011949129833673362440656643086021394946n, 776496453633298175483985398648758586525933812536653089401905292063708816422n),
156
+ ];
157
+ // for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
158
+ const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
159
+ function pedersenPrecompute(p1, p2) {
160
+ const out = [];
161
+ let p = p1;
162
+ for (let i = 0; i < 248; i++) {
163
+ out.push(p);
164
+ p = p.double();
165
+ }
166
+ p = p2;
167
+ for (let i = 0; i < 4; i++) {
168
+ out.push(p);
169
+ p = p.double();
170
+ }
171
+ return out;
172
+ }
173
+ const PEDERSEN_POINTS1 = pedersenPrecompute(PEDERSEN_POINTS[1], PEDERSEN_POINTS[2]);
174
+ const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[4]);
175
+ function pedersenArg(arg) {
176
+ let value;
177
+ if (typeof arg === 'bigint')
178
+ value = arg;
179
+ else if (typeof arg === 'number') {
180
+ if (!Number.isSafeInteger(arg))
181
+ throw new Error(`Invalid pedersenArg: ${arg}`);
182
+ value = BigInt(arg);
183
+ }
184
+ else
185
+ value = bytesToNumber0x(ensureBytes0x(arg));
186
+ // [0..Fp)
187
+ if (!(0n <= value && value < starkCurve.CURVE.Fp.ORDER))
188
+ throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
189
+ return value;
190
+ }
191
+ function pedersenSingle(point, value, constants) {
192
+ let x = pedersenArg(value);
193
+ for (let j = 0; j < 252; j++) {
194
+ const pt = constants[j];
195
+ if (pt.x === point.x)
196
+ throw new Error('Same point');
197
+ if ((x & 1n) !== 0n)
198
+ point = point.add(pt);
199
+ x >>= 1n;
200
+ }
201
+ return point;
202
+ }
203
+ // shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
204
+ export function pedersen(x, y) {
205
+ let point = PEDERSEN_POINTS[0];
206
+ point = pedersenSingle(point, x, PEDERSEN_POINTS1);
207
+ point = pedersenSingle(point, y, PEDERSEN_POINTS2);
208
+ return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
209
+ }
210
+ export function hashChain(data, fn = pedersen) {
211
+ if (!Array.isArray(data) || data.length < 1)
212
+ throw new Error('data should be array of at least 1 element');
213
+ if (data.length === 1)
214
+ return numberToHexEth(pedersenArg(data[0]));
215
+ return Array.from(data)
216
+ .reverse()
217
+ .reduce((acc, i) => fn(i, acc));
218
+ }
219
+ // Same as hashChain, but computes hash even for single element and order is not revesed
220
+ export const computeHashOnElements = (data, fn = pedersen) => [0, ...data, data.length].reduce((x, y) => fn(x, y));
221
+ const MASK_250 = 2n ** 250n - 1n;
222
+ export const keccak = (data) => bytesToNumber0x(keccak_256(data)) & MASK_250;
package/lib/index.d.ts ADDED
File without changes
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ throw new Error('Incorrect usage. Import submodules instead');
@@ -0,0 +1,7 @@
1
+ /**
2
+ * jubjub Twisted Edwards curve.
3
+ * https://neuromancer.sk/std/other/JubJub
4
+ */
5
+ export declare const jubjub: import("./abstract/edwards.js").CurveFn;
6
+ export declare function groupHash(tag: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").ExtendedPointType;
7
+ export declare function findGroupHash(m: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").ExtendedPointType;
package/lib/jubjub.js ADDED
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findGroupHash = exports.groupHash = exports.jubjub = void 0;
4
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
+ const sha256_1 = require("@noble/hashes/sha256");
6
+ const utils_1 = require("@noble/hashes/utils");
7
+ const edwards_js_1 = require("./abstract/edwards.js");
8
+ const blake2s_1 = require("@noble/hashes/blake2s");
9
+ const modular_js_1 = require("./abstract/modular.js");
10
+ /**
11
+ * jubjub Twisted Edwards curve.
12
+ * https://neuromancer.sk/std/other/JubJub
13
+ */
14
+ exports.jubjub = (0, edwards_js_1.twistedEdwards)({
15
+ // Params: a, d
16
+ a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
17
+ d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
18
+ // Finite field 𝔽p over which we'll do calculations
19
+ Fp: (0, modular_js_1.Fp)(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
20
+ // Subgroup order: how many points ed25519 has
21
+ // 2n ** 252n + 27742317777372353535851937790883648493n;
22
+ n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
23
+ // Cofactor
24
+ h: BigInt(8),
25
+ // Base point (x, y) aka generator point
26
+ Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
27
+ Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
28
+ hash: sha256_1.sha256,
29
+ randomBytes: utils_1.randomBytes,
30
+ });
31
+ const GH_FIRST_BLOCK = (0, utils_1.utf8ToBytes)('096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0');
32
+ // Returns point at JubJub curve which is prime order and not zero
33
+ function groupHash(tag, personalization) {
34
+ const h = blake2s_1.blake2s.create({ personalization, dkLen: 32 });
35
+ h.update(GH_FIRST_BLOCK);
36
+ h.update(tag);
37
+ // NOTE: returns ExtendedPoint, in case it will be multiplied later
38
+ let p = exports.jubjub.ExtendedPoint.fromAffine(exports.jubjub.Point.fromHex(h.digest()));
39
+ // NOTE: cannot replace with isSmallOrder, returns Point*8
40
+ p = p.multiply(exports.jubjub.CURVE.h);
41
+ if (p.equals(exports.jubjub.ExtendedPoint.ZERO))
42
+ throw new Error('Point has small order');
43
+ return p;
44
+ }
45
+ exports.groupHash = groupHash;
46
+ function findGroupHash(m, personalization) {
47
+ const tag = (0, utils_1.concatBytes)(m, new Uint8Array([0]));
48
+ for (let i = 0; i < 256; i++) {
49
+ tag[tag.length - 1] = i;
50
+ try {
51
+ return groupHash(tag, personalization);
52
+ }
53
+ catch (e) { }
54
+ }
55
+ throw new Error('findGroupHash tag overflow');
56
+ }
57
+ exports.findGroupHash = findGroupHash;