@noble/curves 0.1.0 → 0.2.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.
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.montgomery = void 0;
4
+ const mod = require("./modular.js");
5
+ const utils_js_1 = require("./utils.js");
6
+ const _0n = BigInt(0);
7
+ const _1n = BigInt(1);
8
+ function validateOpts(curve) {
9
+ for (const i of ['a24']) {
10
+ if (typeof curve[i] !== 'bigint')
11
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
12
+ }
13
+ for (const i of ['montgomeryBits', 'nByteLength']) {
14
+ if (curve[i] === undefined)
15
+ continue; // Optional
16
+ if (!Number.isSafeInteger(curve[i]))
17
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
18
+ }
19
+ for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
20
+ if (curve[fn] === undefined)
21
+ continue; // Optional
22
+ if (typeof curve[fn] !== 'function')
23
+ throw new Error(`Invalid ${fn} function`);
24
+ }
25
+ for (const i of ['Gu']) {
26
+ if (curve[i] === undefined)
27
+ continue; // Optional
28
+ if (typeof curve[i] !== 'string')
29
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
30
+ }
31
+ // Set defaults
32
+ // ...nLength(curve.n, curve.nBitLength),
33
+ return Object.freeze({ ...curve });
34
+ }
35
+ // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
36
+ // Uses only one coordinate instead of two
37
+ function montgomery(curveDef) {
38
+ const CURVE = validateOpts(curveDef);
39
+ const { P } = CURVE;
40
+ const modP = (a) => mod.mod(a, P);
41
+ const montgomeryBits = CURVE.montgomeryBits;
42
+ const montgomeryBytes = Math.ceil(montgomeryBits / 8);
43
+ const fieldLen = CURVE.nByteLength;
44
+ const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
45
+ const powPminus2 = CURVE.powPminus2 || ((x) => mod.pow(x, P - BigInt(2), P));
46
+ /**
47
+ * Checks for num to be in range:
48
+ * For strict == true: `0 < num < max`.
49
+ * For strict == false: `0 <= num < max`.
50
+ * Converts non-float safe numbers to bigints.
51
+ */
52
+ function normalizeScalar(num, max, strict = true) {
53
+ if (!max)
54
+ throw new TypeError('Specify max value');
55
+ if (typeof num === 'number' && Number.isSafeInteger(num))
56
+ num = BigInt(num);
57
+ if (typeof num === 'bigint' && num < max) {
58
+ if (strict) {
59
+ if (_0n < num)
60
+ return num;
61
+ }
62
+ else {
63
+ if (_0n <= num)
64
+ return num;
65
+ }
66
+ }
67
+ throw new TypeError('Expected valid scalar: 0 < scalar < max');
68
+ }
69
+ // cswap from RFC7748
70
+ // NOTE: cswap is not from RFC7748!
71
+ /*
72
+ cswap(swap, x_2, x_3):
73
+ dummy = mask(swap) AND (x_2 XOR x_3)
74
+ x_2 = x_2 XOR dummy
75
+ x_3 = x_3 XOR dummy
76
+ Return (x_2, x_3)
77
+ Where mask(swap) is the all-1 or all-0 word of the same length as x_2
78
+ and x_3, computed, e.g., as mask(swap) = 0 - swap.
79
+ */
80
+ function cswap(swap, x_2, x_3) {
81
+ const dummy = modP(swap * (x_2 - x_3));
82
+ x_2 = modP(x_2 - dummy);
83
+ x_3 = modP(x_3 + dummy);
84
+ return [x_2, x_3];
85
+ }
86
+ // x25519 from 4
87
+ /**
88
+ *
89
+ * @param pointU u coordinate (x) on Montgomery Curve 25519
90
+ * @param scalar by which the point would be multiplied
91
+ * @returns new Point on Montgomery curve
92
+ */
93
+ function montgomeryLadder(pointU, scalar) {
94
+ const { P } = CURVE;
95
+ const u = normalizeScalar(pointU, P);
96
+ // Section 5: Implementations MUST accept non-canonical values and process them as
97
+ // if they had been reduced modulo the field prime.
98
+ const k = normalizeScalar(scalar, P);
99
+ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
100
+ const a24 = CURVE.a24;
101
+ const x_1 = u;
102
+ let x_2 = _1n;
103
+ let z_2 = _0n;
104
+ let x_3 = u;
105
+ let z_3 = _1n;
106
+ let swap = _0n;
107
+ let sw;
108
+ for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
109
+ const k_t = (k >> t) & _1n;
110
+ swap ^= k_t;
111
+ sw = cswap(swap, x_2, x_3);
112
+ x_2 = sw[0];
113
+ x_3 = sw[1];
114
+ sw = cswap(swap, z_2, z_3);
115
+ z_2 = sw[0];
116
+ z_3 = sw[1];
117
+ swap = k_t;
118
+ const A = x_2 + z_2;
119
+ const AA = modP(A * A);
120
+ const B = x_2 - z_2;
121
+ const BB = modP(B * B);
122
+ const E = AA - BB;
123
+ const C = x_3 + z_3;
124
+ const D = x_3 - z_3;
125
+ const DA = modP(D * A);
126
+ const CB = modP(C * B);
127
+ const dacb = DA + CB;
128
+ const da_cb = DA - CB;
129
+ x_3 = modP(dacb * dacb);
130
+ z_3 = modP(x_1 * modP(da_cb * da_cb));
131
+ x_2 = modP(AA * BB);
132
+ z_2 = modP(E * (AA + modP(a24 * E)));
133
+ }
134
+ // (x_2, x_3) = cswap(swap, x_2, x_3)
135
+ sw = cswap(swap, x_2, x_3);
136
+ x_2 = sw[0];
137
+ x_3 = sw[1];
138
+ // (z_2, z_3) = cswap(swap, z_2, z_3)
139
+ sw = cswap(swap, z_2, z_3);
140
+ z_2 = sw[0];
141
+ z_3 = sw[1];
142
+ // z_2^(p - 2)
143
+ const z2 = powPminus2(z_2);
144
+ // Return x_2 * (z_2^(p - 2))
145
+ return modP(x_2 * z2);
146
+ }
147
+ function encodeUCoordinate(u) {
148
+ return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
149
+ }
150
+ function decodeUCoordinate(uEnc) {
151
+ const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
152
+ // Section 5: When receiving such an array, implementations of X25519
153
+ // MUST mask the most significant bit in the final byte.
154
+ // This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
155
+ // fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
156
+ u[fieldLen - 1] &= 127; // 0b0111_1111
157
+ return (0, utils_js_1.bytesToNumberLE)(u);
158
+ }
159
+ function decodeScalar(n) {
160
+ const bytes = (0, utils_js_1.ensureBytes)(n);
161
+ if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
162
+ throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
163
+ return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
164
+ }
165
+ // Multiply point u by scalar
166
+ function scalarMult(u, scalar) {
167
+ const pointU = decodeUCoordinate(u);
168
+ const _scalar = decodeScalar(scalar);
169
+ const pu = montgomeryLadder(pointU, _scalar);
170
+ // The result was not contributory
171
+ // https://cr.yp.to/ecdh.html#validate
172
+ if (pu === _0n)
173
+ throw new Error('Invalid private or public key received');
174
+ return encodeUCoordinate(pu);
175
+ }
176
+ // Multiply base point by scalar
177
+ function scalarMultBase(scalar) {
178
+ return scalarMult(CURVE.Gu, scalar);
179
+ }
180
+ return {
181
+ // 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'
182
+ // Need to add generic conversion between twisted edwards and complimentary curve for JubJub
183
+ scalarMult,
184
+ scalarMultBase,
185
+ // NOTE: these function work on complimentary montgomery curve
186
+ // getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(publicKey, privateKey),
187
+ getPublicKey: (privateKey) => scalarMultBase(privateKey),
188
+ Gu: CURVE.Gu,
189
+ };
190
+ }
191
+ exports.montgomery = montgomery;
package/lib/utils.d.ts CHANGED
@@ -1,8 +1,32 @@
1
1
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ export declare type Hex = Uint8Array | string;
3
+ export declare type PrivKey = Hex | bigint | number;
4
+ export declare type BasicCurve = {
5
+ P: bigint;
6
+ n: bigint;
7
+ nBitLength?: number;
8
+ nByteLength?: number;
9
+ h: bigint;
10
+ Gx: bigint;
11
+ Gy: bigint;
12
+ };
13
+ export declare function validateOpts<T extends BasicCurve>(curve: T): Readonly<{
14
+ readonly nBitLength: number;
15
+ readonly nByteLength: number;
16
+ } & T>;
2
17
  export declare function bytesToHex(uint8a: Uint8Array): string;
