@noble/curves 0.6.3 → 0.7.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 (172) hide show
  1. package/README.md +429 -281
  2. package/{lib/_shortw_utils.d.ts → _shortw_utils.d.ts} +3 -1
  3. package/_shortw_utils.d.ts.map +1 -0
  4. package/{lib/_shortw_utils.js → _shortw_utils.js} +2 -0
  5. package/_shortw_utils.js.map +1 -0
  6. package/{lib/abstract → abstract}/bls.d.ts +4 -9
  7. package/abstract/bls.d.ts.map +1 -0
  8. package/{lib/abstract → abstract}/bls.js +12 -25
  9. package/abstract/bls.js.map +1 -0
  10. package/{lib/abstract → abstract}/curve.d.ts +1 -0
  11. package/abstract/curve.d.ts.map +1 -0
  12. package/{lib/abstract → abstract}/curve.js +1 -0
  13. package/abstract/curve.js.map +1 -0
  14. package/{lib/abstract → abstract}/edwards.d.ts +1 -0
  15. package/abstract/edwards.d.ts.map +1 -0
  16. package/{lib/abstract → abstract}/edwards.js +9 -15
  17. package/abstract/edwards.js.map +1 -0
  18. package/{lib/abstract → abstract}/hash-to-curve.d.ts +5 -5
  19. package/abstract/hash-to-curve.d.ts.map +1 -0
  20. package/{lib/abstract → abstract}/hash-to-curve.js +41 -38
  21. package/abstract/hash-to-curve.js.map +1 -0
  22. package/{lib/abstract → abstract}/modular.d.ts +1 -0
  23. package/abstract/modular.d.ts.map +1 -0
  24. package/{lib/abstract → abstract}/modular.js +2 -1
  25. package/abstract/modular.js.map +1 -0
  26. package/{lib/abstract → abstract}/montgomery.d.ts +1 -0
  27. package/abstract/montgomery.d.ts.map +1 -0
  28. package/{lib/abstract → abstract}/montgomery.js +3 -2
  29. package/abstract/montgomery.js.map +1 -0
  30. package/{lib/abstract → abstract}/poseidon.d.ts +1 -0
  31. package/abstract/poseidon.d.ts.map +1 -0
  32. package/{lib/abstract → abstract}/poseidon.js +1 -0
  33. package/abstract/poseidon.js.map +1 -0
  34. package/{lib/abstract → abstract}/utils.d.ts +12 -1
  35. package/abstract/utils.d.ts.map +1 -0
  36. package/{lib/abstract → abstract}/utils.js +96 -10
  37. package/abstract/utils.js.map +1 -0
  38. package/{lib/abstract → abstract}/weierstrass.d.ts +6 -4
  39. package/abstract/weierstrass.d.ts.map +1 -0
  40. package/{lib/abstract → abstract}/weierstrass.js +55 -93
  41. package/abstract/weierstrass.js.map +1 -0
  42. package/{lib/bls12-381.d.ts → bls12-381.d.ts} +1 -0
  43. package/bls12-381.d.ts.map +1 -0
  44. package/{lib/bls12-381.js → bls12-381.js} +41 -7
  45. package/bls12-381.js.map +1 -0
  46. package/{lib/bn.d.ts → bn.d.ts} +1 -0
  47. package/bn.d.ts.map +1 -0
  48. package/{lib/bn.js → bn.js} +1 -0
  49. package/bn.js.map +1 -0
  50. package/{lib/ed25519.d.ts → ed25519.d.ts} +2 -1
  51. package/ed25519.d.ts.map +1 -0
  52. package/{lib/ed25519.js → ed25519.js} +4 -3
  53. package/ed25519.js.map +1 -0
  54. package/{lib/ed448.d.ts → ed448.d.ts} +2 -1
  55. package/ed448.d.ts.map +1 -0
  56. package/{lib/ed448.js → ed448.js} +2 -1
  57. package/ed448.js.map +1 -0
  58. package/{lib/esm → esm}/_shortw_utils.js +2 -0
  59. package/esm/_shortw_utils.js.map +1 -0
  60. package/{lib/esm → esm}/abstract/bls.js +13 -26
  61. package/esm/abstract/bls.js.map +1 -0
  62. package/{lib/esm → esm}/abstract/curve.js +1 -0
  63. package/esm/abstract/curve.js.map +1 -0
  64. package/{lib/esm → esm}/abstract/edwards.js +9 -15
  65. package/esm/abstract/edwards.js.map +1 -0
  66. package/{lib/esm → esm}/abstract/hash-to-curve.js +40 -36
  67. package/esm/abstract/hash-to-curve.js.map +1 -0
  68. package/{lib/esm → esm}/abstract/modular.js +2 -1
  69. package/esm/abstract/modular.js.map +1 -0
  70. package/{lib/esm → esm}/abstract/montgomery.js +3 -2
  71. package/esm/abstract/montgomery.js.map +1 -0
  72. package/{lib/esm → esm}/abstract/poseidon.js +1 -0
  73. package/esm/abstract/poseidon.js.map +1 -0
  74. package/{lib/esm → esm}/abstract/utils.js +93 -9
  75. package/esm/abstract/utils.js.map +1 -0
  76. package/{lib/esm → esm}/abstract/weierstrass.js +55 -93
  77. package/esm/abstract/weierstrass.js.map +1 -0
  78. package/{lib/esm → esm}/bls12-381.js +41 -7
  79. package/esm/bls12-381.js.map +1 -0
  80. package/{lib/esm → esm}/bn.js +1 -0
  81. package/esm/bn.js.map +1 -0
  82. package/{lib/esm → esm}/ed25519.js +5 -4
  83. package/esm/ed25519.js.map +1 -0
  84. package/{lib/esm → esm}/ed448.js +2 -1
  85. package/esm/ed448.js.map +1 -0
  86. package/{lib → esm}/index.js +1 -0
  87. package/esm/index.js.map +1 -0
  88. package/{lib/esm → esm}/jubjub.js +1 -0
  89. package/esm/jubjub.js.map +1 -0
  90. package/{lib/esm → esm}/p192.js +1 -0
  91. package/esm/p192.js.map +1 -0
  92. package/{lib/esm → esm}/p224.js +1 -0
  93. package/esm/p224.js.map +1 -0
  94. package/{lib/esm → esm}/p256.js +2 -1
  95. package/esm/p256.js.map +1 -0
  96. package/{lib/esm → esm}/p384.js +2 -1
  97. package/esm/p384.js.map +1 -0
  98. package/{lib/esm → esm}/p521.js +2 -1
  99. package/esm/p521.js.map +1 -0
  100. package/{lib/esm → esm}/package.json +0 -0
  101. package/{lib/esm → esm}/pasta.js +1 -0
  102. package/esm/pasta.js.map +1 -0
  103. package/{lib/esm → esm}/secp256k1.js +41 -50
  104. package/esm/secp256k1.js.map +1 -0
  105. package/{lib/esm → esm}/stark.js +1 -0
  106. package/esm/stark.js.map +1 -0
  107. package/index.d.ts +1 -0
  108. package/index.d.ts.map +1 -0
  109. package/index.js +3 -0
  110. package/index.js.map +1 -0
  111. package/{lib/jubjub.d.ts → jubjub.d.ts} +1 -0
  112. package/jubjub.d.ts.map +1 -0
  113. package/{lib/jubjub.js → jubjub.js} +1 -0
  114. package/jubjub.js.map +1 -0
  115. package/{lib/p192.d.ts → p192.d.ts} +5 -2
  116. package/p192.d.ts.map +1 -0
  117. package/{lib/p192.js → p192.js} +1 -0
  118. package/p192.js.map +1 -0
  119. package/{lib/p224.d.ts → p224.d.ts} +5 -2
  120. package/p224.d.ts.map +1 -0
  121. package/{lib/p224.js → p224.js} +1 -0
  122. package/p224.js.map +1 -0
  123. package/{lib/p256.d.ts → p256.d.ts} +6 -3
  124. package/p256.d.ts.map +1 -0
  125. package/{lib/p256.js → p256.js} +2 -1
  126. package/p256.js.map +1 -0
  127. package/{lib/p384.d.ts → p384.d.ts} +6 -3
  128. package/p384.d.ts.map +1 -0
  129. package/{lib/p384.js → p384.js} +2 -1
  130. package/p384.js.map +1 -0
  131. package/{lib/p521.d.ts → p521.d.ts} +6 -3
  132. package/p521.d.ts.map +1 -0
  133. package/{lib/p521.js → p521.js} +2 -1
  134. package/p521.js.map +1 -0
  135. package/package.json +84 -79
  136. package/{lib/pasta.d.ts → pasta.d.ts} +1 -0
  137. package/pasta.d.ts.map +1 -0
  138. package/{lib/pasta.js → pasta.js} +1 -0
  139. package/pasta.js.map +1 -0
  140. package/{lib/secp256k1.d.ts → secp256k1.d.ts} +17 -6
  141. package/secp256k1.d.ts.map +1 -0
  142. package/{lib/secp256k1.js → secp256k1.js} +38 -47
  143. package/secp256k1.js.map +1 -0
  144. package/src/_shortw_utils.ts +20 -0
  145. package/src/abstract/bls.ts +376 -0
  146. package/src/abstract/curve.ts +199 -0
  147. package/src/abstract/edwards.ts +479 -0
  148. package/src/abstract/hash-to-curve.ts +220 -0
  149. package/src/abstract/modular.ts +417 -0
  150. package/src/abstract/montgomery.ts +184 -0
  151. package/src/abstract/poseidon.ts +119 -0
  152. package/src/abstract/utils.ts +246 -0
  153. package/src/abstract/weierstrass.ts +1175 -0
  154. package/src/bls12-381.ts +1274 -0
  155. package/src/bn.ts +21 -0
  156. package/src/ed25519.ts +428 -0
  157. package/src/ed448.ts +241 -0
  158. package/{lib/esm/index.js → src/index.ts} +0 -1
  159. package/src/jubjub.ts +58 -0
  160. package/src/p192.ts +25 -0
  161. package/src/p224.ts +25 -0
  162. package/src/p256.ts +53 -0
  163. package/src/p384.ts +57 -0
  164. package/src/p521.ts +57 -0
  165. package/src/pasta.ts +31 -0
  166. package/src/secp256k1.ts +260 -0
  167. package/src/stark.ts +356 -0
  168. package/{lib/stark.d.ts → stark.d.ts} +3 -1
  169. package/stark.d.ts.map +1 -0
  170. package/{lib/stark.js → stark.js} +1 -0
  171. package/stark.js.map +1 -0
  172. package/lib/index.d.ts +0 -0
