@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.
- package/README.md +339 -20
- package/lib/edwards.d.ts +108 -0
- package/lib/edwards.js +554 -0
- package/lib/esm/edwards.js +550 -0
- package/lib/esm/group.js +107 -0
- package/lib/esm/modular.js +19 -1
- package/lib/esm/montgomery.js +189 -0
- package/lib/esm/utils.js +62 -5
- package/lib/esm/{shortw.js → weierstrass.js} +81 -159
- package/lib/group.d.ts +33 -0
- package/lib/group.js +111 -0
- package/lib/modular.d.ts +7 -1
- package/lib/modular.js +23 -3
- package/lib/montgomery.d.ts +20 -0
- package/lib/montgomery.js +191 -0
- package/lib/utils.d.ts +26 -2
- package/lib/utils.js +71 -7
- package/lib/{shortw.d.ts → weierstrass.d.ts} +22 -35
- package/lib/{shortw.js → weierstrass.js} +80 -158
- package/package.json +23 -11
package/lib/esm/modular.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
58
|
+
export function bytesToNumberBE(bytes) {
|
|
46
59
|
return hexToNumber(bytesToHex(bytes));
|
|
47
60
|
}
|
|
48
|
-
export function
|
|
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
|
-
|
|
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
|
+
}
|