3
18
  export declare function numberToHexUnpadded(num: number | bigint): string;
4
19
  export declare function hexToNumber(hex: string): bigint;
5
20
  export declare function hexToBytes(hex: string): Uint8Array;
6
- export declare function bytesToNumber(bytes: Uint8Array): bigint;
7
- export declare function ensureBytes(hex: string | Uint8Array): Uint8Array;
21
+ export declare function bytesToNumberBE(bytes: Uint8Array): bigint;
22
+ export declare function bytesToNumberLE(uint8a: Uint8Array): bigint;
23
+ export declare const numberToBytesBE: (n: bigint, len: number) => Uint8Array;
24
+ export declare const numberToBytesLE: (n: bigint, len: number) => Uint8Array;
25
+ export declare function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array;
8
26
  export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
27
+ export declare function nLength(n: bigint, nBitLength?: number): {
28
+ nBitLength: number;
29
+ nByteLength: number;
30
+ };
31
+ export declare function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE?: boolean): bigint;
32
+ export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean;
package/lib/utils.js CHANGED
@@ -1,9 +1,23 @@
1
1
  "use strict";
2
2
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
3
- // Convert between types
4
- // ---------------------
5
3
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.concatBytes = exports.ensureBytes = exports.bytesToNumber = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = void 0;
4
+ exports.equalBytes = exports.hashToPrivateScalar = exports.nLength = exports.concatBytes = exports.ensureBytes = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = exports.validateOpts = void 0;
5
+ function validateOpts(curve) {
6
+ for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) {
7
+ if (typeof curve[i] !== 'bigint')
8
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
9
+ }
10
+ for (const i of ['nBitLength', 'nByteLength']) {
11
+ if (curve[i] === undefined)
12
+ continue; // Optional
13
+ if (!Number.isSafeInteger(curve[i]))
14
+ throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
15
+ }
16
+ // Set defaults
17
+ return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve });
18
+ }
19
+ exports.validateOpts = validateOpts;
20
+ const mod = require("./modular.js");
7
21
  const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
