@fgv/ts-extras 5.1.0-31 → 5.1.0-33
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/packlets/crypto-utils/index.browser.js +3 -2
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +287 -0
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js +36 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js +2 -0
- package/dist/packlets/crypto-utils/keystore/index.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -1
- package/dist/ts-extras.d.ts +153 -3
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.js +3 -2
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts +148 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +324 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts +10 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js +76 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/index.js +4 -1
- package/lib/packlets/crypto-utils/keystore/index.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +6 -3
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -1
- package/package.json +15 -10
package/dist/ts-extras.d.ts
CHANGED
|
@@ -751,6 +751,115 @@ declare type EncryptedFileFormat = typeof Constants.ENCRYPTED_FILE_FORMAT;
|
|
|
751
751
|
*/
|
|
752
752
|
declare const encryptedFileFormat: Converter<EncryptedFileFormat>;
|
|
753
753
|
|
|
754
|
+
/**
|
|
755
|
+
* {@link CryptoUtils.KeyStore.IPrivateKeyStorage | IPrivateKeyStorage}
|
|
756
|
+
* implementation that persists each private key as its own AES-256-GCM-encrypted
|
|
757
|
+
* file in a directory. The file content is the key's JWK, encrypted with a
|
|
758
|
+
* consumer-supplied 32-byte key via the supplied
|
|
759
|
+
* {@link CryptoUtils.ICryptoProvider | crypto provider}.
|
|
760
|
+
*
|
|
761
|
+
* `supportsNonExtractable` is `false`: persisting to disk requires exporting the
|
|
762
|
+
* private key to JWK, which only works for `extractable: true` keys. The
|
|
763
|
+
* keystore generates extractable keys when a backend reports `false` here.
|
|
764
|
+
*
|
|
765
|
+
* I/O goes through the {@link FileTree.FileTree | FileTree} abstraction (default
|
|
766
|
+
* `FsTree`), so the same implementation works against an in-memory tree (tests)
|
|
767
|
+
* or any other Node-compatible backend.
|
|
768
|
+
*
|
|
769
|
+
* This backend is **Node-only**: it round-trips private keys through
|
|
770
|
+
* `node:crypto` (`crypto.webcrypto.subtle`), so it is intentionally excluded
|
|
771
|
+
* from the browser entry point. Browser consumers should use
|
|
772
|
+
* `IdbPrivateKeyStorage` from `@fgv/ts-web-extras` instead.
|
|
773
|
+
*
|
|
774
|
+
* Single-process assumption: there is no inter-process locking. Concurrent
|
|
775
|
+
* writers to the same directory may race.
|
|
776
|
+
*
|
|
777
|
+
* @public
|
|
778
|
+
*/
|
|
779
|
+
declare class EncryptedFilePrivateKeyStorage implements IPrivateKeyStorage {
|
|
780
|
+
/**
|
|
781
|
+
* `false` — disk persistence round-trips via JWK, which requires extractable
|
|
782
|
+
* keys.
|
|
783
|
+
*/
|
|
784
|
+
readonly supportsNonExtractable: false;
|
|
785
|
+
private readonly _directory;
|
|
786
|
+
private readonly _encryptionKey;
|
|
787
|
+
private readonly _cryptoProvider;
|
|
788
|
+
private constructor();
|
|
789
|
+
/**
|
|
790
|
+
* Creates a new {@link CryptoUtils.KeyStore.EncryptedFilePrivateKeyStorage}.
|
|
791
|
+
* @param params - {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams}.
|
|
792
|
+
* @returns `Success` with the new instance, or `Failure` if the encryption
|
|
793
|
+
* key is the wrong size or the storage directory cannot be opened.
|
|
794
|
+
*/
|
|
795
|
+
static create(params: IEncryptedFilePrivateKeyStorageCreateParams): Result<EncryptedFilePrivateKeyStorage>;
|
|
796
|
+
/**
|
|
797
|
+
* Stores `key` under `id` as an encrypted JWK file.
|
|
798
|
+
* @param id - Storage handle. Must be a safe filename token
|
|
799
|
+
* (`[A-Za-z0-9._-]+`, not `.`/`..`).
|
|
800
|
+
* @param key - The extractable private `CryptoKey` to persist.
|
|
801
|
+
*/
|
|
802
|
+
store(id: string, key: CryptoKey): Promise<Result<string>>;
|
|
803
|
+
/**
|
|
804
|
+
* Loads the private key stored under `id`, decrypting and re-importing it from
|
|
805
|
+
* JWK.
|
|
806
|
+
* @param id - Storage handle.
|
|
807
|
+
*/
|
|
808
|
+
load(id: string): Promise<Result<CryptoKey>>;
|
|
809
|
+
/**
|
|
810
|
+
* Deletes the entry stored under `id`. Missing ids fail (the read path is
|
|
811
|
+
* keystore-driven and never asks to delete an id it did not store).
|
|
812
|
+
* @param id - Storage handle.
|
|
813
|
+
*/
|
|
814
|
+
delete(id: string): Promise<Result<string>>;
|
|
815
|
+
/**
|
|
816
|
+
* Lists every stored id.
|
|
817
|
+
*/
|
|
818
|
+
list(): Promise<Result<readonly string[]>>;
|
|
819
|
+
private _fileNameFor;
|
|
820
|
+
/**
|
|
821
|
+
* Validates the synchronous preconditions for a store: the id is filename-safe,
|
|
822
|
+
* the key is actually a private key, and its algorithm is one we support.
|
|
823
|
+
* Returns the resolved filename and algorithm so the async pipeline can run
|
|
824
|
+
* without re-deriving them.
|
|
825
|
+
*/
|
|
826
|
+
private _validateKeyToStore;
|
|
827
|
+
/**
|
|
828
|
+
* Exports `key` to JWK, wraps it in the stored envelope, encrypts it with
|
|
829
|
+
* AES-256-GCM, and writes the resulting file as serialized JSON to `fileName`.
|
|
830
|
+
* Returns the stored `id` on success.
|
|
831
|
+
*/
|
|
832
|
+
private _encryptAndWrite;
|
|
833
|
+
private _algorithmOf;
|
|
834
|
+
private _findFile;
|
|
835
|
+
private _writeFile;
|
|
836
|
+
/**
|
|
837
|
+
* Reads `file`, decrypts the AES-256-GCM envelope, and validates it into the
|
|
838
|
+
* typed `IStoredPrivateKeyEnvelope`. Read, decrypt, and shape failures
|
|
839
|
+
* all surface as a decrypt failure for `id`.
|
|
840
|
+
*/
|
|
841
|
+
private _decryptEnvelope;
|
|
842
|
+
/**
|
|
843
|
+
* Parses and shape-validates the stored JWK, then re-imports it as a private
|
|
844
|
+
* `CryptoKey` for the envelope's algorithm. The WebCrypto JWK-import algorithm
|
|
845
|
+
* descriptor is shared between public and private keys for every supported
|
|
846
|
+
* algorithm, so `IKeyPairAlgorithmParams.importPublicKey` is reused here;
|
|
847
|
+
* the public/private distinction is carried by the requested `usages`.
|
|
848
|
+
*/
|
|
849
|
+
private _importPrivateKey;
|
|
850
|
+
/**
|
|
851
|
+
* Computes the key usages to request when re-importing a stored private key.
|
|
852
|
+
* WebCrypto rejects `importKey` if the requested usages include operations
|
|
853
|
+
* absent from the JWK's `key_ops`, so a key originally created with a narrower
|
|
854
|
+
* usage set than the algorithm default (e.g. an ECDH key with only
|
|
855
|
+
* `deriveBits`) would fail to load against the algorithm-wide defaults.
|
|
856
|
+
* Intersect the algorithm's private usages with the JWK's recorded `key_ops`
|
|
857
|
+
* so we request exactly the operations the stored key actually supports;
|
|
858
|
+
* fall back to the algorithm's private usages when `key_ops` is absent.
|
|
859
|
+
*/
|
|
860
|
+
private _importUsagesFor;
|
|
861
|
+
}
|
|
862
|
+
|
|
754
863
|
/**
|
|
755
864
|
* Supported encryption algorithms.
|
|
756
865
|
* @public
|
|
@@ -2260,6 +2369,42 @@ declare interface IEncryptedFile<TMetadata = JsonValue> {
|
|
|
2260
2369
|
readonly keyDerivation?: IKeyDerivationParams;
|
|
2261
2370
|
}
|
|
2262
2371
|
|
|
2372
|
+
/**
|
|
2373
|
+
* Parameters for {@link CryptoUtils.KeyStore.EncryptedFilePrivateKeyStorage.create}.
|
|
2374
|
+
* @public
|
|
2375
|
+
*/
|
|
2376
|
+
declare interface IEncryptedFilePrivateKeyStorageCreateParams {
|
|
2377
|
+
/**
|
|
2378
|
+
* Filesystem path to the directory that holds the encrypted private-key
|
|
2379
|
+
* files. Used only when {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams.tree}
|
|
2380
|
+
* is omitted (the default `FsTree` backing). The directory must already
|
|
2381
|
+
* exist.
|
|
2382
|
+
*/
|
|
2383
|
+
readonly directory: string;
|
|
2384
|
+
/**
|
|
2385
|
+
* Raw AES-256-GCM key (32 bytes) used to encrypt each file's JWK content.
|
|
2386
|
+
* Consumer-supplied and decoupled from the keystore's password lifecycle —
|
|
2387
|
+
* derive it however the application sees fit (typically the same
|
|
2388
|
+
* password-derived key material the keystore vault uses).
|
|
2389
|
+
*/
|
|
2390
|
+
readonly encryptionKey: Uint8Array;
|
|
2391
|
+
/**
|
|
2392
|
+
* {@link CryptoUtils.ICryptoProvider | Crypto provider} used for the
|
|
2393
|
+
* AES-256-GCM encrypt/decrypt of each file's contents.
|
|
2394
|
+
*/
|
|
2395
|
+
readonly cryptoProvider: ICryptoProvider;
|
|
2396
|
+
/**
|
|
2397
|
+
* Optional {@link FileTree.IFileTreeDirectoryItem | FileTree directory}
|
|
2398
|
+
* override. When supplied it is used as the storage directory directly and
|
|
2399
|
+
* {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams.directory} is ignored —
|
|
2400
|
+
* pass an in-memory tree for tests, or another Node-compatible backend. When
|
|
2401
|
+
* omitted, a mutable `FsTree` rooted at `directory` is used. (This backend is
|
|
2402
|
+
* Node-only — it round-trips keys through `node:crypto` — so a browser file
|
|
2403
|
+
* tree is not a supported target.)
|
|
2404
|
+
*/
|
|
2405
|
+
readonly tree?: FileTree.IFileTreeDirectoryItem;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2263
2408
|
/**
|
|
2264
2409
|
* Configuration for encrypted file handling during loading.
|
|
2265
2410
|
* @public
|
|
@@ -3101,9 +3246,12 @@ declare interface IPbkdf2KeyDerivationParams {
|
|
|
3101
3246
|
|
|
3102
3247
|
/**
|
|
3103
3248
|
* Pluggable backend that persists raw asymmetric private keys outside of the
|
|
3104
|
-
* encrypted keystore vault. Concrete implementations
|
|
3105
|
-
*
|
|
3106
|
-
*
|
|
3249
|
+
* encrypted keystore vault. Concrete implementations:
|
|
3250
|
+
* - {@link CryptoUtils.KeyStore.EncryptedFilePrivateKeyStorage} in
|
|
3251
|
+
* `@fgv/ts-extras` — directory-on-disk, AES-256-GCM-encrypted JWK per key
|
|
3252
|
+
* (Node; `supportsNonExtractable: false`).
|
|
3253
|
+
* - `IdbPrivateKeyStorage` in `@fgv/ts-web-extras` — IndexedDB-backed, stores
|
|
3254
|
+
* `CryptoKey` objects directly (browser; `supportsNonExtractable: true`).
|
|
3107
3255
|
*
|
|
3108
3256
|
* The keystore writes storage-first: a private key is always stored here
|
|
3109
3257
|
* before the corresponding public-key vault entry is committed. Conversely,
|
|
@@ -3666,6 +3814,8 @@ declare namespace KeyStore {
|
|
|
3666
3814
|
export {
|
|
3667
3815
|
Converters_2 as Converters,
|
|
3668
3816
|
KeyStore_2 as KeyStore,
|
|
3817
|
+
EncryptedFilePrivateKeyStorage,
|
|
3818
|
+
IEncryptedFilePrivateKeyStorageCreateParams,
|
|
3669
3819
|
isKeyStoreFile,
|
|
3670
3820
|
allKeyPairAlgorithms,
|
|
3671
3821
|
KeyPairAlgorithm,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export * from './model';
|
|
7
7
|
export { AES_256_KEY_SIZE, DEFAULT_ALGORITHM, ENCRYPTED_FILE_FORMAT, GCM_AUTH_TAG_SIZE, GCM_IV_SIZE } from './constants';
|
|
8
|
-
import * as KeyStore from './keystore';
|
|
8
|
+
import * as KeyStore from './keystore/index.browser';
|
|
9
9
|
export { KeyStore };
|
|
10
10
|
import * as Converters from './converters';
|
|
11
11
|
export { Converters };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.browser.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.browser.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,QAAQ,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,CAAC;AAGpB,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AAGtB,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAGvG,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAM3F,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,0BAA0B,EAC1B,QAAQ,EACR,cAAc,EACf,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,8BAA8B,EAC9B,gCAAgC,EAChC,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -70,8 +70,9 @@ Object.defineProperty(exports, "DEFAULT_ALGORITHM", { enumerable: true, get: fun
|
|
|
70
70
|
Object.defineProperty(exports, "ENCRYPTED_FILE_FORMAT", { enumerable: true, get: function () { return constants_1.ENCRYPTED_FILE_FORMAT; } });
|
|
71
71
|
Object.defineProperty(exports, "GCM_AUTH_TAG_SIZE", { enumerable: true, get: function () { return constants_1.GCM_AUTH_TAG_SIZE; } });
|
|
72
72
|
Object.defineProperty(exports, "GCM_IV_SIZE", { enumerable: true, get: function () { return constants_1.GCM_IV_SIZE; } });
|
|
73
|
-
// KeyStore namespace
|
|
74
|
-
|
|
73
|
+
// KeyStore namespace (browser-safe barrel — omits the Node-only
|
|
74
|
+
// EncryptedFilePrivateKeyStorage so the browser entry stays free of node:crypto)
|
|
75
|
+
const KeyStore = __importStar(require("./keystore/index.browser"));
|
|
75
76
|
exports.KeyStore = KeyStore;
|
|
76
77
|
// Converters namespace
|
|
77
78
|
const Converters = __importStar(require("./converters"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.browser.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEZ;;;;GAIG;AAEH,iCAAiC;AACjC,0CAAwB;AAExB,YAAY;AACZ,yCAMqB;AALnB,6GAAA,gBAAgB,OAAA;AAChB,8GAAA,iBAAiB,OAAA;AACjB,kHAAA,qBAAqB,OAAA;AACrB,8GAAA,iBAAiB,OAAA;AACjB,wGAAA,WAAW,OAAA;AAGb,
|
|
1
|
+
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.browser.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEZ;;;;GAIG;AAEH,iCAAiC;AACjC,0CAAwB;AAExB,YAAY;AACZ,yCAMqB;AALnB,6GAAA,gBAAgB,OAAA;AAChB,8GAAA,iBAAiB,OAAA;AACjB,kHAAA,qBAAqB,OAAA;AACrB,8GAAA,iBAAiB,OAAA;AACjB,wGAAA,WAAW,OAAA;AAGb,gEAAgE;AAChE,iFAAiF;AACjF,mEAAqD;AAC5C,4BAAQ;AAEjB,uBAAuB;AACvB,yDAA2C;AAClC,gCAAU;AAEnB,6BAA6B;AAC7B,uEAAuG;AAA9F,oIAAA,wBAAwB,OAAA;AAEjC,8DAA8D;AAC9D,mEAA2F;AAAzD,gIAAA,sBAAsB,OAAA;AAExD,8DAA8D;AAC9D,4DAA4D;AAE5D,yBAAyB;AACzB,iDAOyB;AANvB,oHAAA,mBAAmB,OAAA;AACnB,4GAAA,WAAW,OAAA;AACX,2GAAA,UAAU,OAAA;AAEV,yGAAA,QAAQ,OAAA;AACR,+GAAA,cAAc,OAAA;AAGhB,yBAAyB;AACzB,6CAKuB;AAJrB,6HAAA,8BAA8B,OAAA;AAC9B,+HAAA,gCAAgC,OAAA;AAChC,uHAAA,wBAAwB,OAAA;AACxB,uHAAA,wBAAwB,OAAA;AAG1B,qFAAqF;AACrF,uFAAuF;AACvF,+CAA+D;AAAtD,4GAAA,YAAY,OAAA","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Crypto utilities for encrypted file handling and key management (browser version).\n * Note: For browser crypto provider, use \\@fgv/ts-web-extras.\n * @packageDocumentation\n */\n\n// Re-export all types from model\nexport * from './model';\n\n// Constants\nexport {\n AES_256_KEY_SIZE,\n DEFAULT_ALGORITHM,\n ENCRYPTED_FILE_FORMAT,\n GCM_AUTH_TAG_SIZE,\n GCM_IV_SIZE\n} from './constants';\n\n// KeyStore namespace (browser-safe barrel — omits the Node-only\n// EncryptedFilePrivateKeyStorage so the browser entry stays free of node:crypto)\nimport * as KeyStore from './keystore/index.browser';\nexport { KeyStore };\n\n// Converters namespace\nimport * as Converters from './converters';\nexport { Converters };\n\n// Direct encryption provider\nexport { DirectEncryptionProvider, IDirectEncryptionProviderParams } from './directEncryptionProvider';\n\n// WebCrypto parameter table for asymmetric keypair algorithms\nexport { IKeyPairAlgorithmParams, keyPairAlgorithmParams } from './keyPairAlgorithmParams';\n\n// Note: NodeCryptoProvider is NOT exported in browser version\n// Use BrowserCryptoProvider from @fgv/ts-web-extras instead\n\n// Encrypted file helpers\nexport {\n createEncryptedFile,\n decryptFile,\n fromBase64,\n ICreateEncryptedFileParams,\n toBase64,\n tryDecryptFile\n} from './encryptedFile';\n\n// Multibase/SPKI helpers\nexport {\n exportPublicKeyAsMultibaseSpki,\n importPublicKeyFromMultibaseSpki,\n multibaseBase64UrlDecode,\n multibaseBase64UrlEncode\n} from './spkiHelpers';\n\n// HPKE base mode (RFC 9180) — DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + AES-256-GCM\n// hpkeProvider.ts has no Node-specific imports and is safe in the browser entry point.\nexport { HpkeProvider, IHpkeSealResult } from './hpkeProvider';\n"]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { FileTree } from '@fgv/ts-json-base';
|
|
3
|
+
import { ICryptoProvider } from '../model';
|
|
4
|
+
import { IPrivateKeyStorage } from './privateKeyStorage';
|
|
5
|
+
/**
|
|
6
|
+
* Parameters for {@link CryptoUtils.KeyStore.EncryptedFilePrivateKeyStorage.create}.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export interface IEncryptedFilePrivateKeyStorageCreateParams {
|
|
10
|
+
/**
|
|
11
|
+
* Filesystem path to the directory that holds the encrypted private-key
|
|
12
|
+
* files. Used only when {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams.tree}
|
|
13
|
+
* is omitted (the default `FsTree` backing). The directory must already
|
|
14
|
+
* exist.
|
|
15
|
+
*/
|
|
16
|
+
readonly directory: string;
|
|
17
|
+
/**
|
|
18
|
+
* Raw AES-256-GCM key (32 bytes) used to encrypt each file's JWK content.
|
|
19
|
+
* Consumer-supplied and decoupled from the keystore's password lifecycle —
|
|
20
|
+
* derive it however the application sees fit (typically the same
|
|
21
|
+
* password-derived key material the keystore vault uses).
|
|
22
|
+
*/
|
|
23
|
+
readonly encryptionKey: Uint8Array;
|
|
24
|
+
/**
|
|
25
|
+
* {@link CryptoUtils.ICryptoProvider | Crypto provider} used for the
|
|
26
|
+
* AES-256-GCM encrypt/decrypt of each file's contents.
|
|
27
|
+
*/
|
|
28
|
+
readonly cryptoProvider: ICryptoProvider;
|
|
29
|
+
/**
|
|
30
|
+
* Optional {@link FileTree.IFileTreeDirectoryItem | FileTree directory}
|
|
31
|
+
* override. When supplied it is used as the storage directory directly and
|
|
32
|
+
* {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams.directory} is ignored —
|
|
33
|
+
* pass an in-memory tree for tests, or another Node-compatible backend. When
|
|
34
|
+
* omitted, a mutable `FsTree` rooted at `directory` is used. (This backend is
|
|
35
|
+
* Node-only — it round-trips keys through `node:crypto` — so a browser file
|
|
36
|
+
* tree is not a supported target.)
|
|
37
|
+
*/
|
|
38
|
+
readonly tree?: FileTree.IFileTreeDirectoryItem;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* {@link CryptoUtils.KeyStore.IPrivateKeyStorage | IPrivateKeyStorage}
|
|
42
|
+
* implementation that persists each private key as its own AES-256-GCM-encrypted
|
|
43
|
+
* file in a directory. The file content is the key's JWK, encrypted with a
|
|
44
|
+
* consumer-supplied 32-byte key via the supplied
|
|
45
|
+
* {@link CryptoUtils.ICryptoProvider | crypto provider}.
|
|
46
|
+
*
|
|
47
|
+
* `supportsNonExtractable` is `false`: persisting to disk requires exporting the
|
|
48
|
+
* private key to JWK, which only works for `extractable: true` keys. The
|
|
49
|
+
* keystore generates extractable keys when a backend reports `false` here.
|
|
50
|
+
*
|
|
51
|
+
* I/O goes through the {@link FileTree.FileTree | FileTree} abstraction (default
|
|
52
|
+
* `FsTree`), so the same implementation works against an in-memory tree (tests)
|
|
53
|
+
* or any other Node-compatible backend.
|
|
54
|
+
*
|
|
55
|
+
* This backend is **Node-only**: it round-trips private keys through
|
|
56
|
+
* `node:crypto` (`crypto.webcrypto.subtle`), so it is intentionally excluded
|
|
57
|
+
* from the browser entry point. Browser consumers should use
|
|
58
|
+
* `IdbPrivateKeyStorage` from `@fgv/ts-web-extras` instead.
|
|
59
|
+
*
|
|
60
|
+
* Single-process assumption: there is no inter-process locking. Concurrent
|
|
61
|
+
* writers to the same directory may race.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export declare class EncryptedFilePrivateKeyStorage implements IPrivateKeyStorage {
|
|
66
|
+
/**
|
|
67
|
+
* `false` — disk persistence round-trips via JWK, which requires extractable
|
|
68
|
+
* keys.
|
|
69
|
+
*/
|
|
70
|
+
readonly supportsNonExtractable: false;
|
|
71
|
+
private readonly _directory;
|
|
72
|
+
private readonly _encryptionKey;
|
|
73
|
+
private readonly _cryptoProvider;
|
|
74
|
+
private constructor();
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new {@link CryptoUtils.KeyStore.EncryptedFilePrivateKeyStorage}.
|
|
77
|
+
* @param params - {@link CryptoUtils.KeyStore.IEncryptedFilePrivateKeyStorageCreateParams}.
|
|
78
|
+
* @returns `Success` with the new instance, or `Failure` if the encryption
|
|
79
|
+
* key is the wrong size or the storage directory cannot be opened.
|
|
80
|
+
*/
|
|
81
|
+
static create(params: IEncryptedFilePrivateKeyStorageCreateParams): Result<EncryptedFilePrivateKeyStorage>;
|
|
82
|
+
/**
|
|
83
|
+
* Stores `key` under `id` as an encrypted JWK file.
|
|
84
|
+
* @param id - Storage handle. Must be a safe filename token
|
|
85
|
+
* (`[A-Za-z0-9._-]+`, not `.`/`..`).
|
|
86
|
+
* @param key - The extractable private `CryptoKey` to persist.
|
|
87
|
+
*/
|
|
88
|
+
store(id: string, key: CryptoKey): Promise<Result<string>>;
|
|
89
|
+
/**
|
|
90
|
+
* Loads the private key stored under `id`, decrypting and re-importing it from
|
|
91
|
+
* JWK.
|
|
92
|
+
* @param id - Storage handle.
|
|
93
|
+
*/
|
|
94
|
+
load(id: string): Promise<Result<CryptoKey>>;
|
|
95
|
+
/**
|
|
96
|
+
* Deletes the entry stored under `id`. Missing ids fail (the read path is
|
|
97
|
+
* keystore-driven and never asks to delete an id it did not store).
|
|
98
|
+
* @param id - Storage handle.
|
|
99
|
+
*/
|
|
100
|
+
delete(id: string): Promise<Result<string>>;
|
|
101
|
+
/**
|
|
102
|
+
* Lists every stored id.
|
|
103
|
+
*/
|
|
104
|
+
list(): Promise<Result<readonly string[]>>;
|
|
105
|
+
private _fileNameFor;
|
|
106
|
+
/**
|
|
107
|
+
* Validates the synchronous preconditions for a store: the id is filename-safe,
|
|
108
|
+
* the key is actually a private key, and its algorithm is one we support.
|
|
109
|
+
* Returns the resolved filename and algorithm so the async pipeline can run
|
|
110
|
+
* without re-deriving them.
|
|
111
|
+
*/
|
|
112
|
+
private _validateKeyToStore;
|
|
113
|
+
/**
|
|
114
|
+
* Exports `key` to JWK, wraps it in the stored envelope, encrypts it with
|
|
115
|
+
* AES-256-GCM, and writes the resulting file as serialized JSON to `fileName`.
|
|
116
|
+
* Returns the stored `id` on success.
|
|
117
|
+
*/
|
|
118
|
+
private _encryptAndWrite;
|
|
119
|
+
private _algorithmOf;
|
|
120
|
+
private _findFile;
|
|
121
|
+
private _writeFile;
|
|
122
|
+
/**
|
|
123
|
+
* Reads `file`, decrypts the AES-256-GCM envelope, and validates it into the
|
|
124
|
+
* typed `IStoredPrivateKeyEnvelope`. Read, decrypt, and shape failures
|
|
125
|
+
* all surface as a decrypt failure for `id`.
|
|
126
|
+
*/
|
|
127
|
+
private _decryptEnvelope;
|
|
128
|
+
/**
|
|
129
|
+
* Parses and shape-validates the stored JWK, then re-imports it as a private
|
|
130
|
+
* `CryptoKey` for the envelope's algorithm. The WebCrypto JWK-import algorithm
|
|
131
|
+
* descriptor is shared between public and private keys for every supported
|
|
132
|
+
* algorithm, so `IKeyPairAlgorithmParams.importPublicKey` is reused here;
|
|
133
|
+
* the public/private distinction is carried by the requested `usages`.
|
|
134
|
+
*/
|
|
135
|
+
private _importPrivateKey;
|
|
136
|
+
/**
|
|
137
|
+
* Computes the key usages to request when re-importing a stored private key.
|
|
138
|
+
* WebCrypto rejects `importKey` if the requested usages include operations
|
|
139
|
+
* absent from the JWK's `key_ops`, so a key originally created with a narrower
|
|
140
|
+
* usage set than the algorithm default (e.g. an ECDH key with only
|
|
141
|
+
* `deriveBits`) would fail to load against the algorithm-wide defaults.
|
|
142
|
+
* Intersect the algorithm's private usages with the JWK's recorded `key_ops`
|
|
143
|
+
* so we request exactly the operations the stored key actually supports;
|
|
144
|
+
* fall back to the algorithm's private usages when `key_ops` is absent.
|
|
145
|
+
*/
|
|
146
|
+
private _importUsagesFor;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=encryptedFilePrivateKeyStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptedFilePrivateKeyStorage.d.ts","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,MAAM,EAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAoB,MAAM,UAAU,CAAC;AAG7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,2CAA2C;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IAEnC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC;IAEzC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC;CACjD;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,8BAA+B,YAAW,kBAAkB;IACvE;;;OAGG;IACH,SAAgB,sBAAsB,EAAE,KAAK,CAAS;IAEtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAa;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAElD,OAAO;IAYP;;;;;OAKG;WACW,MAAM,CAClB,MAAM,EAAE,2CAA2C,GAClD,MAAM,CAAC,8BAA8B,CAAC;IAgBzC;;;;;OAKG;IACU,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAMvE;;;;OAIG;IACU,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAUzD;;;;OAIG;IACU,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAsBxD;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAWvD,OAAO,CAAC,YAAY;IAOpB;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;;;OAIG;YACW,gBAAgB;IAuB9B,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAiBlB;;;;OAIG;YACW,gBAAgB;IAW9B;;;;;;OAMG;YACW,iBAAiB;IAsB/B;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;CAQzB"}
|