@fgv/ts-extras 5.1.0-17 → 5.1.0-18

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.
Files changed (33) hide show
  1. package/dist/packlets/crypto-utils/index.browser.js +2 -0
  2. package/dist/packlets/crypto-utils/index.js +2 -0
  3. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +47 -0
  4. package/dist/packlets/crypto-utils/keystore/converters.js +101 -9
  5. package/dist/packlets/crypto-utils/keystore/index.js +1 -0
  6. package/dist/packlets/crypto-utils/keystore/keyStore.js +271 -46
  7. package/dist/packlets/crypto-utils/keystore/model.js +22 -1
  8. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
  9. package/dist/packlets/crypto-utils/model.js +5 -0
  10. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +44 -1
  11. package/dist/test/unit/crypto/keystore/inMemoryPrivateKeyStorage.js +78 -0
  12. package/dist/ts-extras.d.ts +577 -32
  13. package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
  14. package/lib/packlets/crypto-utils/index.browser.js +4 -1
  15. package/lib/packlets/crypto-utils/index.d.ts +1 -0
  16. package/lib/packlets/crypto-utils/index.js +4 -1
  17. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +39 -0
  18. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +50 -0
  19. package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
  20. package/lib/packlets/crypto-utils/keystore/converters.js +100 -8
  21. package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
  22. package/lib/packlets/crypto-utils/keystore/index.js +1 -0
  23. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +77 -9
  24. package/lib/packlets/crypto-utils/keystore/keyStore.js +271 -46
  25. package/lib/packlets/crypto-utils/keystore/model.d.ts +238 -19
  26. package/lib/packlets/crypto-utils/keystore/model.js +24 -2
  27. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +50 -0
  28. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
  29. package/lib/packlets/crypto-utils/model.d.ts +38 -0
  30. package/lib/packlets/crypto-utils/model.js +6 -1
  31. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +26 -1
  32. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +43 -0
  33. package/package.json +7 -7
@@ -193,12 +193,30 @@ declare const aiToolEnablement: Converter<IAiToolEnablement>;
193
193
  */
194
194
  declare const aiWebSearchToolConfig: Converter<IAiWebSearchToolConfig>;
195
195
 
196
+ /**
197
+ * All valid key pair algorithms.
198
+ * @public
199
+ */
200
+ declare const allKeyPairAlgorithms: ReadonlyArray<KeyPairAlgorithm>;
201
+
202
+ /**
203
+ * All valid asymmetric secret types.
204
+ * @public
205
+ */
206
+ declare const allKeyStoreAsymmetricSecretTypes: ReadonlyArray<KeyStoreAsymmetricSecretType>;
207
+
196
208
  /**
197
209
  * All valid key store secret types.
198
210
  * @public
199
211
  */
200
212
  declare const allKeyStoreSecretTypes: ReadonlyArray<KeyStoreSecretType>;
201
213
 
214
+ /**
215
+ * All valid symmetric secret types.
216
+ * @public
217
+ */
218
+ declare const allKeyStoreSymmetricSecretTypes: ReadonlyArray<KeyStoreSymmetricSecretType>;
219
+
202
220
  /**
203
221
  * All valid {@link ModelSpecKey} values.
204
222
  * @public
@@ -388,6 +406,12 @@ declare namespace Converters_2 {
388
406
  export {
389
407
  keystoreFormat,
390
408
  keystoreSecretType,
409
+ keystoreSymmetricSecretType,
410
+ keystoreAsymmetricSecretType,
411
+ keyPairAlgorithm,
412
+ jsonWebKeyShape,
413
+ keystoreSymmetricEntryJson,
414
+ keystoreAsymmetricEntryJson,
391
415
  keystoreSecretEntryJson,
392
416
  keystoreVaultContents,
393
417
  keystoreFile
@@ -440,6 +464,8 @@ declare namespace CryptoUtils {
440
464
  Converters_3 as Converters,
441
465
  DirectEncryptionProvider,
442
466
  IDirectEncryptionProviderParams,
467
+ IKeyPairAlgorithmParams,
468
+ keyPairAlgorithmParams,
443
469
  NodeCryptoProvider,
444
470
  nodeCryptoProvider,
445
471
  createEncryptedFile,
@@ -453,6 +479,8 @@ declare namespace CryptoUtils {
453
479
  EncryptedFileFormat,
454
480
  INamedSecret,
455
481
  IEncryptionResult,
482
+ KeyPairAlgorithm,
483
+ allKeyPairAlgorithms,
456
484
  KeyDerivationFunction,
457
485
  IKeyDerivationParams,
458
486
  IEncryptedFile,
@@ -829,6 +857,50 @@ declare namespace Hash {
829
857
  }
830
858
  export { Hash }
831
859
 
860
+ /**
861
+ * Options for adding an asymmetric keypair to the key store.
862
+ * @public
863
+ */
864
+ declare interface IAddKeyPairOptions {
865
+ /**
866
+ * Algorithm to use for the new keypair.
867
+ */
868
+ readonly algorithm: KeyPairAlgorithm;
869
+ /**
870
+ * Optional description for the entry.
871
+ */
872
+ readonly description?: string;
873
+ /**
874
+ * Whether to replace an existing entry with the same name.
875
+ * Replacement mints a fresh storage `id` and best-effort deletes the
876
+ * displaced storage blob; see the keystore design doc for details.
877
+ */
878
+ readonly replace?: boolean;
879
+ }
880
+
881
+ /**
882
+ * Result of adding an asymmetric keypair to the key store.
883
+ * @public
884
+ */
885
+ declare interface IAddKeyPairResult {
886
+ /**
887
+ * The asymmetric entry that was added.
888
+ */
889
+ readonly entry: IKeyStoreAsymmetricEntry;
890
+ /**
891
+ * Whether this replaced an existing entry.
892
+ */
893
+ readonly replaced: boolean;
894
+ /**
895
+ * Best-effort warning from displaced-resource cleanup. Set when this call
896
+ * replaced a prior entry but the corresponding
897
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete failed; the new
898
+ * keypair is still committed and the orphaned blob is left for consumer-side
899
+ * GC to reconcile.
900
+ */
901
+ readonly warning?: string;
902
+ }
903
+
832
904
  /**
833
905
  * Options for adding a secret derived from a password.
834
906
  * @public
@@ -879,11 +951,19 @@ declare interface IAddSecretResult {
879
951
  /**
880
952
  * The secret entry that was added.
881
953
  */