8
22
  function bytesToHex(uint8a) {
9
23
  if (!(uint8a instanceof Uint8Array))
@@ -49,14 +63,27 @@ function hexToBytes(hex) {
49
63
  }
50
64
  exports.hexToBytes = hexToBytes;
51
65
  // Big Endian
52
- function bytesToNumber(bytes) {
66
+ function bytesToNumberBE(bytes) {
53
67
  return hexToNumber(bytesToHex(bytes));
54
68
  }
55
- exports.bytesToNumber = bytesToNumber;
56
- function ensureBytes(hex) {
69
+ exports.bytesToNumberBE = bytesToNumberBE;
70
+ function bytesToNumberLE(uint8a) {
71
+ if (!(uint8a instanceof Uint8Array))
72
+ throw new Error('Expected Uint8Array');
73
+ return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
74
+ }
75
+ exports.bytesToNumberLE = bytesToNumberLE;
76
+ const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
77
+ exports.numberToBytesBE = numberToBytesBE;
78
+ const numberToBytesLE = (n, len) => (0, exports.numberToBytesBE)(n, len).reverse();
79
+ exports.numberToBytesLE = numberToBytesLE;
80
+ function ensureBytes(hex, expectedLength) {
57
81
  // Uint8Array.from() instead of hash.slice() because node.js Buffer
58
82
  // is instance of Uint8Array, and its slice() creates **mutable** copy
59
- return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
83
+ const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
84
+ if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
85
+ throw new Error(`Expected ${expectedLength} bytes`);
86
+ return bytes;
60
87
  }
61
88
  exports.ensureBytes = ensureBytes;
62
89
  // Copies several Uint8Arrays into one.
@@ -75,3 +102,40 @@ function concatBytes(...arrays) {
75
102
  return result;
76
103
  }
77
104
  exports.concatBytes = concatBytes;
105
+ // CURVE.n lengths
106
+ function nLength(n, nBitLength) {
107
+ // Bit size, byte size of CURVE.n
108
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
109
+ const nByteLength = Math.ceil(_nBitLength / 8);
110
+ return { nBitLength: _nBitLength, nByteLength };
111
+ }
112
+ exports.nLength = nLength;
113
+ /**
114
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
115
+ * and convert them into private scalar, with the modulo bias being neglible.
116
+ * As per FIPS 186 B.4.1.
117
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
118
+ * @param hash hash output from sha512, or a similar function
119
+ * @returns valid private scalar
120
+ */
121
+ const _1n = BigInt(1);
122
+ function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) {
123
+ hash = ensureBytes(hash);
124
+ const orderLen = nLength(CURVE_ORDER).nByteLength;
125
+ const minLen = orderLen + 8;
126
+ if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
127
+ throw new Error('Expected valid bytes of private key as per FIPS 186');
128
+ const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
129
+ return mod.mod(num, CURVE_ORDER - _1n) + _1n;
130
+ }
131
+ exports.hashToPrivateScalar = hashToPrivateScalar;
132
+ function equalBytes(b1, b2) {
133
+ // We don't care about timing attacks here
134
+ if (b1.length !== b2.length)
135
+ return false;
136
+ for (let i = 0; i < b1.length; i++)
137
+ if (b1[i] !== b2[i])
138
+ return false;
139
+ return true;
140
+ }
141
+ exports.equalBytes = equalBytes;
@@ -1,4 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { BasicCurve, Hex, PrivKey } from './utils.js';
3
+ import { Group, GroupConstructor } from './group.js';
2
4
  export declare type CHash = {
3
5
  (message: Uint8Array | string): Uint8Array;
4
6
  blockLen: number;
@@ -15,15 +17,9 @@ declare type EndomorphismOpts = {
15
17
  k2: bigint;
16
18
  };
17
19
  };
18
- export declare type CurveType = {
20
+ export declare type CurveType = BasicCurve & {
19
21
  a: bigint;
20
22
  b: bigint;
21
- P: bigint;
22
- n: bigint;
23
- nBitLength?: number;
24
- nByteLength?: number;
25
- Gx: bigint;
26
- Gy: bigint;
27
23
  lowS?: boolean;
28
24
  hash: CHash;
29
25
  hmac: HmacFnSync;
@@ -32,17 +28,16 @@ export declare type CurveType = {
32
28
  sqrtMod?: (n: bigint) => bigint;
33
29
  endo?: EndomorphismOpts;
34
30
  };
35
- declare type Hex = Uint8Array | string;
36
- declare type PrivKey = Hex | bigint | number;
37
31
  declare function validateOpts(curve: CurveType): Readonly<{
38
- readonly a: bigint;
39
- readonly b: bigint;
32
+ readonly nBitLength: number;
33
+ readonly nByteLength: number;
40
34
  readonly P: bigint;
41
35
  readonly n: bigint;
42
- nBitLength: number;
43
- nByteLength: number;
36
+ readonly h: bigint;
44
37
  readonly Gx: bigint;
45
38
  readonly Gy: bigint;
39
+ readonly a: bigint;
40
+ readonly b: bigint;
46
41
  lowS: boolean;
47
42
  readonly hash: CHash;
48
43
  readonly hmac: HmacFnSync;
@@ -96,28 +91,21 @@ export declare type SignatureConstructor = {
96
91
  fromCompact(hex: Hex): SignatureType;
97
92
  fromDER(hex: Hex): SignatureType;
98
93
  };
99
- export interface JacobianPointType {
94
+ export interface JacobianPointType extends Group<JacobianPointType> {
100
95
  readonly x: bigint;
101
96
  readonly y: bigint;
102
97
  readonly z: bigint;
103
- equals(other: JacobianPointType): boolean;
104
- negate(): JacobianPointType;
105
- double(): JacobianPointType;
106
- add(other: JacobianPointType): JacobianPointType;
107
- subtract(other: JacobianPointType): JacobianPointType;
108
- multiplyUnsafe(scalar: bigint): JacobianPointType;
109
98
  multiply(scalar: number | bigint, affinePoint?: PointType): JacobianPointType;
99
+ multiplyUnsafe(scalar: bigint): JacobianPointType;
110
100
  toAffine(invZ?: bigint): PointType;
111
101
  }
112
- export declare type JacobianPointConstructor = {
102
+ export interface JacobianPointConstructor extends GroupConstructor<JacobianPointType> {
113
103
  new (x: bigint, y: bigint, z: bigint): JacobianPointType;
114
- BASE: JacobianPointType;
115
- ZERO: JacobianPointType;
116
104
  fromAffine(p: PointType): JacobianPointType;
117
105
  toAffineBatch(points: JacobianPointType[]): PointType[];
118
106
  normalizeZ(points: JacobianPointType[]): JacobianPointType[];
119
- };
120
- export interface PointType {
107
+ }
108
+ export interface PointType extends Group<PointType> {
121
109
  readonly x: bigint;
122
110
  readonly y: bigint;
123
111
  _setWindowSize(windowSize: number): void;
@@ -125,21 +113,13 @@ export interface PointType {
125
113
  toRawBytes(isCompressed?: boolean): Uint8Array;
126
114
  toHex(isCompressed?: boolean): string;
127
115
  assertValidity(): void;
128
- equals(other: PointType): boolean;
129
- negate(): PointType;
130
- double(): PointType;
131
- add(other: PointType): PointType;
132
- subtract(other: PointType): PointType;
133
- multiply(scalar: number | bigint): PointType;
134
116
  multiplyAndAddUnsafe(Q: PointType, a: bigint, b: bigint): PointType | undefined;
135
117
  }
136
- export declare type PointConstructor = {
137
- BASE: PointType;
138
- ZERO: PointType;
118
+ export interface PointConstructor extends GroupConstructor<PointType> {
139
119
  new (x: bigint, y: bigint): PointType;
140
120
  fromHex(hex: Hex): PointType;
141
121
  fromPrivateKey(privateKey: PrivKey): PointType;
142
- };
122
+ }
143
123
  export declare type PubKey = Hex | PointType;
144
124
  export declare type CurveFn = {
145
125
  CURVE: ReturnType<typeof validateOpts>;
@@ -155,6 +135,13 @@ export declare type CurveFn = {
155
135
  utils: {
156
136
  mod: (a: bigint, b?: bigint) => bigint;
157
137
  invert: (number: bigint, modulo?: bigint) => bigint;
138
+ _bigintToBytes: (num: bigint) => Uint8Array;
139
+ _bigintToString: (num: bigint) => string;
140
+ _normalizePrivateKey: (key: PrivKey) => bigint;
141
+ _normalizePublicKey: (publicKey: PubKey) => PointType;
142
+ _isWithinCurveOrder: (num: bigint) => boolean;
143
+ _isValidFieldElement: (num: bigint) => boolean;
144
+ _weierstrassEquation: (x: bigint) => bigint;
158
145
  isValidPrivateKey(privateKey: PrivKey): boolean;
159
146
  hashToPrivateKey: (hash: Hex) => Uint8Array;
160
147
  randomPrivateKey: () => Uint8Array;