@gjsify/crypto 0.3.13 → 0.3.15

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/lib/esm/ecdh.js CHANGED
@@ -1,356 +1,438 @@
1
- import { Buffer } from "node:buffer";
2
- import { randomBytes } from "./random.js";
3
1
  import { modPow } from "./bigint-math.js";
2
+ import { randomBytes } from "./random.js";
3
+ import { Buffer } from "node:buffer";
4
+
5
+ //#region src/ecdh.ts
4
6
  const CURVES = {
5
- secp256k1: {
6
- p: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2Fn,
7
- a: 0n,
8
- b: 7n,
9
- Gx: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798n,
10
- Gy: 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8n,
11
- n: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n,
12
- byteLength: 32
13
- },
14
- prime256v1: {
15
- p: 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFn,
16
- a: 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFn - 3n,
17
- b: 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604Bn,
18
- Gx: 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296n,
19
- Gy: 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5n,
20
- n: 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551n,
21
- byteLength: 32
22
- },
23
- secp384r1: {
24
- p: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFFn,
25
- a: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFFn - 3n,
26
- b: 0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEFn,
27
- Gx: 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7n,
28
- Gy: 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5Fn,
29
- n: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973n,
30
- byteLength: 48
31
- },
32
- secp521r1: {
33
- p: 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn,
34
- a: 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn - 3n,
35
- b: 0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00n,
36
- Gx: 0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66n,
37
- Gy: 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650n,
38
- n: 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409n,
39
- byteLength: 66
40
- }
7
+ secp256k1: {
8
+ p: 115792089237316195423570985008687907853269984665640564039457584007908834671663n,
9
+ a: 0n,
10
+ b: 7n,
11
+ Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
12
+ Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
13
+ n: 115792089237316195423570985008687907852837564279074904382605163141518161494337n,
14
+ byteLength: 32
15
+ },
16
+ prime256v1: {
17
+ p: 115792089210356248762697446949407573530086143415290314195533631308867097853951n,
18
+ a: 115792089210356248762697446949407573530086143415290314195533631308867097853951n - 3n,
19
+ b: 41058363725152142129326129780047268409114441015993725554835256314039467401291n,
20
+ Gx: 48439561293906451759052585252797914202762949526041747995844080717082404635286n,
21
+ Gy: 36134250956749795798585127919587881956611106672985015071877198253568414405109n,
22
+ n: 115792089210356248762697446949407573529996955224135760342422259061068512044369n,
23
+ byteLength: 32
24
+ },
25
+ secp384r1: {
26
+ p: 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319n,
27
+ a: 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319n - 3n,
28
+ b: 27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575n,
29
+ Gx: 26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087n,
30
+ Gy: 8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871n,
31
+ n: 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643n,
32
+ byteLength: 48
33
+ },
34
+ secp521r1: {
35
+ p: 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151n,
36
+ a: 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151n - 3n,
37
+ b: 1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984n,
38
+ Gx: 2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846n,
39
+ Gy: 3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784n,
40
+ n: 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449n,
41
+ byteLength: 66
42
+ }
41
43
  };
42
44
  const CURVE_ALIASES = {
43
- "secp256k1": "secp256k1",
44
- "prime256v1": "prime256v1",
45
- "secp256r1": "prime256v1",
46
- "p-256": "prime256v1",
47
- "p256": "prime256v1",
48
- "secp384r1": "secp384r1",
49
- "p-384": "secp384r1",
50
- "p384": "secp384r1",
51
- "secp521r1": "secp521r1",
52
- "p-521": "secp521r1",
53
- "p521": "secp521r1"
45
+ "secp256k1": "secp256k1",
46
+ "prime256v1": "prime256v1",
47
+ "secp256r1": "prime256v1",
48
+ "p-256": "prime256v1",
49
+ "p256": "prime256v1",
50
+ "secp384r1": "secp384r1",
51
+ "p-384": "secp384r1",
52
+ "p384": "secp384r1",
53
+ "secp521r1": "secp521r1",
54
+ "p-521": "secp521r1",
55
+ "p521": "secp521r1"
54
56
  };
57
+ /**
58
+ * Non-negative modulus: always returns a value in [0, mod).
59
+ */
55
60
  function mod(a, m) {
56
- const r = a % m;
57
- return r < 0n ? r + m : r;
61
+ const r = a % m;
62
+ return r < 0n ? r + m : r;
58
63
  }
