@rvoh/dream
    Preparing search index...

    Class Encrypt

    Index

    Constructors

    Methods

    • Decrypts a value previously produced by Encrypt.encrypt.

      Behavior depends on whether legacyOpts is provided:

      Two-arg form (no rotation):

      • null/undefined input returns null.
      • Cipher op / auth tag / payload-shape failure throws DecryptionError.
      • Successful decrypt with non-JSON plaintext throws DecryptionParseError.

      Three-arg form (rotation): tries the current key first; on DecryptionError falls back to the legacy key. If both fail, throws DecryptionRotationError carrying both per-key errors. A DecryptionParseError from the current key is not retried — the cipher already matched, so a parse failure means the encrypted format is wrong (an app bug), not a wrong key.

      MissingEncryptionKey propagates from either form when a key is missing.

      onLegacyKeyUsed (optional fourth argument): during a key rotation, tells you which values still need the legacy key. It is called with no arguments, synchronously, before a three-arg call returns a value that the legacy key opened. It is not called when the current key opens the value, when decryption throws, or in the two-arg form. An error it throws propagates from decrypt as is.

      const value = Encrypt.decrypt<string>(
      encrypted,
      { algorithm: 'aes-256-gcm', key: newKey },
      { algorithm: 'aes-256-gcm', key: oldKey },
      { onLegacyKeyUsed: () => console.warn('this value is still encrypted with the old key') }
      )

      Type Parameters

      • RetType

      Parameters

      • encrypted: string
      • __namedParameters: DecryptOptions
      • OptionallegacyOpts: DecryptOptions
      • __namedParameters: DecryptCallbacks = {}

      Returns RetType | null

      MissingEncryptionKey

      DecryptionError

      DecryptionParseError

      DecryptionRotationError

    • Generates a base64-encoded random key suitable for the given algorithm.

      1. Generate a new key: const newKey = Encrypt.generateKey('aes-256-gcm').
      2. Configure rotation by setting both current and legacy. For encrypted columns: dreamApp.set('encryption', { columns: { current: { algorithm: 'aes-256-gcm', key: newKey }, legacy: { algorithm: 'aes-256-gcm', key: oldKey } } }). For cookies, use the equivalent shape under psychicApp.set('encryption', { cookies: { current, legacy } }).
      3. Deploy. New encryptions use current; existing ciphertext continues to decrypt via legacy fallback.
      4. For cookies, wait at least the cookie maxAge so all in-flight cookies have either expired or been re-issued under the new key. For @Encrypted columns, re-encrypt every existing row under the new key by calling reencryptEncryptedColumns() on each model that has encrypted properties (await User.reencryptEncryptedColumns()), in each environment, from a console or a script rather than a migration. Run it only once every server in that environment runs with the new current: a server still on the old current keeps writing old-key values that its single pass over the table can miss. Writing old-key ciphertext straight into a backing column (setAttribute, setAttributes or updateAttributes given the backing column) after it has run also puts an old-key value back. For any other value encrypted with these keys, Encrypt.reencrypt rewrites it.
      5. Drop legacy from config and deploy again.

      To see which values still need the old key while legacy is configured, register dreamApp.on('encryption:legacy-key-used', ...), which fires each time reading an @Encrypted property needed legacy to open the value, or pass onLegacyKeyUsed to Encrypt.decrypt for values you decrypt yourself. A value nobody reads never fires either, so silence alone does not show that legacy is safe to drop.

      • On a scheduled cadence (90–180 days is a reasonable policy default).
      • Incident response: leaked env file, departing employee with key access, or any suspected key compromise.
      • For cookies: at least the cookie maxAge, so in-flight sessions are not forced to re-authenticate.
      • For @Encrypted columns: until every existing row has been re-encrypted under the new key. Once legacy is dropped, reading the @Encrypted property of a row that is still under the old key throws DecryptionError, the same error a tampered or corrupted value throws, so the error class alone cannot show that legacy was dropped too early. DecryptionRotationError is thrown only while legacy is configured and neither key opens the value. Neither class extends the other, so code that catches DecryptionRotationError during a rotation must also catch DecryptionError to handle such a read once legacy is dropped.

      Parameters

      • algorithm: EncryptAESAlgorithm

      Returns string

    • Opens encrypted as Encrypt.decrypt does, with currentOpts and, when they do not open it, legacyOpts, and encrypts the value again with currentOpts, so that reading it no longer needs the legacy key. Use it during a key rotation for a value stored outside an @deco.Encrypted column, such as a cookie; Model.reencryptEncryptedColumns() does this for every value of a model's encrypted properties.

      const rewritten = Encrypt.reencrypt(
      storedValue,
      { algorithm: 'aes-256-gcm', key: newKey },
      { algorithm: 'aes-256-gcm', key: oldKey }
      )

      Parameters

      • encrypted: string
      • currentOpts: EncryptOptions
      • OptionallegacyOpts: DecryptOptions

      Returns string

      MissingEncryptionKey

      DecryptionError

      DecryptionParseError

      DecryptionRotationError

    • Parameters

      • base64EncodedKey: string
      • algorithm: EncryptAESAlgorithm

      Returns boolean