@gjsify/crypto 0.3.16 → 0.3.17
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/asn1.js +2 -630
- package/lib/esm/bigint-math.js +1 -43
- package/lib/esm/cipher.js +1 -1295
- package/lib/esm/constants.js +1 -14
- package/lib/esm/crypto-utils.js +1 -65
- package/lib/esm/dh.js +1 -451
- package/lib/esm/ecdh.js +1 -438
- package/lib/esm/ecdsa.js +1 -152
- package/lib/esm/hash.js +1 -111
- package/lib/esm/hkdf.js +1 -76
- package/lib/esm/hmac.js +1 -98
- package/lib/esm/index.js +1 -85
- package/lib/esm/key-object.js +1 -371
- package/lib/esm/mgf1.js +1 -33
- package/lib/esm/pbkdf2.js +1 -75
- package/lib/esm/public-encrypt.js +1 -222
- package/lib/esm/random.js +1 -151
- package/lib/esm/rsa-oaep.js +1 -102
- package/lib/esm/rsa-pss.js +1 -107
- package/lib/esm/scrypt.js +1 -135
- package/lib/esm/sign.js +1 -278
- package/lib/esm/timing-safe-equal.js +1 -18
- package/lib/esm/x509.js +1 -223
- package/package.json +8 -8
- package/tsconfig.tsbuildinfo +1 -1
package/lib/esm/ecdh.js
CHANGED
|
@@ -1,438 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { randomBytes } from "./random.js";
|
|
3
|
-
import { Buffer } from "node:buffer";
|
|
4
|
-
|
|
5
|
-
//#region src/ecdh.ts
|
|
6
|
-
const CURVES = {
|
|
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
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const CURVE_ALIASES = {
|
|
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"
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Non-negative modulus: always returns a value in [0, mod).
|
|
59
|
-
*/
|
|
60
|
-
function mod(a, m) {
|
|
61
|
-
const r = a % m;
|
|
62
|
-
return r < 0n ? r + m : r;
|
|
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
|
-
*/
|
|
69
|
-
function modInverse(a, 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);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Add two points on the curve.
|
|
88
|
-
* Returns the point at infinity (null) when appropriate.
|
|
89
|
-
*/
|
|
90
|
-
function pointAdd(P, Q, curve) {
|
|
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
|
-
};
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Double a point on the curve.
|
|
112
|
-
*/
|
|
113
|
-
function pointDouble(P, curve) {
|
|
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
|
-
};
|
|
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
|
-
*/
|
|
136
|
-
function scalarMul(k, P, curve) {
|
|
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;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Convert a BigInt to a Buffer of exactly `len` bytes (big-endian,
|
|
158
|
-
* zero-padded on the left).
|
|
159
|
-
*/
|
|
160
|
-
function bigintToBuffer(n, len) {
|
|
161
|
-
const hex = n.toString(16).padStart(len * 2, "0");
|
|
162
|
-
return Buffer.from(hex, "hex");
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Convert a Buffer (big-endian unsigned) to a BigInt.
|
|
166
|
-
*/
|
|
167
|
-
function bufferToBigint(buf) {
|
|
168
|
-
const hex = Buffer.from(buf).toString("hex");
|
|
169
|
-
if (hex.length === 0) return 0n;
|
|
170
|
-
return BigInt("0x" + hex);
|
|
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
|
-
*/
|
|
180
|
-
function encodePublicKey(point, byteLength, format = "uncompressed") {
|
|
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
|
-
]);
|
|
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
|
-
*/
|
|
209
|
-
function decodePublicKey(buf, curve) {
|
|
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")}`);
|
|
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
|
-
*/
|
|
260
|
-
function sqrtMod(a, p) {
|
|
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
|
-
}
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Coerce an input value to a Buffer. Accepts Buffer, Uint8Array, or a string
|
|
301
|
-
* with an optional encoding.
|
|
302
|
-
*/
|
|
303
|
-
function toBuffer(value, encoding) {
|
|
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.");
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Optionally encode a Buffer to a string. If encoding is null/undefined,
|
|
316
|
-
* return the raw Buffer.
|
|
317
|
-
*/
|
|
318
|
-
function formatReturnValue(buf, encoding) {
|
|
319
|
-
if (encoding) {
|
|
320
|
-
return buf.toString(encoding);
|
|
321
|
-
}
|
|
322
|
-
return buf;
|
|
323
|
-
}
|
|
324
|
-
function resolveCurve(curveName) {
|
|
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
|
-
};
|
|
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
|
-
};
|
|
421
|
-
function createECDH(curveName) {
|
|
422
|
-
return new ECDH(curveName);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Return a list of supported elliptic curve names.
|
|
426
|
-
*/
|
|
427
|
-
function getCurves() {
|
|
428
|
-
return [
|
|
429
|
-
"secp256k1",
|
|
430
|
-
"prime256v1",
|
|
431
|
-
"secp256r1",
|
|
432
|
-
"secp384r1",
|
|
433
|
-
"secp521r1"
|
|
434
|
-
];
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
//#endregion
|
|
438
|
-
export { CURVES, CURVE_ALIASES, createECDH, getCurves, mod, modInverse, pointAdd, scalarMul };
|
|
1
|
+
import{modPow as e}from"./bigint-math.js";import{randomBytes as t}from"./random.js";import{Buffer as n}from"node:buffer";const r={secp256k1:{p:115792089237316195423570985008687907853269984665640564039457584007908834671663n,a:0n,b:7n,Gx:55066263022277343669578718895168534326250603453777594175500187360389116729240n,Gy:32670510020758816978083085130507043184471273380659243275938904335757337482424n,n:115792089237316195423570985008687907852837564279074904382605163141518161494337n,byteLength:32},prime256v1:{p:115792089210356248762697446949407573530086143415290314195533631308867097853951n,a:115792089210356248762697446949407573530086143415290314195533631308867097853951n-3n,b:41058363725152142129326129780047268409114441015993725554835256314039467401291n,Gx:48439561293906451759052585252797914202762949526041747995844080717082404635286n,Gy:36134250956749795798585127919587881956611106672985015071877198253568414405109n,n:115792089210356248762697446949407573529996955224135760342422259061068512044369n,byteLength:32},secp384r1:{p:39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319n,a:39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319n-3n,b:27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575n,Gx:26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087n,Gy:8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871n,n:39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643n,byteLength:48},secp521r1:{p:6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151n,a:6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151n-3n,b:1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984n,Gx:2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846n,Gy:3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784n,n:6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449n,byteLength:66}},i={secp256k1:`secp256k1`,prime256v1:`prime256v1`,secp256r1:`prime256v1`,"p-256":`prime256v1`,p256:`prime256v1`,secp384r1:`secp384r1`,"p-384":`secp384r1`,p384:`secp384r1`,secp521r1:`secp521r1`,"p-521":`secp521r1`,p521:`secp521r1`};function a(e,t){let n=e%t;return n<0n?n+t:n}function o(e,t){if(e=a(e,t),e===0n)throw Error(`No modular inverse for zero`);let[n,r]=[e,t],[i,o]=[1n,0n];for(;r!==0n;){let e=n/r;[n,r]=[r,n-e*r],[i,o]=[o,i-e*o]}if(n!==1n)throw Error(`Modular inverse does not exist`);return a(i,t)}function s(e,t,n){if(e===null)return t;if(t===null)return e;let{p:r}=n;if(e.x===t.x)return a(e.y+t.y,r)===0n?null:c(e,n);let i=a(t.x-e.x,r),s=a(a(t.y-e.y,r)*o(i,r),r),l=a(s*s-e.x-t.x,r);return{x:l,y:a(s*(e.x-l)-e.y,r)}}function c(e,t){if(e===null)return null;let{a:n,p:r}=t;if(e.y===0n)return null;let i=a(a(3n*e.x*e.x+n,r)*o(a(2n*e.y,r),r),r),s=a(i*i-2n*e.x,r);return{x:s,y:a(i*(e.x-s)-e.y,r)}}function l(e,t,n){if(t===null||e===0n)return null;let{n:r}=n;if(e=a(e,r),e===0n)return null;let i=null,o=t,l=e.toString(2);for(let e=0;e<l.length;e++)l[e]===`1`?(i=s(i,o,n),o=c(o,n)):(o=s(i,o,n),i=c(i,n));return i}function u(e,t){let r=e.toString(16).padStart(t*2,`0`);return n.from(r,`hex`)}function d(e){let t=n.from(e).toString(`hex`);return t.length===0?0n:BigInt(`0x`+t)}function f(e,t,r=`uncompressed`){if(e===null)throw Error(`Cannot encode the point at infinity`);let i=u(e.x,t);if(r===`compressed`){let t=e.y%2n==0n?2:3;return n.concat([n.from([t]),i])}let a=u(e.y,t);if(r===`hybrid`){let t=e.y%2n==0n?6:7;return n.concat([n.from([t]),i,a])}return n.concat([n.from([4]),i,a])}function p(e,t){let r=n.from(e);if(r.length===0)throw Error(`Invalid public key: empty buffer`);let i=r[0],{p:o,a:s,b:c,byteLength:l}=t;if(i===4||i===6||i===7){if(r.length!==1+2*l)throw Error(`Invalid public key length: expected ${1+2*l} bytes, got ${r.length}`);let e=d(r.subarray(1,1+l)),t=d(r.subarray(1+l));if(a(t*t,o)!==a(e*e*e+s*e+c,o))throw Error(`Invalid public key: point is not on the curve`);return{x:e,y:t}}if(i===2||i===3){if(r.length!==1+l)throw Error(`Invalid public key length: expected ${1+l} bytes, got ${r.length}`);let e=d(r.subarray(1,1+l)),t=m(a(e*e*e+s*e+c,o),o);if(t===null)throw Error(`Invalid public key: no valid y coordinate for the given x`);let n=i===3;return{x:e,y:t%2n!=0n===n?t:a(o-t,o)}}throw Error(`Invalid public key prefix: 0x${i.toString(16).padStart(2,`0`)}`)}function m(t,n){if(t=a(t,n),t===0n)return 0n;if(e(t,(n-1n)/2n,n)!==1n)return null;if(a(n,4n)===3n)return e(t,(n+1n)/4n,n);let r=0n,i=n-1n;for(;a(i,2n)===0n;)i/=2n,r++;let o=2n;for(;e(o,(n-1n)/2n,n)!==n-1n;)o++;let s=r,c=e(o,i,n),l=e(t,i,n),u=e(t,(i+1n)/2n,n);for(;;){if(l===0n)return 0n;if(l===1n)return u;let t=1n,r=a(l*l,n);for(;r!==1n;)r=a(r*r,n),t++;let i=e(c,e(2n,s-t-1n,n-1n),n);s=t,c=a(i*i,n),l=a(l*c,n),u=a(u*i,n)}}function h(e,t){if(n.isBuffer(e))return e;if(e instanceof Uint8Array)return n.from(e);if(typeof e==`string`)return n.from(e,t||`utf8`);if(ArrayBuffer.isView(e))return n.from(e.buffer,e.byteOffset,e.byteLength);throw TypeError(`The "key" argument must be of type string or an instance of Buffer, TypedArray, or DataView.`)}function g(e,t){return t?e.toString(t):e}function _(e){let t=i[e.toLowerCase()];if(!t)throw Error(`Unsupported curve: ${e}`);let n=r[t];if(!n)throw Error(`Unsupported curve: ${e}`);return{canonical:t,params:n}}var v=class{_curve;_curveName;_privateKey=null;_publicKey=null;constructor(e){let t=_(e);this._curve=t.params,this._curveName=t.canonical}generateKeys(e,n){let{n:r,byteLength:i}=this._curve,a;do a=d(t(i));while(a===0n||a>=r);this._privateKey=a;let o={x:this._curve.Gx,y:this._curve.Gy};return this._publicKey=l(a,o,this._curve),this.getPublicKey(e,n)}computeSecret(e,t,n){if(this._privateKey===null)throw Error(`ECDH key not generated. Call generateKeys() or setPrivateKey() first.`);let r=p(h(e,t),this._curve);if(r===null)throw Error(`Invalid public key: point at infinity`);let i=l(this._privateKey,r,this._curve);if(i===null)throw Error(`Shared secret computation resulted in the point at infinity`);return g(u(i.x,this._curve.byteLength),n)}getPublicKey(e,t){if(this._publicKey===null)throw Error(`ECDH key not generated. Call generateKeys() or setPrivateKey() first.`);return g(f(this._publicKey,this._curve.byteLength,t||`uncompressed`),e)}getPrivateKey(e){if(this._privateKey===null)throw Error(`ECDH key not generated. Call generateKeys() or setPrivateKey() first.`);return g(u(this._privateKey,this._curve.byteLength),e)}setPublicKey(e,t){let n=h(e,t);this._publicKey=p(n,this._curve)}setPrivateKey(e,t){let n=d(h(e,t));if(n===0n||n>=this._curve.n)throw Error(`Private key is out of range [1, n-1]`);this._privateKey=n;let r={x:this._curve.Gx,y:this._curve.Gy};this._publicKey=l(n,r,this._curve)}};function y(e){return new v(e)}function b(){return[`secp256k1`,`prime256v1`,`secp256r1`,`secp384r1`,`secp521r1`]}export{r as CURVES,i as CURVE_ALIASES,y as createECDH,b as getCurves,a as mod,o as modInverse,s as pointAdd,l as scalarMul};
|
package/lib/esm/ecdsa.js
CHANGED
|
@@ -1,152 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { CURVES, CURVE_ALIASES, mod, modInverse, pointAdd, scalarMul } from "./ecdh.js";
|
|
3
|
-
import { Hash } from "./hash.js";
|
|
4
|
-
import { Hmac } from "./hmac.js";
|
|
5
|
-
|
|
6
|
-
//#region src/ecdsa.ts
|
|
7
|
-
function getCurve(curveName) {
|
|
8
|
-
const alias = CURVE_ALIASES[curveName.toLowerCase()];
|
|
9
|
-
if (!alias) throw new Error(`Unsupported curve: ${curveName}`);
|
|
10
|
-
return CURVES[alias];
|
|
11
|
-
}
|
|
12
|
-
/** Truncate hash to curve order bit length (FIPS 186-4 Section 6.4) */
|
|
13
|
-
function truncateHash(hash, curve) {
|
|
14
|
-
const orderBits = curve.n.toString(2).length;
|
|
15
|
-
let e = bytesToBigInt(hash);
|
|
16
|
-
const hashBits = hash.length * 8;
|
|
17
|
-
if (hashBits > orderBits) {
|
|
18
|
-
e >>= BigInt(hashBits - orderBits);
|
|
19
|
-
}
|
|
20
|
-
return e;
|
|
21
|
-
}
|
|
22
|
-
function hmacDigest(algo, key, data) {
|
|
23
|
-
const hmac = new Hmac(algo, key);
|
|
24
|
-
hmac.update(data);
|
|
25
|
-
return new Uint8Array(hmac.digest());
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Generate deterministic k per RFC 6979 Section 3.2.
|
|
29
|
-
* Uses HMAC-DRBG seeded with private key and message hash.
|
|
30
|
-
*/
|
|
31
|
-
function rfc6979(hashAlgo, privKey, msgHash, curve) {
|
|
32
|
-
const qLen = curve.byteLength;
|
|
33
|
-
const hLen = msgHash.length;
|
|
34
|
-
let V = new Uint8Array(hLen).fill(1);
|
|
35
|
-
let K = new Uint8Array(hLen).fill(0);
|
|
36
|
-
const x = bigIntToBytes(privKey, qLen);
|
|
37
|
-
const z = bigIntToBytes(mod(truncateHash(msgHash, curve), curve.n), qLen);
|
|
38
|
-
const concat0 = new Uint8Array(hLen + 1 + qLen + qLen);
|
|
39
|
-
concat0.set(V, 0);
|
|
40
|
-
concat0[hLen] = 0;
|
|
41
|
-
concat0.set(x, hLen + 1);
|
|
42
|
-
concat0.set(z, hLen + 1 + qLen);
|
|
43
|
-
K = hmacDigest(hashAlgo, K, concat0);
|
|
44
|
-
V = hmacDigest(hashAlgo, K, V);
|
|
45
|
-
const concat1 = new Uint8Array(hLen + 1 + qLen + qLen);
|
|
46
|
-
concat1.set(V, 0);
|
|
47
|
-
concat1[hLen] = 1;
|
|
48
|
-
concat1.set(x, hLen + 1);
|
|
49
|
-
concat1.set(z, hLen + 1 + qLen);
|
|
50
|
-
K = hmacDigest(hashAlgo, K, concat1);
|
|
51
|
-
V = hmacDigest(hashAlgo, K, V);
|
|
52
|
-
for (let attempt = 0; attempt < 100; attempt++) {
|
|
53
|
-
let T = new Uint8Array(0);
|
|
54
|
-
while (T.length < qLen) {
|
|
55
|
-
V = hmacDigest(hashAlgo, K, V);
|
|
56
|
-
const newT = new Uint8Array(T.length + V.length);
|
|
57
|
-
newT.set(T, 0);
|
|
58
|
-
newT.set(V, T.length);
|
|
59
|
-
T = newT;
|
|
60
|
-
}
|
|
61
|
-
const k = truncateHash(T.slice(0, qLen), curve);
|
|
62
|
-
if (k >= 1n && k < curve.n) {
|
|
63
|
-
return k;
|
|
64
|
-
}
|
|
65
|
-
const retryConcat = new Uint8Array(hLen + 1);
|
|
66
|
-
retryConcat.set(V, 0);
|
|
67
|
-
retryConcat[hLen] = 0;
|
|
68
|
-
K = hmacDigest(hashAlgo, K, retryConcat);
|
|
69
|
-
V = hmacDigest(hashAlgo, K, V);
|
|
70
|
-
}
|
|
71
|
-
throw new Error("RFC 6979: failed to generate valid k after 100 attempts");
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* ECDSA signature generation per FIPS 186-4 Section 6.4.
|
|
75
|
-
*
|
|
76
|
-
* @param hashAlgo Hash algorithm name (e.g. 'sha256')
|
|
77
|
-
* @param privKeyBytes Private key as big-endian bytes
|
|
78
|
-
* @param data Data to sign (will be hashed)
|
|
79
|
-
* @param curveName Curve name (e.g. 'P-256')
|
|
80
|
-
* @returns Signature as r || s concatenated (each curve.byteLength bytes)
|
|
81
|
-
*/
|
|
82
|
-
function ecdsaSign(hashAlgo, privKeyBytes, data, curveName) {
|
|
83
|
-
const curve = getCurve(curveName);
|
|
84
|
-
const G = {
|
|
85
|
-
x: curve.Gx,
|
|
86
|
-
y: curve.Gy
|
|
87
|
-
};
|
|
88
|
-
const d = bytesToBigInt(privKeyBytes);
|
|
89
|
-
const hash = new Hash(hashAlgo);
|
|
90
|
-
hash.update(data);
|
|
91
|
-
const msgHash = new Uint8Array(hash.digest());
|
|
92
|
-
const e = truncateHash(msgHash, curve);
|
|
93
|
-
const k = rfc6979(hashAlgo, d, msgHash, curve);
|
|
94
|
-
const R = scalarMul(k, G, curve);
|
|
95
|
-
if (R === null) throw new Error("ECDSA: k * G is point at infinity");
|
|
96
|
-
const r = mod(R.x, curve.n);
|
|
97
|
-
if (r === 0n) throw new Error("ECDSA: r is zero");
|
|
98
|
-
const kInv = modInverse(k, curve.n);
|
|
99
|
-
const s = mod(kInv * (e + r * d), curve.n);
|
|
100
|
-
if (s === 0n) throw new Error("ECDSA: s is zero");
|
|
101
|
-
const sigLen = curve.byteLength;
|
|
102
|
-
const sig = new Uint8Array(sigLen * 2);
|
|
103
|
-
sig.set(bigIntToBytes(r, sigLen), 0);
|
|
104
|
-
sig.set(bigIntToBytes(s, sigLen), sigLen);
|
|
105
|
-
return sig;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* ECDSA signature verification per FIPS 186-4 Section 6.4.
|
|
109
|
-
*
|
|
110
|
-
* @param hashAlgo Hash algorithm name (e.g. 'sha256')
|
|
111
|
-
* @param pubKeyBytes Public key as uncompressed point (0x04 || x || y)
|
|
112
|
-
* @param signature Signature as r || s concatenated
|
|
113
|
-
* @param data Signed data (will be hashed)
|
|
114
|
-
* @param curveName Curve name (e.g. 'P-256')
|
|
115
|
-
* @returns true if signature is valid
|
|
116
|
-
*/
|
|
117
|
-
function ecdsaVerify(hashAlgo, pubKeyBytes, signature, data, curveName) {
|
|
118
|
-
const curve = getCurve(curveName);
|
|
119
|
-
const G = {
|
|
120
|
-
x: curve.Gx,
|
|
121
|
-
y: curve.Gy
|
|
122
|
-
};
|
|
123
|
-
const sigLen = curve.byteLength;
|
|
124
|
-
if (pubKeyBytes[0] !== 4 || pubKeyBytes.length !== 1 + sigLen * 2) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
const Qx = bytesToBigInt(pubKeyBytes.slice(1, 1 + sigLen));
|
|
128
|
-
const Qy = bytesToBigInt(pubKeyBytes.slice(1 + sigLen));
|
|
129
|
-
const Q = {
|
|
130
|
-
x: Qx,
|
|
131
|
-
y: Qy
|
|
132
|
-
};
|
|
133
|
-
if (signature.length !== sigLen * 2) return false;
|
|
134
|
-
const r = bytesToBigInt(signature.slice(0, sigLen));
|
|
135
|
-
const s = bytesToBigInt(signature.slice(sigLen));
|
|
136
|
-
if (r < 1n || r >= curve.n || s < 1n || s >= curve.n) return false;
|
|
137
|
-
const hash = new Hash(hashAlgo);
|
|
138
|
-
hash.update(data);
|
|
139
|
-
const msgHash = new Uint8Array(hash.digest());
|
|
140
|
-
const e = truncateHash(msgHash, curve);
|
|
141
|
-
const w = modInverse(s, curve.n);
|
|
142
|
-
const u1 = mod(e * w, curve.n);
|
|
143
|
-
const u2 = mod(r * w, curve.n);
|
|
144
|
-
const R1 = scalarMul(u1, G, curve);
|
|
145
|
-
const R2 = scalarMul(u2, Q, curve);
|
|
146
|
-
const R = pointAdd(R1, R2, curve);
|
|
147
|
-
if (R === null) return false;
|
|
148
|
-
return mod(R.x, curve.n) === r;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
//#endregion
|
|
152
|
-
export { ecdsaSign, ecdsaVerify };
|
|
1
|
+
import{bigIntToBytes as e,bytesToBigInt as t}from"./bigint-math.js";import{CURVES as n,CURVE_ALIASES as r,mod as i,modInverse as a,pointAdd as o,scalarMul as s}from"./ecdh.js";import{Hash as c}from"./hash.js";import{Hmac as l}from"./hmac.js";function u(e){let t=r[e.toLowerCase()];if(!t)throw Error(`Unsupported curve: ${e}`);return n[t]}function d(e,n){let r=n.n.toString(2).length,i=t(e),a=e.length*8;return a>r&&(i>>=BigInt(a-r)),i}function f(e,t,n){let r=new l(e,t);return r.update(n),new Uint8Array(r.digest())}function p(t,n,r,a){let o=a.byteLength,s=r.length,c=new Uint8Array(s).fill(1),l=new Uint8Array(s).fill(0),u=e(n,o),p=e(i(d(r,a),a.n),o),m=new Uint8Array(s+1+o+o);m.set(c,0),m[s]=0,m.set(u,s+1),m.set(p,s+1+o),l=f(t,l,m),c=f(t,l,c);let h=new Uint8Array(s+1+o+o);h.set(c,0),h[s]=1,h.set(u,s+1),h.set(p,s+1+o),l=f(t,l,h),c=f(t,l,c);for(let e=0;e<100;e++){let e=new Uint8Array;for(;e.length<o;){c=f(t,l,c);let n=new Uint8Array(e.length+c.length);n.set(e,0),n.set(c,e.length),e=n}let n=d(e.slice(0,o),a);if(n>=1n&&n<a.n)return n;let r=new Uint8Array(s+1);r.set(c,0),r[s]=0,l=f(t,l,r),c=f(t,l,c)}throw Error(`RFC 6979: failed to generate valid k after 100 attempts`)}function m(n,r,o,l){let f=u(l),m={x:f.Gx,y:f.Gy},h=t(r),g=new c(n);g.update(o);let _=new Uint8Array(g.digest()),v=d(_,f),y=p(n,h,_,f),b=s(y,m,f);if(b===null)throw Error(`ECDSA: k * G is point at infinity`);let x=i(b.x,f.n);if(x===0n)throw Error(`ECDSA: r is zero`);let S=i(a(y,f.n)*(v+x*h),f.n);if(S===0n)throw Error(`ECDSA: s is zero`);let C=f.byteLength,w=new Uint8Array(C*2);return w.set(e(x,C),0),w.set(e(S,C),C),w}function h(e,n,r,l,f){let p=u(f),m={x:p.Gx,y:p.Gy},h=p.byteLength;if(n[0]!==4||n.length!==1+h*2)return!1;let g={x:t(n.slice(1,1+h)),y:t(n.slice(1+h))};if(r.length!==h*2)return!1;let _=t(r.slice(0,h)),v=t(r.slice(h));if(_<1n||_>=p.n||v<1n||v>=p.n)return!1;let y=new c(e);y.update(l);let b=d(new Uint8Array(y.digest()),p),x=a(v,p.n),S=i(b*x,p.n),C=i(_*x,p.n),w=o(s(S,m,p),s(C,g,p),p);return w===null?!1:i(w.x,p.n)===_}export{m as ecdsaSign,h as ecdsaVerify};
|