64
+ /**
65
+ * Modular multiplicative inverse using extended Euclidean algorithm.
66
+ * Returns x such that (a * x) mod m = 1.
67
+ * Throws if a and m are not coprime.
68
+ */
59
69
  function modInverse(a, m) {
60
- a = mod(a, m);
61
- if (a === 0n) {
62
- throw new Error("No modular inverse for zero");
63
- }
64
- let [old_r, r] = [a, m];
65
- let [old_s, s] = [1n, 0n];
66
- while (r !== 0n) {
67
- const q = old_r / r;
68
- [old_r, r] = [r, old_r - q * r];
69
- [old_s, s] = [s, old_s - q * s];
70
- }
71
- if (old_r !== 1n) {
72
- throw new Error("Modular inverse does not exist");
73
- }
74
- return mod(old_s, m);
70
+ a = mod(a, m);
71
+ if (a === 0n) {
72
+ throw new Error("No modular inverse for zero");
73
+ }
74
+ let [old_r, r] = [a, m];
75
+ let [old_s, s] = [1n, 0n];
76
+ while (r !== 0n) {
77
+ const q = old_r / r;
78
+ [old_r, r] = [r, old_r - q * r];
79
+ [old_s, s] = [s, old_s - q * s];
80
+ }
81
+ if (old_r !== 1n) {
82
+ throw new Error("Modular inverse does not exist");
83
+ }
84
+ return mod(old_s, m);
75
85
  }
86
+ /**
87
+ * Add two points on the curve.
88
+ * Returns the point at infinity (null) when appropriate.
89
+ */
76
90
  function pointAdd(P, Q, curve) {
77
- if (P === null) return Q;
78
- if (Q === null) return P;
79
- const { p } = curve;
80
- if (P.x === Q.x) {
81
- if (mod(P.y + Q.y, p) === 0n) {
82
- return null;
83
- }
84
- return pointDouble(P, curve);
85
- }
86
- const dx = mod(Q.x - P.x, p);
87
- const dy = mod(Q.y - P.y, p);
88
- const lambda = mod(dy * modInverse(dx, p), p);
89
- const x3 = mod(lambda * lambda - P.x - Q.x, p);
90
- const y3 = mod(lambda * (P.x - x3) - P.y, p);
91
- return { x: x3, y: y3 };
91
+ if (P === null) return Q;
92
+ if (Q === null) return P;
93
+ const { p } = curve;
94
+ if (P.x === Q.x) {
95
+ if (mod(P.y + Q.y, p) === 0n) {
96
+ return null;
97
+ }
98
+ return pointDouble(P, curve);
99
+ }
100
+ const dx = mod(Q.x - P.x, p);
101
+ const dy = mod(Q.y - P.y, p);
102
+ const lambda = mod(dy * modInverse(dx, p), p);
103
+ const x3 = mod(lambda * lambda - P.x - Q.x, p);
104
+ const y3 = mod(lambda * (P.x - x3) - P.y, p);
105
+ return {
106
+ x: x3,
107
+ y: y3
108
+ };
92
109
  }
110
+ /**
111
+ * Double a point on the curve.
112
+ */
93
113
  function pointDouble(P, curve) {
94
- if (P === null) return null;
95
- const { a, p } = curve;
96
- if (P.y === 0n) return null;
97
- const num = mod(3n * P.x * P.x + a, p);
98
- const den = mod(2n * P.y, p);
99
- const lambda = mod(num * modInverse(den, p), p);
100
- const x3 = mod(lambda * lambda - 2n * P.x, p);
101
- const y3 = mod(lambda * (P.x - x3) - P.y, p);
102
- return { x: x3, y: y3 };
114
+ if (P === null) return null;
115
+ const { a, p } = curve;
116
+ if (P.y === 0n) return null;
117
+ const num = mod(3n * P.x * P.x + a, p);
118
+ const den = mod(2n * P.y, p);
119
+ const lambda = mod(num * modInverse(den, p), p);
120
+ const x3 = mod(lambda * lambda - 2n * P.x, p);
121
+ const y3 = mod(lambda * (P.x - x3) - P.y, p);
122
+ return {
123
+ x: x3,
124
+ y: y3
125
+ };
103
126
  }