882
- readonly entry: IKeyStoreSecretEntry;
954
+ readonly entry: IKeyStoreSymmetricEntry;
883
955
  /**
884
956
  * Whether this replaced an existing secret.
885
957
  */
886
958
  readonly replaced: boolean;
959
+ /**
960
+ * Best-effort warning from displaced-resource cleanup. Set when this call
961
+ * replaced an asymmetric-keypair entry but the corresponding
962
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete failed; the new
963
+ * entry is still committed and the orphaned blob is left for consumer-side
964
+ * GC to reconcile.
965
+ */
966
+ readonly warning?: string;
887
967
  }
888
968
 
889
969
  /**
@@ -1389,6 +1469,32 @@ declare interface ICryptoProvider {
1389
1469
  * @returns Success with decoded bytes, or Failure if invalid base64
1390
1470
  */
1391
1471
  fromBase64(base64: string): Result<Uint8Array>;
1472
+ /**
1473
+ * Generates a new asymmetric keypair for the requested algorithm.
1474
+ * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} to use.
1475
+ * @param extractable - Whether the resulting `CryptoKey` objects may be exported.
1476
+ * Set `false` on backends that store `CryptoKey` references directly (e.g.
1477
+ * IndexedDB). Set `true` when the private key must round-trip through JWK or
1478
+ * PKCS#8 (e.g. encrypted-file backends).
1479
+ * @returns Success with the generated `CryptoKeyPair`, or Failure with error context.
1480
+ */
1481
+ generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;
1482
+ /**
1483
+ * Exports the public half of a keypair as a JSON Web Key.
1484
+ * @param publicKey - The public `CryptoKey` to export. Must be an `extractable`
1485
+ * key generated for an asymmetric algorithm.
1486
+ * @returns Success with the JWK, or Failure with error context.
1487
+ */
1488
+ exportPublicKeyJwk(publicKey: CryptoKey): Promise<Result<JsonWebKey>>;
1489
+ /**
1490
+ * Re-imports a public-key JWK as a `CryptoKey` usable for verification or
1491
+ * encryption (depending on algorithm).
1492
+ * @param jwk - The JSON Web Key produced by {@link CryptoUtils.ICryptoProvider.exportPublicKeyJwk | exportPublicKeyJwk}.
1493
+ * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} the
1494
+ * key was generated for. Determines the import parameters and key usages.
1495
+ * @returns Success with the imported public `CryptoKey`, or Failure with error context.
1496
+ */
1497
+ importPublicKeyJwk(jwk: JsonWebKey, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;
1392
1498
  }
1393
1499
 
1394
1500
  /**
@@ -1534,10 +1640,10 @@ declare interface IEncryptionResult {
1534
1640
  */
1535
1641
  declare interface IImportKeyOptions extends IImportSecretOptions {
1536
1642
  /**
1537
- * Secret type classification for the imported key material.
1643
+ * Symmetric secret type classification for the imported key material.
1538
1644
  * @defaultValue 'encryption-key'
1539
1645
  */
1540
- readonly type?: KeyStoreSecretType;
1646
+ readonly type?: KeyStoreSymmetricSecretType;
1541
1647
  }
1542
1648
 
