@fgv/ts-extras 5.1.0-40 → 5.1.0-42

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 (59) hide show
  1. package/dist/packlets/crypto-utils/hpkeProvider.js +34 -12
  2. package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -1
  3. package/dist/packlets/crypto-utils/index.browser.js +4 -1
  4. package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
  5. package/dist/packlets/crypto-utils/index.js +3 -1
  6. package/dist/packlets/crypto-utils/index.js.map +1 -1
  7. package/dist/packlets/crypto-utils/keystore/converters.js +3 -4
  8. package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -1
  9. package/dist/packlets/crypto-utils/keystore/keyStore.js +155 -16
  10. package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  11. package/dist/packlets/crypto-utils/keystore/model.js +8 -3
  12. package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
  13. package/dist/packlets/crypto-utils/model.js.map +1 -1
  14. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +77 -0
  15. package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
  16. package/dist/packlets/crypto-utils/seedDerivedKeyPair.js +107 -0
  17. package/dist/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
  18. package/dist/packlets/crypto-utils/spkiHelpers.js +31 -0
  19. package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -1
  20. package/dist/ts-extras.d.ts +371 -7
  21. package/lib/packlets/crypto-utils/hpkeProvider.d.ts +11 -3
  22. package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -1
  23. package/lib/packlets/crypto-utils/hpkeProvider.js +34 -12
  24. package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -1
  25. package/lib/packlets/crypto-utils/index.browser.d.ts +2 -1
  26. package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
  27. package/lib/packlets/crypto-utils/index.browser.js +6 -1
  28. package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
  29. package/lib/packlets/crypto-utils/index.d.ts +2 -1
  30. package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
  31. package/lib/packlets/crypto-utils/index.js +5 -1
  32. package/lib/packlets/crypto-utils/index.js.map +1 -1
  33. package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -1
  34. package/lib/packlets/crypto-utils/keystore/converters.js +2 -3
  35. package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -1
  36. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +68 -3
  37. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
  38. package/lib/packlets/crypto-utils/keystore/keyStore.js +154 -15
  39. package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  40. package/lib/packlets/crypto-utils/keystore/model.d.ts +75 -2
  41. package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
  42. package/lib/packlets/crypto-utils/keystore/model.js +9 -4
  43. package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
  44. package/lib/packlets/crypto-utils/model.d.ts +125 -0
  45. package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
  46. package/lib/packlets/crypto-utils/model.js.map +1 -1
  47. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +33 -1
  48. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -1
  49. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +77 -0
  50. package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
  51. package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts +37 -0
  52. package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts.map +1 -0
  53. package/lib/packlets/crypto-utils/seedDerivedKeyPair.js +110 -0
  54. package/lib/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
  55. package/lib/packlets/crypto-utils/spkiHelpers.d.ts +15 -0
  56. package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -1
  57. package/lib/packlets/crypto-utils/spkiHelpers.js +32 -0
  58. package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -1
  59. package/package.json +7 -7
@@ -381,6 +381,12 @@ declare const allKeyPairAlgorithms: ReadonlyArray<KeyPairAlgorithm>;
381
381
  */
382
382
  declare const allKeyStoreAsymmetricSecretTypes: ReadonlyArray<KeyStoreAsymmetricSecretType>;
383
383
 
384
+ /**
385
+ * All recognized key store format versions (readable by the current library).
386
+ * @public
387
+ */
388
+ declare const allKeyStoreFormats: ReadonlyArray<KeyStoreFormat>;
389
+
384
390
  /**
385
391
  * All valid key store secret types.
386
392
  * @public
@@ -714,6 +720,7 @@ declare namespace CryptoUtils {
714
720
  IDirectEncryptionProviderParams,
715
721
  IKeyPairAlgorithmParams,
716
722
  keyPairAlgorithmParams,
723
+ deriveKeyPairFromSeed,
717
724
  NodeCryptoProvider,
718
725
  nodeCryptoProvider,
719
726
  createEncryptedFile,
@@ -730,6 +737,7 @@ declare namespace CryptoUtils {
730
737
  MultibaseSpkiPublicKeyRegExp,
731
738
  multibaseBase64UrlDecode,
732
739
  multibaseBase64UrlEncode,
740
+ spkiToRawX25519,
733
741
  HpkeProvider,
734
742
  IHpkeSealResult,
735
743
  isEncryptedFile,
@@ -738,7 +746,9 @@ declare namespace CryptoUtils {
738
746
  EncryptedFileFormat,
739
747
  INamedSecret,
740
748
  IEncryptionResult,
749
+ IEncryptBytesResult,
741
750
  KeyPairAlgorithm,
751
+ SeedDerivableAlgorithm,
742
752
  IWrapBytesOptions,
743
753
  IWrappedBytes,
744
754
  allKeyPairAlgorithms,
@@ -833,6 +843,41 @@ declare const DEFAULT_RANGEOF_FORMATS: RangeOfFormats;
833
843
  */
834
844
  declare const DEFAULT_SECRET_ITERATIONS: number;
835
845
 
