CluPilotCloud/app/Services/Secrets/SecretCipher.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');
}
}