@noble/curves 0.2.1 → 0.3.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 (62) hide show
  1. package/README.md +14 -19
  2. package/lib/crypto.d.ts +4 -0
  3. package/lib/crypto.js +8 -0
  4. package/lib/cryptoBrowser.d.ts +4 -0
  5. package/lib/cryptoBrowser.js +7 -0
  6. package/lib/definitions/_shortw_utils.d.ts +63 -0
  7. package/lib/definitions/_shortw_utils.js +18 -0
  8. package/lib/definitions/bn.d.ts +7 -0
  9. package/lib/definitions/bn.js +23 -0
  10. package/lib/definitions/ed25519.d.ts +49 -0
  11. package/lib/definitions/ed25519.js +308 -0
  12. package/lib/definitions/ed448.d.ts +3 -0
  13. package/lib/definitions/ed448.js +127 -0
  14. package/lib/definitions/index.d.ts +0 -0
  15. package/lib/definitions/index.js +2 -0
  16. package/lib/definitions/jubjub.d.ts +7 -0
  17. package/lib/definitions/jubjub.js +55 -0
  18. package/lib/definitions/p192.d.ts +112 -0
  19. package/lib/definitions/p192.js +23 -0
  20. package/lib/definitions/p224.d.ts +112 -0
  21. package/lib/definitions/p224.js +24 -0
  22. package/lib/definitions/p256.d.ts +112 -0
  23. package/lib/definitions/p256.js +23 -0
  24. package/lib/definitions/p384.d.ts +112 -0
  25. package/lib/definitions/p384.js +24 -0
  26. package/lib/definitions/p521.d.ts +113 -0
  27. package/lib/definitions/p521.js +36 -0
  28. package/lib/definitions/pasta.d.ts +2 -0
  29. package/lib/definitions/pasta.js +32 -0
  30. package/lib/definitions/secp256k1.d.ts +87 -0
  31. package/lib/definitions/secp256k1.js +245 -0
  32. package/lib/definitions/stark.d.ts +62 -0
  33. package/lib/definitions/stark.js +248 -0
  34. package/lib/edwards.d.ts +2 -2
  35. package/lib/edwards.js +2 -6
  36. package/lib/esm/crypto.js +5 -0
  37. package/lib/esm/cryptoBrowser.js +4 -0
  38. package/lib/esm/definitions/_shortw_utils.js +13 -0
  39. package/lib/esm/definitions/bn.js +20 -0
  40. package/lib/esm/definitions/ed25519.js +304 -0
  41. package/lib/esm/definitions/ed448.js +124 -0
  42. package/lib/esm/definitions/index.js +2 -0
  43. package/lib/esm/definitions/jubjub.js +50 -0
  44. package/lib/esm/definitions/p192.js +20 -0
  45. package/lib/esm/definitions/p224.js +21 -0
  46. package/lib/esm/definitions/p256.js +20 -0
  47. package/lib/esm/definitions/p384.js +21 -0
  48. package/lib/esm/definitions/p521.js +33 -0
  49. package/lib/esm/definitions/pasta.js +29 -0
  50. package/lib/esm/definitions/secp256k1.js +241 -0
  51. package/lib/esm/definitions/stark.js +227 -0
  52. package/lib/esm/edwards.js +3 -7
  53. package/lib/esm/modular.js +14 -9
  54. package/lib/esm/utils.js +17 -0
  55. package/lib/esm/weierstrass.js +18 -9
  56. package/lib/modular.d.ts +8 -1
  57. package/lib/modular.js +14 -9
  58. package/lib/utils.d.ts +4 -0
  59. package/lib/utils.js +19 -1
  60. package/lib/weierstrass.d.ts +5 -3
  61. package/lib/weierstrass.js +17 -8
  62. package/package.json +28 -8