127
+ /**
128
+ * Scalar multiplication using the double-and-add method (constant-time-ish
129
+ * with respect to the bit length of the scalar, but not fully
130
+ * constant-time — acceptable since we are not a production crypto library
131
+ * and match the behaviour of refs/create-ecdh which uses elliptic.js).
132
+ *
133
+ * Uses a fixed-window approach of width 1 (Montgomery ladder variant) to
134
+ * avoid the most trivial timing leaks.
135
+ */
104
136
  function scalarMul(k, P, curve) {
105
- if (P === null) return null;
106
- if (k === 0n) return null;
107
- const { n } = curve;
108
- k = mod(k, n);
109
- if (k === 0n) return null;
110
- let R0 = null;
111
- let R1 = P;
112
- const bits = k.toString(2);
113
- for (let i = 0; i < bits.length; i++) {
114
- if (bits[i] === "1") {
115
- R0 = pointAdd(R0, R1, curve);
116
- R1 = pointDouble(R1, curve);
117
- } else {
118
- R1 = pointAdd(R0, R1, curve);
119
- R0 = pointDouble(R0, curve);
120
- }
121
- }
122
- return R0;
137
+ if (P === null) return null;
138
+ if (k === 0n) return null;
139
+ const { n } = curve;
140
+ k = mod(k, n);
141
+ if (k === 0n) return null;
142
+ let R0 = null;
143
+ let R1 = P;
144
+ const bits = k.toString(2);
145
+ for (let i = 0; i < bits.length; i++) {
146
+ if (bits[i] === "1") {
147
+ R0 = pointAdd(R0, R1, curve);
148
+ R1 = pointDouble(R1, curve);
149
+ } else {
150
+ R1 = pointAdd(R0, R1, curve);
151
+ R0 = pointDouble(R0, curve);
152
+ }
153
+ }
154
+ return R0;
123
155
  }
156
+ /**
157
+ * Convert a BigInt to a Buffer of exactly `len` bytes (big-endian,
158
+ * zero-padded on the left).
159
+ */
124
160
  function bigintToBuffer(n, len) {
125
- const hex = n.toString(16).padStart(len * 2, "0");
126
- return Buffer.from(hex, "hex");
161
+ const hex = n.toString(16).padStart(len * 2, "0");
162
+ return Buffer.from(hex, "hex");
127
163
  }
164
+ /**
165
+ * Convert a Buffer (big-endian unsigned) to a BigInt.
166
+ */
128
167
  function bufferToBigint(buf) {
129
- const hex = Buffer.from(buf).toString("hex");
130
- if (hex.length === 0) return 0n;
131
- return BigInt("0x" + hex);
168
+ const hex = Buffer.from(buf).toString("hex");
169
+ if (hex.length === 0) return 0n;
170
+ return BigInt("0x" + hex);
132
171
  }
172
+ /**
173
+ * Encode a public key point to a Buffer.
174
+ *
175
+ * Formats:
176
+ * 'uncompressed' (default) — 0x04 || x || y
177
+ * 'compressed' — 0x02 (even y) or 0x03 (odd y) || x
178
+ * 'hybrid' — 0x06 (even y) or 0x07 (odd y) || x || y
179
+ */
133
180
  function encodePublicKey(point, byteLength, format = "uncompressed") {
134
- if (point === null) {
135
- throw new Error("Cannot encode the point at infinity");
136
- }
137
- const xBuf = bigintToBuffer(point.x, byteLength);
138
- if (format === "compressed") {
139
- const prefix = point.y % 2n === 0n ? 2 : 3;
140
- return Buffer.concat([Buffer.from([prefix]), xBuf]);
141
- }
142
- const yBuf = bigintToBuffer(point.y, byteLength);
143
- if (format === "hybrid") {
144
- const prefix = point.y % 2n === 0n ? 6 : 7;
145
- return Buffer.concat([Buffer.from([prefix]), xBuf, yBuf]);
146
- }
147
- return Buffer.concat([Buffer.from([4]), xBuf, yBuf]);
181
+ if (point === null) {
182
+ throw new Error("Cannot encode the point at infinity");
183
+ }
184
+ const xBuf = bigintToBuffer(point.x, byteLength);
185
+ if (format === "compressed") {
186
+ const prefix = point.y % 2n === 0n ? 2 : 3;
187
+ return Buffer.concat([Buffer.from([prefix]), xBuf]);
188
+ }
189
+ const yBuf = bigintToBuffer(point.y, byteLength);
190
+ if (format === "hybrid") {
191
+ const prefix = point.y % 2n === 0n ? 6 : 7;
192
+ return Buffer.concat([
193
+ Buffer.from([prefix]),
194
+ xBuf,
195
+ yBuf
196
+ ]);
197
+ }
198
+ return Buffer.concat([
199
+ Buffer.from([4]),
200
+ xBuf,
201
+ yBuf
202
+ ]);
148
203
  }
