diff --git a/server/api/routes/ValidateLicense.php b/server/api/routes/ValidateLicense.php index af21cfe..d530ed4 100644 --- a/server/api/routes/ValidateLicense.php +++ b/server/api/routes/ValidateLicense.php @@ -71,16 +71,22 @@ final class ValidateLicense )->execute([$lic['id'], $machineId, $machineLabel ?: null, $now, $now]); } - // Construit la réponse, signe-la, renvoie - $entUntil = (new \DateTimeImmutable($lic['download_entitlement_until']))->format(\DateTimeInterface::ATOM); - $serverTime = (new \DateTimeImmutable('now'))->format(\DateTimeInterface::ATOM); + // Construit la réponse, signe-la, renvoie. + // Tous les timestamps sont émis en UTC + format figé "yyyy-MM-ddTHH:mm:ssZ" + // pour que la canonicalisation côté client produise EXACTEMENT les mêmes bytes. + $utc = new \DateTimeZone('UTC'); + $fmt = 'Y-m-d\TH:i:s\Z'; + + $issuedAt = (new \DateTimeImmutable($lic['issued_at'], $utc))->format($fmt); + $entUntil = (new \DateTimeImmutable($lic['download_entitlement_until'], $utc))->format($fmt); + $serverTime = (new \DateTimeImmutable('now', $utc))->format($fmt); $expired = strtotime($lic['download_entitlement_until']) < time(); $payload = [ 'status' => $expired ? 'expired' : 'valid', 'licenseId' => 'lic_' . $lic['id'], 'ownerName' => $lic['owner_name'], - 'issuedAt' => (new \DateTimeImmutable($lic['issued_at']))->format(\DateTimeInterface::ATOM), + 'issuedAt' => $issuedAt, 'downloadEntitlementUntil' => $entUntil, 'maxMachines' => (int)$lic['max_machines'], 'serverTime' => $serverTime, diff --git a/server/tools/config.example.php b/server/tools/config.example.php new file mode 100644 index 0000000..081e9f0 --- /dev/null +++ b/server/tools/config.example.php @@ -0,0 +1,43 @@ + 'https://asterionvr.com/PS_Launcher', + + // MySQL (depuis le manager OVH : Hébergements -> Bases de données) + 'db' => [ + // Format DSN OVH typique : mysql:host=.mysql.db;dbname=;charset=utf8mb4 + 'dsn' => 'mysql:host=localhost;dbname=replace_me;charset=utf8mb4', + 'username' => 'replace_me', + 'password' => 'replace_me', + ], + + // HMAC secret pour les URLs présignées de /builds/ (v0.6, optionnel) + // Génère 64 hex chars : `php -r "echo bin2hex(random_bytes(32));"` + 'hmac_secret' => 'replace_with_random_64_bytes_hex', + + // Clés Ed25519 pour signer la réponse de validation license et le manifest. + // Génère le keypair via : `php tools/generate-keypair.php` + // Recopie les hex strings ci-dessous, et embarque la public_key_hex dans le launcher. + 'ed25519' => [ + 'private_key_hex' => '', // 128 hex chars (sodium secret key, contient la pub key) + 'public_key_hex' => '', // 64 hex chars (32 bytes) + ], + + // (v0.4-β) JWT pour les URLs de download protégées (optionnel pour le moment) + 'jwt_secret' => 'replace_with_random_secret', + 'jwt_ttl_seconds' => 900, // 15 min + + // Limites de validation + 'validate_max_per_minute_per_ip' => 10, + + // === BACKOFFICE ADMIN === + // Mot de passe bcrypt pour la connexion à /PS_Launcher/admin/ + // Génère le hash via SSH OVH : + // php -r "echo password_hash('motdepasse_choisi', PASSWORD_DEFAULT);" + // puis colle le résultat ci-dessous. + 'admin_password_hash' => '', +]; diff --git a/src/PSLauncher.Core/Licensing/LicenseService.cs b/src/PSLauncher.Core/Licensing/LicenseService.cs index 774b532..14f7332 100644 --- a/src/PSLauncher.Core/Licensing/LicenseService.cs +++ b/src/PSLauncher.Core/Licensing/LicenseService.cs @@ -203,8 +203,15 @@ public sealed class LicenseService : ILicenseService return JsonSerializer.SerializeToUtf8Bytes(dict, opts); } - private static string? FormatDateAtom(DateTime? d) => - d?.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:sszzz"); + /// + /// Format figé "yyyy-MM-ddTHH:mm:ssZ" en UTC, identique à ce que produit côté serveur + /// PHP `(new DateTimeImmutable($s, new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z')`. + /// + private static string? FormatDateAtom(DateTime? d) + { + if (d is null) return null; + return d.Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss") + "Z"; + } public static bool VerifySignature(LicenseValidationResponse response, string base64Signature, string publicKeyHex) { diff --git a/src/PSLauncher.Core/Resources/server-pubkey.txt b/src/PSLauncher.Core/Resources/server-pubkey.txt index a8672f0..e2761e8 100644 --- a/src/PSLauncher.Core/Resources/server-pubkey.txt +++ b/src/PSLauncher.Core/Resources/server-pubkey.txt @@ -1,6 +1 @@ -# Clé publique Ed25519 du serveur PS_Launcher. -# Génère-la via : `php tools/generate-keypair.php` sur le serveur OVH. -# Recopie ici les 64 hex chars de public_key_hex (rien d'autre, pas de commentaires sur la même ligne), -# puis recompile le launcher. -# Tant que ce fichier commence par '#' ou est vide, la vérification de signature -# côté client est skippée (le launcher accepte la réponse serveur sans vérifier). +a32cacfd6f3d52943c6403690fce44bab4933408f49827cb43fa0d3eda267a8f \ No newline at end of file