60 lines
2.1 KiB
PHP
60 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Licensing;
|
|
|
|
use App\Models\License;
|
|
use Carbon\CarbonInterface;
|
|
use RuntimeException;
|
|
|
|
final class LicenseSigner
|
|
{
|
|
public function sign(License $license, string $installationID, ?CarbonInterface $issuedAt = null): string
|
|
{
|
|
$seed = $this->privateSeed();
|
|
$pair = sodium_crypto_sign_seed_keypair($seed);
|
|
$secret = sodium_crypto_sign_secretkey($pair);
|
|
|
|
$payload = [
|
|
'customerEmail' => $license->customer_email,
|
|
'customerName' => $license->customer_name,
|
|
'edition' => $license->edition,
|
|
'expiresAt' => $license->expires_at?->utc()->format('Y-m-d\\TH:i:s\\Z'),
|
|
'installationID' => strtolower($installationID),
|
|
'issuedAt' => ($issuedAt ?? now())->utc()->format('Y-m-d\\TH:i:s\\Z'),
|
|
'licenseID' => strtolower((string) $license->id),
|
|
'maxMajorVersion' => $license->max_major_version,
|
|
];
|
|
ksort($payload, SORT_STRING);
|
|
$json = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
|
|
$sig = sodium_crypto_sign_detached($json, $secret);
|
|
|
|
return 'RS3.'.self::b64url($json).'.'.self::b64url($sig);
|
|
}
|
|
|
|
public function publicKeyBase64(): string
|
|
{
|
|
$pair = sodium_crypto_sign_seed_keypair($this->privateSeed());
|
|
|
|
return base64_encode(sodium_crypto_sign_publickey($pair));
|
|
}
|
|
|
|
private function privateSeed(): string
|
|
{
|
|
$path = config('rsync-studio.private_key_path');
|
|
if (! $path || ! is_file($path)) {
|
|
throw new RuntimeException('RSYNC_STUDIO_LICENSE_PRIVATE_KEY_PATH is not configured or does not exist.');
|
|
}
|
|
$decoded = base64_decode(trim((string) file_get_contents($path)), true);
|
|
if ($decoded === false || strlen($decoded) !== SODIUM_CRYPTO_SIGN_SEEDBYTES) {
|
|
throw new RuntimeException('The Rsync Studio private key must be a Base64-encoded 32-byte Ed25519 seed.');
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
private static function b64url(string $value): string
|
|
{
|
|
return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
|
|
}
|
|
}
|