using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; namespace MachineGuard; /// /// Implementazione Windows-only della protezione machine-binding tramite DPAPI. /// Utilizza con scope : /// il dato cifrato è legato fisicamente alla macchina e non può essere decifrato /// su un'altra macchina, anche con gli stessi account o credenziali. /// [SupportedOSPlatform("windows")] internal sealed class DpapiMachineGuard : IMachineGuard { private readonly MachineGuardOptions _options; private readonly ILogger _logger; public DpapiMachineGuard(IOptions options, ILogger logger) { _options = options.Value; _logger = logger; } /// public bool Verify() { var secretPath = ResolveSecretFilePath(); if (!File.Exists(secretPath)) { _logger.LogError( "MachineGuard: file secret non trovato in '{Path}'. " + "Eseguire MachineGuardSetup.exe su questa macchina per inizializzare l'autorizzazione.", secretPath); return false; } try { var encryptedBytes = File.ReadAllBytes(secretPath); var decryptedBytes = ProtectedData.Unprotect(encryptedBytes, null, DataProtectionScope.LocalMachine); var decryptedToken = Encoding.UTF8.GetString(decryptedBytes); if (!string.Equals(decryptedToken, MachineGuardToken.ExpectedToken, StringComparison.Ordinal)) { _logger.LogError( "MachineGuard: il token decifrato non corrisponde al token atteso. " + "Questa macchina non è autorizzata a eseguire questa applicazione."); return false; } _logger.LogInformation("MachineGuard: autorizzazione macchina verificata con successo."); return true; } catch (CryptographicException ex) { _logger.LogError(ex, "MachineGuard: decifrazione fallita. " + "Il file secret potrebbe provenire da un'altra macchina o essere corrotto. " + "Percorso: '{Path}'", secretPath); return false; } catch (Exception ex) { _logger.LogError(ex, "MachineGuard: errore imprevisto durante la verifica. Percorso: '{Path}'", secretPath); return false; } } private string ResolveSecretFilePath() { if (!string.IsNullOrWhiteSpace(_options.SecretFilePath)) return _options.SecretFilePath; var appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); if (string.IsNullOrEmpty(appData)) appData = @"C:\ProgramData"; return Path.Combine(appData, "DataCoupler", "machine.guard"); } }