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 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 crypto3 = require('crypto');
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 crypto3__namespace = /*#__PURE__*/_interopNamespace(crypto3);
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 = crypto3__namespace.createHash("sha256");
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 = crypto3__namespace.randomBytes(4).toString("hex");
600
+ const randomSuffix = crypto4__namespace.randomBytes(4).toString("hex");
569
601
  return {
570
602
  id: `k-${String(Date.now())}-${randomSuffix}`,
571
- key: new Uint8Array(crypto3__namespace.randomBytes(32)),
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: crypto3__namespace.randomUUID(),
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