65 lines
1.9 KiB
PHP
65 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Secrets;
|
|
|
|
use Illuminate\Encryption\Encrypter;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* The SECRETS_KEY encrypter, in one place.
|
|
*
|
|
* Its own key rather than APP_KEY, because rotating APP_KEY is ordinary
|
|
* maintenance and would otherwise render every stored credential unreadable —
|
|
* discovered when Stripe stops answering, not when it happens.
|
|
*
|
|
* Extracted from SecretVault so mailbox passwords use the SAME key handling
|
|
* rather than a second copy of it. The copy is what would rot: the two
|
|
* spellings below exist because requiring the `base64:` prefix once made every
|
|
* read and write fail on exactly the setup the config comment documents.
|
|
*/
|
|
final class SecretCipher
|
|
{
|
|
public function encrypt(string $plain): string
|
|
{
|
|
return $this->encrypter()->encryptString($plain);
|
|
}
|
|
|
|
public function decrypt(string $cipher): string
|
|
{
|
|
return $this->encrypter()->decryptString($cipher);
|
|
}
|
|
|
|
/** Is storing credentials possible at all on this installation? */
|
|
public function isUsable(): bool
|
|
{
|
|
return (string) config('admin_access.secrets_key') !== '';
|
|
}
|
|
|
|
private function encrypter(): Encrypter
|
|
{
|
|
$key = (string) config('admin_access.secrets_key');
|
|
|
|
if ($key === '') {
|
|
throw new RuntimeException('SECRETS_KEY is not set — refusing to store or read credentials.');
|
|
}
|
|
|
|
if (str_starts_with($key, 'base64:')) {
|
|
$key = substr($key, 7);
|
|
}
|
|
|
|
if (strlen($key) !== 32) {
|
|
$decoded = base64_decode($key, true);
|
|
|
|
if ($decoded !== false && strlen($decoded) === 32) {
|
|
$key = $decoded;
|
|
}
|
|
}
|
|
|
|
if (strlen($key) !== 32) {
|
|
throw new RuntimeException('SECRETS_KEY must be 32 bytes (or base64 of 32 bytes).');
|
|
}
|
|
|
|
return new Encrypter($key, 'aes-256-cbc');
|
|
}
|
|
}
|