@noble/curves 0.1.0 → 0.2.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.
@@ -1,4 +1,5 @@
1
1
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // Utilities for modular arithmetics
2
3
  const _0n = BigInt(0);
3
4
  const _1n = BigInt(1);
4
5
  const _2n = BigInt(2);
@@ -62,6 +63,15 @@ export function invert(number, modulo) {
62
63
  throw new Error('invert: does not exist');
63
64
  return mod(x, modulo);
64
65
  }
66
+ /**
67
+ * Division over finite field.
68
+ * `a/b mod p == a * invert(b) mod p`
69
+ */
70
+ export function div(numerator, denominator, modulo) {
71
+ const num = mod(numerator, modulo);
72
+ const iden = invert(denominator, modulo);
73
+ return mod(num * iden, modulo);
74
+ }
65
75
  /**
66
76
  * Takes a list of numbers, efficiently inverts all of them.
67
77
  * @param nums list of bigints
@@ -97,7 +107,6 @@ export function legendre(num, fieldPrime) {
97
107
  }
98
108
  /**
99
109
  * Calculates square root of a number in a finite field.
100
- * Used to calculate y - the square root of y².
101
110
  */
102
111
  export function sqrt(number, modulo) {
103
112
  const n = number;
@@ -146,3 +155,12 @@ export function sqrt(number, modulo) {
146
155
  }
147
156
  return r;
148
157
  }
