Tiger_Crypto

@api

Tiger_Crypto — authenticated symmetric encryption for reversible secrets at rest.

Some auth factors store a secret that must be RECOVERED, not just verified: a TOTP shared secret (needed to compute the expected code) and OAuth refresh tokens. A password hash won't do — these have to come back out. So they're encrypted with an app-held key before hitting user_credential.secret.

Uses libsodium's crypto_secretbox (XSalsa20-Poly1305) — bundled in PHP 8.1+, no extension to install. Output is authenticated (tamper-evident) and carries a random per-message nonce, base64-wrapped as nonce . ciphertext for text-column storage.

KEY ROTATION (see also Tiger_Security for the pepper): the CURRENT key is tiger.crypto.key; during a rotation you keep the old key(s) in tiger.crypto.key_retired (comma-separated). Encryption always uses the current key; decryption tries the current key then each retired key, so nothing breaks mid-rotation. tiger crypto:rekey then re-encrypts every stored secret under the current key (reencrypt()), after which the retired key can be removed. Because this data is REVERSIBLE, rotation is lossless — unlike the pepper, which migrates lazily on login.

Keys live ONLY in local.ini / a secrets manager, NEVER the DB (that's where the ciphertext is) and NEVER the repo.

Methods

encrypt()

encrypt($plaintext)

Encrypt a plaintext string under the CURRENT key; base64 blob safe for a text column.

  • $plaintext string — the value to encrypt

Returns string — base64-encoded nonce . ciphertext

decrypt()

decrypt($blob)

Decrypt a blob from encrypt(), trying the current key then any retired keys (so a rotation window Just Works). Throws on a missing key, malformed input, or when no configured key authenticates — callers treat any throw as "secret unusable".

  • $blob string — the base64 blob produced by encrypt()

Returns string — the recovered plaintext

Throws RuntimeException — on malformed input or when no configured key authenticates

reencrypt()

reencrypt($blob)

Re-encrypt a blob under the CURRENT key (decrypting via current-or-retired). For rekey.

  • $blob string — the existing ciphertext blob

Returns string — the blob re-encrypted under the current key

isConfigured()

isConfigured()

True when a usable current key is configured — lets callers gate features (TOTP enrollment).

Returns bool — whether a usable current key is configured

generateKey()

generateKey()

Mint a fresh base64 key for local.ini / a secrets manager. Handy for install/rotate.

Returns string — a new base64-encoded 32-byte key