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.
$plaintextstring— 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".
$blobstring— 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.
$blobstring— 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