using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; namespace FSO.Server.Protocol.Utils { public class ChallengeResponse { private static int _salt_byte_length = 320; private static int _nonce_byte_length = 32; private static RNGCryptoServiceProvider _random_crypt_prov = new RNGCryptoServiceProvider(); private static Random _random_gen = new Random(); private static int _min_iteration_count = 4000; private static int _max_iteration_count = 5000; private const int _sha_output_length = 20; private static byte[] ComputeHMACHash(byte[] data, string secret) { using (var _hmac_sha_1 = new HMACSHA1(data, true)) { byte[] _hash_bytes = _hmac_sha_1.ComputeHash(Encoding.UTF8.GetBytes(secret)); return _hash_bytes; } } public static string AnswerChallenge(string challenge, string secret) { var components = new Dictionary(); var parts = challenge.Split(','); foreach(var part in parts){ var subParts = part.Split(new char[] { '=' }, 2); components.Add(subParts[0], subParts[1]); } var iterations = int.Parse(components["i"]); var salt = components["s"]; var salted = Hi(secret, Convert.FromBase64String(salt), iterations); return Convert.ToBase64String(ComputeHMACHash(salted, secret)); } public static string GetChallenge() { var numIterations = GetRandomInteger(); return "i=" + numIterations + ",s=" + Convert.ToBase64String(GetRandomByteArray(_nonce_byte_length)); } private static byte[] Hi(string password, byte[] salt, int iteration_count) { Rfc2898DeriveBytes _pdb = new Rfc2898DeriveBytes(password, salt, iteration_count); return _pdb.GetBytes(_sha_output_length); } private static int GetRandomInteger() { if (_random_gen == null) _random_gen = new Random(); int _random_int = _random_gen.Next(_min_iteration_count, _max_iteration_count); if (_random_int < 0) _random_int *= -1; return _random_int; } private static byte[] GetRandomByteArray(int byte_array_length) { byte[] _random_byte_array = new byte[byte_array_length]; _random_crypt_prov.GetBytes(_random_byte_array); return _random_byte_array; } } }