AttestoPhoenix.RefreshSuccessorCipher (AttestoPhoenix v3.2.1)

Copy Markdown View Source

Authenticated encryption for the successor stored during refresh rotation.

The ciphertext format is provided by Plug.Crypto.MessageEncryptor and is intentionally kept here with its existing AAD and derived keys. The application secret is read when encrypting or decrypting, but its value MUST remain stable across nodes and deployments. Changing it makes retry state written under the old value unreadable; drain past the configured grace window before an intentional rotation.

Summary

Functions

Builds deterministic authenticated data for a persisted successor bundle.

Reports whether a usable refresh-successor secret is configured.

Decrypts and safely decodes a refresh-token successor ciphertext.

Decrypts a successor using the supplied authenticated-data value.

Derives the encryption and signing keys from a refresh-successor secret.

Encrypts a successor term with the configured refresh-successor secret.

Encrypts a successor using an explicit authenticated-data value.

Functions

binding_aad(parent_hash, family_id, parent_generation, child_hash, retry_until)

@spec binding_aad(String.t(), String.t(), non_neg_integer(), String.t(), integer()) ::
  binary()

Builds deterministic authenticated data for a persisted successor bundle.

Term encoding avoids delimiter ambiguity in host-controlled identifiers. The payload and deadline are also authenticated by the encryption envelope; keeping the binding fields here prevents moving a valid ciphertext to a different parent, family, generation, or child row.

configured?()

@spec configured?() :: boolean()

Reports whether a usable refresh-successor secret is configured.

This checks only presence and the minimum 32-byte length. Operators must keep the value stable across every node and deployment that can serve the same refresh-token families.

decrypt(ciphertext)

@spec decrypt(binary()) :: {:ok, term()} | :error

Decrypts and safely decodes a refresh-token successor ciphertext.

Authentication failures and missing or invalid configuration return :error, matching the pre-consolidation call sites.

decrypt(ciphertext, aad)

@spec decrypt(binary(), binary()) :: {:ok, term()} | :error

Decrypts a successor using the supplied authenticated-data value.

derive_keys(secret)

@spec derive_keys(term()) :: {:ok, binary(), binary()} | :error

Derives the encryption and signing keys from a refresh-successor secret.

Kept public so the exact derivation has direct deterministic coverage.

encrypt(successor)

@spec encrypt(term()) :: {:ok, binary()} | :error

Encrypts a successor term with the configured refresh-successor secret.

Returns :error when the secret is missing or too short. The caller owns the persisted version wrapper around the returned ciphertext.

encrypt(successor, aad)

@spec encrypt(term(), binary()) :: {:ok, binary()} | :error

Encrypts a successor using an explicit authenticated-data value.

The Ecto refresh store uses this form to bind the ciphertext to the parent token hash, family, generations, child hash, and fixed retry deadline. The successor itself remains inside the authenticated ciphertext.