846
+ /**
847
+ * Derives an asymmetric keypair *deterministically* from a fixed secret seed
848
+ * using `globalThis.crypto.subtle`, shared by the Node and browser
849
+ * {@link CryptoUtils.ICryptoProvider} implementations (both speak the same
850
+ * WebCrypto API). See
851
+ * {@link CryptoUtils.ICryptoProvider.importKeyPairFromSeed | importKeyPairFromSeed}
852
+ * for the public contract.
853
+ *
854
+ * For `'ed25519'`: wraps the 32-byte seed in the RFC 8410 PKCS#8 envelope, then
855
+ * runs a transient-extractable-then-derive dance so the public key is
856
+ * recoverable even when the caller requests a non-extractable private key:
857
+ *
858
+ * 1. Import the PKCS#8 as a *transient* extractable private key.
859
+ * 2. Export it to JWK to read the deterministic public coordinate `x`.
860
+ * 3. Import `{ kty: 'OKP', crv: 'Ed25519', x }` as the returned public key.
861
+ * 4. For the returned private key: reuse the transient key when `extractable`
862
+ * is `true`; otherwise re-import the same PKCS#8 as a non-extractable key.
863
+ * The transient extractable key is never returned when `extractable` is
864
+ * `false`, so seed material cannot escape through it.
865
+ *
866
+ * The seed bytes are copied into a freshly allocated PKCS#8 buffer, so the
867
+ * `BufferSource` handed to WebCrypto is always backed by a plain `ArrayBuffer`
868
+ * (side-stepping the Node-20 `SharedArrayBuffer`-view rejection) and the
869
+ * caller's `seed` is never mutated.
870
+ *
871
+ * @param algorithm - The seed-derivable algorithm; only `'ed25519'` is
872
+ * supported today. Any other value fails loudly rather than being mis-handled.
873
+ * @param seed - The 32-byte secret seed. Any other length fails loudly, before
874
+ * any WebCrypto call.
875
+ * @param extractable - Whether the returned private key may be exported.
876
+ * @returns `Success` with the derived `CryptoKeyPair`, or `Failure` with error context.
877
+ * @public
878
+ */
879
+ declare function deriveKeyPairFromSeed(algorithm: SeedDerivableAlgorithm, seed: Uint8Array, extractable: boolean): Promise<Result<CryptoKeyPair>>;
880
+
836
881
  /**
837
882
  * An {@link IEncryptionProvider} that uses a pre-supplied key and crypto provider.
838
883
  *
@@ -1435,15 +1480,23 @@ declare class HpkeProvider {
1435
1480
  *
1436
1481
  * @param recipientPrivateKey - Recipient's X25519 private `CryptoKey`
1437
1482
  * (`algorithm.name === 'X25519'`, `type === 'private'`, `usages` includes `'deriveBits'`).
1438
- * **Must be extractable** (`extractable: true`) the recipient's public key bytes
1439
- * are recovered from the JWK `x` field during Decap.
1483
+ * **Must be extractable** (`extractable: true`) only when `recipientPublicKey` is not
1484
+ * supplied — the recipient's public key bytes are then recovered from the JWK `x` field
1485
+ * during Decap. When `recipientPublicKey` is supplied, `recipientPrivateKey` may be
1486
+ * non-extractable.
1440
1487
  * @param info - Context-binding bytes. Must exactly match `info` from `sealBase`.
1441
1488
  * @param aad - Must exactly match `aad` from `sealBase`.
1442
1489
  * @param enc - The encapsulated key from `sealBase` — exactly 32 bytes.
1443
1490
  * @param ciphertext - The ciphertext from `sealBase` — `plaintext.length + 16` bytes.
1491
+ * @param recipientPublicKey - Optional raw 32-byte X25519 public key matching
1492
+ * `recipientPrivateKey` (`pkRm` in RFC 9180 §4.1). Public material — supplying it lets
1493
+ * Decap build `kem_context` without exporting `recipientPrivateKey` to JWK, so
1494
+ * `recipientPrivateKey` no longer needs to be extractable. A mismatched value only breaks
1495
+ * the caller's own decryption (AEAD authentication fails) — it cannot be used to attack
1496
+ * another party's ciphertext.
1444
1497
  * @returns `Success` with decrypted plaintext bytes, or `Failure` with error context.
1445
1498
  */
1446
- openBase(recipientPrivateKey: CryptoKey, info: Uint8Array, aad: Uint8Array, enc: Uint8Array, ciphertext: Uint8Array): Promise<Result<Uint8Array>>;
1499
+ openBase(recipientPrivateKey: CryptoKey, info: Uint8Array, aad: Uint8Array, enc: Uint8Array, ciphertext: Uint8Array, recipientPublicKey?: Uint8Array): Promise<Result<Uint8Array>>;
1447
1500
  /**
1448
1501
  * HKDF-SHA256 key derivation (RFC 5869). Extract-then-Expand using SHA-256.
1449
1502
  *
@@ -1508,6 +1561,28 @@ declare interface IAddKeyPairOptions {
1508
1561
  * downgrading.
1509
1562
  */
1510
1563
  readonly extractable?: boolean;
