using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Text; namespace Azaion.Common.Services; public static class Security { private const int BUFFER_SIZE = 524288; // 512 KB buffer size public static string ToHash(this string str) => Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str))); public static string MakeEncryptionKey(string email, string password, string? hardwareHash) => $"{email}-{password}-{hardwareHash}-#%@AzaionKey@%#---".ToHash(); public static SecureString ToSecureString(this string str) { var secureString = new SecureString(); foreach (var c in str.ToCharArray()) secureString.AppendChar(c); return secureString; } public static string? ToRealString(this SecureString value) { var valuePtr = IntPtr.Zero; try { valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value); return Marshal.PtrToStringUni(valuePtr); } finally { Marshal.ZeroFreeGlobalAllocUnicode(valuePtr); } } public static async Task EncryptTo(this Stream stream, Stream toStream, string key, CancellationToken cancellationToken = default) { if (stream is { CanRead: false }) throw new ArgumentNullException(nameof(stream)); if (key is not { Length: > 0 }) throw new ArgumentNullException(nameof(key)); using var aes = Aes.Create(); aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key)); aes.GenerateIV(); using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); await using var cs = new CryptoStream(toStream, encryptor, CryptoStreamMode.Write, leaveOpen: true); // Prepend IV to the encrypted data await toStream.WriteAsync(aes.IV.AsMemory(0, aes.IV.Length), cancellationToken); var buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, cancellationToken)) > 0) await cs.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken); } public static async Task DecryptTo(this Stream encryptedStream, Stream toStream, string key, CancellationToken cancellationToken = default) { using var aes = Aes.Create(); aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key)); // Read the IV from the start of the input stream var iv = new byte[aes.BlockSize / 8]; _ = await encryptedStream.ReadAsync(iv, cancellationToken); aes.IV = iv; using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); await using var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read, leaveOpen: true); // Read and write in chunks var buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = await cryptoStream.ReadAsync(buffer, cancellationToken)) > 0) await toStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken); } }