|  | @@ -4,40 +4,96 @@ using System.Diagnostics;
 | 
	
		
			
				|  |  |  using System.Diagnostics.CodeAnalysis;
 | 
	
		
			
				|  |  |  using System.IO;
 | 
	
		
			
				|  |  |  using System.Linq;
 | 
	
		
			
				|  |  | +using System.Net.NetworkInformation;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace InABox.Core
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      public static class LicenseUtils
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        #region License Generation
 | 
	
		
			
				|  |  | +        #region Mac Addresses
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        public static String[] GetMacAddresses()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return NetworkInterface
 | 
	
		
			
				|  |  | +                .GetAllNetworkInterfaces()
 | 
	
		
			
				|  |  | +                .Where(nic =>
 | 
	
		
			
				|  |  | +                    nic.OperationalStatus == OperationalStatus.Up &&
 | 
	
		
			
				|  |  | +                    nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
 | 
	
		
			
				|  |  | +                .Select(nic => nic.GetPhysicalAddress().ToString()).ToArray();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Generate a new, out-of-the-box license.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>The new license, valid for 1 month.</returns>
 | 
	
		
			
				|  |  | -        public static LicenseData GenerateNewLicense()
 | 
	
		
			
				|  |  | +        public static bool ValidateMacAddresses(String[] addresses)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            return new LicenseData
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                LastRenewal = DateTime.Now,
 | 
	
		
			
				|  |  | -                Expiry = DateTime.Now.AddMonths(1),
 | 
	
		
			
				|  |  | -                CustomerID = Guid.Empty,
 | 
	
		
			
				|  |  | -                RenewalAvailable = DateTime.Now.AddMonths(1).AddDays(-7)
 | 
	
		
			
				|  |  | -            };
 | 
	
		
			
				|  |  | +            var hardware = GetMacAddresses();
 | 
	
		
			
				|  |  | +            return hardware.Any(addresses.Contains);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        #endregion
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        #region License Generation
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        public static LicenseData RenewLicense(LicenseData oldLicense, DateTime renewed, DateTime newExpiry, DateTime renewAvailable)
 | 
	
		
			
				|  |  | +        public static LicenseData RenewLicense(LicenseData oldLicense, DateTime renewed, DateTime newExpiry, DateTime renewAvailable, String[] addresses)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              return new LicenseData
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  LastRenewal = renewed,
 | 
	
		
			
				|  |  |                  Expiry = newExpiry,
 | 
	
		
			
				|  |  | -                CustomerID = oldLicense.CustomerID,
 | 
	
		
			
				|  |  | -                RenewalAvailable = renewAvailable
 | 
	
		
			
				|  |  | +                CustomerID = oldLicense?.CustomerID ?? Guid.Empty,
 | 
	
		
			
				|  |  | +                RenewalAvailable = renewAvailable,
 | 
	
		
			
				|  |  | +                Addresses = addresses,
 | 
	
		
			
				|  |  | +                IsDynamic = oldLicense?.IsDynamic ?? false
 | 
	
		
			
				|  |  |              };
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  |          private static readonly byte[] LicenseKey = Convert.FromBase64String("dCyTyQkj1o1rqJJQlT+Jcnkxr+OQnO4KCoF/b+6cx54=");
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        public static string? EncryptLicenseRequest(LicenseRequest request)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return Encryption.EncryptV2(Serialization.Serialize(request), LicenseKey);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        public static bool TryEncryptLicenseRequest(LicenseRequest request, [NotNullWhen(true)] out string? result, [NotNullWhen(false)] out string? error)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return Encryption.TryEncryptV2(Serialization.Serialize(request), LicenseKey, out result, out error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Decrypts <paramref name="request"/>.
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="request">The request to decrypt.</param>
 | 
	
		
			
				|  |  | +        /// <returns>
 | 
	
		
			
				|  |  | +        /// The new license request, or <see langword="null"/> if errors occurred.
 | 
	
		
			
				|  |  | +        /// </returns>
 | 
	
		
			
				|  |  | +        public static bool TryDecryptLicenseRequest(string data, [NotNullWhen(true)] out LicenseRequest? result, [NotNullWhen(false)] out string? error)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (!Encryption.TryDecryptV2(data, LicenseKey, out var decrypted, out error))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                result = null;
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            result = Serialization.Deserialize<LicenseRequest>(decrypted);
 | 
	
		
			
				|  |  | +            if(result == null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                error = "Request deserialization failed";
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Decrypts <paramref name="request"/>, throwing an <see cref="Exception"/> on fail.
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="request">The data to decrypt.</param>
 | 
	
		
			
				|  |  | +        /// <returns>
 | 
	
		
			
				|  |  | +        /// The new license request.
 | 
	
		
			
				|  |  | +        /// </returns>
 | 
	
		
			
				|  |  | +        public static LicenseRequest DecryptLicenseRequest(string data)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (!TryDecryptLicenseRequest(data, out var result, out var error))
 | 
	
		
			
				|  |  | +                throw new Exception(error);
 | 
	
		
			
				|  |  | +            return result;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Encrypts the license data.
 | 
	
	
		
			
				|  | @@ -50,6 +106,7 @@ namespace InABox.Core
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              return Encryption.EncryptV2(Serialization.Serialize(license), LicenseKey);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  |          public static bool TryEncryptLicense(LicenseData license, [NotNullWhen(true)] out string? result, [NotNullWhen(false)] out string? error)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              return Encryption.TryEncryptV2(Serialization.Serialize(license), LicenseKey, out result, out error);
 | 
	
	
		
			
				|  | @@ -161,17 +218,17 @@ namespace InABox.Core
 | 
	
		
			
				|  |  |              return result;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        public static void LoadSummary(LicenseSummary summary)
 | 
	
		
			
				|  |  | +        public static void LoadSummary(LicenseFeeResponse feeResponse)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              _licensefees.Clear();
 | 
	
		
			
				|  |  |              _periods.Clear();
 | 
	
		
			
				|  |  |              _userDiscounts.Clear();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            foreach (var license in summary.LicenseFees)
 | 
	
		
			
				|  |  | +            foreach (var license in feeResponse.LicenseFees)
 | 
	
		
			
				|  |  |                  _licensefees[license.Key] = license.Value;
 | 
	
		
			
				|  |  | -            foreach (var (months, period) in summary.TimeDiscounts)
 | 
	
		
			
				|  |  | +            foreach (var (months, period) in feeResponse.TimeDiscounts)
 | 
	
		
			
				|  |  |                  _periods[months] = period;
 | 
	
		
			
				|  |  | -            foreach (var (users, discount) in summary.UserDiscounts)
 | 
	
		
			
				|  |  | +            foreach (var (users, discount) in feeResponse.UserDiscounts)
 | 
	
		
			
				|  |  |                  _userDiscounts[users] = discount;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -230,14 +287,14 @@ namespace InABox.Core
 | 
	
		
			
				|  |  |          #endregion
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public class LicenseSummaryRequest
 | 
	
		
			
				|  |  | +    public class LicenseFeeRequest
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          public Guid RegistrationID { get; set; }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      
 | 
	
		
			
				|  |  | -    public class LicenseSummary
 | 
	
		
			
				|  |  | +    public class LicenseFeeResponse
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        public LicenseSummary()
 | 
	
		
			
				|  |  | +        public LicenseFeeResponse()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              LicenseFees = new Dictionary<string, double>();
 | 
	
		
			
				|  |  |              TimeDiscounts = new Dictionary<int, double>();
 |