1564
+ /**
1565
+ * Opt in to private-key escrow. When `true`, an encrypted copy of the
1566
+ * private key (as a JWK) is carried inside the vault entry
1567
+ * (`escrowedPrivateKeyJwk`), enabling cross-device recovery from the vault
1568
+ * file plus the master password alone via
1569
+ * `getKeyPair(name, { rehydrate: true })`.
1570
+ *
1571
+ * Orthogonal to `extractable`: the escrow copy is always captured (the
1572
+ * transient keypair is generated extractable so it can be exported to JWK),
1573
+ * while the LIVE stored key's extractability still follows the normal rule
1574
+ * (`extractable` override, else the backend default). So escrow does not
1575
+ * change how extractable the day-to-day key is — only whether a recovery
1576
+ * copy is retained in the vault.
1577
+ *
1578
+ * @defaultValue false — no escrow copy is written (today's behavior).
1579
+ *
1580
+ * SECURITY: escrow makes the master password the sole gate protecting a
1581
+ * private key that would otherwise be unrecoverable from the vault. Use a
1582
+ * strong KDF (Argon2id-derived or high-iteration PBKDF2) for escrow-bearing
1583
+ * stores. See the {@link CryptoUtils.KeyStore.KeyStore} class docs.
1584
+ */
1585
+ readonly escrow?: boolean;
1511
1586
  }
1512
1587
 
1513
1588
  /**
@@ -2681,6 +2756,68 @@ declare interface ICryptoProvider {
2681
2756
  * @returns Success with decrypted UTF-8 string, or Failure with error
2682
2757
  */
2683
2758
  decrypt(encryptedData: Uint8Array, key: Uint8Array, iv: Uint8Array, authTag: Uint8Array): Promise<Result<string>>;
