@fgv/ts-extras 5.1.0-27 → 5.1.0-29
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.browser.js +2 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/converters.js +24 -4
- package/dist/packlets/crypto-utils/converters.js.map +1 -1
- package/dist/packlets/crypto-utils/hpkeProvider.js +333 -0
- package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -0
- package/dist/packlets/crypto-utils/index.browser.js +3 -0
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/index.js +2 -0
- package/dist/packlets/crypto-utils/index.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/converters.js +2 -2
- package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/keyStore.js +108 -3
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/dist/packlets/crypto-utils/model.js +21 -0
- package/dist/packlets/crypto-utils/model.js.map +1 -1
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js +74 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
- package/dist/packlets/mustache/index.js.map +1 -1
- package/dist/packlets/mustache/interfaces.js.map +1 -1
- package/dist/packlets/mustache/mustacheTemplate.js +42 -4
- package/dist/packlets/mustache/mustacheTemplate.js.map +1 -1
- package/dist/ts-extras.d.ts +472 -18
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts +2 -1
- package/lib/index.browser.d.ts.map +1 -1
- package/lib/index.browser.js +3 -1
- package/lib/index.browser.js.map +1 -1
- package/lib/packlets/crypto-utils/converters.d.ts +12 -1
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/converters.js +25 -5
- package/lib/packlets/crypto-utils/converters.js.map +1 -1
- package/lib/packlets/crypto-utils/hpkeProvider.d.ts +142 -0
- package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/hpkeProvider.js +337 -0
- package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.js +5 -1
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
- package/lib/packlets/crypto-utils/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.js +4 -1
- package/lib/packlets/crypto-utils/index.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/converters.js +1 -1
- package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +32 -2
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.js +116 -11
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.d.ts +21 -3
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/lib/packlets/crypto-utils/model.d.ts +165 -9
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/model.js +22 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -1
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +39 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js +74 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
- package/lib/packlets/mustache/index.d.ts +1 -1
- package/lib/packlets/mustache/index.d.ts.map +1 -1
- package/lib/packlets/mustache/index.js.map +1 -1
- package/lib/packlets/mustache/interfaces.d.ts +34 -0
- package/lib/packlets/mustache/interfaces.d.ts.map +1 -1
- package/lib/packlets/mustache/interfaces.js.map +1 -1
- package/lib/packlets/mustache/mustacheTemplate.d.ts +2 -0
- package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -1
- package/lib/packlets/mustache/mustacheTemplate.js +42 -4
- package/lib/packlets/mustache/mustacheTemplate.js.map +1 -1
- package/package.json +7 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JsonValue } from '@fgv/ts-json-base';
|
|
2
2
|
import { Result } from '@fgv/ts-utils';
|
|
3
|
-
import { ICryptoProvider, IEncryptedFile, IEncryptionConfig, IEncryptionProvider, IKeyDerivationParams, SecretProvider } from '../model';
|
|
4
|
-
import { IAddKeyPairOptions, IAddKeyPairResult, IAddSecretFromPasswordOptions, IAddSecretFromPasswordResult, IAddSecretOptions, IAddSecretResult, IImportKeyOptions, IImportSecretOptions, IKeyStoreCreateParams, IKeyStoreEntry, IKeyStoreFile, IKeyStoreOpenParams, IRemoveSecretResult, KeyStoreLockState, KeyStoreSecretType } from './model';
|
|
3
|
+
import { ICryptoProvider, IEncryptedFile, IEncryptionConfig, IEncryptionProvider, IArgon2idKeyDerivationParams, IArgon2idProvider, IKeyDerivationParams, SecretProvider } from '../model';
|
|
4
|
+
import { IAddKeyPairOptions, IAddKeyPairResult, IAddSecretFromPasswordArgon2idOptions, IAddSecretFromPasswordOptions, IAddSecretFromPasswordResult, IAddSecretOptions, IAddSecretResult, IImportKeyOptions, IImportSecretOptions, IKeyStoreCreateParams, IKeyStoreEntry, IKeyStoreFile, IKeyStoreOpenParams, IRemoveSecretResult, KeyStoreLockState, KeyStoreSecretType } from './model';
|
|
5
5
|
/**
|
|
6
6
|
* Password-protected key store for managing encryption secrets.
|
|
7
7
|
*
|
|
@@ -231,6 +231,36 @@ export declare class KeyStore implements IEncryptionProvider {
|
|
|
231
231
|
* @public
|
|
232
232
|
*/
|
|
233
233
|
verifySecretFromPassword(name: string, password: string, keyDerivation: IKeyDerivationParams): Promise<Result<boolean>>;
|
|
234
|
+
/**
|
|
235
|
+
* Adds a secret derived from a password using Argon2id (RFC 9106).
|
|
236
|
+
*
|
|
237
|
+
* The Argon2id provider must be supplied explicitly; the KeyStore does not
|
|
238
|
+
* hold one by default (consumers opt in by depending on the argon2 package).
|
|
239
|
+
*
|
|
240
|
+
* Returns the key derivation parameters so callers can store them alongside
|
|
241
|
+
* encrypted artifacts, enabling future re-derivation and verification.
|
|
242
|
+
*
|
|
243
|
+
* @param name - Unique name for the secret
|
|
244
|
+
* @param password - Password or passphrase
|
|
245
|
+
* @param argon2idProvider - Argon2id provider (Node or Browser implementation)
|
|
246
|
+
* @param options - Optional: Argon2id params (defaults to ARGON2ID_OWASP_MIN), description, replace flag
|
|
247
|
+
* @returns Success with entry and keyDerivation params, Failure if locked or invalid
|
|
248
|
+
* @public
|
|
249
|
+
*/
|
|
250
|
+
addSecretFromPasswordArgon2id(name: string, password: string, argon2idProvider: IArgon2idProvider, options?: IAddSecretFromPasswordArgon2idOptions): Promise<Result<IAddSecretFromPasswordResult>>;
|
|
251
|
+
/**
|
|
252
|
+
* Verifies a candidate password against an Argon2id-derived entry using the
|
|
253
|
+
* supplied key derivation parameters. Constant-time comparison.
|
|
254
|
+
*
|
|
255
|
+
* @param name - Name of the secret to verify against
|
|
256
|
+
* @param password - Candidate password to test
|
|
257
|
+
* @param argon2idProvider - Argon2id provider (must produce bit-identical output for identical inputs)
|
|
258
|
+
* @param keyDerivation - The Argon2id key derivation parameters returned by `addSecretFromPasswordArgon2id`
|
|
259
|
+
* @returns Success(true) if candidate matches stored key, Success(false) if not,
|
|
260
|
+
* Failure if locked, secret missing, wrong type, or derivation fails
|
|
261
|
+
* @public
|
|
262
|
+
*/
|
|
263
|
+
verifySecretFromPasswordArgon2id(name: string, password: string, argon2idProvider: IArgon2idProvider, keyDerivation: IArgon2idKeyDerivationParams): Promise<Result<boolean>>;
|
|
234
264
|
/**
|
|
235
265
|
* Removes a secret by name. Vault-first: the in-memory vault entry is dropped
|
|
236
266
|
* before any storage cleanup runs. For asymmetric-keypair entries, best-effort
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyStore.d.ts","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/keyStore.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAuB,MAAM,EAAW,MAAM,eAAe,CAAC;AAGrE,OAAO,EACL,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"keyStore.d.ts","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/keyStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,7 +56,8 @@ exports.KeyStore = void 0;
|
|
|
56
56
|
const ts_utils_1 = require("@fgv/ts-utils");
|
|
57
57
|
const Constants = __importStar(require("../constants"));
|
|
58
58
|
const encryptedFile_1 = require("../encryptedFile");
|
|
59
|
-
const model_1 = require("
|
|
59
|
+
const model_1 = require("../model");
|
|
60
|
+
const model_2 = require("./model");
|
|
60
61
|
const converters_1 = require("./converters");
|
|
61
62
|
/**
|
|
62
63
|
* Gets the current ISO timestamp.
|
|
@@ -120,7 +121,7 @@ class KeyStore {
|
|
|
120
121
|
*/
|
|
121
122
|
static create(params) {
|
|
122
123
|
var _a;
|
|
123
|
-
const iterations = (_a = params.iterations) !== null && _a !== void 0 ? _a :
|
|
124
|
+
const iterations = (_a = params.iterations) !== null && _a !== void 0 ? _a : model_2.DEFAULT_KEYSTORE_ITERATIONS;
|
|
124
125
|
if (iterations < 1) {
|
|
125
126
|
return (0, ts_utils_1.fail)('Iterations must be at least 1');
|
|
126
127
|
}
|
|
@@ -164,7 +165,7 @@ class KeyStore {
|
|
|
164
165
|
return (0, ts_utils_1.fail)('Password cannot be empty');
|
|
165
166
|
}
|
|
166
167
|
// Generate salt for this key store using crypto provider
|
|
167
|
-
const saltResult = this._cryptoProvider.generateRandomBytes(
|
|
168
|
+
const saltResult = this._cryptoProvider.generateRandomBytes(model_2.MIN_SALT_LENGTH);
|
|
168
169
|
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
169
170
|
if (saltResult.isFailure()) {
|
|
170
171
|
return (0, ts_utils_1.fail)(`Failed to generate salt: ${saltResult.message}`);
|
|
@@ -471,9 +472,9 @@ class KeyStore {
|
|
|
471
472
|
if (existing && !(options === null || options === void 0 ? void 0 : options.replace)) {
|
|
472
473
|
return (0, ts_utils_1.fail)(`Secret '${name}' already exists - use replace=true to overwrite`);
|
|
473
474
|
}
|
|
474
|
-
const iterations = (_a = options === null || options === void 0 ? void 0 : options.iterations) !== null && _a !== void 0 ? _a :
|
|
475
|
+
const iterations = (_a = options === null || options === void 0 ? void 0 : options.iterations) !== null && _a !== void 0 ? _a : model_2.DEFAULT_SECRET_ITERATIONS;
|
|
475
476
|
// Generate a random salt for this secret's key derivation
|
|
476
|
-
const saltResult = this._cryptoProvider.generateRandomBytes(
|
|
477
|
+
const saltResult = this._cryptoProvider.generateRandomBytes(model_2.MIN_SALT_LENGTH);
|
|
477
478
|
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
478
479
|
if (saltResult.isFailure()) {
|
|
479
480
|
return (0, ts_utils_1.fail)(`Failed to generate salt: ${saltResult.message}`);
|
|
@@ -567,6 +568,111 @@ class KeyStore {
|
|
|
567
568
|
}
|
|
568
569
|
return (0, ts_utils_1.succeed)(KeyStore._timingSafeEqual(derivedResult.value, entry.key));
|
|
569
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* Adds a secret derived from a password using Argon2id (RFC 9106).
|
|
573
|
+
*
|
|
574
|
+
* The Argon2id provider must be supplied explicitly; the KeyStore does not
|
|
575
|
+
* hold one by default (consumers opt in by depending on the argon2 package).
|
|
576
|
+
*
|
|
577
|
+
* Returns the key derivation parameters so callers can store them alongside
|
|
578
|
+
* encrypted artifacts, enabling future re-derivation and verification.
|
|
579
|
+
*
|
|
580
|
+
* @param name - Unique name for the secret
|
|
581
|
+
* @param password - Password or passphrase
|
|
582
|
+
* @param argon2idProvider - Argon2id provider (Node or Browser implementation)
|
|
583
|
+
* @param options - Optional: Argon2id params (defaults to ARGON2ID_OWASP_MIN), description, replace flag
|
|
584
|
+
* @returns Success with entry and keyDerivation params, Failure if locked or invalid
|
|
585
|
+
* @public
|
|
586
|
+
*/
|
|
587
|
+
async addSecretFromPasswordArgon2id(name, password, argon2idProvider, options) {
|
|
588
|
+
var _a;
|
|
589
|
+
if (!this._secrets) {
|
|
590
|
+
return (0, ts_utils_1.fail)('Key store is locked');
|
|
591
|
+
}
|
|
592
|
+
if (!name || name.length === 0) {
|
|
593
|
+
return (0, ts_utils_1.fail)('Secret name cannot be empty');
|
|
594
|
+
}
|
|
595
|
+
if (!password || password.length === 0) {
|
|
596
|
+
return (0, ts_utils_1.fail)('Password cannot be empty');
|
|
597
|
+
}
|
|
598
|
+
const existing = this._secrets.get(name);
|
|
599
|
+
if (existing && !(options === null || options === void 0 ? void 0 : options.replace)) {
|
|
600
|
+
return (0, ts_utils_1.fail)(`Secret '${name}' already exists - use replace=true to overwrite`);
|
|
601
|
+
}
|
|
602
|
+
const params = (_a = options === null || options === void 0 ? void 0 : options.params) !== null && _a !== void 0 ? _a : model_1.ARGON2ID_OWASP_MIN;
|
|
603
|
+
const saltResult = this._cryptoProvider.generateRandomBytes(model_2.MIN_SALT_LENGTH);
|
|
604
|
+
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
605
|
+
if (saltResult.isFailure()) {
|
|
606
|
+
return (0, ts_utils_1.fail)(`Failed to generate salt: ${saltResult.message}`);
|
|
607
|
+
}
|
|
608
|
+
const keyResult = await argon2idProvider.argon2id(password, saltResult.value, params);
|
|
609
|
+
if (keyResult.isFailure()) {
|
|
610
|
+
return (0, ts_utils_1.fail)(`Argon2id key derivation failed: ${keyResult.message}`);
|
|
611
|
+
}
|
|
612
|
+
if (keyResult.value.length !== Constants.AES_256_KEY_SIZE) {
|
|
613
|
+
return (0, ts_utils_1.fail)(`Argon2id outputBytes must be ${Constants.AES_256_KEY_SIZE} for KeyStore secrets, got ${keyResult.value.length}`);
|
|
614
|
+
}
|
|
615
|
+
const entry = {
|
|
616
|
+
name,
|
|
617
|
+
type: 'encryption-key',
|
|
618
|
+
key: keyResult.value,
|
|
619
|
+
description: options === null || options === void 0 ? void 0 : options.description,
|
|
620
|
+
createdAt: getCurrentTimestamp()
|
|
621
|
+
};
|
|
622
|
+
const warning = existing ? await this._releaseEntryResources(existing) : undefined;
|
|
623
|
+
this._secrets.set(name, entry);
|
|
624
|
+
this._dirty = true;
|
|
625
|
+
const keyDerivation = {
|
|
626
|
+
kdf: 'argon2id',
|
|
627
|
+
salt: this._cryptoProvider.toBase64(saltResult.value),
|
|
628
|
+
memoryKiB: params.memoryKiB,
|
|
629
|
+
iterations: params.iterations,
|
|
630
|
+
parallelism: params.parallelism
|
|
631
|
+
};
|
|
632
|
+
return (0, ts_utils_1.succeed)({ entry, replaced: existing !== undefined, warning, keyDerivation });
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Verifies a candidate password against an Argon2id-derived entry using the
|
|
636
|
+
* supplied key derivation parameters. Constant-time comparison.
|
|
637
|
+
*
|
|
638
|
+
* @param name - Name of the secret to verify against
|
|
639
|
+
* @param password - Candidate password to test
|
|
640
|
+
* @param argon2idProvider - Argon2id provider (must produce bit-identical output for identical inputs)
|
|
641
|
+
* @param keyDerivation - The Argon2id key derivation parameters returned by `addSecretFromPasswordArgon2id`
|
|
642
|
+
* @returns Success(true) if candidate matches stored key, Success(false) if not,
|
|
643
|
+
* Failure if locked, secret missing, wrong type, or derivation fails
|
|
644
|
+
* @public
|
|
645
|
+
*/
|
|
646
|
+
async verifySecretFromPasswordArgon2id(name, password, argon2idProvider, keyDerivation) {
|
|
647
|
+
if (!this._secrets) {
|
|
648
|
+
return (0, ts_utils_1.fail)('Key store is locked');
|
|
649
|
+
}
|
|
650
|
+
if (!password || password.length === 0) {
|
|
651
|
+
return (0, ts_utils_1.fail)('Password cannot be empty');
|
|
652
|
+
}
|
|
653
|
+
const entry = this._secrets.get(name);
|
|
654
|
+
if (!entry) {
|
|
655
|
+
return (0, ts_utils_1.fail)(`Secret '${name}' not found`);
|
|
656
|
+
}
|
|
657
|
+
if (entry.type !== 'encryption-key') {
|
|
658
|
+
return (0, ts_utils_1.fail)(`Secret '${name}' is not a password-verifiable encryption key (type: ${entry.type})`);
|
|
659
|
+
}
|
|
660
|
+
const saltResult = this._cryptoProvider.fromBase64(keyDerivation.salt);
|
|
661
|
+
if (saltResult.isFailure()) {
|
|
662
|
+
return (0, ts_utils_1.fail)(`Invalid salt: ${saltResult.message}`);
|
|
663
|
+
}
|
|
664
|
+
const params = {
|
|
665
|
+
memoryKiB: keyDerivation.memoryKiB,
|
|
666
|
+
iterations: keyDerivation.iterations,
|
|
667
|
+
parallelism: keyDerivation.parallelism,
|
|
668
|
+
outputBytes: entry.key.length
|
|
669
|
+
};
|
|
670
|
+
const derivedResult = await argon2idProvider.argon2id(password, saltResult.value, params);
|
|
671
|
+
if (derivedResult.isFailure()) {
|
|
672
|
+
return (0, ts_utils_1.fail)(`Argon2id key derivation failed: ${derivedResult.message}`);
|
|
673
|
+
}
|
|
674
|
+
return (0, ts_utils_1.succeed)(KeyStore._timingSafeEqual(derivedResult.value, entry.key));
|
|
675
|
+
}
|
|
570
676
|
/**
|
|
571
677
|
* Removes a secret by name. Vault-first: the in-memory vault entry is dropped
|
|
572
678
|
* before any storage cleanup runs. For asymmetric-keypair entries, best-effort
|
|
@@ -897,7 +1003,7 @@ class KeyStore {
|
|
|
897
1003
|
}
|
|
898
1004
|
}
|
|
899
1005
|
// Generate new salt for the new password using crypto provider
|
|
900
|
-
const saltResult = this._cryptoProvider.generateRandomBytes(
|
|
1006
|
+
const saltResult = this._cryptoProvider.generateRandomBytes(model_2.MIN_SALT_LENGTH);
|
|
901
1007
|
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
902
1008
|
if (saltResult.isFailure()) {
|
|
903
1009
|
return (0, ts_utils_1.fail)(`Failed to generate salt: ${saltResult.message}`);
|
|
@@ -1009,7 +1115,7 @@ class KeyStore {
|
|
|
1009
1115
|
}
|
|
1010
1116
|
}
|
|
1011
1117
|
const vaultContents = {
|
|
1012
|
-
version:
|
|
1118
|
+
version: model_2.KEYSTORE_FORMAT,
|
|
1013
1119
|
secrets: secretEntries
|
|
1014
1120
|
};
|
|
1015
1121
|
// Serialize and encrypt
|
|
@@ -1025,7 +1131,7 @@ class KeyStore {
|
|
|
1025
1131
|
}
|
|
1026
1132
|
const { iv, authTag, encryptedData } = encryptResult.value;
|
|
1027
1133
|
const keystoreFileData = {
|
|
1028
|
-
format:
|
|
1134
|
+
format: model_2.KEYSTORE_FORMAT,
|
|
1029
1135
|
algorithm: Constants.DEFAULT_ALGORITHM,
|
|
1030
1136
|
iv: this._cryptoProvider.toBase64(iv),
|
|
1031
1137
|
authTag: this._cryptoProvider.toBase64(authTag),
|
|
@@ -1047,7 +1153,7 @@ class KeyStore {
|
|
|
1047
1153
|
*/
|
|
1048
1154
|
async _decryptVault(derivedKey) {
|
|
1049
1155
|
const keystoreFile = this._keystoreFile;
|
|
1050
|
-
/* c8 ignore next 3 - defensive
|
|
1156
|
+
/* c8 ignore next 3 - defensive: _decryptVault is only called after a successful open() or create() */
|
|
1051
1157
|
if (keystoreFile === undefined) {
|
|
1052
1158
|
return (0, ts_utils_1.fail)('No key store file loaded');
|
|
1053
1159
|
}
|
|
@@ -1155,8 +1261,7 @@ class KeyStore {
|
|
|
1155
1261
|
* the position of the first differing byte.
|
|
1156
1262
|
*/
|
|
1157
1263
|
static _timingSafeEqual(a, b) {
|
|
1158
|
-
/* c8 ignore next
|
|
1159
|
-
PBKDF2-derived 32-byte keys against encryption-key entries (also 32 bytes) */
|
|
1264
|
+
/* c8 ignore next 3 - defensive: callers compare equal-length 32-byte PBKDF2 keys */
|
|
1160
1265
|
if (a.length !== b.length) {
|
|
1161
1266
|
return false;
|
|
1162
1267
|
}
|