204
+ /**
205
+ * Decode a public key from a Buffer to an ECPoint.
206
+ *
207
+ * Supports uncompressed (0x04), compressed (0x02/0x03), and hybrid (0x06/0x07).
208
+ */
149
209
  function decodePublicKey(buf, curve) {
150
- const data = Buffer.from(buf);
151
- if (data.length === 0) {
152
- throw new Error("Invalid public key: empty buffer");
153
- }
154
- const prefix = data[0];
155
- const { p, a, b, byteLength } = curve;
156
- if (prefix === 4 || prefix === 6 || prefix === 7) {
157
- if (data.length !== 1 + 2 * byteLength) {
158
- throw new Error(
159
- `Invalid public key length: expected ${1 + 2 * byteLength} bytes, got ${data.length}`
160
- );
161
- }
162
- const x = bufferToBigint(data.subarray(1, 1 + byteLength));
163
- const y = bufferToBigint(data.subarray(1 + byteLength));
164
- const lhs = mod(y * y, p);
165
- const rhs = mod(x * x * x + a * x + b, p);
166
- if (lhs !== rhs) {
167
- throw new Error("Invalid public key: point is not on the curve");
168
- }
169
- return { x, y };
170
- }
171
- if (prefix === 2 || prefix === 3) {
172
- if (data.length !== 1 + byteLength) {
173
- throw new Error(
174
- `Invalid public key length: expected ${1 + byteLength} bytes, got ${data.length}`
175
- );
176
- }
177
- const x = bufferToBigint(data.subarray(1, 1 + byteLength));
178
- const ySquared = mod(x * x * x + a * x + b, p);
179
- const y = sqrtMod(ySquared, p);
180
- if (y === null) {
181
- throw new Error("Invalid public key: no valid y coordinate for the given x");
182
- }
183
- const isOdd = prefix === 3;
184
- const finalY = y % 2n !== 0n === isOdd ? y : mod(p - y, p);
185
- return { x, y: finalY };
186
- }
187
- throw new Error(`Invalid public key prefix: 0x${prefix.toString(16).padStart(2, "0")}`);
210
+ const data = Buffer.from(buf);
211
+ if (data.length === 0) {
212
+ throw new Error("Invalid public key: empty buffer");
213
+ }
214
+ const prefix = data[0];
215
+ const { p, a, b, byteLength } = curve;
216
+ if (prefix === 4 || prefix === 6 || prefix === 7) {
217
+ if (data.length !== 1 + 2 * byteLength) {
218
+ throw new Error(`Invalid public key length: expected ${1 + 2 * byteLength} bytes, got ${data.length}`);
219
+ }
220
+ const x = bufferToBigint(data.subarray(1, 1 + byteLength));
221
+ const y = bufferToBigint(data.subarray(1 + byteLength));
222
+ const lhs = mod(y * y, p);
223
+ const rhs = mod(x * x * x + a * x + b, p);
224
+ if (lhs !== rhs) {
225
+ throw new Error("Invalid public key: point is not on the curve");
226
+ }
227
+ return {
228
+ x,
229
+ y
230
+ };
231
+ }
232
+ if (prefix === 2 || prefix === 3) {
233
+ if (data.length !== 1 + byteLength) {
234
+ throw new Error(`Invalid public key length: expected ${1 + byteLength} bytes, got ${data.length}`);
235
+ }
236
+ const x = bufferToBigint(data.subarray(1, 1 + byteLength));
237
+ const ySquared = mod(x * x * x + a * x + b, p);
238
+ const y = sqrtMod(ySquared, p);
239
+ if (y === null) {
240
+ throw new Error("Invalid public key: no valid y coordinate for the given x");
241
+ }
242
+ const isOdd = prefix === 3;
243
+ const finalY = y % 2n !== 0n === isOdd ? y : mod(p - y, p);
244
+ return {
245
+ x,
246
+ y: finalY
247
+ };
248
+ }
249
+ throw new Error(`Invalid public key prefix: 0x${prefix.toString(16).padStart(2, "0")}`);
188
250
  }