158
+ // Little-endian check for first LE bit (last BE bit);
159
+ export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
160
+ // An idea on modular arithmetic for bls12-381:
161
+ // const FIELD = {add, pow, sqrt, mul};
162
+ // Functions will take field elements, no need for an additional class
163
+ // Could be faster. 1 bigint field will just do operations and mod later:
164
+ // instead of 'r = mod(r * b, P)' we will write r = mul(r, b);
165
+ // Could be insecure without shape check, so it needs to be done.
166
+ // Functions could be inlined by JIT.
@@ -0,0 +1,189 @@
1
+ import * as mod from './modular.js';
2
+ import { ensureBytes, numberToBytesLE, bytesToNumberLE,
3
+ // nLength,
4
+ } from './utils.js';
5
+ const _0n = BigInt(0);
6
+ const _1n = BigInt(1);
7
+ function validateOpts(curve) {
8
+ for (const i of ['a24']) {
9
+ if (typeof curve[i] !== 'bigint')
10
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
11
+ }
12
+ for (const i of ['montgomeryBits', 'nByteLength']) {
13
+ if (curve[i] === undefined)
14
+ continue; // Optional
15
+ if (!Number.isSafeInteger(curve[i]))
16
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
17
+ }
18
+ for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
19
+ if (curve[fn] === undefined)
20
+ continue; // Optional
21
+ if (typeof curve[fn] !== 'function')
22
+ throw new Error(`Invalid ${fn} function`);
23
+ }
24
+ for (const i of ['Gu']) {
25
+ if (curve[i] === undefined)
26
+ continue; // Optional
27
+ if (typeof curve[i] !== 'string')
28
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
29
+ }
30
+ // Set defaults
31
+ // ...nLength(curve.n, curve.nBitLength),
32
+ return Object.freeze({ ...curve });
33
+ }
34
+ // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
35
+ // Uses only one coordinate instead of two
36
+ export function montgomery(curveDef) {
37
+ const CURVE = validateOpts(curveDef);
38
+ const { P } = CURVE;
39
+ const modP = (a) => mod.mod(a, P);
40
+ const montgomeryBits = CURVE.montgomeryBits;
41
+ const montgomeryBytes = Math.ceil(montgomeryBits / 8);
42
+ const fieldLen = CURVE.nByteLength;
43
+ const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
44
+ const powPminus2 = CURVE.powPminus2 || ((x) => mod.pow(x, P - BigInt(2), P));
45
+ /**
46
+ * Checks for num to be in range:
47
+ * For strict == true: `0 < num < max`.
48
+ * For strict == false: `0 <= num < max`.
49
+ * Converts non-float safe numbers to bigints.
50
+ */
51
+ function normalizeScalar(num, max, strict = true) {
52
+ if (!max)
53
+ throw new TypeError('Specify max value');
54
+ if (typeof num === 'number' && Number.isSafeInteger(num))
55
+ num = BigInt(num);
56
+ if (typeof num === 'bigint' && num < max) {
57
+ if (strict) {
58
+ if (_0n < num)
59
+ return num;
60
+ }
61
+ else {
62
+ if (_0n <= num)
63
+ return num;
64
+ }
65
+ }
66
+ throw new TypeError('Expected valid scalar: 0 < scalar < max');
67
+ }
68
+ // cswap from RFC7748
69
+ // NOTE: cswap is not from RFC7748!
70
+ /*
71
+ cswap(swap, x_2, x_3):
72
+ dummy = mask(swap) AND (x_2 XOR x_3)
73
+ x_2 = x_2 XOR dummy
74
+ x_3 = x_3 XOR dummy
75
+ Return (x_2, x_3)
76
+ Where mask(swap) is the all-1 or all-0 word of the same length as x_2
77
+ and x_3, computed, e.g., as mask(swap) = 0 - swap.
78
+ */
79
+ function cswap(swap, x_2, x_3) {
80
+ const dummy = modP(swap * (x_2 - x_3));
81
+ x_2 = modP(x_2 - dummy);
82
+ x_3 = modP(x_3 + dummy);
83
+ return [x_2, x_3];
84
+ }
85
+ // x25519 from 4
86
+ /**
87
+ *
88
+ * @param pointU u coordinate (x) on Montgomery Curve 25519
89
+ * @param scalar by which the point would be multiplied
90
+ * @returns new Point on Montgomery curve
91
+ */
92
+ function montgomeryLadder(pointU, scalar) {
93
+ const { P } = CURVE;
94
+ const u = normalizeScalar(pointU, P);
95
+ // Section 5: Implementations MUST accept non-canonical values and process them as
96
+ // if they had been reduced modulo the field prime.
97
+ const k = normalizeScalar(scalar, P);
98
+ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
99
+ const a24 = CURVE.a24;
100
+ const x_1 = u;
101
+ let x_2 = _1n;
102
+ let z_2 = _0n;
103
+ let x_3 = u;
104
+ let z_3 = _1n;
105
+ let swap = _0n;
106
+ let sw;
107
+ for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
108
+ const k_t = (k >> t) & _1n;
109
+ swap ^= k_t;
110
+ sw = cswap(swap, x_2, x_3);
111
+ x_2 = sw[0];
112
+ x_3 = sw[1];
113
+ sw = cswap(swap, z_2, z_3);
114
+ z_2 = sw[0];
115
+ z_3 = sw[1];
116
+ swap = k_t;
117
+ const A = x_2 + z_2;
118
+ const AA = modP(A * A);
119
+ const B = x_2 - z_2;
120
+ const BB = modP(B * B);
121
+ const E = AA - BB;
122
+ const C = x_3 + z_3;
123
+ const D = x_3 - z_3;
124
+ const DA = modP(D * A);
125
+ const CB = modP(C * B);
126
+ const dacb = DA + CB;
127
+ const da_cb = DA - CB;
128
+ x_3 = modP(dacb * dacb);
129
+ z_3 = modP(x_1 * modP(da_cb * da_cb));
130
+ x_2 = modP(AA * BB);
131
+ z_2 = modP(E * (AA + modP(a24 * E)));
132
+ }
133
+ // (x_2, x_3) = cswap(swap, x_2, x_3)
134
+ sw = cswap(swap, x_2, x_3);
135
+ x_2 = sw[0];
136
+ x_3 = sw[1];
137
+ // (z_2, z_3) = cswap(swap, z_2, z_3)
138
+ sw = cswap(swap, z_2, z_3);
139
+ z_2 = sw[0];
140
+ z_3 = sw[1];
141
+ // z_2^(p - 2)
142
+ const z2 = powPminus2(z_2);
143
+ // Return x_2 * (z_2^(p - 2))
144
+ return modP(x_2 * z2);
145
+ }
146
+ function encodeUCoordinate(u) {
147
+ return numberToBytesLE(modP(u), montgomeryBytes);
148
+ }
149
+ function decodeUCoordinate(uEnc) {
150
+ const u = ensureBytes(uEnc, montgomeryBytes);
151
+ // Section 5: When receiving such an array, implementations of X25519
152
+ // MUST mask the most significant bit in the final byte.
153
+ // This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
154
+ // fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
155
+ u[fieldLen - 1] &= 127; // 0b0111_1111
156
+ return bytesToNumberLE(u);
157
+ }
158
+ function decodeScalar(n) {
159
+ const bytes = ensureBytes(n);
160
+ if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
161
+ throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
162
+ return bytesToNumberLE(adjustScalarBytes(bytes));
163
+ }
164
+ // Multiply point u by scalar
165
+ function scalarMult(u, scalar) {
166
+ const pointU = decodeUCoordinate(u);
167
+ const _scalar = decodeScalar(scalar);
168
+ const pu = montgomeryLadder(pointU, _scalar);
169
+ // The result was not contributory
170
+ // https://cr.yp.to/ecdh.html#validate
171
+ if (pu === _0n)
172
+ throw new Error('Invalid private or public key received');
173
+ return encodeUCoordinate(pu);
174
+ }
175
+ // Multiply base point by scalar
176
+ function scalarMultBase(scalar) {
177
+ return scalarMult(CURVE.Gu, scalar);
178
+ }
179
+ return {
180
+ // NOTE: we can get 'y' coordinate from 'u', but Point.fromHex also wants 'x' coordinate oddity flag, and we cannot get 'x' without knowing 'v'
181
+ // Need to add generic conversion between twisted edwards and complimentary curve for JubJub
182
+ scalarMult,
183
+ scalarMultBase,
184
+ // NOTE: these function work on complimentary montgomery curve
185
+ // getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(publicKey, privateKey),
186
+ getPublicKey: (privateKey) => scalarMultBase(privateKey),
187
+ Gu: CURVE.Gu,
188
+ };
189
+ }
package/lib/esm/utils.js CHANGED
@@ -1,6 +1,19 @@
1
1
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- // Convert between types
3
- // ---------------------
2
+ export function validateOpts(curve) {
3
+ for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) {
4
+ if (typeof curve[i] !== 'bigint')
5
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
6
+ }
7
+ for (const i of ['nBitLength', 'nByteLength']) {
8
+ if (curve[i] === undefined)
9
+ continue; // Optional
10
+ if (!Number.isSafeInteger(curve[i]))
11
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
12
+ }
13
+ // Set defaults
14
+ return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve });
15
+ }
16
+ import * as mod from './modular.js';
4
17
  const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
