vaultkeeper 0.2.0 → 0.4.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.
- package/dist/index.cjs +124 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -1
- package/dist/index.d.ts +117 -1
- package/dist/index.js +123 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ var child_process = require('child_process');
|
|
|
4
4
|
var fs5 = require('fs/promises');
|
|
5
5
|
var path5 = require('path');
|
|
6
6
|
var os4 = require('os');
|
|
7
|
-
var
|
|
7
|
+
var crypto4 = require('crypto');
|
|
8
8
|
var fs4 = require('fs');
|
|
9
9
|
var jose = require('jose');
|
|
10
10
|
|
|
@@ -29,7 +29,7 @@ function _interopNamespace(e) {
|
|
|
29
29
|
var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
|
|
30
30
|
var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
|
|
31
31
|
var os4__namespace = /*#__PURE__*/_interopNamespace(os4);
|
|
32
|
-
var
|
|
32
|
+
var crypto4__namespace = /*#__PURE__*/_interopNamespace(crypto4);
|
|
33
33
|
var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
|
|
34
34
|
|
|
35
35
|
// src/errors.ts
|
|
@@ -197,6 +197,11 @@ var RotationInProgressError = class extends VaultError {
|
|
|
197
197
|
}
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
+
// src/backend/types.ts
|
|
201
|
+
function isListableBackend(backend) {
|
|
202
|
+
return "list" in backend && typeof backend.list === "function";
|
|
203
|
+
}
|
|
204
|
+
|
|
200
205
|
// src/backend/registry.ts
|
|
201
206
|
var BackendRegistry = class {
|
|
202
207
|
static backends = /* @__PURE__ */ new Map();
|
|
@@ -232,6 +237,33 @@ var BackendRegistry = class {
|
|
|
232
237
|
static getTypes() {
|
|
233
238
|
return Array.from(this.backends.keys());
|
|
234
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Returns backend types that are available on the current system.
|
|
242
|
+
*
|
|
243
|
+
* @remarks
|
|
244
|
+
* Creates each registered backend via its factory, calls `isAvailable()`,
|
|
245
|
+
* and returns only the type identifiers whose backend reports availability.
|
|
246
|
+
* If a backend's `isAvailable()` call throws, that backend is excluded from
|
|
247
|
+
* the result rather than propagating the error.
|
|
248
|
+
*
|
|
249
|
+
* @returns Promise resolving to an array of available backend type identifiers
|
|
250
|
+
* @public
|
|
251
|
+
*/
|
|
252
|
+
static async getAvailableTypes() {
|
|
253
|
+
const entries = Array.from(this.backends.entries());
|
|
254
|
+
const results = await Promise.all(
|
|
255
|
+
entries.map(async ([type, factory]) => {
|
|
256
|
+
try {
|
|
257
|
+
const backend = factory();
|
|
258
|
+
const available = await backend.isAvailable();
|
|
259
|
+
return available ? type : null;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
return results.filter((type) => type !== null);
|
|
266
|
+
}
|
|
235
267
|
};
|
|
236
268
|
async function execCommand(command, args, options) {
|
|
237
269
|
const result = await execCommandFull(command, args);
|
|
@@ -265,7 +297,7 @@ path5__namespace.join(".vaultkeeper", "file");
|
|
|
265
297
|
path5__namespace.join(".vaultkeeper", "yubikey");
|
|
266
298
|
function hashExecutable(filePath) {
|
|
267
299
|
return new Promise((resolve, reject) => {
|
|
268
|
-
const hash =
|
|
300
|
+
const hash = crypto4__namespace.createHash("sha256");
|
|
269
301
|
const stream = fs4__namespace.createReadStream(filePath);
|
|
270
302
|
stream.on("data", (chunk) => {
|
|
271
303
|
hash.update(chunk);
|
|
@@ -565,10 +597,10 @@ var KeyManager = class {
|
|
|
565
597
|
#rotating = false;
|
|
566
598
|
/** Generate a new 32-byte key with a timestamp-based id. */
|
|
567
599
|
generateKey() {
|
|
568
|
-
const randomSuffix =
|
|
600
|
+
const randomSuffix = crypto4__namespace.randomBytes(4).toString("hex");
|
|
569
601
|
return {
|
|
570
602
|
id: `k-${String(Date.now())}-${randomSuffix}`,
|
|
571
|
-
key: new Uint8Array(
|
|
603
|
+
key: new Uint8Array(crypto4__namespace.randomBytes(32)),
|
|
572
604
|
createdAt: /* @__PURE__ */ new Date()
|
|
573
605
|
};
|
|
574
606
|
}
|
|
@@ -984,6 +1016,50 @@ function createSecretAccessor(secretValue) {
|
|
|
984
1016
|
return proxy;
|
|
985
1017
|
}
|
|
986
1018
|
|
|
1019
|
+
// src/access/sign-util.ts
|
|
1020
|
+
var ALLOWED_ALGORITHMS = /* @__PURE__ */ new Set(["sha256", "sha384", "sha512"]);
|
|
1021
|
+
function resolveAlgorithmForKey(key, override) {
|
|
1022
|
+
const keyType = key.asymmetricKeyType;
|
|
1023
|
+
if (keyType === "ed25519" || keyType === "ed448") {
|
|
1024
|
+
return { signAlg: null, label: keyType };
|
|
1025
|
+
}
|
|
1026
|
+
const alg = override ?? "sha256";
|
|
1027
|
+
if (!ALLOWED_ALGORITHMS.has(alg)) {
|
|
1028
|
+
throw new VaultError(
|
|
1029
|
+
`Unsupported signing algorithm '${alg}'. Allowed: ${[...ALLOWED_ALGORITHMS].join(", ")}`
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
return { signAlg: alg, label: alg };
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/access/delegated-sign.ts
|
|
1036
|
+
function delegatedSign(secretPem, request) {
|
|
1037
|
+
const key = crypto4__namespace.createPrivateKey(secretPem);
|
|
1038
|
+
const { signAlg, label } = resolveAlgorithmForKey(key, request.algorithm);
|
|
1039
|
+
const data = Buffer.isBuffer(request.data) ? request.data : Buffer.from(request.data);
|
|
1040
|
+
const signature = crypto4__namespace.sign(signAlg, data, key);
|
|
1041
|
+
return {
|
|
1042
|
+
signature: signature.toString("base64"),
|
|
1043
|
+
algorithm: label
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function delegatedVerify(request) {
|
|
1047
|
+
let key;
|
|
1048
|
+
try {
|
|
1049
|
+
key = crypto4__namespace.createPublicKey(request.publicKey);
|
|
1050
|
+
} catch {
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
const { signAlg } = resolveAlgorithmForKey(key, request.algorithm);
|
|
1054
|
+
const sig = Buffer.from(request.signature, "base64");
|
|
1055
|
+
try {
|
|
1056
|
+
const data = Buffer.isBuffer(request.data) ? request.data : Buffer.from(request.data);
|
|
1057
|
+
return crypto4__namespace.verify(signAlg, data, key, sig);
|
|
1058
|
+
} catch {
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
987
1063
|
// src/doctor/checks.ts
|
|
988
1064
|
function parseVersion(raw) {
|
|
989
1065
|
const match = /(\d+)\.(\d+)\.(\d+)/.exec(raw);
|
|
@@ -1236,7 +1312,7 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
1236
1312
|
}
|
|
1237
1313
|
const now = Math.floor(Date.now() / 1e3);
|
|
1238
1314
|
const claims = {
|
|
1239
|
-
jti:
|
|
1315
|
+
jti: crypto4__namespace.randomUUID(),
|
|
1240
1316
|
exp: now + ttlMinutes * 60,
|
|
1241
1317
|
iat: now,
|
|
1242
1318
|
sub: secretName,
|
|
@@ -1341,6 +1417,47 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
1341
1417
|
const claims = validateCapabilityToken(token);
|
|
1342
1418
|
return createSecretAccessor(claims.val);
|
|
1343
1419
|
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Sign data using the private key embedded in a capability token.
|
|
1422
|
+
*
|
|
1423
|
+
* The signing key is extracted from the token's encrypted claims, used
|
|
1424
|
+
* for a single `crypto.sign()` call, and never exposed to the caller.
|
|
1425
|
+
* The algorithm is auto-detected from the key type unless overridden
|
|
1426
|
+
* in the request.
|
|
1427
|
+
*
|
|
1428
|
+
* @param token - A `CapabilityToken` obtained from `authorize()`.
|
|
1429
|
+
* @param request - The data to sign and optional algorithm override.
|
|
1430
|
+
* @returns The base64-encoded signature and algorithm label, together
|
|
1431
|
+
* with the vault metadata (`vaultResponse`).
|
|
1432
|
+
* @throws {VaultError} If `token` is invalid or was not created by this
|
|
1433
|
+
* vault instance.
|
|
1434
|
+
*/
|
|
1435
|
+
async sign(token, request) {
|
|
1436
|
+
const claims = validateCapabilityToken(token);
|
|
1437
|
+
const result = delegatedSign(claims.val, request);
|
|
1438
|
+
await Promise.resolve();
|
|
1439
|
+
return {
|
|
1440
|
+
result,
|
|
1441
|
+
vaultResponse: { keyStatus: "current" }
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Verify a signature using a public key.
|
|
1446
|
+
*
|
|
1447
|
+
* This is a static method — no VaultKeeper instance, secrets, or
|
|
1448
|
+
* capability tokens are required. It is safe to call from CI or any
|
|
1449
|
+
* context that has access to public key material.
|
|
1450
|
+
*
|
|
1451
|
+
* Never throws. Returns `false` for invalid key material, malformed
|
|
1452
|
+
* signatures, or any verification failure.
|
|
1453
|
+
*
|
|
1454
|
+
* @param request - The data, signature, public key, and optional
|
|
1455
|
+
* algorithm override.
|
|
1456
|
+
* @returns `true` if the signature is valid, `false` otherwise.
|
|
1457
|
+
*/
|
|
1458
|
+
static verify(request) {
|
|
1459
|
+
return delegatedVerify(request);
|
|
1460
|
+
}
|
|
1344
1461
|
/**
|
|
1345
1462
|
* Rotate the current encryption key.
|
|
1346
1463
|
*
|
|
@@ -1476,5 +1593,6 @@ exports.TokenRevokedError = TokenRevokedError;
|
|
|
1476
1593
|
exports.UsageLimitExceededError = UsageLimitExceededError;
|
|
1477
1594
|
exports.VaultError = VaultError;
|
|
1478
1595
|
exports.VaultKeeper = VaultKeeper;
|
|
1596
|
+
exports.isListableBackend = isListableBackend;
|
|
1479
1597
|
//# sourceMappingURL=index.cjs.map
|
|
1480
1598
|
//# sourceMappingURL=index.cjs.map
|