251
+ /**
252
+ * Compute the modular square root of a modulo p (Tonelli-Shanks algorithm).
253
+ *
254
+ * For the special case p ≡ 3 (mod 4), uses the simpler formula: a^((p+1)/4) mod p.
255
+ * All four supported NIST curves have p ≡ 3 (mod 4), but we include
256
+ * Tonelli-Shanks for completeness.
257
+ *
258
+ * Returns null if a is not a quadratic residue mod p.
259
+ */
189
260
  function sqrtMod(a, p) {
190
- a = mod(a, p);
191
- if (a === 0n) return 0n;
192
- if (modPow(a, (p - 1n) / 2n, p) !== 1n) {
193
- return null;
194
- }
195
- if (mod(p, 4n) === 3n) {
196
- return modPow(a, (p + 1n) / 4n, p);
197
- }
198
- let S = 0n;
199
- let Q = p - 1n;
200
- while (mod(Q, 2n) === 0n) {
201
- Q /= 2n;
202
- S++;
203
- }
204
- let z = 2n;
205
- while (modPow(z, (p - 1n) / 2n, p) !== p - 1n) {
206
- z++;
207
- }
208
- let M = S;
209
- let c = modPow(z, Q, p);
210
- let t = modPow(a, Q, p);
211
- let R = modPow(a, (Q + 1n) / 2n, p);
212
- while (true) {
213
- if (t === 0n) return 0n;
214
- if (t === 1n) return R;
215
- let i = 1n;
216
- let temp = mod(t * t, p);
217
- while (temp !== 1n) {
218
- temp = mod(temp * temp, p);
219
- i++;
220
- }
221
- const b = modPow(c, modPow(2n, M - i - 1n, p - 1n), p);
222
- M = i;
223
- c = mod(b * b, p);
224
- t = mod(t * c, p);
225
- R = mod(R * b, p);
226
- }
261
+ a = mod(a, p);
262
+ if (a === 0n) return 0n;
263
+ if (modPow(a, (p - 1n) / 2n, p) !== 1n) {
264
+ return null;
265
+ }
266
+ if (mod(p, 4n) === 3n) {
267
+ return modPow(a, (p + 1n) / 4n, p);
268
+ }
269
+ let S = 0n;
270
+ let Q = p - 1n;
271
+ while (mod(Q, 2n) === 0n) {
272
+ Q /= 2n;
273
+ S++;
274
+ }
275
+ let z = 2n;
276
+ while (modPow(z, (p - 1n) / 2n, p) !== p - 1n) {
277
+ z++;
278
+ }
279
+ let M = S;
280
+ let c = modPow(z, Q, p);
281
+ let t = modPow(a, Q, p);
282
+ let R = modPow(a, (Q + 1n) / 2n, p);
283
+ while (true) {
284
+ if (t === 0n) return 0n;
285
+ if (t === 1n) return R;
286
+ let i = 1n;
287
+ let temp = mod(t * t, p);
288
+ while (temp !== 1n) {
289
+ temp = mod(temp * temp, p);
290
+ i++;
291
+ }
292
+ const b = modPow(c, modPow(2n, M - i - 1n, p - 1n), p);
293
+ M = i;
294
+ c = mod(b * b, p);
295
+ t = mod(t * c, p);
296
+ R = mod(R * b, p);
297
+ }
227
298
  }
299
+ /**
300
+ * Coerce an input value to a Buffer. Accepts Buffer, Uint8Array, or a string
301
+ * with an optional encoding.
302
+ */
228
303
  function toBuffer(value, encoding) {
229
- if (Buffer.isBuffer(value)) return value;
230
- if (value instanceof Uint8Array) return Buffer.from(value);
231
- if (typeof value === "string") {
232
- return Buffer.from(value, encoding || "utf8");
233
- }
234
- if (ArrayBuffer.isView(value)) {
235
- return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
236
- }
237
- throw new TypeError(
238
- 'The "key" argument must be of type string or an instance of Buffer, TypedArray, or DataView.'
239
- );
304
+ if (Buffer.isBuffer(value)) return value;
305
+ if (value instanceof Uint8Array) return Buffer.from(value);
306
+ if (typeof value === "string") {
307
+ return Buffer.from(value, encoding || "utf8");
308
+ }
309
+ if (ArrayBuffer.isView(value)) {
310
+ return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
311
+ }
312
+ throw new TypeError("The \"key\" argument must be of type string or an instance of Buffer, TypedArray, or DataView.");
240
313
  }