2759
+ /**
2760
+ * Encrypts raw bytes using AES-256-GCM with a **caller-supplied nonce** and
2761
+ * optional additional authenticated data (AAD).
2762
+ *
2763
+ * This is the raw-byte sibling of {@link CryptoUtils.ICryptoProvider.encrypt | encrypt}.
2764
+ * Unlike `encrypt`, it takes and returns `Uint8Array` (no UTF-8 coding), the
2765
+ * caller owns the nonce (rather than the provider generating one), and it
2766
+ * binds optional `aad` into the GCM authentication. Use it when you need to
2767
+ * bind context (e.g. an actor id, key version, or row kind) into the
2768
+ * authentication so a wrapped secret cannot be replayed across
2769
+ * users/versions/kinds, or when you manage nonces yourself.
2770
+ *
2771
+ * @remarks
2772
+ * **⚠️ NONCE UNIQUENESS IS THE CALLER'S RESPONSIBILITY AND IS CRITICAL.**
2773
+ * Because the caller supplies the nonce, this primitive cannot guarantee
2774
+ * uniqueness. Reusing a `(key, nonce)` pair for two different messages is
2775
+ * **catastrophic** for AES-GCM: it breaks confidentiality (the XOR of the two
2776
+ * plaintexts leaks) AND authentication (the GCM authentication key can be
2777
+ * recovered, letting an attacker forge tags for arbitrary messages under that
2778
+ * key). The caller MUST use a unique nonce for every message encrypted under a
2779
+ * given key — draw it from {@link CryptoUtils.ICryptoProvider.generateRandomBytes | generateRandomBytes(12)}
2780
+ * (12 random bytes has negligible collision probability well within a single
2781
+ * key's message budget) or from a strictly-increasing counter. Never hardcode
2782
+ * a nonce and never reuse one.
2783
+ *
2784
+ * @param key - 32-byte AES-256 key. Wrong lengths fail with error context.
2785
+ * @param nonce - 12-byte (96-bit) GCM nonce. MUST be unique per message under
2786
+ * `key` (see the nonce-uniqueness warning above). Wrong lengths fail with
2787
+ * error context.
2788
+ * @param plaintext - The bytes to encrypt. Empty plaintext is permitted and
2789
+ * round-trips (GCM produces a valid tag over zero-length plaintext).
2790
+ * @param aad - Optional additional authenticated data bound into the GCM tag
2791
+ * but NOT encrypted. If provided at encrypt time, the identical bytes must be
2792
+ * supplied to `decryptBytes` or decryption fails authentication. Absent means
2793
+ * no AAD.
2794
+ * @returns `Success` with the {@link CryptoUtils.IEncryptBytesResult | ciphertext and 16-byte auth tag},
2795
+ * or `Failure` with error context.
2796
+ */
2797
+ encryptBytes(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Promise<Result<IEncryptBytesResult>>;
2798
+ /**
2799
+ * Decrypts raw bytes produced by
2800
+ * {@link CryptoUtils.ICryptoProvider.encryptBytes | encryptBytes} using
2801
+ * AES-256-GCM. The inverse of `encryptBytes`: the `ciphertext` and `authTag`
2802
+ * from an `encryptBytes` result, together with the same `key`, `nonce`, and
2803
+ * `aad`, recover the original plaintext.
2804
+ *
2805
+ * Fails (never throws) on any authentication failure: a tampered ciphertext
2806
+ * or tag, the wrong key or nonce, or an `aad` that differs from the one used
2807
+ * at encrypt time. AES-GCM authentication is fail-closed — a mismatched `aad`
2808
+ * fails exactly as a tampered ciphertext does.
2809
+ *
2810
+ * @param key - 32-byte AES-256 key (the same key used to encrypt).
2811
+ * @param nonce - 12-byte (96-bit) GCM nonce (the same nonce used to encrypt).
2812
+ * @param ciphertext - The ciphertext from the `encryptBytes` result.
2813
+ * @param authTag - The 16-byte (128-bit) GCM authentication tag from the
2814
+ * `encryptBytes` result.
2815
+ * @param aad - The identical additional authenticated data supplied at encrypt
2816
+ * time (or absent if none was supplied). A mismatch fails authentication.
2817
+ * @returns `Success` with the decrypted plaintext bytes, or `Failure` with
2818
+ * error context (including all authentication failures).
2819
+ */
2820
+ decryptBytes(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, authTag: Uint8Array, aad?: Uint8Array): Promise<Result<Uint8Array>>;
2684
2821
  /**
2685
2822
  * Generates a random 32-byte key suitable for AES-256.
2686
2823
  * @returns Success with generated key, or Failure with error
@@ -2737,6 +2874,28 @@ declare interface ICryptoProvider {
2737
2874
  * @returns Success with the generated `CryptoKeyPair`, or Failure with error context.
2738
2875
  */
2739
2876
  generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;
2877
+ /**
2878
+ * Derives an asymmetric keypair *deterministically* from a fixed secret seed.
2879
+ * The same `seed` always yields the same keypair on every runtime, so this is
2880
+ * the primitive to use when a keypair must be reconstructable from stored seed
2881
+ * material (key escrow, HD-style derivation, deterministic test vectors) rather
2882
+ * than freshly sampled by {@link CryptoUtils.ICryptoProvider.generateKeyPair | generateKeyPair}.
2883
+ *
2884
+ * For `'ed25519'` the private key *is* its 32-byte seed and the public key is a
2885
+ * deterministic function of that seed (RFC 8032), so the returned public key is
2886
+ * recovered even when the caller requests a non-extractable private key. The
2887
+ * transient extractable key used internally to recover the public half is never
2888
+ * returned or logged when `extractable` is `false`.
2889
+ * @param algorithm - The {@link CryptoUtils.SeedDerivableAlgorithm | seed-derivable algorithm}.
2890
+ * Only `'ed25519'` is supported today; any other value fails loudly with context.
2891
+ * @param seed - The secret seed. For `'ed25519'` it must be exactly 32 bytes;
2892
+ * any other length fails loudly, before any WebCrypto call. The bytes are copied,
2893
+ * not retained or mutated.
2894
+ * @param extractable - Whether the returned private key may be exported. The
2895
+ * returned public key is identical for a given seed regardless of this flag.
2896
+ * @returns Success with the derived `CryptoKeyPair`, or Failure with error context.
2897
+ */
2898
+ importKeyPairFromSeed(algorithm: SeedDerivableAlgorithm, seed: Uint8Array, extractable: boolean): Promise<Result<CryptoKeyPair>>;
2740
2899
  /**
2741
2900
  * Exports the public half of a keypair as a JSON Web Key.
2742
2901
  * @param publicKey - The public `CryptoKey` to export. Must be an `extractable`
@@ -2905,6 +3064,34 @@ declare interface IDirectEncryptionProviderParams {
2905
3064
  readonly boundSecretName?: string;
2906
3065
  }
2907
3066
 
3067
+ /**
3068
+ * Result of a raw-byte AES-256-GCM encryption via
3069
+ * {@link CryptoUtils.ICryptoProvider.encryptBytes | encryptBytes}. The
3070
+ * authentication tag is returned separately from the ciphertext, mirroring the
3071
+ * separated-tag convention of {@link CryptoUtils.IEncryptionResult}. The exact
3072
+ * byte layout is:
3073
+ * - `ciphertext`: the AES-GCM ciphertext, byte-for-byte the same length as the
3074
+ * input plaintext (GCM is a stream cipher — no padding). Empty plaintext
3075
+ * yields an empty `ciphertext`.
3076
+ * - `authTag`: the 16-byte (128-bit) GCM authentication tag, computed over the
3077
+ * ciphertext, the nonce, and any `aad`.
3078
+ *
3079
+ * The caller persists both fields alongside the (caller-owned) nonce and feeds
3080
+ * them back to {@link CryptoUtils.ICryptoProvider.decryptBytes | decryptBytes}.
3081
+ * @public
3082
+ */
3083
+ declare interface IEncryptBytesResult {
3084
+ /**
3085
+ * The AES-256-GCM ciphertext. Same length as the input plaintext (no tag
3086
+ * appended — the tag is carried separately in the `authTag` field).
3087
+ */
3088
+ readonly ciphertext: Uint8Array;
3089
+ /**
3090
+ * The 16-byte (128-bit) GCM authentication tag.
3091
+ */
3092
+ readonly authTag: Uint8Array;
3093
+ }
3094
+
2908
3095
  /**
2909
3096
  * Generic encrypted file format.
2910
3097
  * This is the JSON structure stored in encrypted files.
@@ -3278,6 +3465,26 @@ declare interface IGenerateJsonCompletionResult<T> {
3278
3465
  readonly response: IAiCompletionResponse;
3279
3466
  }
3280
3467
 
3468
+ /**
3469
+ * Options for retrieving an asymmetric keypair via {@link CryptoUtils.KeyStore.KeyStore.getKeyPair}.
3470
+ * @public
3471
+ */
3472
+ declare interface IGetKeyPairOptions {
3473
+ /**
3474
+ * Opt in to escrow rehydration. When `true` AND no private-key blob exists
3475
+ * under the entry's storage `id` AND the entry carries an
3476
+ * `escrowedPrivateKeyJwk`, the escrowed JWK is imported (non-extractable
3477
+ * where the backend supports it) and stored under the entry's `id` before
3478
+ * being returned — filling a gap on a fresh device from the recovered vault.
3479
+ *
3480
+ * Fill-a-gap only: an existing storage blob is never overwritten. When a
3481
+ * blob is present it is loaded as usual and the escrow copy is ignored.
3482
+ *
3483
+ * @defaultValue false — today's behavior: load from storage or fail.
3484
+ */
3485
+ readonly rehydrate?: boolean;
3486
+ }
3487
+
3281
3488
  /**
3282
3489
  * Provider-specific config for gpt-image-1.
3283
3490
  * @public
@@ -3453,6 +3660,21 @@ declare interface IKeyStoreAsymmetricEntry {
3453
3660
  * The public key as a JSON Web Key.
3454
3661
  */
3455
3662
  readonly publicKeyJwk: JsonWebKey;
3663
+ /**
3664
+ * Optional escrowed copy of the private key, as a JSON Web Key. Present only
3665
+ * when the entry was created with `addKeyPair(name, { escrow: true })`.
3666
+ *
3667
+ * This is an opt-in cross-device recovery affordance: the private key is
3668
+ * carried inside the vault's AES-GCM ciphertext (same custody class as
3669
+ * `publicKeyJwk`), so a recovered vault plus the master password can
3670
+ * reconstitute the signing/decryption identity on a fresh device via
3671
+ * `getKeyPair(name, { rehydrate: true })`.
3672
+ *
3673
+ * SECURITY: when present, the master password becomes the sole gate
3674
+ * protecting this private key. Use a strong KDF for escrow-bearing stores.
3675
+ * See the {@link CryptoUtils.KeyStore.KeyStore} class docs.
3676
+ */
3677
+ readonly escrowedPrivateKeyJwk?: JsonWebKey;
3456
3678
  /**
3457
3679
  * Optional description for this entry.
3458
3680
  */
@@ -3491,6 +3713,12 @@ declare interface IKeyStoreAsymmetricEntryJson {
3491
3713
  * The public key as a JSON Web Key.
3492
3714
  */
3493
3715
  readonly publicKeyJwk: JsonWebKey;
3716
+ /**
3717
+ * Optional escrowed copy of the private key, as a JSON Web Key. Present only
3718
+ * on `'keystore-v2'` vaults whose entry was created with escrow enabled. A
3719
+ * v1 vault omits the field; a v2 reader treats its absence as "no escrow".
3720
+ */
3721
+ readonly escrowedPrivateKeyJwk?: JsonWebKey;
3494
3722
  /**
3495
3723
  * Optional description.
3496
3724
  */
@@ -4604,6 +4832,7 @@ declare namespace KeyStore {
4604
4832
  allKeyPairAlgorithms,
4605
4833
  KeyPairAlgorithm,
4606
4834
  KeyStoreFormat,
4835
+ allKeyStoreFormats,
4607
4836
  KEYSTORE_FORMAT,
4608
4837
  DEFAULT_KEYSTORE_ITERATIONS,
4609
4838
  MIN_SALT_LENGTH,
@@ -4635,6 +4864,7 @@ declare namespace KeyStore {
4635
4864
  IAddSecretFromPasswordResult,
4636
4865
  IAddSecretFromPasswordArgon2idOptions,
4637
4866
  IAddKeyPairOptions,
4867
+ IGetKeyPairOptions,
4638
4868
  IAddKeyPairResult,
4639
4869
  IRemoveSecretResult,
4640
4870
  IPrivateKeyStorage
@@ -4670,6 +4900,20 @@ declare namespace KeyStore {
4670
4900
  * const encryptionConfig = keystore2.getEncryptionConfig().orThrow();
4671
4901
  * ```
4672
4902
  *
4903
+ * @remarks
4904
+ * SECURITY — private-key escrow. By default an asymmetric keypair's private
4905
+ * key lives only in the per-device {@link CryptoUtils.KeyStore.IPrivateKeyStorage}
4906
+ * backend; the vault carries only the public JWK, so a vault recovered on a new
4907
+ * device reconstitutes with no private keys (a lost device is a lost identity).
4908
+ * `addKeyPair(name, { escrow: true })` opts into carrying an encrypted private-key
4909
+ * copy inside the vault ciphertext, so the vault file plus the master password can
4910
+ * recover the identity on a fresh device via `getKeyPair(name, { rehydrate: true })`.
4911
+ *
4912
+ * When escrow is used, **the master password becomes the sole gate** protecting a
4913
+ * private signing/decryption key that was previously unrecoverable from the vault.
4914
+ * Configure escrow-bearing stores with a strong KDF — an Argon2id-derived master
4915
+ * key or a high-iteration PBKDF2 count — and treat the vault file accordingly.
4916
+ *
4673
4917
  * @public
4674
4918
  */
4675
4919
  declare class KeyStore_2 implements IEncryptionProvider {
@@ -4776,6 +5020,12 @@ declare class KeyStore_2 implements IEncryptionProvider {
4776
5020
  * Gets a secret by name. Returns the {@link CryptoUtils.KeyStore.IKeyStoreEntry | discriminated union}
4777
5021
  * — callers must check `entry.type` before accessing `key`/`id` since asymmetric
4778
5022
  * entries carry no raw key material.
5023
+ *
5024
+ * SECURITY: for an escrow-enabled asymmetric-keypair entry the returned object
5025
+ * carries `escrowedPrivateKeyJwk` in cleartext (same custody class as
5026
+ * `publicKeyJwk`, gated by the same unlock) — do not log or serialize a
5027
+ * `getSecret()` result casually.
5028
+ *
4779
5029
  * @param name - Name of the secret
4780
5030
  * @returns Success with secret entry, Failure if not found or locked
4781
5031
  * @public
@@ -4962,12 +5212,23 @@ declare class KeyStore_2 implements IEncryptionProvider {
4962
5212
  * the keystore never caches private `CryptoKey` references between calls.
4963
5213
  * The public key is re-imported from the vault's JWK so callers always
4964
5214
  * receive a `CryptoKey` rather than the JWK form.
5215
+ *
5216
+ * With `options.rehydrate: true`, if the storage backend holds no blob for
5217
+ * the entry's `id` and the entry carries an `escrowedPrivateKeyJwk` (see
5218
+ * `addKeyPair(name, { escrow: true })`), the escrowed JWK is imported and
5219
+ * stored under the entry's `id` before being returned — recovering the
5220
+ * private key on a fresh device from the vault plus master password. This is
5221
+ * fill-a-gap only: an existing storage blob is never overwritten.
5222
+ *
4965
5223
  * @param name - Name of the entry
5224
+ * @param options - Optional {@link CryptoUtils.KeyStore.IGetKeyPairOptions}
5225
+ * (currently the `rehydrate` escrow-recovery flag).
4966
5226
  * @returns Success with `{ publicKey, privateKey }`, Failure if not found,
4967
- * locked, wrong type, no provider, or storage load failed.
5227
+ * locked, wrong type, no provider, or storage load (and any escrow
5228
+ * rehydration) failed.
4968
5229
  * @public
4969
5230
  */
4970
- getKeyPair(name: string): Promise<Result<{
5231
+ getKeyPair(name: string, options?: IGetKeyPairOptions): Promise<Result<{
4971
5232
  publicKey: CryptoKey;
4972
5233
  privateKey: CryptoKey;
4973
5234
  }>>;
@@ -5053,6 +5314,40 @@ declare class KeyStore_2 implements IEncryptionProvider {
5053
5314
  * @returns A warning string if storage cleanup failed, otherwise undefined.
5054
5315
  */
5055
5316
  private _releaseEntryResources;
5317
+ /**
5318
+ * Exports the transient extractable private `CryptoKey` to a JWK for escrow,
5319
+ * via WebCrypto's `exportKey('jwk', ...)` (cross-runtime through
5320
+ * `globalThis.crypto.subtle`). The result is carried in the vault entry — the
5321
+ * same custody class as the public JWK — and is never returned from a public
5322
+ * method nor logged.
5323
+ */
5324
+ private _exportPrivateKeyJwk;
5325
+ /**
5326
+ * Re-imports an escrowed private-key JWK as a `CryptoKey` for `algorithm`
5327
+ * with the requested extractability. The WebCrypto JWK-import descriptor is
5328
+ * shared between the public and private halves for every supported algorithm,
5329
+ * so `IKeyPairAlgorithmParams.importPublicKey` is reused; the private/public
5330
+ * distinction is carried by the requested usages (see
5331
+ * {@link KeyStore._privateKeyUsagesFor}). Cross-runtime through
5332
+ * `globalThis.crypto.subtle`.
5333
+ */
5334
+ private _importEscrowedPrivateKey;
5335
+ /**
5336
+ * Loads the private key for `entry` from storage, or — when
5337
+ * `options.rehydrate` is set, storage holds no blob, and the entry carries an
5338
+ * escrowed JWK — imports the escrowed key, persists it under the entry's `id`
5339
+ * (fill-a-gap; never overwrites an existing blob), and returns it.
5340
+ */
5341
+ private _loadOrRehydratePrivateKey;
5342
+ /**
5343
+ * Computes the key usages to request when importing an escrowed private JWK.
5344
+ * Mirrors `EncryptedFilePrivateKeyStorage`: intersect the algorithm's private
5345
+ * usages (its keypair usages minus the public-only ones) with the JWK's
5346
+ * recorded `key_ops` so we request exactly the operations the stored key
5347
+ * supports; fall back to the algorithm's private usages when `key_ops` is
5348
+ * absent.
5349
+ */
5350
+ private static _privateKeyUsagesFor;
5056
5351
  /**
5057
5352
  * Constant-time byte comparison. Returns false immediately for length
5058
5353
  * mismatch (length is not secret); for equal-length inputs, walks the full
@@ -5069,7 +5364,7 @@ declare class KeyStore_2 implements IEncryptionProvider {
5069
5364
  }
5070
5365
 
5071
5366
  /**
5072
- * Current format version constant.
5367
+ * Current format version constant. New vaults are written as `'keystore-v2'`.
5073
5368
  * @public
5074
5369
  */
5075
5370
  declare const KEYSTORE_FORMAT: KeyStoreFormat;
@@ -5107,9 +5402,15 @@ declare const keystoreFile: Converter<IKeyStoreFile>;
5107
5402
 
5108
5403
  /**
5109
5404
  * Format version for key store files.
5405
+ *
5406
+ * - `'keystore-v1'`: the original format (no private-key escrow).
5407
+ * - `'keystore-v2'`: adds the optional `escrowedPrivateKeyJwk` field on
5408
+ * asymmetric-keypair entries. A strict superset of v1 — the field is
5409
+ * optional, so a v2 reader opens a v1 vault with no special-casing, and a
5410
+ * v1 vault opened and re-saved is silently upgraded to v2.
5110
5411
  * @public
5111
5412
  */
5112
- declare type KeyStoreFormat = 'keystore-v1';
5413
+ declare type KeyStoreFormat = 'keystore-v1' | 'keystore-v2';
5113
5414
 
5114
5415
  /**
5115
5416
  * Converter for {@link CryptoUtils.KeyStore.KeyStoreFormat | key store format} version.
@@ -5447,6 +5748,30 @@ declare class NodeCryptoProvider implements ICryptoProvider {
5447
5748
  * @returns `Success` with decrypted UTF-8 string, or `Failure` with an error.
5448
5749
  */
5449
5750
  decrypt(encryptedData: Uint8Array, key: Uint8Array, iv: Uint8Array, authTag: Uint8Array): Promise<Result<string>>;
5751
+ /**
5752
+ * Encrypts raw bytes using AES-256-GCM with a caller-supplied nonce and
5753
+ * optional AAD. See {@link CryptoUtils.ICryptoProvider.encryptBytes | ICryptoProvider.encryptBytes}
5754
+ * — in particular the caller's responsibility to use a unique `nonce` per
5755
+ * message under a given `key`.
5756
+ * @param key - 32-byte AES-256 key.
5757
+ * @param nonce - 12-byte GCM nonce (must be unique per message under `key`).
5758
+ * @param plaintext - The bytes to encrypt (empty permitted).
5759
+ * @param aad - Optional additional authenticated data bound into the tag.
5760
+ * @returns `Success` with the ciphertext and 16-byte auth tag, or `Failure` with an error.
5761
+ */
5762
+ encryptBytes(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Promise<Result<IEncryptBytesResult>>;
5763
+ /**
5764
+ * Decrypts raw bytes produced by {@link NodeCryptoProvider.encryptBytes} using
5765
+ * AES-256-GCM. See {@link CryptoUtils.ICryptoProvider.decryptBytes | ICryptoProvider.decryptBytes}.
5766
+ * Fails (never throws) on any authentication failure, including a mismatched `aad`.
5767
+ * @param key - 32-byte AES-256 key.
5768
+ * @param nonce - 12-byte GCM nonce (the same one used to encrypt).
5769
+ * @param ciphertext - The ciphertext from the `encryptBytes` result.
5770
+ * @param authTag - The 16-byte GCM auth tag from the `encryptBytes` result.
5771
+ * @param aad - The identical AAD supplied at encrypt time (or absent).
5772
+ * @returns `Success` with the decrypted plaintext bytes, or `Failure` with an error.
5773
+ */
5774
+ decryptBytes(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, authTag: Uint8Array, aad?: Uint8Array): Promise<Result<Uint8Array>>;
5450
5775
  /**
5451
5776
  * Generates a random 32-byte key suitable for AES-256.
5452
5777
  * @returns `Success` with generated key, or `Failure` with an error.
@@ -5497,6 +5822,14 @@ declare class NodeCryptoProvider implements ICryptoProvider {
5497
5822
  * @returns `Success` with the generated `CryptoKeyPair`, or `Failure` with an error.
5498
5823
  */
5499
5824
  generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;
5825
+ /**
5826
+ * Derives an asymmetric keypair deterministically from a fixed secret seed.
5827
+ * @param algorithm - The seed-derivable algorithm (only `'ed25519'` today).
5828
+ * @param seed - The 32-byte secret seed.
5829
+ * @param extractable - Whether the returned private key may be exported.
5830
+ * @returns `Success` with the derived `CryptoKeyPair`, or `Failure` with an error.
5831
+ */
5832
+ importKeyPairFromSeed(algorithm: SeedDerivableAlgorithm, seed: Uint8Array, extractable: boolean): Promise<Result<CryptoKeyPair>>;
5500
5833
  /**
5501
5834
  * Exports a public `CryptoKey` as a JSON Web Key.
5502
5835
  * @remarks
@@ -5948,6 +6281,21 @@ declare function resolveProviderModel(descriptor: IAiProviderDescriptor, modelOv
5948
6281
  */
5949
6282
  declare type SecretProvider = (secretName: string) => Promise<Result<Uint8Array>>;
5950
6283
 
6284
+ /**
6285
+ * The subset of {@link CryptoUtils.KeyPairAlgorithm} whose keypair can be
6286
+ * derived *deterministically* from a fixed secret seed, for use with
6287
+ * {@link CryptoUtils.ICryptoProvider.importKeyPairFromSeed | importKeyPairFromSeed}.
6288
+ *
6289
+ * Only `'ed25519'` is supported today: an Ed25519 private key *is* a 32-byte
6290
+ * seed and its public key is a deterministic function of that seed (RFC 8032),
6291
+ * so the same seed always yields the same keypair on every runtime. The type is
6292
+ * a proper subset because algorithms like RSA or the NIST curves are not
6293
+ * recoverable from a bare seed. It is intentionally left open to grow (e.g.
6294
+ * `'x25519'`) without a breaking change.
6295
+ * @public
6296
+ */
6297
+ declare type SeedDerivableAlgorithm = 'ed25519';
6298
+
5951
6299
  /**
5952
6300
  * Default system-prompt suffix appended when {@link AiAssist.IGenerateJsonCompletionParams.promptHint}
5953
6301
  * is `'smart'` (the default). Designed to discourage code fences and prose in
@@ -5956,6 +6304,22 @@ declare type SecretProvider = (secretName: string) => Promise<Result<Uint8Array>
5956
6304
  */
5957
6305
  declare const SMART_JSON_PROMPT_HINT: string;
5958
6306
 
6307
+ /**
6308
+ * Strips the fixed DER prefix from an X25519 SubjectPublicKeyInfo blob, returning the raw
6309
+ * 32-byte public key.
6310
+ *
6311
+ * An X25519 SPKI is always exactly 44 bytes: a fixed 12-byte prefix (SEQUENCE / AlgorithmIdentifier
6312
+ * with OID 1.3.101.110 / BIT STRING) followed by the 32-byte raw key. Use this to convert a
6313
+ * SPKI-held recipient public key into the raw form accepted by {@link HpkeProvider.openBase}'s
6314
+ * `recipientPublicKey` parameter.
6315
+ *
6316
+ * @param spki - The DER-encoded X25519 SubjectPublicKeyInfo bytes.
6317
+ * @returns `Success` with the raw 32-byte public key, or `Failure` if `spki` is not a
6318
+ * well-formed 44-byte X25519 SPKI blob.
6319
+ * @public
6320
+ */
6321
+ declare function spkiToRawX25519(spki: Uint8Array): Result<Uint8Array>;
6322
+
5959
6323
  /**
5960
6324
  * Whether a provider declares any embedding capability at all.
5961
6325
  *
@@ -93,15 +93,23 @@ export declare class HpkeProvider {
93
93
  *
94
94
  * @param recipientPrivateKey - Recipient's X25519 private `CryptoKey`
95
95
  * (`algorithm.name === 'X25519'`, `type === 'private'`, `usages` includes `'deriveBits'`).
96
- * **Must be extractable** (`extractable: true`) the recipient's public key bytes
97
- * are recovered from the JWK `x` field during Decap.
96
+ * **Must be extractable** (`extractable: true`) only when `recipientPublicKey` is not
97
+ * supplied — the recipient's public key bytes are then recovered from the JWK `x` field
98
+ * during Decap. When `recipientPublicKey` is supplied, `recipientPrivateKey` may be
99
+ * non-extractable.
98
100
  * @param info - Context-binding bytes. Must exactly match `info` from `sealBase`.
99
101
  * @param aad - Must exactly match `aad` from `sealBase`.
100
102
  * @param enc - The encapsulated key from `sealBase` — exactly 32 bytes.
101
103
  * @param ciphertext - The ciphertext from `sealBase` — `plaintext.length + 16` bytes.
104
+ * @param recipientPublicKey - Optional raw 32-byte X25519 public key matching
105
+ * `recipientPrivateKey` (`pkRm` in RFC 9180 §4.1). Public material — supplying it lets
106
+ * Decap build `kem_context` without exporting `recipientPrivateKey` to JWK, so
107
+ * `recipientPrivateKey` no longer needs to be extractable. A mismatched value only breaks
108
+ * the caller's own decryption (AEAD authentication fails) — it cannot be used to attack
109
+ * another party's ciphertext.
102
110
  * @returns `Success` with decrypted plaintext bytes, or `Failure` with error context.
103
111
  */
104
- openBase(recipientPrivateKey: CryptoKey, info: Uint8Array, aad: Uint8Array, enc: Uint8Array, ciphertext: Uint8Array): Promise<Result<Uint8Array>>;
112
+ openBase(recipientPrivateKey: CryptoKey, info: Uint8Array, aad: Uint8Array, enc: Uint8Array, ciphertext: Uint8Array, recipientPublicKey?: Uint8Array): Promise<Result<Uint8Array>>;
105
113
  /**
106
114
  * HKDF-SHA256 key derivation (RFC 5869). Extract-then-Expand using SHA-256.
107
115
  *
@@ -1 +1 @@
1
- {"version":3,"file":"hpkeProvider.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/hpkeProvider.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAoD,MAAM,eAAe,CAAC;AAoOzF;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IAEvC,OAAO;IAIP;;;;;;;OAOG;WACW,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IAIhE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACU,QAAQ,CACnB,kBAAkB,EAAE,SAAS,EAC7B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAenC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACU,QAAQ,CACnB,mBAAmB,EAAE,SAAS,EAC9B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAuB9B;;;;;;;;;;;;;OAaG;IACU,IAAI,CACf,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAQ9B;;;;;;;;OAQG;WACW,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,UAAU;IAIjE;;;;;;;;;OASG;WACW,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,eAAe,CAAC;CAY5E"}
1
+ {"version":3,"file":"hpkeProvider.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/hpkeProvider.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAoD,MAAM,eAAe,CAAC;AA+OzF;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IAEvC,OAAO;IAIP;;;;;;;OAOG;WACW,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IAIhE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACU,QAAQ,CACnB,kBAAkB,EAAE,SAAS,EAC7B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAenC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACU,QAAQ,CACnB,mBAAmB,EAAE,SAAS,EAC9B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,UAAU,EACtB,kBAAkB,CAAC,EAAE,UAAU,GAC9B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IA4B9B;;;;;;;;;;;;;OAaG;IACU,IAAI,CACf,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAQ9B;;;;;;;;OAQG;WACW,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,UAAU;IAIjE;;;;;;;;;OASG;WACW,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,eAAe,CAAC;CAY5E"}