@fgv/ts-extras 5.1.0-15 → 5.1.0-16
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.
|
@@ -584,49 +584,29 @@ export class KeyStore {
|
|
|
584
584
|
if (keyResult.isFailure()) {
|
|
585
585
|
return fail(`Key derivation failed: ${keyResult.message}`);
|
|
586
586
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (jsonResult.isFailure()) {
|
|
606
|
-
return fail(`Failed to serialize vault: ${jsonResult.message}`);
|
|
587
|
+
return this._encryptVault(keyResult.value);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Saves the key store using a pre-derived key, bypassing PBKDF2 key
|
|
591
|
+
* derivation. Use this when the derived key has been stored externally
|
|
592
|
+
* (e.g., in another key store) and the original password is no longer
|
|
593
|
+
* available.
|
|
594
|
+
*
|
|
595
|
+
* The supplied key must be the same key that was (or would be) derived
|
|
596
|
+
* from the master password using the key store's PBKDF2 parameters.
|
|
597
|
+
*
|
|
598
|
+
* @param derivedKey - The pre-derived master key (32 bytes for AES-256)
|
|
599
|
+
* @returns Success with IKeyStoreFile, Failure if locked or key invalid
|
|
600
|
+
* @public
|
|
601
|
+
*/
|
|
602
|
+
async saveWithKey(derivedKey) {
|
|
603
|
+
if (!this._secrets || !this._salt) {
|
|
604
|
+
return fail('Key store is locked');
|
|
607
605
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (encryptResult.isFailure()) {
|
|
611
|
-
return fail(`Encryption failed: ${encryptResult.message}`);
|
|
606
|
+
if (derivedKey.length !== Constants.AES_256_KEY_SIZE) {
|
|
607
|
+
return fail(`Key must be ${Constants.AES_256_KEY_SIZE} bytes, got ${derivedKey.length}`);
|
|
612
608
|
}
|
|
613
|
-
|
|
614
|
-
const keystoreFileData = {
|
|
615
|
-
format: KEYSTORE_FORMAT,
|
|
616
|
-
algorithm: Constants.DEFAULT_ALGORITHM,
|
|
617
|
-
iv: this._cryptoProvider.toBase64(iv),
|
|
618
|
-
authTag: this._cryptoProvider.toBase64(authTag),
|
|
619
|
-
encryptedData: this._cryptoProvider.toBase64(encryptedData),
|
|
620
|
-
keyDerivation: {
|
|
621
|
-
kdf: 'pbkdf2',
|
|
622
|
-
salt: this._cryptoProvider.toBase64(this._salt),
|
|
623
|
-
iterations: this._iterations
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
this._keystoreFile = keystoreFileData;
|
|
627
|
-
this._dirty = false;
|
|
628
|
-
this._isNew = false;
|
|
629
|
-
return succeed(keystoreFileData);
|
|
609
|
+
return this._encryptVault(derivedKey);
|
|
630
610
|
}
|
|
631
611
|
/**
|
|
632
612
|
* Changes the master password.
|
|
@@ -741,8 +721,60 @@ export class KeyStore {
|
|
|
741
721
|
});
|
|
742
722
|
}
|
|
743
723
|
// ============================================================================
|
|
744
|
-
// Private: Vault Decryption
|
|
724
|
+
// Private: Vault Encryption / Decryption
|
|
745
725
|
// ============================================================================
|
|
726
|
+
/**
|
|
727
|
+
* Encrypts the vault with a derived key and returns the key store file.
|
|
728
|
+
* Shared by `save()` and `saveWithKey()`.
|
|
729
|
+
*/
|
|
730
|
+
async _encryptVault(derivedKey) {
|
|
731
|
+
// _secrets and _salt are guaranteed non-undefined by callers
|
|
732
|
+
const secrets = this._secrets;
|
|
733
|
+
const salt = this._salt;
|
|
734
|
+
// Build vault contents
|
|
735
|
+
const secretEntries = {};
|
|
736
|
+
for (const [name, entry] of secrets) {
|
|
737
|
+
secretEntries[name] = {
|
|
738
|
+
name: entry.name,
|
|
739
|
+
type: entry.type,
|
|
740
|
+
key: this._cryptoProvider.toBase64(entry.key),
|
|
741
|
+
description: entry.description,
|
|
742
|
+
createdAt: entry.createdAt
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
const vaultContents = {
|
|
746
|
+
version: KEYSTORE_FORMAT,
|
|
747
|
+
secrets: secretEntries
|
|
748
|
+
};
|
|
749
|
+
// Serialize and encrypt
|
|
750
|
+
const jsonResult = captureResult(() => JSON.stringify(vaultContents));
|
|
751
|
+
/* c8 ignore next 3 - error path tested but coverage intermittently missed */
|
|
752
|
+
if (jsonResult.isFailure()) {
|
|
753
|
+
return fail(`Failed to serialize vault: ${jsonResult.message}`);
|
|
754
|
+
}
|
|
755
|
+
const encryptResult = await this._cryptoProvider.encrypt(jsonResult.value, derivedKey);
|
|
756
|
+
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
757
|
+
if (encryptResult.isFailure()) {
|
|
758
|
+
return fail(`Encryption failed: ${encryptResult.message}`);
|
|
759
|
+
}
|
|
760
|
+
const { iv, authTag, encryptedData } = encryptResult.value;
|
|
761
|
+
const keystoreFileData = {
|
|
762
|
+
format: KEYSTORE_FORMAT,
|
|
763
|
+
algorithm: Constants.DEFAULT_ALGORITHM,
|
|
764
|
+
iv: this._cryptoProvider.toBase64(iv),
|
|
765
|
+
authTag: this._cryptoProvider.toBase64(authTag),
|
|
766
|
+
encryptedData: this._cryptoProvider.toBase64(encryptedData),
|
|
767
|
+
keyDerivation: {
|
|
768
|
+
kdf: 'pbkdf2',
|
|
769
|
+
salt: this._cryptoProvider.toBase64(salt),
|
|
770
|
+
iterations: this._iterations
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
this._keystoreFile = keystoreFileData;
|
|
774
|
+
this._dirty = false;
|
|
775
|
+
this._isNew = false;
|
|
776
|
+
return succeed(keystoreFileData);
|
|
777
|
+
}
|
|
746
778
|
/**
|
|
747
779
|
* Decrypts the vault with a derived key and loads secrets into memory.
|
|
748
780
|
* Shared by `unlock()` and `unlockWithKey()`.
|
package/dist/ts-extras.d.ts
CHANGED
|
@@ -1792,6 +1792,20 @@ declare class KeyStore_2 implements IEncryptionProvider {
|
|
|
1792
1792
|
* @public
|
|
1793
1793
|
*/
|
|
1794
1794
|
save(password: string): Promise<Result<IKeyStoreFile>>;
|
|
1795
|
+
/**
|
|
1796
|
+
* Saves the key store using a pre-derived key, bypassing PBKDF2 key
|
|
1797
|
+
* derivation. Use this when the derived key has been stored externally
|
|
1798
|
+
* (e.g., in another key store) and the original password is no longer
|
|
1799
|
+
* available.
|
|
1800
|
+
*
|
|
1801
|
+
* The supplied key must be the same key that was (or would be) derived
|
|
1802
|
+
* from the master password using the key store's PBKDF2 parameters.
|
|
1803
|
+
*
|
|
1804
|
+
* @param derivedKey - The pre-derived master key (32 bytes for AES-256)
|
|
1805
|
+
* @returns Success with IKeyStoreFile, Failure if locked or key invalid
|
|
1806
|
+
* @public
|
|
1807
|
+
*/
|
|
1808
|
+
saveWithKey(derivedKey: Uint8Array): Promise<Result<IKeyStoreFile>>;
|
|
1795
1809
|
/**
|
|
1796
1810
|
* Changes the master password.
|
|
1797
1811
|
* Re-encrypts the vault with the new password-derived key.
|
|
@@ -1816,6 +1830,11 @@ declare class KeyStore_2 implements IEncryptionProvider {
|
|
|
1816
1830
|
* @public
|
|
1817
1831
|
*/
|
|
1818
1832
|
getEncryptionConfig(): Result<Pick<IEncryptionConfig, 'secretProvider' | 'cryptoProvider'>>;
|
|
1833
|
+
/**
|
|
1834
|
+
* Encrypts the vault with a derived key and returns the key store file.
|
|
1835
|
+
* Shared by `save()` and `saveWithKey()`.
|
|
1836
|
+
*/
|
|
1837
|
+
private _encryptVault;
|
|
1819
1838
|
/**
|
|
1820
1839
|
* Decrypts the vault with a derived key and loads secrets into memory.
|
|
1821
1840
|
* Shared by `unlock()` and `unlockWithKey()`.
|
|
@@ -232,6 +232,20 @@ export declare class KeyStore implements IEncryptionProvider {
|
|
|
232
232
|
* @public
|
|
233
233
|
*/
|
|
234
234
|
save(password: string): Promise<Result<IKeyStoreFile>>;
|
|
235
|
+
/**
|
|
236
|
+
* Saves the key store using a pre-derived key, bypassing PBKDF2 key
|
|
237
|
+
* derivation. Use this when the derived key has been stored externally
|
|
238
|
+
* (e.g., in another key store) and the original password is no longer
|
|
239
|
+
* available.
|
|
240
|
+
*
|
|
241
|
+
* The supplied key must be the same key that was (or would be) derived
|
|
242
|
+
* from the master password using the key store's PBKDF2 parameters.
|
|
243
|
+
*
|
|
244
|
+
* @param derivedKey - The pre-derived master key (32 bytes for AES-256)
|
|
245
|
+
* @returns Success with IKeyStoreFile, Failure if locked or key invalid
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
saveWithKey(derivedKey: Uint8Array): Promise<Result<IKeyStoreFile>>;
|
|
235
249
|
/**
|
|
236
250
|
* Changes the master password.
|
|
237
251
|
* Re-encrypts the vault with the new password-derived key.
|
|
@@ -256,6 +270,11 @@ export declare class KeyStore implements IEncryptionProvider {
|
|
|
256
270
|
* @public
|
|
257
271
|
*/
|
|
258
272
|
getEncryptionConfig(): Result<Pick<IEncryptionConfig, 'secretProvider' | 'cryptoProvider'>>;
|
|
273
|
+
/**
|
|
274
|
+
* Encrypts the vault with a derived key and returns the key store file.
|
|
275
|
+
* Shared by `save()` and `saveWithKey()`.
|
|
276
|
+
*/
|
|
277
|
+
private _encryptVault;
|
|
259
278
|
/**
|
|
260
279
|
* Decrypts the vault with a derived key and loads secrets into memory.
|
|
261
280
|
* Shared by `unlock()` and `unlockWithKey()`.
|
|
@@ -620,49 +620,29 @@ class KeyStore {
|
|
|
620
620
|
if (keyResult.isFailure()) {
|
|
621
621
|
return (0, ts_utils_1.fail)(`Key derivation failed: ${keyResult.message}`);
|
|
622
622
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
if (jsonResult.isFailure()) {
|
|
642
|
-
return (0, ts_utils_1.fail)(`Failed to serialize vault: ${jsonResult.message}`);
|
|
623
|
+
return this._encryptVault(keyResult.value);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Saves the key store using a pre-derived key, bypassing PBKDF2 key
|
|
627
|
+
* derivation. Use this when the derived key has been stored externally
|
|
628
|
+
* (e.g., in another key store) and the original password is no longer
|
|
629
|
+
* available.
|
|
630
|
+
*
|
|
631
|
+
* The supplied key must be the same key that was (or would be) derived
|
|
632
|
+
* from the master password using the key store's PBKDF2 parameters.
|
|
633
|
+
*
|
|
634
|
+
* @param derivedKey - The pre-derived master key (32 bytes for AES-256)
|
|
635
|
+
* @returns Success with IKeyStoreFile, Failure if locked or key invalid
|
|
636
|
+
* @public
|
|
637
|
+
*/
|
|
638
|
+
async saveWithKey(derivedKey) {
|
|
639
|
+
if (!this._secrets || !this._salt) {
|
|
640
|
+
return (0, ts_utils_1.fail)('Key store is locked');
|
|
643
641
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (encryptResult.isFailure()) {
|
|
647
|
-
return (0, ts_utils_1.fail)(`Encryption failed: ${encryptResult.message}`);
|
|
642
|
+
if (derivedKey.length !== Constants.AES_256_KEY_SIZE) {
|
|
643
|
+
return (0, ts_utils_1.fail)(`Key must be ${Constants.AES_256_KEY_SIZE} bytes, got ${derivedKey.length}`);
|
|
648
644
|
}
|
|
649
|
-
|
|
650
|
-
const keystoreFileData = {
|
|
651
|
-
format: model_1.KEYSTORE_FORMAT,
|
|
652
|
-
algorithm: Constants.DEFAULT_ALGORITHM,
|
|
653
|
-
iv: this._cryptoProvider.toBase64(iv),
|
|
654
|
-
authTag: this._cryptoProvider.toBase64(authTag),
|
|
655
|
-
encryptedData: this._cryptoProvider.toBase64(encryptedData),
|
|
656
|
-
keyDerivation: {
|
|
657
|
-
kdf: 'pbkdf2',
|
|
658
|
-
salt: this._cryptoProvider.toBase64(this._salt),
|
|
659
|
-
iterations: this._iterations
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
this._keystoreFile = keystoreFileData;
|
|
663
|
-
this._dirty = false;
|
|
664
|
-
this._isNew = false;
|
|
665
|
-
return (0, ts_utils_1.succeed)(keystoreFileData);
|
|
645
|
+
return this._encryptVault(derivedKey);
|
|
666
646
|
}
|
|
667
647
|
/**
|
|
668
648
|
* Changes the master password.
|
|
@@ -777,8 +757,60 @@ class KeyStore {
|
|
|
777
757
|
});
|
|
778
758
|
}
|
|
779
759
|
// ============================================================================
|
|
780
|
-
// Private: Vault Decryption
|
|
760
|
+
// Private: Vault Encryption / Decryption
|
|
781
761
|
// ============================================================================
|
|
762
|
+
/**
|
|
763
|
+
* Encrypts the vault with a derived key and returns the key store file.
|
|
764
|
+
* Shared by `save()` and `saveWithKey()`.
|
|
765
|
+
*/
|
|
766
|
+
async _encryptVault(derivedKey) {
|
|
767
|
+
// _secrets and _salt are guaranteed non-undefined by callers
|
|
768
|
+
const secrets = this._secrets;
|
|
769
|
+
const salt = this._salt;
|
|
770
|
+
// Build vault contents
|
|
771
|
+
const secretEntries = {};
|
|
772
|
+
for (const [name, entry] of secrets) {
|
|
773
|
+
secretEntries[name] = {
|
|
774
|
+
name: entry.name,
|
|
775
|
+
type: entry.type,
|
|
776
|
+
key: this._cryptoProvider.toBase64(entry.key),
|
|
777
|
+
description: entry.description,
|
|
778
|
+
createdAt: entry.createdAt
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
const vaultContents = {
|
|
782
|
+
version: model_1.KEYSTORE_FORMAT,
|
|
783
|
+
secrets: secretEntries
|
|
784
|
+
};
|
|
785
|
+
// Serialize and encrypt
|
|
786
|
+
const jsonResult = (0, ts_utils_1.captureResult)(() => JSON.stringify(vaultContents));
|
|
787
|
+
/* c8 ignore next 3 - error path tested but coverage intermittently missed */
|
|
788
|
+
if (jsonResult.isFailure()) {
|
|
789
|
+
return (0, ts_utils_1.fail)(`Failed to serialize vault: ${jsonResult.message}`);
|
|
790
|
+
}
|
|
791
|
+
const encryptResult = await this._cryptoProvider.encrypt(jsonResult.value, derivedKey);
|
|
792
|
+
/* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */
|
|
793
|
+
if (encryptResult.isFailure()) {
|
|
794
|
+
return (0, ts_utils_1.fail)(`Encryption failed: ${encryptResult.message}`);
|
|
795
|
+
}
|
|
796
|
+
const { iv, authTag, encryptedData } = encryptResult.value;
|
|
797
|
+
const keystoreFileData = {
|
|
798
|
+
format: model_1.KEYSTORE_FORMAT,
|
|
799
|
+
algorithm: Constants.DEFAULT_ALGORITHM,
|
|
800
|
+
iv: this._cryptoProvider.toBase64(iv),
|
|
801
|
+
authTag: this._cryptoProvider.toBase64(authTag),
|
|
802
|
+
encryptedData: this._cryptoProvider.toBase64(encryptedData),
|
|
803
|
+
keyDerivation: {
|
|
804
|
+
kdf: 'pbkdf2',
|
|
805
|
+
salt: this._cryptoProvider.toBase64(salt),
|
|
806
|
+
iterations: this._iterations
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
this._keystoreFile = keystoreFileData;
|
|
810
|
+
this._dirty = false;
|
|
811
|
+
this._isNew = false;
|
|
812
|
+
return (0, ts_utils_1.succeed)(keystoreFileData);
|
|
813
|
+
}
|
|
782
814
|
/**
|
|
783
815
|
* Decrypts the vault with a derived key and loads secrets into memory.
|
|
784
816
|
* Shared by `unlock()` and `unlockWithKey()`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgv/ts-extras",
|
|
3
|
-
"version": "5.1.0-
|
|
3
|
+
"version": "5.1.0-16",
|
|
4
4
|
"description": "Assorted Typescript Utilities",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "dist/ts-extras.d.ts",
|
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
"@types/js-yaml": "~4.0.9",
|
|
87
87
|
"typedoc": "~0.28.16",
|
|
88
88
|
"typedoc-plugin-markdown": "~4.9.0",
|
|
89
|
-
"@fgv/
|
|
90
|
-
"@fgv/
|
|
91
|
-
"@fgv/ts-utils-jest": "5.1.0-
|
|
92
|
-
"@fgv/ts-utils": "5.1.0-
|
|
89
|
+
"@fgv/typedoc-compact-theme": "5.1.0-16",
|
|
90
|
+
"@fgv/heft-dual-rig": "5.1.0-16",
|
|
91
|
+
"@fgv/ts-utils-jest": "5.1.0-16",
|
|
92
|
+
"@fgv/ts-utils": "5.1.0-16"
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
95
|
"luxon": "^3.7.2",
|
|
@@ -97,10 +97,10 @@
|
|
|
97
97
|
"papaparse": "^5.4.1",
|
|
98
98
|
"fflate": "~0.8.2",
|
|
99
99
|
"js-yaml": "~4.1.1",
|
|
100
|
-
"@fgv/ts-json-base": "5.1.0-
|
|
100
|
+
"@fgv/ts-json-base": "5.1.0-16"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
|
-
"@fgv/ts-utils": "5.1.0-
|
|
103
|
+
"@fgv/ts-utils": "5.1.0-16"
|
|
104
104
|
},
|
|
105
105
|
"repository": {
|
|
106
106
|
"type": "git",
|