314
+ /**
315
+ * Optionally encode a Buffer to a string. If encoding is null/undefined,
316
+ * return the raw Buffer.
317
+ */
241
318
  function formatReturnValue(buf, encoding) {
242
- if (encoding) {
243
- return buf.toString(encoding);
244
- }
245
- return buf;
319
+ if (encoding) {
320
+ return buf.toString(encoding);
321
+ }
322
+ return buf;
246
323
  }
247
324
  function resolveCurve(curveName) {
248
- const lower = curveName.toLowerCase();
249
- const canonical = CURVE_ALIASES[lower];
250
- if (!canonical) {
251
- throw new Error(`Unsupported curve: ${curveName}`);
252
- }
253
- const params = CURVES[canonical];
254
- if (!params) {
255
- throw new Error(`Unsupported curve: ${curveName}`);
256
- }
257
- return { canonical, params };
258
- }
259
- class ECDH {
260
- _curve;
261
- _curveName;
262
- _privateKey = null;
263
- _publicKey = null;
264
- constructor(curveName) {
265
- const resolved = resolveCurve(curveName);
266
- this._curve = resolved.params;
267
- this._curveName = resolved.canonical;
268
- }
269
- generateKeys(encoding, format) {
270
- const { n, byteLength } = this._curve;
271
- let k;
272
- do {
273
- const bytes = randomBytes(byteLength);
274
- k = bufferToBigint(bytes);
275
- } while (k === 0n || k >= n);
276
- this._privateKey = k;
277
- const G = { x: this._curve.Gx, y: this._curve.Gy };
278
- this._publicKey = scalarMul(k, G, this._curve);
279
- return this.getPublicKey(encoding, format);
280
- }
281
- /**
282
- * Compute the shared secret using the other party's public key.
283
- */
284
- computeSecret(otherPublicKey, inputEncoding, outputEncoding) {
285
- if (this._privateKey === null) {
286
- throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
287
- }
288
- const pubBuf = toBuffer(otherPublicKey, inputEncoding);
289
- const otherPoint = decodePublicKey(pubBuf, this._curve);
290
- if (otherPoint === null) {
291
- throw new Error("Invalid public key: point at infinity");
292
- }
293
- const sharedPoint = scalarMul(this._privateKey, otherPoint, this._curve);
294
- if (sharedPoint === null) {
295
- throw new Error("Shared secret computation resulted in the point at infinity");
296
- }
297
- const secret = bigintToBuffer(sharedPoint.x, this._curve.byteLength);
298
- return formatReturnValue(secret, outputEncoding);
299
- }
300
- getPublicKey(encoding, format) {
301
- if (this._publicKey === null) {
302
- throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
303
- }
304
- const buf = encodePublicKey(this._publicKey, this._curve.byteLength, format || "uncompressed");
305
- return formatReturnValue(buf, encoding);
306
- }
307
- getPrivateKey(encoding) {
308
- if (this._privateKey === null) {
309
- throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
310
- }
311
- const buf = bigintToBuffer(this._privateKey, this._curve.byteLength);
312
- return formatReturnValue(buf, encoding);
313
- }
314
- /**
315
- * Set the public key. The key can be in uncompressed, compressed, or hybrid format.
316
- */
317
- setPublicKey(key, encoding) {
318
- const buf = toBuffer(key, encoding);
319
- this._publicKey = decodePublicKey(buf, this._curve);
320
- }
321
- /**
322
- * Set the private key and derive the corresponding public key.
323
- */
324
- setPrivateKey(key, encoding) {
325
- const buf = toBuffer(key, encoding);
326
- const k = bufferToBigint(buf);
327
- if (k === 0n || k >= this._curve.n) {
328
- throw new Error("Private key is out of range [1, n-1]");
329
- }
330
- this._privateKey = k;
331
- const G = { x: this._curve.Gx, y: this._curve.Gy };
332
- this._publicKey = scalarMul(k, G, this._curve);
333
- }
325
+ const lower = curveName.toLowerCase();
326
+ const canonical = CURVE_ALIASES[lower];
327
+ if (!canonical) {
328
+ throw new Error(`Unsupported curve: ${curveName}`);
329
+ }
330
+ const params = CURVES[canonical];
331
+ if (!params) {
332
+ throw new Error(`Unsupported curve: ${curveName}`);
333
+ }
334
+ return {
335
+ canonical,
336
+ params
337
+ };
334
338
  }
