using System.Security.Cryptography; using System.Text; using Microsoft.Win32; using System.Numerics; namespace MxAsbClient; internal static class AsbRegistry { private const string Entropy = "wonderware"; private static string RegistryPath => Environment.Is64BitProcess ? @"SOFTWARE\Wow6432Node\ArchestrA\ArchestrAServices" : @"SOFTWARE\ArchestrA\ArchestrAServices"; public static string GetDefaultSolutionName() { return Registry.GetValue($@"HKEY_LOCAL_MACHINE\{RegistryPath}", "DefaultASBSolution", string.Empty)?.ToString() ?? string.Empty; } public static string GetSolutionPassphrase(string? solutionName, Action? trace = null) { trace?.Invoke("asb.stage=registry-solution"); string effectiveSolution = string.IsNullOrWhiteSpace(solutionName) ? GetDefaultSolutionName() : solutionName!; if (string.IsNullOrWhiteSpace(effectiveSolution)) { throw new InvalidOperationException("ASB default solution name was not found in the registry."); } trace?.Invoke("asb.stage=registry-open-solution"); using RegistryKey? key = Registry.LocalMachine.OpenSubKey($@"{RegistryPath}\{effectiveSolution}", writable: false); if (key?.GetValue("sharedsecret") is not byte[] protectedBytes) { throw new InvalidOperationException($"ASB sharedsecret was not found for solution '{effectiveSolution}'."); } trace?.Invoke("asb.stage=registry-unprotect"); byte[] clear = ProtectedData.Unprotect(protectedBytes, Encoding.Unicode.GetBytes(Entropy), DataProtectionScope.LocalMachine); trace?.Invoke("asb.stage=registry-passphrase-ready"); return Encoding.Unicode.GetString(clear); } public static AsbSolutionCryptoParameters GetCryptoParameters(string? solutionName) { string effectiveSolution = string.IsNullOrWhiteSpace(solutionName) ? GetDefaultSolutionName() : solutionName!; using RegistryKey? key = Registry.LocalMachine.OpenSubKey($@"{RegistryPath}\{effectiveSolution}", writable: false); if (key is null) { throw new InvalidOperationException($"ASB solution registry key was not found for '{effectiveSolution}'."); } string primeText = key.GetValue("Prime")?.ToString() ?? AsbSolutionCryptoParameters.DefaultPrimeText; string generatorText = key.GetValue("Generator")?.ToString() ?? "22"; string hashAlgorithm = key.GetValue("HashAlgorthim")?.ToString() ?? "MD5"; int keySize = int.TryParse(key.GetValue("keySize")?.ToString(), out int parsedKeySize) ? parsedKeySize : 256; return new AsbSolutionCryptoParameters( BigInteger.Parse(primeText), BigInteger.Parse(generatorText), hashAlgorithm, keySize); } } internal sealed record AsbSolutionCryptoParameters(BigInteger Prime, BigInteger Generator, string HashAlgorithm, int KeySize) { public const string DefaultPrimeText = "179769313486231590770839156793787453197860296048756011706444423684197180216158519368947833795864925541502180565485980503646440548199239100050792877003355816639229553136239076508735759914822574862575007425302077447712589550957937778424442426617334727629299387668709205606050270810842907692932019128194"; }