@@ -0,0 +1,241 @@
1
+ /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { sha256 } from '@noble/hashes/sha256';
3
+ import { mod, pow2 } from '../modular';
4
+ import { createCurve } from './_shortw_utils.js';
5
+ import { ensureBytes, concatBytes, hexToBytes, bytesToNumberBE, randomBytes } from '../utils';
6
+ /**
7
+ * secp256k1 belongs to Koblitz curves: it has
8
+ * efficiently computable Frobenius endomorphism.
9
+ * Endomorphism improves efficiency:
10
+ * Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
11
+ * Should always be used for Jacobian's double-and-add multiplication.
12
+ * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
13
+ * https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
14
+ */
15
+ const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
16
+ const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
17
+ const _1n = BigInt(1);
18
+ const _2n = BigInt(2);
19
+ const divNearest = (a, b) => (a + b / _2n) / b;
20
+ /**
21
+ * Allows to compute square root √y 2x faster.
22
+ * To calculate √y, we need to exponentiate it to a very big number:
23
+ * `y² = x³ + ax + b; y = y² ^ (p+1)/4`
24
+ * We are unwrapping the loop and multiplying it bit-by-bit.
25
+ * (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
26
+ */
27
+ // prettier-ignore
28
+ function sqrtMod(y) {
29
+ const P = secp256k1P;
30
+ const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11);
31
+ const _22n = BigInt(22);
32
+ const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
33
+ const b2 = (y * y * y) % P; // x^3, 11
34
+ const b3 = (b2 * b2 * y) % P; // x^7
35
+ const b6 = (pow2(b3, _3n, P) * b3) % P;
36
+ const b9 = (pow2(b6, _3n, P) * b3) % P;
37
+ const b11 = (pow2(b9, _2n, P) * b2) % P;
38
+ const b22 = (pow2(b11, _11n, P) * b11) % P;
39
+ const b44 = (pow2(b22, _22n, P) * b22) % P;
40
+ const b88 = (pow2(b44, _44n, P) * b44) % P;
41
+ const b176 = (pow2(b88, _88n, P) * b88) % P;
42
+ const b220 = (pow2(b176, _44n, P) * b44) % P;
43
+ const b223 = (pow2(b220, _3n, P) * b3) % P;
44
+ const t1 = (pow2(b223, _23n, P) * b22) % P;
45
+ const t2 = (pow2(t1, _6n, P) * b2) % P;
46
+ return pow2(t2, _2n, P);
47
+ }
48
+ export const secp256k1 = createCurve({
49
+ // Params: a, b
50
+ // Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
51
+ a: BigInt(0),
52
+ b: BigInt(7),
53
+ // Field over which we'll do calculations;
54
+ // 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
55
+ P: secp256k1P,
56
+ // Curve order, total count of valid points in the field
57
+ n: secp256k1N,
58
+ // Base point (x, y) aka generator point
59
+ Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
60
+ Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
61
+ h: BigInt(1),
62
+ // Alllow only low-S signatures by default in sign() and verify()
63
+ lowS: true,
64
+ sqrtMod,
65
+ endo: {
66
+ // Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
67
+ beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
68
+ splitScalar: (k) => {
69
+ const n = secp256k1N;
70
+ const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
71
+ const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
72
+ const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
73
+ const b2 = a1;
74
+ const POW_2_128 = BigInt('0x100000000000000000000000000000000');
75
+ const c1 = divNearest(b2 * k, n);
76
+ const c2 = divNearest(-b1 * k, n);
77
+ let k1 = mod(k - c1 * a1 - c2 * a2, n);
78
+ let k2 = mod(-c1 * b1 - c2 * b2, n);
79
+ const k1neg = k1 > POW_2_128;
80
+ const k2neg = k2 > POW_2_128;
81
+ if (k1neg)
82
+ k1 = n - k1;
83
+ if (k2neg)
84
+ k2 = n - k2;
85
+ if (k1 > POW_2_128 || k2 > POW_2_128) {
86
+ throw new Error('splitScalar: Endomorphism failed, k=' + k);
87
+ }
88
+ return { k1neg, k1, k2neg, k2 };
89
+ },
90
+ },
91
+ }, sha256);
92
+ // Schnorr
93
+ const _0n = BigInt(0);
94
+ const numTo32b = secp256k1.utils._bigintToBytes;
95
+ const numTo32bStr = secp256k1.utils._bigintToString;
96
+ const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
97
+ // TODO: export?
98
+ function normalizePublicKey(publicKey) {
99
+ if (publicKey instanceof secp256k1.Point) {
100
+ publicKey.assertValidity();
101
+ return publicKey;
102
+ }
103
+ else {
104
+ const bytes = ensureBytes(publicKey);
105
+ // Schnorr is 32 bytes
106
+ if (bytes.length === 32) {
107
+ const x = bytesToNumberBE(bytes);
108
+ if (!isValidFieldElement(x))
109
+ throw new Error('Point is not on curve');
110
+ const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
111
+ let y = sqrtMod(y2); // y = y² ^ (p+1)/4
112
+ const isYOdd = (y & _1n) === _1n;
113
+ // Schnorr
114
+ if (isYOdd)
115
+ y = mod(-y, secp256k1.CURVE.P);
116
+ const point = new secp256k1.Point(x, y);
117
+ point.assertValidity();
118
+ return point;
119
+ }
120
+ // Do we need that in schnorr at all?
121
+ return secp256k1.Point.fromHex(publicKey);
122
+ }
123
+ }
124
+ const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
125
+ const isValidFieldElement = secp256k1.utils._isValidFieldElement;
126
+ const TAGS = {
127
+ challenge: 'BIP0340/challenge',
128
+ aux: 'BIP0340/aux',
129
+ nonce: 'BIP0340/nonce',
130
+ };
131
+ /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
132
+ const TAGGED_HASH_PREFIXES = {};
133
+ export function taggedHash(tag, ...messages) {
134
+ let tagP = TAGGED_HASH_PREFIXES[tag];
135
+ if (tagP === undefined) {
136
+ const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
137
+ tagP = concatBytes(tagH, tagH);
138
+ TAGGED_HASH_PREFIXES[tag] = tagP;
139
+ }
140
+ return sha256(concatBytes(tagP, ...messages));
141
+ }
142
+ const toRawX = (point) => point.toRawBytes(true).slice(1);
143
+ // Schnorr signatures are superior to ECDSA from above.
144
+ // Below is Schnorr-specific code as per BIP0340.
145
+ function schnorrChallengeFinalize(ch) {
146
+ return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
147
+ }
148
+ // Do we need this at all for Schnorr?
149
+ class SchnorrSignature {
150
+ constructor(r, s) {
151
+ this.r = r;
152
+ this.s = s;
153
+ this.assertValidity();
154
+ }
155
+ static fromHex(hex) {
156
+ const bytes = ensureBytes(hex);
157
+ if (bytes.length !== 64)
158
+ throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
159
+ const r = bytesToNumberBE(bytes.subarray(0, 32));
160
+ const s = bytesToNumberBE(bytes.subarray(32, 64));
161
+ return new SchnorrSignature(r, s);
162
+ }
163
+ assertValidity() {
164
+ const { r, s } = this;
165
+ if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
166
+ throw new Error('Invalid signature');
167
+ }
168
+ toHex() {
169
+ return numTo32bStr(this.r) + numTo32bStr(this.s);
170
+ }
171
+ toRawBytes() {
172
+ return hexToBytes(this.toHex());
173
+ }
174
+ }
175
+ function schnorrGetScalar(priv) {
176
+ const point = secp256k1.Point.fromPrivateKey(priv);
177
+ const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
178
+ return { point, scalar, x: toRawX(point) };
179
+ }
180
+ /**
181
+ * Synchronously creates Schnorr signature. Improved security: verifies itself before
182
+ * producing an output.
183
+ * @param msg message (not message hash)
184
+ * @param privateKey private key
185
+ * @param auxRand random bytes that would be added to k. Bad RNG won't break it.
186
+ */
187
+ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
188
+ if (message == null)
189
+ throw new TypeError(`sign: Expected valid message, not "${message}"`);
190
+ const m = ensureBytes(message);
191
+ // checks for isWithinCurveOrder
192
+ const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
193
+ const rand = ensureBytes(auxRand);
194
+ if (rand.length !== 32)
195
+ throw new TypeError('sign: Expected 32 bytes of aux randomness');
196
+ const tag = taggedHash;
197
+ const t0h = tag(TAGS.aux, rand);
198
+ const t = numTo32b(d ^ bytesToNumberBE(t0h));
199
+ const k0h = tag(TAGS.nonce, t, px, m);
200
+ const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
201
+ if (k0 === _0n)
202
+ throw new Error('sign: Creation of signature failed. k is zero');
203
+ const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
204
+ const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
205
+ const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
206
+ if (!schnorrVerify(sig, m, px))
207
+ throw new Error('sign: Invalid signature produced');
208
+ return sig;
209
+ }
210
+ /**
211
+ * Verifies Schnorr signature synchronously.
212
+ */
213
+ function schnorrVerify(signature, message, publicKey) {
214
+ try {
215
+ const raw = signature instanceof SchnorrSignature;
216
+ const sig = raw ? signature : SchnorrSignature.fromHex(signature);
217
+ if (raw)
218
+ sig.assertValidity(); // just in case
219
+ const { r, s } = sig;
220
+ const m = ensureBytes(message);
221
+ const P = normalizePublicKey(publicKey);
222
+ const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
223
+ // Finalize
224
+ // R = s⋅G - e⋅P
225
+ // -eP == (n-e)P
226
+ const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, secp256k1.CURVE.n));
227
+ if (!R || !R.hasEvenY() || R.x !== r)
228
+ return false;
229
+ return true;
230
+ }
231
+ catch (error) {
232
+ return false;
233
+ }
234
+ }
235
+ export const schnorr = {
236
+ Signature: SchnorrSignature,
237
+ // Schnorr's pubkey is just `x` of Point (BIP340)
238
+ getPublicKey: (privateKey) => toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
239
+ sign: schnorrSign,
240
+ verify: schnorrVerify,
241
+ };
@@ -0,0 +1,227 @@
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 { hmac } from '@noble/hashes/hmac';
5
+ import { concatBytes } from '@noble/hashes/utils';
6
+ import { weierstrass } from '../weierstrass';
7
+ import * as cutils from '../utils';
8
+ // Stark-friendly elliptic curve
9
+ // https://docs.starkware.co/starkex/stark-curve.html
10
+ // TODO: clarify exports; it is exporting both starkCurve and sign() now, can be confusing
11
+ function getHash(hash) {
12
+ return {
13
+ hash,
14
+ hmac: (key, ...msgs) => hmac(hash, key, concatBytes(...msgs)),
15
+ };
16
+ }
17
+ const CURVE_N = BigInt('3618502788666131213697322783095070105526743751716087489154079457884512865583');
18
+ const nBitLength = 252;
19
+ export const starkCurve = weierstrass({
20
+ // Params: a, b
21
+ a: BigInt(1),
22
+ b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
23
+ // Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
24
+ // There is no efficient sqrt for field (P%4==1)
25
+ P: BigInt('0x800000000000011000000000000000000000000000000000000000000000001'),
26
+ // Curve order, total count of valid points in the field.
27
+ n: CURVE_N,
28
+ nBitLength: nBitLength,
29
+ // Base point (x, y) aka generator point
30
+ Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
31
+ Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
32
+ h: BigInt(1),
33
+ // Default options
34
+ lowS: false,
35
+ ...getHash(sha256),
36
+ truncateHash: (hash, truncateOnly = false) => {
37
+ // TODO: cleanup, ugly code
38
+ // Fix truncation
39
+ if (!truncateOnly) {
40
+ let hashS = bytesToNumber0x(hash).toString(16);
41
+ if (hashS.length === 63) {
42
+ hashS += '0';
43
+ hash = hexToBytes0x(hashS);
44
+ }
45
+ }
46
+ // Truncate zero bytes on left (compat with elliptic)
47
+ while (hash[0] === 0)
48
+ hash = hash.subarray(1);
49
+ const byteLength = hash.length;
50
+ const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
51
+ let h = hash.length ? bytesToNumber0x(hash) : 0n;
52
+ if (delta > 0)
53
+ h = h >> BigInt(delta);
54
+ if (!truncateOnly && h >= CURVE_N)
55
+ h -= CURVE_N;
56
+ return h;
57
+ },
58
+ });
59
+ // Custom Starknet type conversion functions that can handle 0x and unpadded hex
60
+ function hexToBytes0x(hex) {
61
+ if (typeof hex !== 'string') {
62
+ throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
63
+ }
64
+ hex = strip0x(hex);
65
+ if (hex.length & 1)
66
+ hex = '0' + hex; // padding
67
+ if (hex.length % 2)
68
+ throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
69
+ const array = new Uint8Array(hex.length / 2);
70
+ for (let i = 0; i < array.length; i++) {
71
+ const j = i * 2;
72
+ const hexByte = hex.slice(j, j + 2);
73
+ const byte = Number.parseInt(hexByte, 16);
74
+ if (Number.isNaN(byte) || byte < 0)
75
+ throw new Error('Invalid byte sequence');
76
+ array[i] = byte;
77
+ }
78
+ return array;
79
+ }
80
+ function hexToNumber0x(hex) {
81
+ if (typeof hex !== 'string') {
82
+ throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
83
+ }
84
+ // Big Endian
85
+ // TODO: strip vs no strip?
86
+ return BigInt(`0x${strip0x(hex)}`);
87
+ }
88
+ function bytesToNumber0x(bytes) {
89
+ return hexToNumber0x(cutils.bytesToHex(bytes));
90
+ }
91
+ function ensureBytes0x(hex) {
92
+ // Uint8Array.from() instead of hash.slice() because node.js Buffer
93
+ // is instance of Uint8Array, and its slice() creates **mutable** copy
94
+ return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
95
+ }
96
+ function normalizePrivateKey(privKey) {
97
+ return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
98
+ }
99
+ function getPublicKey0x(privKey, isCompressed) {
100
+ return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
101
+ }
102
+ function getSharedSecret0x(privKeyA, pubKeyB) {
103
+ return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
104
+ }
105
+ function sign0x(msgHash, privKey, opts) {
106
+ return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
107
+ }
108
+ function verify0x(signature, msgHash, pubKey) {
109
+ const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
110
+ return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
111
+ }
112
+ const { CURVE, Point, JacobianPoint, Signature } = starkCurve;
113
+ export const utils = starkCurve.utils;
114
+ export { CURVE, Point, Signature, JacobianPoint, getPublicKey0x as getPublicKey, getSharedSecret0x as getSharedSecret, sign0x as sign, verify0x as verify, };
115
+ const stripLeadingZeros = (s) => s.replace(/^0+/gm, '');
116
+ export const bytesToHexEth = (uint8a) => `0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
117
+ export const strip0x = (hex) => hex.replace(/^0x/i, '');
118
+ export const numberToHexEth = (num) => `0x${num.toString(16)}`;
119
+ // 1. seed generation
120
+ function hashKeyWithIndex(key, index) {
121
+ let indexHex = cutils.numberToHexUnpadded(index);
122
+ if (indexHex.length & 1)
123
+ indexHex = '0' + indexHex;
124
+ return bytesToNumber0x(sha256(cutils.concatBytes(key, hexToBytes0x(indexHex))));
125
+ }
126
+ export function grindKey(seed) {
127
+ const _seed = ensureBytes0x(seed);
128
+ const sha256mask = 2n ** 256n;
129
+ const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
130
+ for (let i = 0;; i++) {
131
+ const key = hashKeyWithIndex(_seed, i);
132
+ // key should be in [0, limit)
133
+ if (key < limit)
134
+ return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
135
+ }
136
+ }
137
+ export function getStarkKey(privateKey) {
138
+ return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
139
+ }
140
+ export function ethSigToPrivate(signature) {
141
+ signature = strip0x(signature.replace(/^0x/, ''));
142
+ if (signature.length !== 130)
143
+ throw new Error('Wrong ethereum signature');
144
+ return grindKey(signature.substring(0, 64));
145
+ }
146
+ const MASK_31 = 2n ** 31n - 1n;
147
+ const int31 = (n) => Number(n & MASK_31);
148
+ export function getAccountPath(layer, application, ethereumAddress, index) {
149
+ const layerNum = int31(bytesToNumber0x(sha256(layer)));
150
+ const applicationNum = int31(bytesToNumber0x(sha256(application)));
151
+ const eth = hexToNumber0x(ethereumAddress);
152
+ return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
153
+ }
154
+ // https://docs.starkware.co/starkex/pedersen-hash-function.html
155
+ const PEDERSEN_POINTS = [
156
+ new Point(2089986280348253421170679821480865132823066470938446095505822317253594081284n, 1713931329540660377023406109199410414810705867260802078187082345529207694986n),
157
+ new Point(996781205833008774514500082376783249102396023663454813447423147977397232763n, 1668503676786377725805489344771023921079126552019160156920634619255970485781n),
158
+ new Point(2251563274489750535117886426533222435294046428347329203627021249169616184184n, 1798716007562728905295480679789526322175868328062420237419143593021674992973n),
159
+ new Point(2138414695194151160943305727036575959195309218611738193261179310511854807447n, 113410276730064486255102093846540133784865286929052426931474106396135072156n),
160
+ new Point(2379962749567351885752724891227938183011949129833673362440656643086021394946n, 776496453633298175483985398648758586525933812536653089401905292063708816422n),
161
+ ];
162
+ // for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
163
+ const PEDERSEN_POINTS_JACOBIAN = PEDERSEN_POINTS.map(JacobianPoint.fromAffine);
164
+ function pedersenPrecompute(p1, p2) {
165
+ const out = [];
166
+ let p = p1;
167
+ for (let i = 0; i < 248; i++) {
168
+ out.push(p);
169
+ p = p.double();
170
+ }
171
+ p = p2;
172
+ for (let i = 0; i < 4; i++) {
173
+ out.push(p);
174
+ p = p.double();
175
+ }
176
+ return out;
177
+ }
178
+ const PEDERSEN_POINTS1 = pedersenPrecompute(PEDERSEN_POINTS_JACOBIAN[1], PEDERSEN_POINTS_JACOBIAN[2]);
179
+ const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS_JACOBIAN[3], PEDERSEN_POINTS_JACOBIAN[4]);
180
+ function pedersenArg(arg) {
181
+ let value;
182
+ if (typeof arg === 'bigint')
183
+ value = arg;
184
+ else if (typeof arg === 'number') {
185
+ if (!Number.isSafeInteger(arg))
186
+ throw new Error(`Invalid pedersenArg: ${arg}`);
187
+ value = BigInt(arg);
188
+ }
189
+ else
190
+ value = bytesToNumber0x(ensureBytes0x(arg));
191
+ // [0..Fp)
192
+ if (!(0n <= value && value < starkCurve.CURVE.P))
193
+ throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
194
+ return value;
195
+ }
196
+ function pedersenSingle(point, value, constants) {
197
+ let x = pedersenArg(value);
198
+ for (let j = 0; j < 252; j++) {
199
+ const pt = constants[j];
200
+ if (pt.x === point.x)
201
+ throw new Error('Same point');
202
+ if ((x & 1n) !== 0n)
203
+ point = point.add(pt);
204
+ x >>= 1n;
205
+ }
206
+ return point;
207
+ }
208
+ // shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
209
+ export function pedersen(x, y) {
210
+ let point = PEDERSEN_POINTS_JACOBIAN[0];
211
+ point = pedersenSingle(point, x, PEDERSEN_POINTS1);
212
+ point = pedersenSingle(point, y, PEDERSEN_POINTS2);
213
+ return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
214
+ }
215
+ export function hashChain(data, fn = pedersen) {
216
+ if (!Array.isArray(data) || data.length < 1)
217
+ throw new Error('data should be array of at least 1 element');
218
+ if (data.length === 1)
219
+ return numberToHexEth(pedersenArg(data[0]));
220
+ return Array.from(data)
221
+ .reverse()
222
+ .reduce((acc, i) => fn(i, acc));
223
+ }
224
+ // Same as hashChain, but computes hash even for single element and order is not revesed
225
+ export const computeHashOnElements = (data, fn = pedersen) => [0, ...data, data.length].reduce((x, y) => fn(x, y));
226
+ const MASK_250 = 2n ** 250n - 1n;
227
+ export const keccak = (data) => bytesToNumber0x(keccak_256(data)) & MASK_250;
@@ -8,7 +8,7 @@
8
8
  // 4. Point decompression code is different too (unexpected), now using generalized formula
9
9
  // 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
10
10
  import * as mod from './modular.js';
11
- import { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js'; // TODO: import * as u from './utils.js'?
11
+ import { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, randomBytes as utilRandomBytes } from './utils.js'; // TODO: import * as u from './utils.js'?
12
12
  import { wNAF } from './group.js';
13
13
  // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
14
14
  const _0n = BigInt(0);
@@ -24,18 +24,14 @@ function validateOpts(curve) {
24
24
  if (typeof opts[i] !== 'bigint')
25
25
  throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
26
26
  }
27
- for (const fn of ['randomBytes']) {
28
- if (typeof opts[fn] !== 'function')
29
- throw new Error(`Invalid ${fn} function`);
30
- }
31
- for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio']) {
27
+ for (const fn of ['adjustScalarBytes', 'domain', 'randomBytes', 'uvRatio']) {
32
28
  if (opts[fn] === undefined)
33
29
  continue; // Optional
34
30
  if (typeof opts[fn] !== 'function')
35
31
  throw new Error(`Invalid ${fn} function`);
36
32
  }
37
33
  // Set defaults
38
- return Object.freeze({ ...opts });
34
+ return Object.freeze({ randomBytes: utilRandomBytes, ...opts });
39
35
  }
40
36
  // NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation
41
37
  export function twistedEdwards(curveDef) {
@@ -3,10 +3,6 @@
3
3
  const _0n = BigInt(0);
4
4
  const _1n = BigInt(1);
5
5
  const _2n = BigInt(2);
6
- const _3n = BigInt(3);
7
- const _4n = BigInt(4);
8
- const _5n = BigInt(5);
9
- const _8n = BigInt(8);
10
6
  // Calculates a modulo b
11
7
  export function mod(a, b) {
12
8
  const result = a % b;
@@ -14,6 +10,7 @@ export function mod(a, b) {
14
10
  }
15
11
  /**
16
12
  * Efficiently exponentiate num to power and do modular division.
13
+ * Unsafe in some contexts: uses ladder, so can expose bigint bits.
17
14
  * @example
18
15
  * powMod(2n, 6n, 11n) // 64n % 11n == 9n
19
16
  */
@@ -101,22 +98,29 @@ export function invertBatch(nums, modulo) {
101
98
  }, inverted);
102
99
  return scratch;
103
100
  }
104
- // Calculates Legendre symbol: num^((P-1)/2)
105
- export function legendre(num, fieldPrime) {
106
- return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
101
+ /**
102
+ * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
103
+ * * (a | p) 1 if a is a square (mod p)
104
+ * * (a | p) ≡ -1 if a is not a square (mod p)
105
+ * * (a | p) ≡ 0 if a ≡ 0 (mod p)
106
+ */
107
+ export function legendre(num, P) {
108
+ return pow(num, (P - _1n) / _2n, P);
107
109
  }
108
110
  /**
109
111
  * Calculates square root of a number in a finite field.
110
112
  */
111
113
  export function sqrt(number, modulo) {
114
+ // prettier-ignore
115
+ const _3n = BigInt(3), _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
112
116
  const n = number;
113
117
  const P = modulo;
114
118
  const p1div4 = (P + _1n) / _4n;
115
- // P = 3 (mod 4)
119
+ // P 3 (mod 4)
116
120
  // sqrt n = n^((P+1)/4)
117
121
  if (P % _4n === _3n)
118
122
  return pow(n, p1div4, P);
119
- // P = 5 (mod 8)
123
+ // P 5 (mod 8)
120
124
  if (P % _8n === _5n) {
121
125
  const n2 = mod(n * _2n, P);
122
126
  const v = pow(n2, (P - _5n) / _8n, P);
@@ -126,6 +130,7 @@ export function sqrt(number, modulo) {
126
130
  return r;
127
131
  }
128
132
  // Other cases: Tonelli-Shanks algorithm
133
+ // Check whether n is square
129
134
  if (legendre(n, P) !== _1n)
130
135
  throw new Error('Cannot find square root');
131
136
  let q, s, z;
package/lib/esm/utils.js CHANGED
@@ -1,4 +1,7 @@
1
1
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // The import here is via the package name. This is to ensure
3
+ // that exports mapping/resolution does fall into place.
4
+ import { crypto } from '@noble/curves/crypto';
2
5
  export function validateOpts(curve) {
3
6
  for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) {
4
7
  if (typeof curve[i] !== 'bigint')
@@ -122,3 +125,17 @@ export function equalBytes(b1, b2) {
122
125
  return false;
123
126
  return true;
124
127
  }
128
+ /**
129
+ * Cryptographically secure PRNG
130
+ */
131
+ export function randomBytes(bytesLength = 32) {
132
+ if (crypto.web) {
133
+ return crypto.web.getRandomValues(new Uint8Array(bytesLength));
134
+ }
135
+ else if (crypto.node) {
136
+ return new Uint8Array(crypto.node.randomBytes(bytesLength).buffer);
137
+ }
138
+ else {
139
+ throw new Error("The environment doesn't have randomBytes function");
140
+ }
141
+ }
@@ -8,21 +8,27 @@
8
8
  // 3. truncateHash() truncateOnly mode
9
9
  // 4. DRBG supports outputLen bigger than outputLen of hmac
10
10
  import * as mod from './modular.js';
11
- import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js';
11
+ import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, randomBytes as utilRandomBytes, } from './utils.js';
12
12
  import { wNAF } from './group.js';
13
13
  // Should be separate from overrides, since overrides can use information about curve (for example nBits)
14
14
  function validateOpts(curve) {
15
15
  const opts = utilOpts(curve);
16
- if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
17
- throw new Error('Invalid hash function');
18
- if (typeof opts.hmac !== 'function')
19
- throw new Error('Invalid hmac function');
20
- if (typeof opts.randomBytes !== 'function')
21
- throw new Error('Invalid randomBytes function');
22
16
  for (const i of ['a', 'b']) {
23
17
  if (typeof opts[i] !== 'bigint')
24
18
  throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
25
19
  }
20
+ for (const fn of ['hash', 'hmac']) {
21
+ if (typeof opts[fn] !== 'function')
22
+ throw new Error(`Invalid ${fn} function`);
23
+ }
24
+ for (const fn of ['randomBytes']) {
25
+ if (opts[fn] === undefined)
26
+ continue; // Optional
27
+ if (typeof opts[fn] !== 'function')
28
+ throw new Error(`Invalid ${fn} function`);
29
+ }
30
+ if (!Number.isSafeInteger(opts.hash.outputLen))
31
+ throw new Error('Invalid hash function');
26
32
  const endo = opts.endo;
27
33
  if (endo) {
28
34
  if (opts.a !== _0n) {
@@ -35,7 +41,7 @@ function validateOpts(curve) {
35
41
  }
36
42
  }
37
43
  // Set defaults
38
- return Object.freeze({ lowS: true, ...opts });
44
+ return Object.freeze({ lowS: true, randomBytes: utilRandomBytes, ...opts });
39
45
  }
40
46
  // TODO: convert bits to bytes aligned to 32 bits? (224 for example)
41
47
  // DER encoding utilities
@@ -183,6 +189,9 @@ export function weierstrass(curveDef) {
183
189
  }
184
190
  function normalizePrivateKey(key) {
185
191
  let num;
192
+ if (typeof CURVE.normalizePrivateKey === 'function') {
193
+ key = CURVE.normalizePrivateKey(key);
194
+ }
186
195
  if (typeof key === 'bigint') {
187
196
  num = key;
188
197
  }
@@ -190,7 +199,7 @@ export function weierstrass(curveDef) {
190
199
  num = BigInt(key);
191
200
  }
192
201
  else if (typeof key === 'string') {
193
- key = key.padStart(2 * groupLen, '0'); // Eth-like hexes
202
+ // key = key.padStart(2 * groupLen, '0'); // Eth-like hexes
194
203
  if (key.length !== 2 * groupLen)
195
204
  throw new Error(`Expected ${groupLen} bytes of private key`);
196
205
  num = hexToNumber(key);
package/lib/modular.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  export declare function mod(a: bigint, b: bigint): bigint;
3
3
  /**
4
4
  * Efficiently exponentiate num to power and do modular division.
5
+ * Unsafe in some contexts: uses ladder, so can expose bigint bits.
5
6
  * @example
6
7
  * powMod(2n, 6n, 11n) // 64n % 11n == 9n
7
8
  */
@@ -23,7 +24,13 @@ export declare function div(numerator: bigint, denominator: bigint, modulo: bigi
23
24
  * // => [1n, 11n, 16n]
24
25
  */
25
26
  export declare function invertBatch(nums: bigint[], modulo: bigint): bigint[];
26
- export declare function legendre(num: bigint, fieldPrime: bigint): bigint;
27
+ /**
28
+ * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
29
+ * * (a | p) ≡ 1 if a is a square (mod p)
30
+ * * (a | p) ≡ -1 if a is not a square (mod p)
31
+ * * (a | p) ≡ 0 if a ≡ 0 (mod p)
32
+ */
33
+ export declare function legendre(num: bigint, P: bigint): bigint;
27
34
  /**
28
35
  * Calculates square root of a number in a finite field.
29
36
  */