5
18
  export function bytesToHex(uint8a) {
6
19
  if (!(uint8a instanceof Uint8Array))
@@ -42,13 +55,23 @@ export function hexToBytes(hex) {
42
55
  return array;
43
56
  }
44
57
  // Big Endian
45
- export function bytesToNumber(bytes) {
58
+ export function bytesToNumberBE(bytes) {
46
59
  return hexToNumber(bytesToHex(bytes));
47
60
  }
48
- export function ensureBytes(hex) {
61
+ export function bytesToNumberLE(uint8a) {
62
+ if (!(uint8a instanceof Uint8Array))
63
+ throw new Error('Expected Uint8Array');
64
+ return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
65
+ }
66
+ export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
67
+ export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse();
68
+ export function ensureBytes(hex, expectedLength) {
49
69
  // Uint8Array.from() instead of hash.slice() because node.js Buffer
50
70
  // is instance of Uint8Array, and its slice() creates **mutable** copy
51
- return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
71
+ const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
72
+ if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
73
+ throw new Error(`Expected ${expectedLength} bytes`);
74
+ return bytes;
52
75
  }
53
76
  // Copies several Uint8Arrays into one.
54
77
  export function concatBytes(...arrays) {
@@ -65,3 +88,37 @@ export function concatBytes(...arrays) {
65
88
  }
66
89
  return result;
67
90
  }
91
+ // CURVE.n lengths
92
+ export function nLength(n, nBitLength) {
93
+ // Bit size, byte size of CURVE.n
94
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
95
+ const nByteLength = Math.ceil(_nBitLength / 8);
96
+ return { nBitLength: _nBitLength, nByteLength };
97
+ }
98
+ /**
99
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
100
+ * and convert them into private scalar, with the modulo bias being neglible.
101
+ * As per FIPS 186 B.4.1.
102
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
103
+ * @param hash hash output from sha512, or a similar function
104
+ * @returns valid private scalar
105
+ */
106
+ const _1n = BigInt(1);
107
+ export function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) {
108
+ hash = ensureBytes(hash);
109
+ const orderLen = nLength(CURVE_ORDER).nByteLength;
110
+ const minLen = orderLen + 8;
111
+ if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
112
+ throw new Error('Expected valid bytes of private key as per FIPS 186');
113
+ const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
114
+ return mod.mod(num, CURVE_ORDER - _1n) + _1n;
115
+ }
116
+ export function equalBytes(b1, b2) {
117
+ // We don't care about timing attacks here
118
+ if (b1.length !== b2.length)
119
+ return false;
120
+ for (let i = 0; i < b1.length; i++)
121
+ if (b1[i] !== b2[i])
122
+ return false;
123
+ return true;
124
+ }