339
+ var ECDH = class {
340
+ _curve;
341
+ _curveName;
342
+ _privateKey = null;
343
+ _publicKey = null;
344
+ constructor(curveName) {
345
+ const resolved = resolveCurve(curveName);
346
+ this._curve = resolved.params;
347
+ this._curveName = resolved.canonical;
348
+ }
349
+ generateKeys(encoding, format) {
350
+ const { n, byteLength } = this._curve;
351
+ let k;
352
+ do {
353
+ const bytes = randomBytes(byteLength);
354
+ k = bufferToBigint(bytes);
355
+ } while (k === 0n || k >= n);
356
+ this._privateKey = k;
357
+ const G = {
358
+ x: this._curve.Gx,
359
+ y: this._curve.Gy
360
+ };
361
+ this._publicKey = scalarMul(k, G, this._curve);
362
+ return this.getPublicKey(encoding, format);
363
+ }
364
+ /**
365
+ * Compute the shared secret using the other party's public key.
366
+ */
367
+ computeSecret(otherPublicKey, inputEncoding, outputEncoding) {
368
+ if (this._privateKey === null) {
369
+ throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
370
+ }
371
+ const pubBuf = toBuffer(otherPublicKey, inputEncoding);
372
+ const otherPoint = decodePublicKey(pubBuf, this._curve);
373
+ if (otherPoint === null) {
374
+ throw new Error("Invalid public key: point at infinity");
375
+ }
376
+ const sharedPoint = scalarMul(this._privateKey, otherPoint, this._curve);
377
+ if (sharedPoint === null) {
378
+ throw new Error("Shared secret computation resulted in the point at infinity");
379
+ }
380
+ const secret = bigintToBuffer(sharedPoint.x, this._curve.byteLength);
381
+ return formatReturnValue(secret, outputEncoding);
382
+ }
383
+ getPublicKey(encoding, format) {
384
+ if (this._publicKey === null) {
385
+ throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
386
+ }
387
+ const buf = encodePublicKey(this._publicKey, this._curve.byteLength, format || "uncompressed");
388
+ return formatReturnValue(buf, encoding);
389
+ }
390
+ getPrivateKey(encoding) {
391
+ if (this._privateKey === null) {
392
+ throw new Error("ECDH key not generated. Call generateKeys() or setPrivateKey() first.");
393
+ }
394
+ const buf = bigintToBuffer(this._privateKey, this._curve.byteLength);
395
+ return formatReturnValue(buf, encoding);
396
+ }
397
+ /**
398
+ * Set the public key. The key can be in uncompressed, compressed, or hybrid format.
399
+ */
400
+ setPublicKey(key, encoding) {
401
+ const buf = toBuffer(key, encoding);
402
+ this._publicKey = decodePublicKey(buf, this._curve);
403
+ }
404
+ /**
405
+ * Set the private key and derive the corresponding public key.
406
+ */
407
+ setPrivateKey(key, encoding) {
408
+ const buf = toBuffer(key, encoding);
409
+ const k = bufferToBigint(buf);
410
+ if (k === 0n || k >= this._curve.n) {
411
+ throw new Error("Private key is out of range [1, n-1]");
412
+ }
413
+ this._privateKey = k;
414
+ const G = {
415
+ x: this._curve.Gx,
416
+ y: this._curve.Gy
417
+ };
418
+ this._publicKey = scalarMul(k, G, this._curve);
419
+ }
420
+ };
335
421
  function createECDH(curveName) {
336
- return new ECDH(curveName);
422
+ return new ECDH(curveName);
337
423
  }
424
+ /**
425
+ * Return a list of supported elliptic curve names.
426
+ */
338
427
  function getCurves() {
339
- return [
340
- "secp256k1",
341
- "prime256v1",
342
- "secp256r1",
343
- "secp384r1",
344
- "secp521r1"
345
- ];
428
+ return [
429
+ "secp256k1",
430
+ "prime256v1",
431
+ "secp256r1",
432
+ "secp384r1",
433
+ "secp521r1"
434
+ ];
346
435
  }
347
- export {
348
- CURVES,
349
- CURVE_ALIASES,
350
- createECDH,
351
- getCurves,
352
- mod,
353
- modInverse,
354
- pointAdd,
355
- scalarMul
356
- };
436
+
437
+ //#endregion
438
+ export { CURVES, CURVE_ALIASES, createECDH, getCurves, mod, modInverse, pointAdd, scalarMul };