//using Newtonsoft.Json; //using System.Diagnostics.CodeAnalysis; //using System.Security.Cryptography; //using System.Text; //namespace SimpleHttpServer.Login; //internal struct SerialLoginData { // public string passwordSalt; // public string extraDataSalt; // public string pwd; // public string extraData; // public LoginData ToPlainData() { // return new LoginData { // passwordSalt = Convert.FromBase64String(passwordSalt), // extraDataSalt = Convert.FromBase64String(extraDataSalt) // }; // } //} //internal struct LoginData { // public byte[] passwordSalt; // public byte[] extraDataSalt; // public byte[] passwordHash; // public byte[] encryptedExtraData; // public SerialLoginData ToSerial() { // return new SerialLoginData { // passwordSalt = Convert.ToBase64String(passwordSalt), // extraDataSalt = Convert.ToBase64String(extraDataSalt), // pwd = Convert.ToBase64String(passwordHash), // extraData = Convert.ToBase64String(encryptedExtraData) // }; // } //} //internal struct LoginDataProviderConfig { // /// // /// Size of the password salt and the extradata salt. So each salt will be of size . // /// // public int SALT_SIZE = 32; // public int KEY_LENGTH = 256 / 8; // public int PBKDF2_ITERATIONS = 600_000; // public LoginDataProviderConfig() { } //} //public class LoginProvider { // private static readonly Func JsonSerialize = t => Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(t)); // private static readonly Func JsonDeserialize = b => JsonConvert.DeserializeObject(Encoding.UTF8.GetString(b))!; // [ThreadStatic] // private static SHA256? _sha256PerThread; // private static SHA256 Sha256PerThread { get => _sha256PerThread ??= SHA256.Create(); } // private readonly LoginDataProviderConfig config; // private readonly ReaderWriterLockSlim ldLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); // private readonly string ldPath; // private readonly Dictionary loginDatas; // private Func DataSerializer = JsonSerialize; // private Func DataDeserializer = JsonDeserialize; // public void SetDataSerializers(Func serializer, Func deserializer) { // DataSerializer = serializer ?? JsonSerialize; // DataDeserializer = deserializer ?? JsonDeserialize; // } // public LoginProvider(string ldPath, string confPath) { // this.ldPath = ldPath; // loginDatas = LoadLoginDatas(ldPath); // config = LoadLoginProviderConfig(confPath); // } // private static Dictionary LoadLoginDatas(string path) { // Dictionary tempData; // if (!File.Exists(path)) { // File.WriteAllText(path, "{}", Encoding.UTF8); // tempData = new(); // } else { // tempData = JsonConvert.DeserializeObject>(File.ReadAllText(path))!; // if (tempData == null) { // throw new InvalidDataException($"could not read login data from file {path}"); // } // } // var ld = new Dictionary(); // foreach (var pair in tempData) { // ld.Add(pair.Key, pair.Value.ToPlainData()); // } // return ld; // } // private void SaveLoginData() { // var serial = new Dictionary(); // ldLock.EnterWriteLock(); // try { // foreach (var pair in loginDatas) { // serial.Add(pair.Key, pair.Value.ToSerial()); // } // } finally { // ldLock.ExitWriteLock(); // } // File.WriteAllText(ldPath, JsonConvert.SerializeObject(serial)); // } // private static LoginDataProviderConfig LoadLoginProviderConfig(string path) { // if (!File.Exists(path)) { // var conf = new LoginDataProviderConfig(); // File.WriteAllText(path, JsonConvert.SerializeObject(conf)); // return conf; // } // return JsonConvert.DeserializeObject(File.ReadAllText(path)); // } // public bool AddUser(string username, string password, TExtraData additional) { // ldLock.EnterWriteLock(); // try { // if (loginDatas.ContainsKey(username)) { // return false; // } // var passwordSalt = RandomNumberGenerator.GetBytes(config.SALT_SIZE); // var extraDataSalt = RandomNumberGenerator.GetBytes(config.SALT_SIZE); // LoginData ld = new LoginData() { // passwordSalt = passwordSalt, // extraDataSalt = extraDataSalt, // passwordHash = ComputeSaltedSha256Hash(password, passwordSalt), // encryptedExtraData = EncryptExtraData(password, extraDataSalt, additional), // }; // loginDatas.Add(username, ld); // SaveLoginData(); // } finally { // ldLock.ExitWriteLock(); // } // return true; // } // public bool RemoveUser(string username) { // ldLock.EnterWriteLock(); // try { // var removed = loginDatas.Remove(username); // if (removed) { // SaveLoginData(); // } // return removed; // } finally { // ldLock.ExitWriteLock(); // } // } // public bool ModifyUser(string username, string newPassword, TExtraData newExtraData) { // ldLock.EnterWriteLock(); // try { // if (!loginDatas.ContainsKey(username)) { // return false; // } // loginDatas.Remove(username, out var data); // data.passwordHash = ComputeSaltedSha256Hash(newPassword, data.passwordSalt); // data.encryptedExtraData = EncryptExtraData(newPassword, data.extraDataSalt, newExtraData); // loginDatas.Add(username, data); // SaveLoginData(); // } finally { // ldLock.ExitWriteLock(); // } // return true; // } // public bool TryAuthenticate(string username, string password, [MaybeNullWhen(false)] out TExtraData extraData) { // LoginData data; // ldLock.EnterReadLock(); // try { // if (!loginDatas.TryGetValue(username, out data)) { // extraData = default; // return false; // } // } finally { // ldLock.ExitReadLock(); // } // var hash = ComputeSaltedSha256Hash(password, data.passwordSalt); // if (!hash.SequenceEqual(data.passwordHash)) { // extraData = default; // return false; // } // extraData = DecryptExtraData(password, data.extraDataSalt, data.encryptedExtraData); // return true; // } // /// // /// Threadsafe as the SHA256 instance () is per thread. // /// // /// // /// // /// // private static byte[] ComputeSaltedSha256Hash(string data, byte[] salt) { // var dataBytes = Encoding.UTF8.GetBytes(data); // var buf = new byte[data.Length + salt.Length]; // Buffer.BlockCopy(dataBytes, 0, buf, 0, dataBytes.Length); // Buffer.BlockCopy(salt, 0, buf, dataBytes.Length, salt.Length); // return Sha256PerThread.ComputeHash(buf); // } // private byte[] EncryptExtraData(string pwd, byte[] salt, TExtraData extraData) { // var pbkdf2 = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(pwd), salt, config.PBKDF2_ITERATIONS, HashAlgorithmName.SHA256); // var key = pbkdf2.GetBytes(config.KEY_LENGTH / 8); // var plainBytes = DataSerializer(extraData); // using var aes = Aes.Create(); // aes.KeySize = config.KEY_LENGTH; // aes.Key = key; // aes.Mode = CipherMode.CBC; // aes.Padding = PaddingMode.PKCS7; // ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); // byte[] cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length); // var encryptedBytes = new byte[aes.IV.Length + cipherBytes.Length]; // Array.Copy(aes.IV, 0, encryptedBytes, 0, aes.IV.Length); // Array.Copy(cipherBytes, 0, encryptedBytes, aes.IV.Length, cipherBytes.Length); // return encryptedBytes; // } // private TExtraData DecryptExtraData(string pwd, byte[] salt, byte[] encryptedData) { // var pbkdf2 = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(pwd), salt, config.PBKDF2_ITERATIONS, HashAlgorithmName.SHA256); // var key = pbkdf2.GetBytes(config.KEY_LENGTH / 8); // using var aes = Aes.Create(); // aes.KeySize = config.KEY_LENGTH; // aes.Key = key; // aes.Mode = CipherMode.CBC; // aes.Padding = PaddingMode.PKCS7; // var iv = new byte[aes.BlockSize / 8]; // var cipherBytes = new byte[encryptedData.Length - iv.Length]; // Array.Copy(encryptedData, 0, iv, 0, iv.Length); // Array.Copy(encryptedData, iv.Length, cipherBytes, 0, cipherBytes.Length); // aes.IV = iv; // ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); // byte[] plainBytes = decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length); // return DataDeserializer(plainBytes); // } //}