1543
1649
  /**
@@ -1571,6 +1677,111 @@ declare interface IKeyDerivationParams {
1571
1677
  readonly iterations: number;
1572
1678
  }
1573
1679
 
1680
+ /**
1681
+ * WebCrypto parameters for a single {@link CryptoUtils.KeyPairAlgorithm}.
1682
+ * Implementations of {@link CryptoUtils.ICryptoProvider} use this table to
1683
+ * translate the small public algorithm enum into the WebCrypto algorithm
1684
+ * objects and key-usage arrays expected by `crypto.subtle`.
1685
+ * @public
1686
+ */
1687
+ declare interface IKeyPairAlgorithmParams {
1688
+ /**
1689
+ * Algorithm parameters for `crypto.subtle.generateKey`. Always an asymmetric
1690
+ * variant — these algorithms produce a `CryptoKeyPair`, not a single key.
1691
+ */
1692
+ readonly generateKey: RsaHashedKeyGenParams | EcKeyGenParams;
1693
+ /**
1694
+ * Algorithm parameters for `crypto.subtle.importKey('jwk', ...)` when
1695
+ * importing the public half of a keypair.
1696
+ */
1697
+ readonly importPublicKey: RsaHashedImportParams | EcKeyImportParams;
1698
+ /**
1699
+ * Default key usages for the generated `CryptoKeyPair`. Both halves receive
1700
+ * the usages WebCrypto considers valid for their role; the platform filters.
1701
+ */
1702
+ readonly keyPairUsages: ReadonlyArray<KeyUsage>;
1703
+ /**
1704
+ * Key usages applied when re-importing only the public key.
1705
+ */
1706
+ readonly publicKeyUsages: ReadonlyArray<KeyUsage>;
1707
+ }
1708
+
1709
+ /**
1710
+ * An asymmetric keypair entry stored in the vault (in-memory representation).
1711
+ * Holds only the public key (as a JWK) and a stable handle (`id`) the
1712
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider uses to fetch the private key.
1713
+ * @public
1714
+ */
1715
+ declare interface IKeyStoreAsymmetricEntry {
1716
+ /**
1717
+ * Unique name for this entry (used as vault lookup key, renameable).
1718
+ */
1719
+ readonly name: string;
1720
+ /**
1721
+ * Asymmetric secret type discriminator.
1722
+ */
1723
+ readonly type: KeyStoreAsymmetricSecretType;
1724
+ /**
1725
+ * Immutable handle used by {@link CryptoUtils.KeyStore.IPrivateKeyStorage} to address the
1726
+ * private key. Independent of `name`; survives renames.
1727
+ */
1728
+ readonly id: string;
1729
+ /**
1730
+ * Algorithm used to generate this keypair.
1731
+ */
1732
+ readonly algorithm: KeyPairAlgorithm;
1733
+ /**
1734
+ * The public key as a JSON Web Key.
1735
+ */
1736
+ readonly publicKeyJwk: JsonWebKey;
1737
+ /**
1738
+ * Optional description for this entry.
1739
+ */
1740
+ readonly description?: string;
1741
+ /**
1742
+ * When this entry was added (ISO 8601).
1743
+ */
1744
+ readonly createdAt: string;
1745
+ }
1746
+
1747
+ /**
1748
+ * JSON-serializable representation of an asymmetric keypair entry.
1749
+ * The private key is not present here — it lives in the
1750
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider, addressed by `id`.
1751
+ * @public
1752
+ */
1753
+ declare interface IKeyStoreAsymmetricEntryJson {
1754
+ /**
1755
+ * Unique name for this entry.
1756
+ */
1757
+ readonly name: string;
1758
+ /**
1759
+ * Asymmetric secret type discriminator.
1760
+ */
1761
+ readonly type: KeyStoreAsymmetricSecretType;
1762
+ /**
1763
+ * Immutable handle used by {@link CryptoUtils.KeyStore.IPrivateKeyStorage} to address the
1764
+ * private key.
1765
+ */
1766
+ readonly id: string;
1767
+ /**
1768
+ * Algorithm used to generate this keypair.
1769
+ */
1770
+ readonly algorithm: KeyPairAlgorithm;
1771
+ /**
1772
+ * The public key as a JSON Web Key.
1773
+ */
1774
+ readonly publicKeyJwk: JsonWebKey;
1775
+ /**
1776
+ * Optional description.
1777
+ */
1778
+ readonly description?: string;
1779
+ /**
1780
+ * When this entry was added (ISO 8601).
1781
+ */
1782
+ readonly createdAt: string;
1783
+ }
1784
+
1574
1785
  /**
1575
1786
  * Parameters for creating a new key store.
1576
1787
  * @public
@@ -1584,8 +1795,26 @@ declare interface IKeyStoreCreateParams {
1584
1795
  * PBKDF2 iterations (defaults to DEFAULT_KEYSTORE_ITERATIONS).
1585
1796
  */
1586
1797
  readonly iterations?: number;
1798
+ /**
1799
+ * Optional private-key storage backend. Required to use `addKeyPair` /
1800
+ * `getKeyPair`; absent backends still permit opening, listing, and reading
1801
+ * public-key metadata for asymmetric entries.
1802
+ */
1803
+ readonly privateKeyStorage?: IPrivateKeyStorage;
1587
1804
  }
1588
1805
 
1806
+ /**
1807
+ * Any vault entry, discriminated by `type`.
1808
+ * @public
1809
+ */
1810
+ declare type IKeyStoreEntry = IKeyStoreSymmetricEntry | IKeyStoreAsymmetricEntry;
1811
+
1812
+ /**
1813
+ * Any JSON vault entry, discriminated by `type`.
1814
+ * @public
1815
+ */
1816
+ declare type IKeyStoreEntryJson = IKeyStoreSymmetricEntryJson | IKeyStoreAsymmetricEntryJson;
1817
+
1589
1818
  /**
1590
1819
  * The encrypted key store file format.
1591
1820
  * @public
@@ -1630,22 +1859,46 @@ declare interface IKeyStoreOpenParams {
1630
1859
  * The encrypted key store file content.
1631
1860
  */
1632
1861
  readonly keystoreFile: IKeyStoreFile;
1862
+ /**
1863
+ * Optional private-key storage backend. Required to use `addKeyPair` /
1864
+ * `getKeyPair`; absent backends still permit opening, listing, and reading
1865
+ * public-key metadata for asymmetric entries.
1866
+ */
1867
+ readonly privateKeyStorage?: IPrivateKeyStorage;
1633
1868
  }
1634
1869
 
1635
1870
  /**
1636
- * A secret entry stored in the vault (in-memory representation).
1871
+ * Backwards-compatible alias for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntry}.
1872
+ * @deprecated Use {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntry} for symmetric
1873
+ * entries or {@link CryptoUtils.KeyStore.IKeyStoreEntry} for the discriminated union.
1874
+ * @public
1875
+ */
1876
+ declare type IKeyStoreSecretEntry = IKeyStoreSymmetricEntry;
1877
+
1878
+ /**
1879
+ * Backwards-compatible alias for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson}.
1880
+ * @deprecated Use {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson} for
1881
+ * symmetric entries or {@link CryptoUtils.KeyStore.IKeyStoreEntryJson} for the
1882
+ * discriminated union.
1637
1883
  * @public
1638
1884
  */