@@ -0,0 +1,199 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // Abelian group utilities
3
+ import { Field, validateField, nLength } from './modular.js';
4
+ import { validateObject } from './utils.js';
5
+ const _0n = BigInt(0);
6
+ const _1n = BigInt(1);
7
+
8
+ export type AffinePoint<T> = {
9
+ x: T;
10
+ y: T;
11
+ } & { z?: never; t?: never };
12
+
13
+ export interface Group<T extends Group<T>> {
14
+ double(): T;
15
+ negate(): T;
16
+ add(other: T): T;
17
+ subtract(other: T): T;
18
+ equals(other: T): boolean;
19
+ multiply(scalar: bigint): T;
20
+ }
21
+
22
+ export type GroupConstructor<T> = {
23
+ BASE: T;
24
+ ZERO: T;
25
+ };
26
+ export type Mapper<T> = (i: T[]) => T[];
27
+
28
+ // Elliptic curve multiplication of Point by scalar. Fragile.
29
+ // Scalars should always be less than curve order: this should be checked inside of a curve itself.
30
+ // Creates precomputation tables for fast multiplication:
31
+ // - private scalar is split by fixed size windows of W bits
32
+ // - every window point is collected from window's table & added to accumulator
33
+ // - since windows are different, same point inside tables won't be accessed more than once per calc
34
+ // - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
35
+ // - +1 window is neccessary for wNAF
36
+ // - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
37
+ // TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
38
+ // windows to be in different memory locations
39
+ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
40
+ const constTimeNegate = (condition: boolean, item: T): T => {
41
+ const neg = item.negate();
42
+ return condition ? neg : item;
43
+ };
44
+ const opts = (W: number) => {
45
+ const windows = Math.ceil(bits / W) + 1; // +1, because
46
+ const windowSize = 2 ** (W - 1); // -1 because we skip zero
47
+ return { windows, windowSize };
48
+ };
49
+ return {
50
+ constTimeNegate,
51
+ // non-const time multiplication ladder
52
+ unsafeLadder(elm: T, n: bigint) {
53
+ let p = c.ZERO;
54
+ let d: T = elm;
55
+ while (n > _0n) {
56
+ if (n & _1n) p = p.add(d);
57
+ d = d.double();
58
+ n >>= _1n;
59
+ }
60
+ return p;
61
+ },
62
+
63
+ /**
64
+ * Creates a wNAF precomputation window. Used for caching.
65
+ * Default window size is set by `utils.precompute()` and is equal to 8.
66
+ * Number of precomputed points depends on the curve size:
67
+ * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
68
+ * - 𝑊 is the window size
69
+ * - 𝑛 is the bitlength of the curve order.
70
+ * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
71
+ * @returns precomputed point tables flattened to a single array
72
+ */
73
+ precomputeWindow(elm: T, W: number): Group<T>[] {
74
+ const { windows, windowSize } = opts(W);
75
+ const points: T[] = [];
76
+ let p: T = elm;
77
+ let base = p;
78
+ for (let window = 0; window < windows; window++) {
79
+ base = p;
80
+ points.push(base);
81
+ // =1, because we skip zero
82
+ for (let i = 1; i < windowSize; i++) {
83
+ base = base.add(p);
84
+ points.push(base);
85
+ }
86
+ p = base.double();
87
+ }
88
+ return points;
89
+ },
90
+
91
+ /**
92
+ * Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
93
+ * @param W window size
94
+ * @param precomputes precomputed tables
95
+ * @param n scalar (we don't check here, but should be less than curve order)
96
+ * @returns real and fake (for const-time) points
97
+ */
98
+ wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
99
+ // TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
100
+ // But need to carefully remove other checks before wNAF. ORDER == bits here
101
+ const { windows, windowSize } = opts(W);
102
+
103
+ let p = c.ZERO;
104
+ let f = c.BASE;
105
+
106
+ const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
107
+ const maxNumber = 2 ** W;
108
+ const shiftBy = BigInt(W);
109
+
110
+ for (let window = 0; window < windows; window++) {
111
+ const offset = window * windowSize;
112
+ // Extract W bits.
113
+ let wbits = Number(n & mask);
114
+
115
+ // Shift number by W bits.
116
+ n >>= shiftBy;
117
+
118
+ // If the bits are bigger than max size, we'll split those.
119
+ // +224 => 256 - 32
120
+ if (wbits > windowSize) {
121
+ wbits -= maxNumber;
122
+ n += _1n;
123
+ }
124
+
125
+ // This code was first written with assumption that 'f' and 'p' will never be infinity point:
126
+ // since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
127
+ // there is negate now: it is possible that negated element from low value
128
+ // would be the same as high element, which will create carry into next window.
129
+ // It's not obvious how this can fail, but still worth investigating later.
130
+
131
+ // Check if we're onto Zero point.
132
+ // Add random point inside current window to f.
133
+ const offset1 = offset;
134
+ const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
135
+ const cond1 = window % 2 !== 0;
136
+ const cond2 = wbits < 0;
137
+ if (wbits === 0) {
138
+ // The most important part for const-time getPublicKey
139
+ f = f.add(constTimeNegate(cond1, precomputes[offset1]));
140
+ } else {
141
+ p = p.add(constTimeNegate(cond2, precomputes[offset2]));
142
+ }
143
+ }
144
+ // JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
145
+ // Even if the variable is still unused, there are some checks which will
146
+ // throw an exception, so compiler needs to prove they won't happen, which is hard.
147
+ // At this point there is a way to F be infinity-point even if p is not,
148
+ // which makes it less const-time: around 1 bigint multiply.
149
+ return { p, f };
150
+ },
151
+
152
+ wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
153
+ // @ts-ignore
154
+ const W: number = P._WINDOW_SIZE || 1;
155
+ // Calculate precomputes on a first run, reuse them after
156
+ let comp = precomputesMap.get(P);
157
+ if (!comp) {
158
+ comp = this.precomputeWindow(P, W) as T[];
159
+ if (W !== 1) {
160
+ precomputesMap.set(P, transform(comp));
161
+ }
162
+ }
163
+ return this.wNAF(W, comp, n);
164
+ },
165
+ };
166
+ }
167
+
168
+ // Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
169
+ // Though generator can be different (Fp2 / Fp6 for BLS).
170
+ export type BasicCurve<T> = {
171
+ Fp: Field<T>; // Field over which we'll do calculations (Fp)
172
+ n: bigint; // Curve order, total count of valid points in the field
173
+ nBitLength?: number; // bit length of curve order
174
+ nByteLength?: number; // byte length of curve order
175
+ h: bigint; // cofactor. we can assign default=1, but users will just ignore it w/o validation
176
+ hEff?: bigint; // Number to multiply to clear cofactor
177
+ Gx: T; // base point X coordinate
178
+ Gy: T; // base point Y coordinate
179
+ allowInfinityPoint?: boolean; // bls12-381 requires it. ZERO point is valid, but invalid pubkey
180
+ };
181
+
182
+ export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
183
+ validateField(curve.Fp);
184
+ validateObject(
185
+ curve,
186
+ {
187
+ n: 'bigint',
188
+ h: 'bigint',
189
+ Gx: 'field',
190
+ Gy: 'field',
191
+ },
192
+ {
193
+ nBitLength: 'isSafeInteger',
194
+ nByteLength: 'isSafeInteger',
195
+ }
196
+ );
197
+ // Set defaults
198
+ return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
199
+ }
@@ -0,0 +1,479 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
3
+ import { mod } from './modular.js';
4
+ import * as ut from './utils.js';
5
+ import { ensureBytes, FHash, Hex } from './utils.js';
6
+ import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
7
+
8
+ // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
9
+ const _0n = BigInt(0);
10
+ const _1n = BigInt(1);
11
+ const _2n = BigInt(2);
12
+ const _8n = BigInt(8);
13
+
14
+ // Edwards curves must declare params a & d.
15
+ export type CurveType = BasicCurve<bigint> & {
16
+ a: bigint; // curve param a
17
+ d: bigint; // curve param d
18
+ hash: FHash; // Hashing
19
+ randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
20
+ adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
21
+ domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
22
+ uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
23
+ preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
24
+ mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
25
+ };
26
+
27
+ function validateOpts(curve: CurveType) {
28
+ const opts = validateBasic(curve);
29
+ ut.validateObject(
30
+ curve,
31
+ {
32
+ hash: 'function',
33
+ a: 'bigint',
34
+ d: 'bigint',
35
+ randomBytes: 'function',
36
+ },
37
+ {
38
+ adjustScalarBytes: 'function',
39
+ domain: 'function',
40
+ uvRatio: 'function',
41
+ mapToCurve: 'function',
42
+ }
43
+ );
44
+ // Set defaults
45
+ return Object.freeze({ ...opts } as const);
46
+ }
47
+
48
+ // Instance of Extended Point with coordinates in X, Y, Z, T
49
+ export interface ExtPointType extends Group<ExtPointType> {
50
+ readonly ex: bigint;
51
+ readonly ey: bigint;
52
+ readonly ez: bigint;
53
+ readonly et: bigint;
54
+ assertValidity(): void;
55
+ multiply(scalar: bigint): ExtPointType;
56
+ multiplyUnsafe(scalar: bigint): ExtPointType;
57
+ isSmallOrder(): boolean;
58
+ isTorsionFree(): boolean;
59
+ clearCofactor(): ExtPointType;
60
+ toAffine(iz?: bigint): AffinePoint<bigint>;
61
+ }
62
+ // Static methods of Extended Point with coordinates in X, Y, Z, T
63
+ export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
64
+ new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
65
+ fromAffine(p: AffinePoint<bigint>): ExtPointType;
66
+ fromHex(hex: Hex): ExtPointType;
67
+ fromPrivateKey(privateKey: Hex): ExtPointType;
68
+ }
69
+
70
+ export type CurveFn = {
71
+ CURVE: ReturnType<typeof validateOpts>;
72
+ getPublicKey: (privateKey: Hex) => Uint8Array;
73
+ sign: (message: Hex, privateKey: Hex) => Uint8Array;
74
+ verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
75
+ ExtendedPoint: ExtPointConstructor;
76
+ utils: {
77
+ randomPrivateKey: () => Uint8Array;
78
+ getExtendedPublicKey: (key: Hex) => {
79
+ head: Uint8Array;
80
+ prefix: Uint8Array;
81
+ scalar: bigint;
82
+ point: ExtPointType;
83
+ pointBytes: Uint8Array;
84
+ };
85
+ };
86
+ };
87
+
88
+ // It is not generic twisted curve for now, but ed25519/ed448 generic implementation
89
+ export function twistedEdwards(curveDef: CurveType): CurveFn {
90
+ const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
91
+ const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
92
+ const MASK = _2n ** BigInt(nByteLength * 8);
93
+ const modP = Fp.create; // Function overrides
94
+
95
+ // sqrt(u/v)
96
+ const uvRatio =
97
+ CURVE.uvRatio ||
98
+ ((u: bigint, v: bigint) => {
99
+ try {
100
+ return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
101
+ } catch (e) {
102
+ return { isValid: false, value: _0n };
103
+ }
104
+ });
105
+ const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
106
+ const domain =
107
+ CURVE.domain ||
108
+ ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
109
+ if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
110
+ return data;
111
+ }); // NOOP
112
+ const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
113
+ const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
114
+ const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
115
+ function assertInRange(n: bigint, max: bigint) {
116
+ // n in [1..max-1]
117
+ if (inRange(n, max)) return n;
118
+ throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
119
+ }
120
+ function assertGE0(n: bigint) {
121
+ // n in [0..CURVE_ORDER-1]
122
+ return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
123
+ }
124
+ const pointPrecomputes = new Map<Point, Point[]>();
125
+ function isPoint(other: unknown) {
126
+ if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
127
+ }
128
+ // Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
129
+ // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
130
+ class Point implements ExtPointType {
131
+ static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
132
+ static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
133
+
134
+ constructor(
135
+ readonly ex: bigint,
136
+ readonly ey: bigint,
137
+ readonly ez: bigint,
138
+ readonly et: bigint
139
+ ) {
140
+ if (!in0MaskRange(ex)) throw new Error('x required');
141
+ if (!in0MaskRange(ey)) throw new Error('y required');
142
+ if (!in0MaskRange(ez)) throw new Error('z required');
143
+ if (!in0MaskRange(et)) throw new Error('t required');
144
+ }
145
+
146
+ get x(): bigint {
147
+ return this.toAffine().x;
148
+ }
149
+ get y(): bigint {
150
+ return this.toAffine().y;
151
+ }
152
+
153
+ static fromAffine(p: AffinePoint<bigint>): Point {
154
+ if (p instanceof Point) throw new Error('extended point not allowed');
155
+ const { x, y } = p || {};
156
+ if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
157
+ return new Point(x, y, _1n, modP(x * y));
158
+ }
159
+ static normalizeZ(points: Point[]): Point[] {
160
+ const toInv = Fp.invertBatch(points.map((p) => p.ez));
161
+ return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
162
+ }
163
+
164
+ // We calculate precomputes for elliptic curve point multiplication
165
+ // using windowed method. This specifies window size and
166
+ // stores precomputed values. Usually only base point would be precomputed.
167
+ _WINDOW_SIZE?: number;
168
+
169
+ // "Private method", don't use it directly
170
+ _setWindowSize(windowSize: number) {
171
+ this._WINDOW_SIZE = windowSize;
172
+ pointPrecomputes.delete(this);
173
+ }
174
+ // Not required for fromHex(), which always creates valid points.
175
+ // Could be useful for fromAffine().
176
+ assertValidity(): void {
177
+ const { a, d } = CURVE;
178
+ if (this.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
179
+ // Equation in affine coordinates: ax² + y² = 1 + dx²y²
180
+ // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
181
+ const { ex: X, ey: Y, ez: Z, et: T } = this;
182
+ const X2 = modP(X * X); // X²
183
+ const Y2 = modP(Y * Y); // Y²
184
+ const Z2 = modP(Z * Z); // Z²
185
+ const Z4 = modP(Z2 * Z2); // Z⁴
186
+ const aX2 = modP(X2 * a); // aX²
187
+ const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
188
+ const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
189
+ if (left !== right) throw new Error('bad point: equation left != right (1)');
190
+ // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
191
+ const XY = modP(X * Y);
192
+ const ZT = modP(Z * T);
193
+ if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
194
+ }
195
+
196
+ // Compare one point to another.
197
+ equals(other: Point): boolean {
198
+ isPoint(other);
199
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
200
+ const { ex: X2, ey: Y2, ez: Z2 } = other;
201
+ const X1Z2 = modP(X1 * Z2);
202
+ const X2Z1 = modP(X2 * Z1);
203
+ const Y1Z2 = modP(Y1 * Z2);
204
+ const Y2Z1 = modP(Y2 * Z1);
205
+ return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
206
+ }
207
+
208
+ protected is0(): boolean {
209
+ return this.equals(Point.ZERO);
210
+ }
211
+
212
+ negate(): Point {
213
+ // Flips point sign to a negative one (-x, y in affine coords)
214
+ return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
215
+ }
216
+
217
+ // Fast algo for doubling Extended Point.
218
+ // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
219
+ // Cost: 4M + 4S + 1*a + 6add + 1*2.
220
+ double(): Point {
221
+ const { a } = CURVE;
222
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
223
+ const A = modP(X1 * X1); // A = X12
224
+ const B = modP(Y1 * Y1); // B = Y12
225
+ const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
226
+ const D = modP(a * A); // D = a*A
227
+ const x1y1 = X1 + Y1;
228
+ const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
229
+ const G = D + B; // G = D+B
230
+ const F = G - C; // F = G-C
231
+ const H = D - B; // H = D-B
232
+ const X3 = modP(E * F); // X3 = E*F
233
+ const Y3 = modP(G * H); // Y3 = G*H
234
+ const T3 = modP(E * H); // T3 = E*H
235
+ const Z3 = modP(F * G); // Z3 = F*G
236
+ return new Point(X3, Y3, Z3, T3);
237
+ }
238
+
239
+ // Fast algo for adding 2 Extended Points.
240
+ // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
241
+ // Cost: 9M + 1*a + 1*d + 7add.
242
+ add(other: Point) {
243
+ isPoint(other);
244
+ const { a, d } = CURVE;
245
+ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
246
+ const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
247
+ // Faster algo for adding 2 Extended Points when curve's a=-1.
248
+ // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
249
+ // Cost: 8M + 8add + 2*2.
250
+ // Note: It does not check whether the `other` point is valid.
251
+ if (a === BigInt(-1)) {
252
+ const A = modP((Y1 - X1) * (Y2 + X2));
253
+ const B = modP((Y1 + X1) * (Y2 - X2));
254
+ const F = modP(B - A);
255
+ if (F === _0n) return this.double(); // Same point. Tests say it doesn't affect timing
256
+ const C = modP(Z1 * _2n * T2);
257
+ const D = modP(T1 * _2n * Z2);
258
+ const E = D + C;
259
+ const G = B + A;
260
+ const H = D - C;
261
+ const X3 = modP(E * F);
262
+ const Y3 = modP(G * H);
263
+ const T3 = modP(E * H);
264
+ const Z3 = modP(F * G);
265
+ return new Point(X3, Y3, Z3, T3);
266
+ }
267
+ const A = modP(X1 * X2); // A = X1*X2
268
+ const B = modP(Y1 * Y2); // B = Y1*Y2
269
+ const C = modP(T1 * d * T2); // C = T1*d*T2
270
+ const D = modP(Z1 * Z2); // D = Z1*Z2
271
+ const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
272
+ const F = D - C; // F = D-C
273
+ const G = D + C; // G = D+C
274
+ const H = modP(B - a * A); // H = B-a*A
275
+ const X3 = modP(E * F); // X3 = E*F
276
+ const Y3 = modP(G * H); // Y3 = G*H
277
+ const T3 = modP(E * H); // T3 = E*H
278
+ const Z3 = modP(F * G); // Z3 = F*G
279
+
280
+ return new Point(X3, Y3, Z3, T3);
281
+ }
282
+
283
+ subtract(other: Point): Point {
284
+ return this.add(other.negate());
285
+ }
286
+
287
+ private wNAF(n: bigint): { p: Point; f: Point } {
288
+ return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
289
+ }
290
+
291
+ // Constant-time multiplication.
292
+ multiply(scalar: bigint): Point {
293
+ const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
294
+ return Point.normalizeZ([p, f])[0];
295
+ }
296
+
297
+ // Non-constant-time multiplication. Uses double-and-add algorithm.
298
+ // It's faster, but should only be used when you don't care about
299
+ // an exposed private key e.g. sig verification.
300
+ multiplyUnsafe(scalar: bigint): Point {
301
+ let n = assertGE0(scalar);
302
+ if (n === _0n) return I;
303
+ if (this.equals(I) || n === _1n) return this;
304
+ if (this.equals(G)) return this.wNAF(n).p;
305
+ return wnaf.unsafeLadder(this, n);
306
+ }
307
+
308
+ // Checks if point is of small order.
309
+ // If you add something to small order point, you will have "dirty"
310
+ // point with torsion component.
311
+ // Multiplies point by cofactor and checks if the result is 0.
312
+ isSmallOrder(): boolean {
313
+ return this.multiplyUnsafe(cofactor).is0();
314
+ }
315
+
316
+ // Multiplies point by curve order and checks if the result is 0.
317
+ // Returns `false` is the point is dirty.
318
+ isTorsionFree(): boolean {
319
+ return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
320
+ }
321
+
322
+ // Converts Extended point to default (x, y) coordinates.
323
+ // Can accept precomputed Z^-1 - for example, from invertBatch.
324
+ toAffine(iz?: bigint): AffinePoint<bigint> {
325
+ const { ex: x, ey: y, ez: z } = this;
326
+ const is0 = this.is0();
327
+ if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
328
+ const ax = modP(x * iz);
329
+ const ay = modP(y * iz);
330
+ const zz = modP(z * iz);
331
+ if (is0) return { x: _0n, y: _1n };
332
+ if (zz !== _1n) throw new Error('invZ was invalid');
333
+ return { x: ax, y: ay };
334
+ }
335
+
336
+ clearCofactor(): Point {
337
+ const { h: cofactor } = CURVE;
338
+ if (cofactor === _1n) return this;
339
+ return this.multiplyUnsafe(cofactor);
340
+ }
341
+
342
+ // Converts hash string or Uint8Array to Point.
343
+ // Uses algo from RFC8032 5.1.3.
344
+ static fromHex(hex: Hex, strict = true): Point {
345
+ const { d, a } = CURVE;
346
+ const len = Fp.BYTES;
347
+ hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
348
+ const normed = hex.slice(); // copy again, we'll manipulate it
349
+ const lastByte = hex[len - 1]; // select last byte
350
+ normed[len - 1] = lastByte & ~0x80; // clear last bit
351
+ const y = ut.bytesToNumberLE(normed);
352
+ if (y === _0n) {
353
+ // y=0 is allowed
354
+ } else {
355
+ // RFC8032 prohibits >= p, but ZIP215 doesn't
356
+ if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
357
+ else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
358
+ }
359
+
360
+ // Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
361
+ // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
362
+ const y2 = modP(y * y); // denominator is always non-0 mod p.
363
+ const u = modP(y2 - _1n); // u = y² - 1
364
+ const v = modP(d * y2 - a); // v = d y² + 1.
365
+ let { isValid, value: x } = uvRatio(u, v); // √(u/v)
366
+ if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
367
+ const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
368
+ const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
369
+ if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
370
+ return Point.fromAffine({ x, y });
371
+ }
372
+ static fromPrivateKey(privKey: Hex) {
373
+ return getExtendedPublicKey(privKey).point;
374
+ }
375
+ toRawBytes(): Uint8Array {
376
+ const { x, y } = this.toAffine();
377
+ const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
378
+ bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
379
+ return bytes; // and use the last byte to encode sign of x
380
+ }
381
+ toHex(): string {
382
+ return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
383
+ }
384
+ }
385
+ const { BASE: G, ZERO: I } = Point;
386
+ const wnaf = wNAF(Point, nByteLength * 8);
387
+
388
+ function modN(a: bigint) {
389
+ return mod(a, CURVE_ORDER);
390
+ }
391
+ // Little-endian SHA512 with modulo n
392
+ function modN_LE(hash: Uint8Array): bigint {
393
+ return modN(ut.bytesToNumberLE(hash));
394
+ }
395
+
396
+ /** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
397
+ function getExtendedPublicKey(key: Hex) {
398
+ const len = nByteLength;
399
+ key = ensureBytes('private key', key, len);
400
+ // Hash private key with curve's hash function to produce uniformingly random input
401
+ // Check byte lengths: ensure(64, h(ensure(32, key)))
402
+ const hashed = ensureBytes('hashed private key', cHash(key), 2 * len);
403
+ const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
404
+ const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
405
+ const scalar = modN_LE(head); // The actual private scalar
406
+ const point = G.multiply(scalar); // Point on Edwards curve aka public key
407
+ const pointBytes = point.toRawBytes(); // Uint8Array representation
408
+ return { head, prefix, scalar, point, pointBytes };
409
+ }
410
+
411
+ // Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
412
+ function getPublicKey(privKey: Hex): Uint8Array {
413
+ return getExtendedPublicKey(privKey).pointBytes;
414
+ }
415
+
416
+ // int('LE', SHA512(dom2(F, C) || msgs)) mod N
417
+ function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
418
+ const msg = ut.concatBytes(...msgs);
419
+ return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!preHash)));
420
+ }
421
+
422
+ /** Signs message with privateKey. RFC8032 5.1.6 */
423
+ function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
424
+ msg = ensureBytes('message', msg);
425
+ if (preHash) msg = preHash(msg); // for ed25519ph etc.
426
+ const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
427
+ const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
428
+ const R = G.multiply(r).toRawBytes(); // R = rG
429
+ const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
430
+ const s = modN(r + k * scalar); // S = (r + k * s) mod L
431
+ assertGE0(s); // 0 <= s < l
432
+ const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
433
+ return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
434
+ }
435
+
436
+ function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
437
+ const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
438
+ sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
439
+ msg = ensureBytes('message', msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
440
+ if (preHash) msg = preHash(msg); // for ed25519ph, etc
441
+ const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
442
+ const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
443
+ const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
444
+ const SB = G.multiplyUnsafe(s);
445
+ const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
446
+ const RkA = R.add(A.multiplyUnsafe(k));
447
+ // [8][S]B = [8]R + [8][k]A'
448
+ return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
449
+ }
450
+
451
+ G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
452
+
453
+ const utils = {
454
+ getExtendedPublicKey,
455
+ // ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
456
+ randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
457
+
458
+ /**
459
+ * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
460
+ * values. This slows down first getPublicKey() by milliseconds (see Speed section),
461
+ * but allows to speed-up subsequent getPublicKey() calls up to 20x.
462
+ * @param windowSize 2, 4, 8, 16
463
+ */
464
+ precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
465
+ point._setWindowSize(windowSize);
466
+ point.multiply(BigInt(3));
467
+ return point;
468
+ },
469
+ };
470
+
471
+ return {
472
+ CURVE,
473
+ getPublicKey,
474
+ sign,
475
+ verify,
476
+ ExtendedPoint: Point,
477
+ utils,
478
+ };
479
+ }