@catbee/utils 2.0.2 → 2.0.4
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/config/index.cjs +0 -5
- package/config/index.mjs +0 -5
- package/crypto/index.cjs +297 -1
- package/crypto/index.d.ts +229 -2
- package/crypto/index.mjs +292 -3
- package/date/index.cjs +276 -0
- package/date/index.d.ts +44 -1
- package/date/index.mjs +273 -1
- package/error/index.cjs +58 -0
- package/error/index.d.ts +62 -0
- package/error/index.mjs +54 -0
- package/index.cjs +7 -0
- package/index.d.ts +1 -0
- package/index.mjs +1 -0
- package/logger/index.cjs +13 -12
- package/logger/index.d.ts +5 -5
- package/logger/index.mjs +13 -12
- package/package.json +6 -2
- package/server/index.cjs +129 -272
- package/server/index.d.ts +22 -59
- package/server/index.mjs +129 -272
- package/types/index.d.ts +0 -22
package/config/index.cjs
CHANGED
|
@@ -94,11 +94,6 @@ var defaultServerConfig = {
|
|
|
94
94
|
exposeHeader: env.Env.getBoolean("SERVER_REQUEST_ID_EXPOSE_HEADER", true),
|
|
95
95
|
generator: /* @__PURE__ */ __name(() => id.uuid(), "generator")
|
|
96
96
|
},
|
|
97
|
-
metrics: {
|
|
98
|
-
enable: env.Env.getBoolean("SERVER_METRICS_ENABLE", false),
|
|
99
|
-
path: env.Env.get("SERVER_METRICS_PATH", "/metrics"),
|
|
100
|
-
withGlobalPrefix: env.Env.getBoolean("SERVER_METRICS_WITH_GLOBAL_PREFIX", false)
|
|
101
|
-
},
|
|
102
97
|
serviceVersion: {
|
|
103
98
|
enable: env.Env.getBoolean("SERVER_SERVICE_VERSION_ENABLE", false),
|
|
104
99
|
headerName: env.Env.get("SERVER_SERVICE_VERSION_HEADER_NAME", "x-service-version"),
|
package/config/index.mjs
CHANGED
|
@@ -92,11 +92,6 @@ var defaultServerConfig = {
|
|
|
92
92
|
exposeHeader: Env.getBoolean("SERVER_REQUEST_ID_EXPOSE_HEADER", true),
|
|
93
93
|
generator: /* @__PURE__ */ __name(() => uuid(), "generator")
|
|
94
94
|
},
|
|
95
|
-
metrics: {
|
|
96
|
-
enable: Env.getBoolean("SERVER_METRICS_ENABLE", false),
|
|
97
|
-
path: Env.get("SERVER_METRICS_PATH", "/metrics"),
|
|
98
|
-
withGlobalPrefix: Env.getBoolean("SERVER_METRICS_WITH_GLOBAL_PREFIX", false)
|
|
99
|
-
},
|
|
100
95
|
serviceVersion: {
|
|
101
96
|
enable: Env.getBoolean("SERVER_SERVICE_VERSION_ENABLE", false),
|
|
102
97
|
headerName: Env.get("SERVER_SERVICE_VERSION_HEADER_NAME", "x-service-version"),
|
package/crypto/index.cjs
CHANGED
|
@@ -56,6 +56,287 @@ function md5(input) {
|
|
|
56
56
|
return hash("md5", input);
|
|
57
57
|
}
|
|
58
58
|
__name(md5, "md5");
|
|
59
|
+
var DEFAULT_SIGNATURE_ENCODING = "base64url";
|
|
60
|
+
var subtle = globalThis.crypto?.subtle ?? crypto.webcrypto.subtle;
|
|
61
|
+
function getDigestLength(hash2) {
|
|
62
|
+
switch (hash2) {
|
|
63
|
+
case "SHA-256":
|
|
64
|
+
return 32;
|
|
65
|
+
case "SHA-384":
|
|
66
|
+
return 48;
|
|
67
|
+
case "SHA-512":
|
|
68
|
+
return 64;
|
|
69
|
+
default:
|
|
70
|
+
return 32;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
__name(getDigestLength, "getDigestLength");
|
|
74
|
+
function resolveKeyAlgorithm({ type = "RSA-PSS", modulusLength = 2048, hash: hash2 = "SHA-256", namedCurve = "P-256" }) {
|
|
75
|
+
switch (type) {
|
|
76
|
+
case "RSA":
|
|
77
|
+
if (!Number.isInteger(modulusLength) || modulusLength < 2048) {
|
|
78
|
+
throw new Error("RSA modulusLength must be an integer greater than or equal to 2048");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
82
|
+
modulusLength,
|
|
83
|
+
publicExponent: new Uint8Array([
|
|
84
|
+
1,
|
|
85
|
+
0,
|
|
86
|
+
1
|
|
87
|
+
]),
|
|
88
|
+
hash: hash2
|
|
89
|
+
};
|
|
90
|
+
case "RSA-PSS":
|
|
91
|
+
if (!Number.isInteger(modulusLength) || modulusLength < 2048) {
|
|
92
|
+
throw new Error("RSA-PSS modulusLength must be an integer greater than or equal to 2048");
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
name: "RSA-PSS",
|
|
96
|
+
modulusLength,
|
|
97
|
+
publicExponent: new Uint8Array([
|
|
98
|
+
1,
|
|
99
|
+
0,
|
|
100
|
+
1
|
|
101
|
+
]),
|
|
102
|
+
hash: hash2
|
|
103
|
+
};
|
|
104
|
+
case "ECDSA":
|
|
105
|
+
return {
|
|
106
|
+
name: "ECDSA",
|
|
107
|
+
namedCurve
|
|
108
|
+
};
|
|
109
|
+
case "Ed25519":
|
|
110
|
+
return {
|
|
111
|
+
name: "Ed25519"
|
|
112
|
+
};
|
|
113
|
+
default:
|
|
114
|
+
throw new Error(`Unsupported key type: ${type}. Supported types: RSA, RSA-PSS, ECDSA, Ed25519`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
__name(resolveKeyAlgorithm, "resolveKeyAlgorithm");
|
|
118
|
+
function toBase64(buffer) {
|
|
119
|
+
if (typeof Buffer !== "undefined") {
|
|
120
|
+
return Buffer.from(buffer).toString("base64");
|
|
121
|
+
}
|
|
122
|
+
let binary = "";
|
|
123
|
+
const bytes = new Uint8Array(buffer);
|
|
124
|
+
const chunkSize = 32768;
|
|
125
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
126
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
|
|
127
|
+
}
|
|
128
|
+
return btoa(binary);
|
|
129
|
+
}
|
|
130
|
+
__name(toBase64, "toBase64");
|
|
131
|
+
function toPEM(base64, type, formatPemLines, addPrefixSuffix) {
|
|
132
|
+
const formattedBase64 = formatPemLines ? base64.match(/.{1,64}/g)?.join("\n") ?? base64 : base64;
|
|
133
|
+
if (!addPrefixSuffix) {
|
|
134
|
+
return formattedBase64;
|
|
135
|
+
}
|
|
136
|
+
const prefix = `-----BEGIN ${type}-----`;
|
|
137
|
+
const suffix = `-----END ${type}-----`;
|
|
138
|
+
return `${prefix}
|
|
139
|
+
${formattedBase64}
|
|
140
|
+
${suffix}`;
|
|
141
|
+
}
|
|
142
|
+
__name(toPEM, "toPEM");
|
|
143
|
+
function fromPEM(pem) {
|
|
144
|
+
const trimmedPem = pem.trim();
|
|
145
|
+
const format = trimmedPem.includes("BEGIN PRIVATE KEY") ? "pkcs8" : trimmedPem.includes("BEGIN PUBLIC KEY") ? "spki" : void 0;
|
|
146
|
+
if (!format) {
|
|
147
|
+
throw new Error("Unsupported PEM format. Expected PUBLIC KEY or PRIVATE KEY PEM block");
|
|
148
|
+
}
|
|
149
|
+
const base64 = trimmedPem.replace(/-----BEGIN [A-Z ]+-----/g, "").replace(/-----END [A-Z ]+-----/g, "").replace(/\s+/g, "");
|
|
150
|
+
return {
|
|
151
|
+
format,
|
|
152
|
+
binary: toArrayBuffer(Buffer.from(base64, "base64"))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
__name(fromPEM, "fromPEM");
|
|
156
|
+
function toBinary(data, encoding = "utf8") {
|
|
157
|
+
if (typeof data === "string") {
|
|
158
|
+
return Uint8Array.from(Buffer.from(data, encoding));
|
|
159
|
+
}
|
|
160
|
+
if (Buffer.isBuffer(data)) {
|
|
161
|
+
return Uint8Array.from(data);
|
|
162
|
+
}
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
165
|
+
__name(toBinary, "toBinary");
|
|
166
|
+
function toArrayBuffer(data) {
|
|
167
|
+
const byteView = data instanceof Uint8Array ? data : Uint8Array.from(data);
|
|
168
|
+
const copy = new Uint8Array(byteView.byteLength);
|
|
169
|
+
copy.set(byteView);
|
|
170
|
+
return copy.buffer;
|
|
171
|
+
}
|
|
172
|
+
__name(toArrayBuffer, "toArrayBuffer");
|
|
173
|
+
function inferKeyTypeFromJwk(jwk, fallback) {
|
|
174
|
+
if (fallback) {
|
|
175
|
+
return fallback;
|
|
176
|
+
}
|
|
177
|
+
const keyType = typeof jwk.kty === "string" ? jwk.kty : void 0;
|
|
178
|
+
const curve = typeof jwk.crv === "string" ? jwk.crv : void 0;
|
|
179
|
+
const algorithm = typeof jwk.alg === "string" ? jwk.alg : void 0;
|
|
180
|
+
if (keyType === "OKP" && curve === "Ed25519") {
|
|
181
|
+
return "Ed25519";
|
|
182
|
+
}
|
|
183
|
+
if (keyType === "EC") {
|
|
184
|
+
return "ECDSA";
|
|
185
|
+
}
|
|
186
|
+
if (keyType === "RSA") {
|
|
187
|
+
return algorithm?.startsWith("PS") ? "RSA-PSS" : "RSA";
|
|
188
|
+
}
|
|
189
|
+
throw new Error("Unable to infer key type from JWK. Provide import options with an explicit type");
|
|
190
|
+
}
|
|
191
|
+
__name(inferKeyTypeFromJwk, "inferKeyTypeFromJwk");
|
|
192
|
+
function resolveDefaultUsages(format, usages) {
|
|
193
|
+
return usages ?? (format === "pkcs8" ? [
|
|
194
|
+
"sign"
|
|
195
|
+
] : [
|
|
196
|
+
"verify"
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
__name(resolveDefaultUsages, "resolveDefaultUsages");
|
|
200
|
+
function inferJwkUsages(jwk, usages) {
|
|
201
|
+
if (usages) {
|
|
202
|
+
return usages;
|
|
203
|
+
}
|
|
204
|
+
if (Array.isArray(jwk.key_ops)) {
|
|
205
|
+
const inferredFromKeyOps = jwk.key_ops.filter((usage) => usage === "sign" || usage === "verify");
|
|
206
|
+
if (inferredFromKeyOps.length === 0) {
|
|
207
|
+
throw new Error("JWK key_ops must include at least one of sign or verify when provided");
|
|
208
|
+
}
|
|
209
|
+
return inferredFromKeyOps;
|
|
210
|
+
}
|
|
211
|
+
if (typeof jwk.d === "string" && jwk.d.length > 0) {
|
|
212
|
+
return [
|
|
213
|
+
"sign"
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
return [
|
|
217
|
+
"verify"
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
__name(inferJwkUsages, "inferJwkUsages");
|
|
221
|
+
function resolveSignAlgorithm(key, options = {}) {
|
|
222
|
+
const hash2 = options.hash ?? "SHA-256";
|
|
223
|
+
switch (key.algorithm.name) {
|
|
224
|
+
case "RSASSA-PKCS1-v1_5":
|
|
225
|
+
return "RSASSA-PKCS1-v1_5";
|
|
226
|
+
case "RSA-PSS": {
|
|
227
|
+
const keyAlgorithm = key.algorithm;
|
|
228
|
+
const pssHash = options.hash ?? keyAlgorithm.hash.name;
|
|
229
|
+
return {
|
|
230
|
+
name: "RSA-PSS",
|
|
231
|
+
saltLength: options.saltLength ?? getDigestLength(pssHash)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case "ECDSA":
|
|
235
|
+
return {
|
|
236
|
+
name: "ECDSA",
|
|
237
|
+
hash: hash2
|
|
238
|
+
};
|
|
239
|
+
case "Ed25519":
|
|
240
|
+
return "Ed25519";
|
|
241
|
+
default:
|
|
242
|
+
throw new Error(`Unsupported signing key algorithm: ${key.algorithm.name}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
__name(resolveSignAlgorithm, "resolveSignAlgorithm");
|
|
246
|
+
function arrayBufferToBuffer(buffer) {
|
|
247
|
+
return Buffer.from(buffer);
|
|
248
|
+
}
|
|
249
|
+
__name(arrayBufferToBuffer, "arrayBufferToBuffer");
|
|
250
|
+
async function generateKeys(options = {}) {
|
|
251
|
+
const { type = "RSA-PSS", modulusLength = 2048, hash: hash2 = "SHA-256", namedCurve = "P-256", extractable = false, includeCryptoKeys = false, formatPemLines = true, addPrefixSuffix = true } = options;
|
|
252
|
+
const algorithm = resolveKeyAlgorithm({
|
|
253
|
+
type,
|
|
254
|
+
modulusLength,
|
|
255
|
+
hash: hash2,
|
|
256
|
+
namedCurve
|
|
257
|
+
});
|
|
258
|
+
const usages = [
|
|
259
|
+
"sign",
|
|
260
|
+
"verify"
|
|
261
|
+
];
|
|
262
|
+
const keyPair = await subtle.generateKey(algorithm, extractable, usages);
|
|
263
|
+
const [publicKeyBuf, privateKeyBuf] = await Promise.all([
|
|
264
|
+
subtle.exportKey("spki", keyPair.publicKey),
|
|
265
|
+
extractable ? subtle.exportKey("pkcs8", keyPair.privateKey) : Promise.resolve(void 0)
|
|
266
|
+
]);
|
|
267
|
+
const publicKeyBase64 = toBase64(publicKeyBuf);
|
|
268
|
+
const privateKeyBase64 = privateKeyBuf ? toBase64(privateKeyBuf) : void 0;
|
|
269
|
+
const result = {
|
|
270
|
+
type,
|
|
271
|
+
publicKey: toPEM(publicKeyBase64, "PUBLIC KEY", formatPemLines, addPrefixSuffix),
|
|
272
|
+
publicKeyBuffer: publicKeyBuf
|
|
273
|
+
};
|
|
274
|
+
if (privateKeyBase64 && privateKeyBuf) {
|
|
275
|
+
result.privateKey = toPEM(privateKeyBase64, "PRIVATE KEY", formatPemLines, addPrefixSuffix);
|
|
276
|
+
result.privateKeyBuffer = privateKeyBuf;
|
|
277
|
+
}
|
|
278
|
+
if (includeCryptoKeys) {
|
|
279
|
+
result.privateKeyCrypto = keyPair.privateKey;
|
|
280
|
+
result.publicKeyCrypto = keyPair.publicKey;
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
__name(generateKeys, "generateKeys");
|
|
285
|
+
async function sign(data, privateKeyCrypto, options = {}) {
|
|
286
|
+
const algorithm = resolveSignAlgorithm(privateKeyCrypto, options);
|
|
287
|
+
const signature = await subtle.sign(algorithm, privateKeyCrypto, toArrayBuffer(toBinary(data, options.inputEncoding)));
|
|
288
|
+
return arrayBufferToBuffer(signature).toString(options.outputEncoding ?? DEFAULT_SIGNATURE_ENCODING);
|
|
289
|
+
}
|
|
290
|
+
__name(sign, "sign");
|
|
291
|
+
async function verify(data, signature, publicKeyCrypto, options = {}) {
|
|
292
|
+
const algorithm = resolveSignAlgorithm(publicKeyCrypto, options);
|
|
293
|
+
const signatureBytes = typeof signature === "string" ? Uint8Array.from(Buffer.from(signature, options.signatureEncoding ?? DEFAULT_SIGNATURE_ENCODING)) : toBinary(signature);
|
|
294
|
+
return subtle.verify(algorithm, publicKeyCrypto, toArrayBuffer(signatureBytes), toArrayBuffer(toBinary(data, options.inputEncoding)));
|
|
295
|
+
}
|
|
296
|
+
__name(verify, "verify");
|
|
297
|
+
async function importKey(key, options = {}) {
|
|
298
|
+
const extractable = options.extractable ?? false;
|
|
299
|
+
if (typeof key === "string") {
|
|
300
|
+
if (!options.type) {
|
|
301
|
+
throw new Error("Key type is required when importing PEM keys");
|
|
302
|
+
}
|
|
303
|
+
const { format, binary } = fromPEM(key);
|
|
304
|
+
const algorithm2 = resolveKeyAlgorithm({
|
|
305
|
+
type: options.type,
|
|
306
|
+
hash: options.hash,
|
|
307
|
+
namedCurve: options.namedCurve
|
|
308
|
+
});
|
|
309
|
+
return subtle.importKey(format, binary, algorithm2, extractable, resolveDefaultUsages(format, options.usages));
|
|
310
|
+
}
|
|
311
|
+
const keyType = inferKeyTypeFromJwk(key, options.type);
|
|
312
|
+
const algorithm = resolveKeyAlgorithm({
|
|
313
|
+
type: keyType,
|
|
314
|
+
hash: options.hash,
|
|
315
|
+
namedCurve: options.namedCurve
|
|
316
|
+
});
|
|
317
|
+
return subtle.importKey("jwk", key, algorithm, extractable, inferJwkUsages(key, options.usages));
|
|
318
|
+
}
|
|
319
|
+
__name(importKey, "importKey");
|
|
320
|
+
async function exportKey(key, format = "jwk", options = {}) {
|
|
321
|
+
if (format === "jwk") {
|
|
322
|
+
return subtle.exportKey("jwk", key);
|
|
323
|
+
}
|
|
324
|
+
const binary = key.type === "private" ? await subtle.exportKey("pkcs8", key) : await subtle.exportKey("spki", key);
|
|
325
|
+
return toPEM(toBase64(binary), key.type === "private" ? "PRIVATE KEY" : "PUBLIC KEY", options.formatPemLines ?? true, options.addPrefixSuffix ?? true);
|
|
326
|
+
}
|
|
327
|
+
__name(exportKey, "exportKey");
|
|
328
|
+
async function fingerprint(publicKey, encoding = "base64url") {
|
|
329
|
+
if (publicKey.type !== "public") {
|
|
330
|
+
throw new Error("fingerprint requires a public key");
|
|
331
|
+
}
|
|
332
|
+
const spki = await subtle.exportKey("spki", publicKey);
|
|
333
|
+
return crypto.createHash("sha256").update(arrayBufferToBuffer(spki)).digest(encoding);
|
|
334
|
+
}
|
|
335
|
+
__name(fingerprint, "fingerprint");
|
|
336
|
+
async function getKeyId(publicKey) {
|
|
337
|
+
return fingerprint(publicKey, "base64url");
|
|
338
|
+
}
|
|
339
|
+
__name(getKeyId, "getKeyId");
|
|
59
340
|
function randomString() {
|
|
60
341
|
return sha256(id.uuid());
|
|
61
342
|
}
|
|
@@ -176,8 +457,16 @@ function generateNonce(byteLength = 16, encoding = "hex") {
|
|
|
176
457
|
}
|
|
177
458
|
__name(generateNonce, "generateNonce");
|
|
178
459
|
function secureRandomInt(min, max) {
|
|
179
|
-
if (min
|
|
460
|
+
if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
|
|
461
|
+
throw new TypeError("min and max must be safe integers");
|
|
462
|
+
}
|
|
463
|
+
if (min > max) {
|
|
464
|
+
throw new RangeError("min must be less than or equal to max");
|
|
465
|
+
}
|
|
180
466
|
const range = max - min + 1;
|
|
467
|
+
if (!Number.isSafeInteger(range) || range <= 0) {
|
|
468
|
+
throw new RangeError("Range is too large");
|
|
469
|
+
}
|
|
181
470
|
const bytesNeeded = Math.ceil(Math.log2(range) / 8);
|
|
182
471
|
const maxValid = Math.floor(256 ** bytesNeeded / range) * range;
|
|
183
472
|
let randomValue;
|
|
@@ -213,13 +502,18 @@ __name(verifyPassword, "verifyPassword");
|
|
|
213
502
|
exports.createSignedToken = createSignedToken;
|
|
214
503
|
exports.decrypt = decrypt;
|
|
215
504
|
exports.encrypt = encrypt;
|
|
505
|
+
exports.exportKey = exportKey;
|
|
506
|
+
exports.fingerprint = fingerprint;
|
|
216
507
|
exports.generateApiKey = generateApiKey;
|
|
508
|
+
exports.generateKeys = generateKeys;
|
|
217
509
|
exports.generateNonce = generateNonce;
|
|
218
510
|
exports.generateRandomBytes = generateRandomBytes;
|
|
219
511
|
exports.generateRandomBytesAsString = generateRandomBytesAsString;
|
|
512
|
+
exports.getKeyId = getKeyId;
|
|
220
513
|
exports.hash = hash;
|
|
221
514
|
exports.hashPassword = hashPassword;
|
|
222
515
|
exports.hmac = hmac;
|
|
516
|
+
exports.importKey = importKey;
|
|
223
517
|
exports.md5 = md5;
|
|
224
518
|
exports.pbkdf2Hash = pbkdf2Hash;
|
|
225
519
|
exports.randomString = randomString;
|
|
@@ -228,5 +522,7 @@ exports.secureRandomInt = secureRandomInt;
|
|
|
228
522
|
exports.sha1 = sha1;
|
|
229
523
|
exports.sha256 = sha256;
|
|
230
524
|
exports.sha256Hmac = sha256Hmac;
|
|
525
|
+
exports.sign = sign;
|
|
526
|
+
exports.verify = verify;
|
|
231
527
|
exports.verifyPassword = verifyPassword;
|
|
232
528
|
exports.verifySignedToken = verifySignedToken;
|
package/crypto/index.d.ts
CHANGED
|
@@ -75,6 +75,233 @@ declare function sha256(input: string, encoding?: BinaryToTextEncoding): string;
|
|
|
75
75
|
* @returns {string} MD5 hash as a string.
|
|
76
76
|
*/
|
|
77
77
|
declare function md5(input: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Supported asymmetric key types for generation.
|
|
80
|
+
*
|
|
81
|
+
* - `RSA` → RSASSA-PKCS1-v1_5 (legacy compatibility)
|
|
82
|
+
* - `RSA-PSS` → Recommended RSA variant with modern padding
|
|
83
|
+
* - `ECDSA` → Elliptic Curve (fast, smaller keys)
|
|
84
|
+
* - `Ed25519` → Modern, simple, highly secure (recommended)
|
|
85
|
+
*/
|
|
86
|
+
type EncKeyType = 'RSA' | 'RSA-PSS' | 'ECDSA' | 'Ed25519';
|
|
87
|
+
/**
|
|
88
|
+
* Options to configure key pair generation.
|
|
89
|
+
*/
|
|
90
|
+
interface GenerateKeyOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Type of key algorithm to generate.
|
|
93
|
+
* @default 'RSA-PSS'
|
|
94
|
+
*/
|
|
95
|
+
type?: EncKeyType;
|
|
96
|
+
/**
|
|
97
|
+
* RSA modulus length in bits.
|
|
98
|
+
* Recommended: 2048 or 3072 (4096 for high security).
|
|
99
|
+
* @default 2048
|
|
100
|
+
*/
|
|
101
|
+
modulusLength?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Hash algorithm used for signing.
|
|
104
|
+
*
|
|
105
|
+
* ⚠️ For ECDSA, hash is used during sign/verify, not key generation.
|
|
106
|
+
* @default 'SHA-256'
|
|
107
|
+
*/
|
|
108
|
+
hash?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
109
|
+
/**
|
|
110
|
+
* Named curve for ECDSA keys.
|
|
111
|
+
* @default 'P-256'
|
|
112
|
+
*/
|
|
113
|
+
namedCurve?: 'P-256' | 'P-384' | 'P-521';
|
|
114
|
+
/**
|
|
115
|
+
* Whether the private key can be exported.
|
|
116
|
+
*
|
|
117
|
+
* ⚠️ Set to `false` in production if you don't need to export keys.
|
|
118
|
+
* @default false
|
|
119
|
+
*/
|
|
120
|
+
extractable?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Whether to include generated CryptoKey objects in the result.
|
|
123
|
+
*
|
|
124
|
+
* Useful when private key export is disabled (`extractable: false`) but
|
|
125
|
+
* you still want to sign/verify with the in-memory keys.
|
|
126
|
+
* @default false
|
|
127
|
+
*/
|
|
128
|
+
includeCryptoKeys?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Whether to format Base64 output into 64-character lines (PEM style).
|
|
131
|
+
* @default true
|
|
132
|
+
*/
|
|
133
|
+
formatPemLines?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Whether to include PEM prefix/suffix headers.
|
|
136
|
+
*
|
|
137
|
+
* Example:
|
|
138
|
+
* -----BEGIN PRIVATE KEY-----
|
|
139
|
+
* -----END PRIVATE KEY-----
|
|
140
|
+
*
|
|
141
|
+
* @default true
|
|
142
|
+
*/
|
|
143
|
+
addPrefixSuffix?: boolean;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Result object returned from {@link generateKeys}
|
|
147
|
+
*/
|
|
148
|
+
interface GenerateKeyResult {
|
|
149
|
+
/** Algorithm type used */
|
|
150
|
+
type: EncKeyType;
|
|
151
|
+
/** PEM or Base64 encoded private key (only when extractable is true) */
|
|
152
|
+
privateKey?: string;
|
|
153
|
+
/** PEM or Base64 encoded public key */
|
|
154
|
+
publicKey: string;
|
|
155
|
+
/** Raw PKCS8 private key buffer (only when extractable is true) */
|
|
156
|
+
privateKeyBuffer?: ArrayBuffer;
|
|
157
|
+
/** Raw SPKI public key buffer */
|
|
158
|
+
publicKeyBuffer: ArrayBuffer;
|
|
159
|
+
/** Optional generated private CryptoKey */
|
|
160
|
+
privateKeyCrypto?: CryptoKey;
|
|
161
|
+
/** Optional generated public CryptoKey */
|
|
162
|
+
publicKeyCrypto?: CryptoKey;
|
|
163
|
+
}
|
|
164
|
+
type SupportedAlgorithm = RsaHashedKeyGenParams | EcKeyGenParams | {
|
|
165
|
+
name: 'Ed25519';
|
|
166
|
+
};
|
|
167
|
+
type SignatureEncoding = 'base64' | 'base64url' | 'hex';
|
|
168
|
+
interface SignatureOptions {
|
|
169
|
+
/**
|
|
170
|
+
* Hash algorithm used for ECDSA signatures.
|
|
171
|
+
*
|
|
172
|
+
* ⚠️ For ECDSA, hash is used during sign/verify, not key generation.
|
|
173
|
+
* @default 'SHA-256'
|
|
174
|
+
*/
|
|
175
|
+
hash?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
176
|
+
/**
|
|
177
|
+
* Salt length for RSA-PSS signatures.
|
|
178
|
+
* Defaults to the digest length of the configured hash.
|
|
179
|
+
*/
|
|
180
|
+
saltLength?: number;
|
|
181
|
+
/**
|
|
182
|
+
* Output encoding for generated signatures.
|
|
183
|
+
* @default 'base64url'
|
|
184
|
+
*/
|
|
185
|
+
outputEncoding?: SignatureEncoding;
|
|
186
|
+
/**
|
|
187
|
+
* Input encoding when the payload is a string.
|
|
188
|
+
* @default 'utf8'
|
|
189
|
+
*/
|
|
190
|
+
inputEncoding?: BufferEncoding;
|
|
191
|
+
}
|
|
192
|
+
interface VerifyOptions extends Omit<SignatureOptions, 'outputEncoding'> {
|
|
193
|
+
/**
|
|
194
|
+
* Encoding of the provided signature when it is a string.
|
|
195
|
+
* @default 'base64url'
|
|
196
|
+
*/
|
|
197
|
+
signatureEncoding?: SignatureEncoding;
|
|
198
|
+
}
|
|
199
|
+
interface ImportKeyOptions {
|
|
200
|
+
/**
|
|
201
|
+
* Explicit key type. Required for PEM import when the algorithm cannot be inferred.
|
|
202
|
+
*/
|
|
203
|
+
type?: EncKeyType;
|
|
204
|
+
/**
|
|
205
|
+
* Hash algorithm used with RSA and ECDSA operations.
|
|
206
|
+
*
|
|
207
|
+
* ⚠️ For ECDSA, hash is used during sign/verify, not key generation.
|
|
208
|
+
* @default 'SHA-256'
|
|
209
|
+
*/
|
|
210
|
+
hash?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
211
|
+
/**
|
|
212
|
+
* Named curve for ECDSA keys.
|
|
213
|
+
* @default 'P-256'
|
|
214
|
+
*/
|
|
215
|
+
namedCurve?: 'P-256' | 'P-384' | 'P-521';
|
|
216
|
+
/**
|
|
217
|
+
* Whether the imported key can be exported.
|
|
218
|
+
* @default false
|
|
219
|
+
*/
|
|
220
|
+
extractable?: boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Allowed operations for the imported key.
|
|
223
|
+
* Defaults to `['sign']` for private keys and `['verify']` for public keys.
|
|
224
|
+
*/
|
|
225
|
+
usages?: Array<'sign' | 'verify'>;
|
|
226
|
+
}
|
|
227
|
+
interface ExportKeyOptions {
|
|
228
|
+
/**
|
|
229
|
+
* Whether to format Base64 output into 64-character lines (PEM style).
|
|
230
|
+
* @default true
|
|
231
|
+
*/
|
|
232
|
+
formatPemLines?: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* Whether to include PEM prefix/suffix headers.
|
|
235
|
+
* @default true
|
|
236
|
+
*/
|
|
237
|
+
addPrefixSuffix?: boolean;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Generates an asymmetric cryptographic key pair using Web Crypto API.
|
|
241
|
+
*
|
|
242
|
+
* Supports RSA, RSA-PSS, ECDSA, and Ed25519.
|
|
243
|
+
*
|
|
244
|
+
* @param options - Configuration for key generation
|
|
245
|
+
*
|
|
246
|
+
* @returns Promise resolving to generated key pair (PEM + raw buffers)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* const keys = await generateKeys({
|
|
251
|
+
* type: 'RSA-PSS',
|
|
252
|
+
* modulusLength: 2048
|
|
253
|
+
* });
|
|
254
|
+
*
|
|
255
|
+
* console.log(keys.publicKey);
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* const keys = await generateKeys({
|
|
261
|
+
* type: 'Ed25519',
|
|
262
|
+
* extractable: false
|
|
263
|
+
* });
|
|
264
|
+
* ```
|
|
265
|
+
*
|
|
266
|
+
* @remarks
|
|
267
|
+
* - Uses `crypto.subtle.generateKey`
|
|
268
|
+
* - Private key is exported in PKCS#8 format
|
|
269
|
+
* - Public key is exported in SPKI format
|
|
270
|
+
* - Ed25519 requires modern runtime support (Node 18+, modern browsers)
|
|
271
|
+
*
|
|
272
|
+
* ⚠️ Security Notes:
|
|
273
|
+
* - Avoid logging private keys in production
|
|
274
|
+
* - Prefer `extractable: false` when possible
|
|
275
|
+
* - Store keys securely (e.g., KMS, HSM)
|
|
276
|
+
*/
|
|
277
|
+
declare function generateKeys(options?: GenerateKeyOptions): Promise<GenerateKeyResult>;
|
|
278
|
+
/**
|
|
279
|
+
* Signs data with a private key generated or imported through Web Crypto.
|
|
280
|
+
* ⚠️ ECDSA signatures are DER-encoded.
|
|
281
|
+
* Some systems (e.g., JWT ES256, blockchain) require raw (r || s) format.
|
|
282
|
+
* Conversion may be required depending on the consumer.
|
|
283
|
+
*/
|
|
284
|
+
declare function sign(data: string | Buffer | Uint8Array, privateKeyCrypto: CryptoKey, options?: SignatureOptions): Promise<string>;
|
|
285
|
+
/**
|
|
286
|
+
* Verifies a signature with a public key generated or imported through Web Crypto.
|
|
287
|
+
*/
|
|
288
|
+
declare function verify(data: string | Buffer | Uint8Array, signature: string | Buffer | Uint8Array, publicKeyCrypto: CryptoKey, options?: VerifyOptions): Promise<boolean>;
|
|
289
|
+
/**
|
|
290
|
+
* Imports a PEM or JWK asymmetric key into Web Crypto.
|
|
291
|
+
*/
|
|
292
|
+
declare function importKey(key: string | JsonWebKey, options?: ImportKeyOptions): Promise<CryptoKey>;
|
|
293
|
+
/**
|
|
294
|
+
* Exports an asymmetric CryptoKey as JWK or PEM.
|
|
295
|
+
*/
|
|
296
|
+
declare function exportKey(key: CryptoKey, format?: 'jwk' | 'pem', options?: ExportKeyOptions): Promise<JsonWebKey | string>;
|
|
297
|
+
/**
|
|
298
|
+
* Produces a stable SHA-256 fingerprint for a public key.
|
|
299
|
+
*/
|
|
300
|
+
declare function fingerprint(publicKey: CryptoKey, encoding?: SignatureEncoding): Promise<string>;
|
|
301
|
+
/**
|
|
302
|
+
* Generates a unique identifier for a public key by computing its fingerprint.
|
|
303
|
+
*/
|
|
304
|
+
declare function getKeyId(publicKey: CryptoKey): Promise<string>;
|
|
78
305
|
/**
|
|
79
306
|
* Generates a cryptographically strong random string by hashing a random UUID with SHA-256.
|
|
80
307
|
*
|
|
@@ -244,5 +471,5 @@ declare function hashPassword(password: string, saltLength?: number, keyLength?:
|
|
|
244
471
|
*/
|
|
245
472
|
declare function verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
246
473
|
|
|
247
|
-
export { createSignedToken, decrypt, encrypt, generateApiKey, generateNonce, generateRandomBytes, generateRandomBytesAsString, hash, hashPassword, hmac, md5, pbkdf2Hash, randomString, safeCompare, secureRandomInt, sha1, sha256, sha256Hmac, verifyPassword, verifySignedToken };
|
|
248
|
-
export type { BufferEncoding, DecryptionOptions, EncryptionOptions, EncryptionResult };
|
|
474
|
+
export { createSignedToken, decrypt, encrypt, exportKey, fingerprint, generateApiKey, generateKeys, generateNonce, generateRandomBytes, generateRandomBytesAsString, getKeyId, hash, hashPassword, hmac, importKey, md5, pbkdf2Hash, randomString, safeCompare, secureRandomInt, sha1, sha256, sha256Hmac, sign, verify, verifyPassword, verifySignedToken };
|
|
475
|
+
export type { BufferEncoding, DecryptionOptions, EncKeyType, EncryptionOptions, EncryptionResult, ExportKeyOptions, GenerateKeyOptions, GenerateKeyResult, ImportKeyOptions, SignatureEncoding, SignatureOptions, SupportedAlgorithm, VerifyOptions };
|