1639
- declare interface IKeyStoreSecretEntry {
1885
+ declare type IKeyStoreSecretEntryJson = IKeyStoreSymmetricEntryJson;
1886
+
1887
+ /**
1888
+ * A symmetric secret entry stored in the vault (in-memory representation).
1889
+ * Holds the raw key material directly — for `'encryption-key'` it is a 32-byte
1890
+ * AES-256 key; for `'api-key'` it is the UTF-8 encoded API key string.
1891
+ * @public
1892
+ */
1893
+ declare interface IKeyStoreSymmetricEntry {
1640
1894
  /**
1641
1895
  * Unique name for this secret (used as lookup key).
1642
1896
  */
1643
1897
  readonly name: string;
1644
1898
  /**
1645
- * Secret type discriminator.
1646
- * Defaults to `'encryption-key'` for backwards compatibility.
1899
+ * Symmetric secret type discriminator.
1647
1900
  */
1648
- readonly type: KeyStoreSecretType;
1901
+ readonly type: KeyStoreSymmetricSecretType;
1649
1902
  /**
1650
1903
  * The secret data.
1651
1904
  * - For `'encryption-key'`: 32-byte AES-256 key.
@@ -1663,19 +1916,34 @@ declare interface IKeyStoreSecretEntry {
1663
1916
  }
1664
1917
 
1665
1918
  /**
1666
- * JSON-serializable version of secret entry (for storage).
1919
+ * JSON-serializable representation of a symmetric secret entry.
1920
+ *
1921
+ * @remarks
1922
+ * Describes the *normalized* shape after parsing. `type` is required here
1923
+ * because the converter (see
1924
+ * {@link CryptoUtils.KeyStore.Converters.keystoreSymmetricEntryJson | keystoreSymmetricEntryJson})
1925
+ * injects the default `'encryption-key'` when reading vaults written before
1926
+ * asymmetric-keypair support added the discriminator. Raw on-wire bytes from
1927
+ * a legacy vault may therefore omit `type`; downstream code only ever sees
1928
+ * the post-conversion shape declared here.
1929
+ *
1667
1930
  * @public
1668
1931
  */
1669
- declare interface IKeyStoreSecretEntryJson {
1932
+ declare interface IKeyStoreSymmetricEntryJson {
1670
1933
  /**
1671
1934
  * Unique name for this secret.
1672
1935
  */
1673
1936
  readonly name: string;
1674
1937
  /**
1675
- * Secret type discriminator.
1676
- * Optional for backwards compatibility — missing means `'encryption-key'`.
1938
+ * Symmetric secret type discriminator.
1939
+ *
1940
+ * Required on this normalized model type. Vaults written prior to the
1941
+ * asymmetric-keypair support may omit this field on the wire; the
1942
+ * converter injects `'encryption-key'` when missing for backwards
1943
+ * compatibility, so by the time a value of this type is observed the
1944
+ * discriminator is always present.
1677
1945
  */
1678
- readonly type?: KeyStoreSecretType;
1946
+ readonly type: KeyStoreSymmetricSecretType;
1679
1947
  /**
1680
1948
  * Base64-encoded secret data.
1681
1949
  */
@@ -1691,7 +1959,7 @@ declare interface IKeyStoreSecretEntryJson {
1691
1959
  }
1692
1960
 
1693
1961
  /**
1694
- * The decrypted vault contents - a versioned map of secrets.
1962
+ * The decrypted vault contents - a versioned map of entries.
1695
1963
  * @public
1696
1964
  */
1697
1965
  declare interface IKeyStoreVaultContents {
@@ -1700,9 +1968,9 @@ declare interface IKeyStoreVaultContents {
1700
1968
  */
1701
1969
  readonly version: KeyStoreFormat;
1702
1970
  /**
1703
- * Map of secret name to secret entry.
1971
+ * Map of entry name to entry (symmetric or asymmetric).
1704
1972
  */
1705
- readonly secrets: Record<string, IKeyStoreSecretEntryJson>;
1973
+ readonly secrets: Record<string, IKeyStoreEntryJson>;
1706
1974
  }
1707
1975
 
1708
1976
  /**
@@ -1785,6 +2053,55 @@ declare interface INamedSecret {
1785
2053
  readonly key: Uint8Array;
1786
2054
  }
1787
2055
 
2056
+ /**
2057
+ * Pluggable backend that persists raw asymmetric private keys outside of the
2058
+ * encrypted keystore vault. Concrete implementations live in platform-specific
2059
+ * packages (e.g. an IndexedDB-backed implementation in `@fgv/ts-web-extras` or
2060
+ * an encrypted-file implementation in `@fgv/ts-chocolate`).
2061
+ *
2062
+ * The keystore writes storage-first: a private key is always stored here
2063
+ * before the corresponding public-key vault entry is committed. Conversely,
2064
+ * deletes hit the vault first and then this storage best-effort. As a result,
2065
+ * crashes or skipped saves can leave orphaned blobs here; callers are expected
2066
+ * to reconcile via {@link CryptoUtils.KeyStore.IPrivateKeyStorage.list} cross-referenced
2067
+ * against the keystore's asymmetric entries.
2068
+ *
2069
+ * @public
2070
+ */
2071
+ declare interface IPrivateKeyStorage {
2072
+ /**
2073
+ * Whether keys generated for this backend may be marked
2074
+ * `extractable: false`. `true` on backends that store `CryptoKey`
2075
+ * objects directly (e.g. IndexedDB). `false` on backends that must
2076
+ * round-trip via JWK (e.g. encrypted-file backends).
2077
+ */
2078
+ readonly supportsNonExtractable: boolean;
2079
+ /**
2080
+ * Stores `key` under `id`. Returns the stored `id` on success so the
2081
+ * call can compose into a Result chain.
2082
+ * @param id - Storage handle to write under.
2083
+ * @param key - The private `CryptoKey` to persist.
2084
+ */
2085
+ store(id: string, key: CryptoKey): Promise<Result<string>>;
2086
+ /**
2087
+ * Loads the private key previously stored under `id`.
2088
+ * @param id - Storage handle to look up.
2089
+ */
2090
+ load(id: string): Promise<Result<CryptoKey>>;
2091
+ /**
2092
+ * Deletes the entry stored under `id`. Returns the deleted `id` on
2093
+ * success so the call can compose into a Result chain.
2094
+ * @param id - Storage handle to remove.
2095
+ */
2096
+ delete(id: string): Promise<Result<string>>;
2097
+ /**
2098
+ * Lists every `id` currently held by the backend. Used by consumers to
2099
+ * garbage-collect orphans left by crashes or aborted sessions; the
2100
+ * keystore itself does not invoke this automatically.
2101
+ */
2102
+ list(): Promise<Result<readonly string[]>>;
2103
+ }
2104
+
1788
2105
  /**
1789
2106
  * Parameters for a provider completion request.
1790
2107
  * @public
@@ -1884,6 +2201,24 @@ declare interface IProviderListModelsParams {
1884
2201
  readonly signal?: AbortSignal;
1885
2202
  }
1886
2203
 
2204
+ /**
2205
+ * Result of removing a secret from the key store.
2206
+ * @public
2207
+ */
2208
+ declare interface IRemoveSecretResult {
2209
+ /**
2210
+ * The secret entry that was removed from the vault.
2211
+ */
2212
+ readonly entry: IKeyStoreEntry;
2213
+ /**
2214
+ * Best-effort warning from {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete
2215
+ * for asymmetric entries when the storage call failed. The vault entry is
2216
+ * still considered removed and the orphaned blob is left for consumer-side
2217
+ * GC to reconcile.
2218
+ */
2219
+ readonly warning?: string;
2220
+ }
2221
+
1887
2222
  /**
1888
2223
  * Required version of options with all fields populated.
1889
2224
  * @internal
@@ -2015,6 +2350,24 @@ declare interface JarRecordParserOptions {
2015
2350
  readonly fixedContinuationSize?: number;
2016
2351
  }
2017
2352
 
2353
+ /**
2354
+ * In-place shape check for a JSON Web Key. Asserts only that the input is a
2355
+ * non-array object whose `kty` discriminator is a string; every other JWK
2356
+ * field passes through untouched. This is intentionally **not** a true JWK
2357
+ * validator — per-algorithm correctness (RSA `n`/`e`, EC `crv`/`x`/`y`,
2358
+ * key-size constraints, etc.) is delegated to `crypto.subtle.importKey` at
2359
+ * first use, which is the authoritative checker. The "shape" suffix in the
2360
+ * name is the warning sign for readers expecting full validation.
2361
+ * @remarks
2362
+ * Built with `Validators.object` (in-place, non-strict) so unknown JWK fields
2363
+ * survive the round-trip; the cast to `FieldValidators<JsonWebKey>` is required
2364
+ * only because TypeScript's mapped type demands an entry for every key in
2365
+ * `JsonWebKey`. At runtime the `ObjectValidator` only inspects keys present in
2366
+ * the field-validators map.
2367
+ * @public
2368
+ */
2369
+ declare const jsonWebKeyShape: Validator<JsonWebKey>;
2370
+
2018
2371
  /**
2019
2372
  * Supported key derivation functions.
2020
2373
  * @public
@@ -2033,18 +2386,54 @@ declare const keyDerivationFunction: Converter<KeyDerivationFunction>;
2033
2386
  */
2034
2387
  declare const keyDerivationParams: Converter<IKeyDerivationParams>;
2035
2388
 
2389
+ /**
2390
+ * Asymmetric keypair algorithms supported by the crypto provider.
2391
+ * - `'ecdsa-p256'`: ECDSA over the P-256 curve, for signing.
2392
+ * - `'rsa-oaep-2048'`: RSA-OAEP, 2048-bit modulus with SHA-256, for encryption.
2393
+ * @public
2394
+ */
2395
+ declare type KeyPairAlgorithm = 'ecdsa-p256' | 'rsa-oaep-2048';
2396
+
2397
+ /**
2398
+ * Converter for {@link CryptoUtils.KeyStore.KeyPairAlgorithm | key pair algorithm}.
2399
+ * @public
2400
+ */
2401
+ declare const keyPairAlgorithm: Converter<KeyPairAlgorithm>;
2402
+
2403
+ /**
2404
+ * Lookup table from {@link CryptoUtils.KeyPairAlgorithm} to the WebCrypto
2405
+ * parameters needed to drive `crypto.subtle`. Shared between every
2406
+ * {@link CryptoUtils.ICryptoProvider} implementation since both Node and
2407
+ * browser providers speak the same WebCrypto API. Exposed for downstream
2408
+ * provider implementations (e.g. browser-side providers in `@fgv/ts-web-extras`).
2409
+ * @public
2410
+ */
2411
+ declare const keyPairAlgorithmParams: Readonly<Record<KeyPairAlgorithm, IKeyPairAlgorithmParams>>;
2412
+
2036
2413
  declare namespace KeyStore {
2037
2414
  export {
2038
2415
  Converters_2 as Converters,
2039
2416
  KeyStore_2 as KeyStore,
2040
2417
  isKeyStoreFile,
2418
+ allKeyPairAlgorithms,
2419
+ KeyPairAlgorithm,
2041
2420
  KeyStoreFormat,
2042
2421
  KEYSTORE_FORMAT,
2043
2422
  DEFAULT_KEYSTORE_ITERATIONS,
2044
2423
  MIN_SALT_LENGTH,
2424
+ KeyStoreSymmetricSecretType,
2425
+ allKeyStoreSymmetricSecretTypes,
2426
+ KeyStoreAsymmetricSecretType,
2427
+ allKeyStoreAsymmetricSecretTypes,
2045
2428
  KeyStoreSecretType,
2046
2429
  allKeyStoreSecretTypes,
2430
+ IKeyStoreSymmetricEntry,
2431
+ IKeyStoreAsymmetricEntry,
2432
+ IKeyStoreEntry,
2047
2433
  IKeyStoreSecretEntry,
2434
+ IKeyStoreSymmetricEntryJson,
2435
+ IKeyStoreAsymmetricEntryJson,
2436
+ IKeyStoreEntryJson,
2048
2437
  IKeyStoreSecretEntryJson,
2049
2438
  IKeyStoreVaultContents,
2050
2439
  IKeyStoreFile,
@@ -2057,7 +2446,11 @@ declare namespace KeyStore {
2057
2446
  IImportKeyOptions,
2058
2447
  IAddSecretFromPasswordOptions,
2059
2448
  DEFAULT_SECRET_ITERATIONS,
2060
- IAddSecretFromPasswordResult
2449
+ IAddSecretFromPasswordResult,
2450
+ IAddKeyPairOptions,
2451
+ IAddKeyPairResult,
2452
+ IRemoveSecretResult,
2453
+ IPrivateKeyStorage
2061
2454
  }
2062
2455
  }
2063
2456
 
@@ -2094,6 +2487,7 @@ declare namespace KeyStore {
2094
2487
  */
2095
2488
  declare class KeyStore_2 implements IEncryptionProvider {
2096
2489
  private readonly _cryptoProvider;
2490
+ private readonly _privateKeyStorage;
2097
2491
  private readonly _iterations;
2098
2492
  private _keystoreFile;
2099
2493
  private _salt;
@@ -2192,12 +2586,23 @@ declare class KeyStore_2 implements IEncryptionProvider {
2192
2586
  */
2193
2587
  listSecrets(): Result<readonly string[]>;
2194
2588
  /**
2195
- * Gets a secret by name.
2589
+ * Gets a secret by name. Returns the {@link CryptoUtils.KeyStore.IKeyStoreEntry | discriminated union}
2590
+ * — callers must check `entry.type` before accessing `key`/`id` since asymmetric
2591
+ * entries carry no raw key material.
2196
2592
  * @param name - Name of the secret
2197
2593
  * @returns Success with secret entry, Failure if not found or locked
2198
2594
  * @public
2199
2595
  */
2200
- getSecret(name: string): Result<IKeyStoreSecretEntry>;
2596
+ getSecret(name: string): Result<IKeyStoreEntry>;
2597
+ /**
2598
+ * Returns the public-key JWK for an asymmetric-keypair entry.
2599
+ * Available without {@link CryptoUtils.KeyStore.IPrivateKeyStorage} since the
2600
+ * public key lives in the vault metadata directly.
2601
+ * @param name - Name of the entry
2602
+ * @returns Success with the JWK, Failure if not found, locked, or wrong type
2603
+ * @public
2604
+ */
2605
+ getPublicKeyJwk(name: string): Result<JsonWebKey>;
2201
2606
  /**
2202
2607
  * Checks if a secret exists.
2203
2608
  * @param name - Name of the secret
@@ -2227,7 +2632,7 @@ declare class KeyStore_2 implements IEncryptionProvider {
2227
2632
  * @returns Success with entry, Failure if locked, key invalid, or exists and !replace
2228
2633
  * @public
2229
2634
  */
2230
- importSecret(name: string, key: Uint8Array, options?: IImportKeyOptions): Result<IAddSecretResult>;
2635
+ importSecret(name: string, key: Uint8Array, options?: IImportKeyOptions): Promise<Result<IAddSecretResult>>;
2231
2636
  /**
2232
2637
  * Adds a secret derived from a password using PBKDF2.
2233
2638
  *
@@ -2244,12 +2649,16 @@ declare class KeyStore_2 implements IEncryptionProvider {
2244
2649
  */
2245
2650
  addSecretFromPassword(name: string, password: string, options?: IAddSecretFromPasswordOptions): Promise<Result<IAddSecretFromPasswordResult>>;
2246
2651
  /**
2247
- * Removes a secret by name.
2652
+ * Removes a secret by name. Vault-first: the in-memory vault entry is dropped
2653
+ * before any storage cleanup runs. For asymmetric-keypair entries, best-effort
2654
+ * calls {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete on the entry's
2655
+ * `id`; a failure is reported via `warning` on the result but does not roll
2656
+ * back the vault removal.
2248
2657
  * @param name - Name of the secret to remove
2249
- * @returns Success with removed entry, Failure if not found or locked
2658
+ * @returns Success with removed entry (and optional warning), Failure if not found or locked
2250
2659
  * @public
2251
2660
  */
2252
- removeSecret(name: string): Result<IKeyStoreSecretEntry>;
2661
+ removeSecret(name: string): Promise<Result<IRemoveSecretResult>>;
2253
2662
  /**
2254
2663
  * Imports an API key string into the vault.
2255
2664
  * The string is UTF-8 encoded and stored with type `'api-key'`.
@@ -2259,7 +2668,7 @@ declare class KeyStore_2 implements IEncryptionProvider {
2259
2668
  * @returns Success with entry, Failure if locked, empty, or exists and !replace
2260
2669
  * @public
2261
2670
  */
2262
- importApiKey(name: string, apiKey: string, options?: IImportSecretOptions): Result<IAddSecretResult>;
2671
+ importApiKey(name: string, apiKey: string, options?: IImportSecretOptions): Promise<Result<IAddSecretResult>>;
2263
2672
  /**
2264
2673
  * Retrieves an API key string by name.
2265
2674
  * Only works for secrets with type `'api-key'`.
@@ -2268,6 +2677,41 @@ declare class KeyStore_2 implements IEncryptionProvider {
2268
2677
  * @public
2269
2678
  */
2270
2679
  getApiKey(name: string): Result<string>;
2680
+ /**
2681
+ * Adds a new asymmetric keypair to the vault. Storage-first: the private key
2682
+ * is stored under a freshly-minted `id` before the public-key vault entry is
2683
+ * committed. If the storage call fails, no vault entry is written and the
2684
+ * operation returns Failure.
2685
+ *
2686
+ * When `replace: true` displaces an existing entry (asymmetric or symmetric),
2687
+ * a fresh `id` is minted; the displaced entry's resources are released
2688
+ * best-effort. Failure of the storage delete is reported via `warning` on the
2689
+ * result but does not roll back the replacement.
2690
+ *
2691
+ * Requires a {@link CryptoUtils.KeyStore.IPrivateKeyStorage} backend
2692
+ * supplied at construction.
2693
+ *
2694
+ * @param name - Unique name for the entry
2695
+ * @param options - Algorithm, optional description, replace flag
2696
+ * @returns Success with the new entry, Failure if locked, no provider, or storage write failed
2697
+ * @public
2698
+ */
2699
+ addKeyPair(name: string, options: IAddKeyPairOptions): Promise<Result<IAddKeyPairResult>>;
2700
+ /**
2701
+ * Retrieves the keypair for an asymmetric-keypair entry. The private key is
2702
+ * loaded from {@link CryptoUtils.KeyStore.IPrivateKeyStorage} on every call —
2703
+ * the keystore never caches private `CryptoKey` references between calls.
2704
+ * The public key is re-imported from the vault's JWK so callers always
2705
+ * receive a `CryptoKey` rather than the JWK form.
2706
+ * @param name - Name of the entry
2707
+ * @returns Success with `{ publicKey, privateKey }`, Failure if not found,
2708
+ * locked, wrong type, no provider, or storage load failed.
2709
+ * @public
2710
+ */
2711
+ getKeyPair(name: string): Promise<Result<{
2712
+ publicKey: CryptoKey;
2713
+ privateKey: CryptoKey;
2714
+ }>>;
2271
2715
  /**
2272
2716
  * Lists secret names filtered by type.
2273
2717
  * @param type - The secret type to filter by
@@ -2282,7 +2726,7 @@ declare class KeyStore_2 implements IEncryptionProvider {
2282
2726
  * @returns Success with updated entry, Failure if source not found, target exists, or locked
2283
2727
  * @public
2284
2728
  */
2285
- renameSecret(oldName: string, newName: string): Result<IKeyStoreSecretEntry>;
2729
+ renameSecret(oldName: string, newName: string): Result<IKeyStoreEntry>;
2286
2730
  /**
2287
2731
  * Saves the key store, returning the encrypted file content.
2288
2732
  * Requires the master password to encrypt.
@@ -2339,6 +2783,23 @@ declare class KeyStore_2 implements IEncryptionProvider {
2339
2783
  * Shared by `unlock()` and `unlockWithKey()`.
2340
2784
  */
2341
2785
  private _decryptVault;
2786
+ /**
2787
+ * Releases the resources held by an entry being displaced from the vault.
2788
+ * Symmetric entries get their key buffer zeroed in place. Asymmetric entries
2789
+ * have their private-key blob best-effort deleted from
2790
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}; if the storage call fails,
2791
+ * a warning string is returned but the displacement still proceeds — the
2792
+ * orphaned blob is left for consumer-side GC. Without a configured provider,
2793
+ * asymmetric cleanup is silently skipped.
2794
+ * @returns A warning string if storage cleanup failed, otherwise undefined.
2795
+ */
2796
+ private _releaseEntryResources;
2797
+ /**
2798
+ * Mints a fresh UUID v4 storage handle using the crypto provider's
2799
+ * {@link CryptoUtils.ICryptoProvider.generateRandomBytes | generateRandomBytes}.
2800
+ * Random-bytes failures propagate as Failure.
2801
+ */
2802
+ private _generateId;
2342
2803
  }
2343
2804
 
2344
2805
  /**
@@ -2347,6 +2808,31 @@ declare class KeyStore_2 implements IEncryptionProvider {
2347
2808
  */
2348
2809
  declare const KEYSTORE_FORMAT: KeyStoreFormat;
2349
2810
 
2811
+ /**
2812
+ * Converter for {@link CryptoUtils.KeyStore.IKeyStoreAsymmetricEntryJson | asymmetric keypair entry} in JSON form.
2813
+ * The `publicKeyJwk` field passes through {@link CryptoUtils.KeyStore.Converters.jsonWebKeyShape | jsonWebKeyShape}
2814
+ * (shape check only — see its docs); cryptographic correctness is enforced by
2815
+ * `crypto.subtle.importKey` at use.
2816
+ * @public
2817
+ */
2818
+ declare const keystoreAsymmetricEntryJson: Converter<IKeyStoreAsymmetricEntryJson>;
2819
+
2820
+ /**
2821
+ * Discriminator for asymmetric secret types stored in the vault.
2822
+ * - `'asymmetric-keypair'`: A public/private key pair. The public key is held in
2823
+ * the vault as a JWK; the private key lives in the supplied
2824
+ * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider.
2825
+ * @public
2826
+ */
2827
+ declare type KeyStoreAsymmetricSecretType = 'asymmetric-keypair';
2828
+
2829
+ /**
2830
+ * Converter for {@link CryptoUtils.KeyStore.KeyStoreAsymmetricSecretType | asymmetric secret type} discriminator.
2831
+ * Accepts only `'asymmetric-keypair'`.
2832
+ * @public
2833
+ */
2834
+ declare const keystoreAsymmetricSecretType: Converter<KeyStoreAsymmetricSecretType>;
2835
+
2350
2836
  /**
2351
2837
  * Converter for {@link CryptoUtils.KeyStore.IKeyStoreFile | encrypted key store file}.
2352
2838
  * @public
@@ -2372,25 +2858,59 @@ declare const keystoreFormat: Converter<KeyStoreFormat>;
2372
2858
  declare type KeyStoreLockState = 'locked' | 'unlocked';
2373
2859
 
2374
2860
  /**
2375
- * Converter for {@link CryptoUtils.KeyStore.IKeyStoreSecretEntryJson | key store secret entry} in JSON format.
2376
- * The `type` field is optional for backwards compatibility — missing means `'encryption-key'`.
2861
+ * Discriminated-union converter for any {@link CryptoUtils.KeyStore.IKeyStoreEntryJson | key store entry} in JSON form.
2862
+ * Routes by the `type` field: `'asymmetric-keypair'` is parsed by
2863
+ * {@link CryptoUtils.KeyStore.Converters.keystoreAsymmetricEntryJson | keystoreAsymmetricEntryJson},
2864
+ * anything else (including a missing `type` field for backwards compatibility) by
2865
+ * {@link CryptoUtils.KeyStore.Converters.keystoreSymmetricEntryJson | keystoreSymmetricEntryJson}.
2866
+ * @public
2867
+ */
2868
+ declare const keystoreSecretEntryJson: Converter<IKeyStoreEntryJson>;
2869
+
2870
+ /**
2871
+ * Discriminator for any secret type stored in the vault.
2872
+ * @public
2873
+ */
2874
+ declare type KeyStoreSecretType = KeyStoreSymmetricSecretType | KeyStoreAsymmetricSecretType;
2875
+
2876
+ /**
2877
+ * Converter for {@link CryptoUtils.KeyStore.KeyStoreSecretType | any key store secret type} discriminator.
2878
+ * Accepts both symmetric and asymmetric type values.
2879
+ * @public
2880
+ */
2881
+ declare const keystoreSecretType: Converter<KeyStoreSecretType>;
2882
+
2883
+ /**
2884
+ * Converter for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson | symmetric secret entry} in JSON form.
2885
+ *
2886
+ * @remarks
2887
+ * Backwards compatibility with vaults written before asymmetric-keypair
2888
+ * support: those entries may lack the `type` discriminator on the wire. To
2889
+ * keep the model type honest (`type` is required on
2890
+ * {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson}, see its docs),
2891
+ * we declare `type` in `optionalFields` so the inner `Converters.object` will
2892
+ * accept input without it, then `.map()` injects the default
2893
+ * `'encryption-key'` when missing. The output therefore always carries the
2894
+ * discriminator and downstream code never sees the legacy missing-type form.
2895
+ *
2377
2896
  * @public
2378
2897
  */
2379
- declare const keystoreSecretEntryJson: Converter<IKeyStoreSecretEntryJson>;
2898
+ declare const keystoreSymmetricEntryJson: Converter<IKeyStoreSymmetricEntryJson>;
2380
2899
 
2381
2900
  /**
2382
- * Discriminator for secret types stored in the vault.
2901
+ * Discriminator for symmetric secret types stored in the vault.
2383
2902
  * - `'encryption-key'`: A 32-byte AES-256 encryption key.
2384
2903
  * - `'api-key'`: An arbitrary-length API key string (UTF-8 encoded).
2385
2904
  * @public
2386
2905
  */
2387
- declare type KeyStoreSecretType = 'encryption-key' | 'api-key';
2906
+ declare type KeyStoreSymmetricSecretType = 'encryption-key' | 'api-key';
2388
2907
 
2389
2908
  /**
2390
- * Converter for {@link CryptoUtils.KeyStore.KeyStoreSecretType | key store secret type} discriminator.
2909
+ * Converter for {@link CryptoUtils.KeyStore.KeyStoreSymmetricSecretType | symmetric secret type} discriminator.
2910
+ * Accepts only `'encryption-key'` and `'api-key'`.
2391
2911
  * @public
2392
2912
  */
2393
- declare const keystoreSecretType: Converter<KeyStoreSecretType>;
2913
+ declare const keystoreSymmetricSecretType: Converter<KeyStoreSymmetricSecretType>;
2394
2914
 
2395
2915
  /**
2396
2916
  * Converter for {@link CryptoUtils.KeyStore.IKeyStoreVaultContents | key store vault contents} (decrypted state).
@@ -2604,6 +3124,31 @@ declare class NodeCryptoProvider implements ICryptoProvider {
2604
3124
  * @returns Success with decoded bytes, or Failure if invalid base64
2605
3125
  */
2606
3126
  fromBase64(base64: string): Result<Uint8Array>;
3127
+ /**
3128
+ * Generates a new asymmetric keypair using Node's WebCrypto.
3129
+ * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} to use.
3130
+ * @param extractable - Whether the resulting keys may be exported.
3131
+ * @returns `Success` with the generated `CryptoKeyPair`, or `Failure` with an error.
3132
+ */
3133
+ generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;
3134
+ /**
3135
+ * Exports a public `CryptoKey` as a JSON Web Key.
3136
+ * @remarks
3137
+ * Rejects non-public keys at runtime. WebCrypto's `exportKey('jwk', ...)`
3138
+ * does not enforce public-vs-private; without this guard a caller that
3139
+ * passed an extractable private key would receive its private fields
3140
+ * (`d`, `p`, `q`, ...) as JWK, defeating the method's name.
3141
+ * @param publicKey - Extractable public key to export.
3142
+ * @returns `Success` with the JWK, or `Failure` if not a public key or if export fails.
3143
+ */
3144
+ exportPublicKeyJwk(publicKey: CryptoKey): Promise<Result<JsonWebKey>>;
3145
+ /**
3146
+ * Imports a public-key JWK as a `CryptoKey` for the requested algorithm.
3147
+ * @param jwk - The JSON Web Key produced by a prior export.
3148
+ * @param algorithm - The algorithm the key was generated for.
3149
+ * @returns `Success` with the imported public `CryptoKey`, or `Failure` with an error.
3150
+ */
3151
+ importPublicKeyJwk(jwk: JsonWebKey, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;
2607
3152
  }
2608
3153
 
2609
3154
  /**