TheBlueSky.SwiftAuthenticator by Essam Almohammadi

<PackageReference Include="TheBlueSky.SwiftAuthenticator" Version="2.0.0" />

 Authenticator

public sealed class Authenticator
using System; using System.Security.Cryptography; using TheBlueSky.SwiftAuthenticator.Externals; namespace TheBlueSky.SwiftAuthenticator { public sealed class Authenticator { private static readonly RandomNumberGenerator RandomNumberGenerator = new RNGCryptoServiceProvider(); private readonly AuthenticatorOptions options = new AuthenticatorOptions(); public AuthenticatorAlgorithm AuthenticatorAlgorithm => options.AuthenticatorAlgorithm; public int NumberOfPasswordDigits => options.NumberOfPasswordDigits; public int SizeOfTimeStep => options.SizeOfTimeStep; public DateTime StartDateTime => options.StartDateTime; public Authenticator(Action<AuthenticatorOptions> setupAction = null) { setupAction?.Invoke(options); } public string GenerateCounterBasedPassword(string secret, ulong iterationNumber) { return GenerateCounterBasedPassword(secret, iterationNumber, NumberOfPasswordDigits); } public string GenerateCounterBasedPassword(string secret, ulong iterationNumber, int digits) { if (AuthenticatorAlgorithm != 0) throw new InvalidOperationException("HMAC-SHA-1 is the only supported algorithm when generating counter-based passwords (refert to https://tools.ietf.org/html/rfc4226#section-5)."); return GeneratePassword(secret, iterationNumber, digits); } public string GenerateTimeBasedPassword(string secret, Func<DateTime> nowFunc = null) { return GenerateTimeBasedPassword(secret, nowFunc, NumberOfPasswordDigits, SizeOfTimeStep); } public string GenerateTimeBasedPassword(string secret, Func<DateTime> nowFunc, int digits = 6, int timeStep = 30) { if (timeStep < 1) throw new ArgumentException("The time step value must be a positive Int32 value.", "timeStep"); DateTime obj = nowFunc?.Invoke() ?? DateTime.UtcNow; if (obj < StartDateTime) throw new ArgumentException(string.Format("The value produced by {0} must be greater than the value of {1}.{2}.", "nowFunc", "Authenticator", StartDateTime), "nowFunc"); ulong iterationNumber = (ulong)((obj - StartDateTime).TotalSeconds / (double)timeStep); return GeneratePassword(secret, iterationNumber, digits); } public static string GenerateSecret(int size = 20) { if (size % 5 != 0 || size < 5) throw new ArgumentException("The size must be multiples of 40 bits, due to Base32 encoding requirements (refert to https://tools.ietf.org/html/rfc4648#section-6).", "size"); byte[] array = new byte[size]; RandomNumberGenerator.GetBytes(array); return Base32.ToBase32(array); } private string GeneratePassword(string secret, ulong iterationNumber, int digits) { if (secret == null) throw new ArgumentNullException("secret"); if (digits < 6) throw new ArgumentException("The password value must be at least a 6-digit value (refer to https://tools.ietf.org/html/rfc4226#section-4).", "digits"); if (digits > 10) throw new ArgumentException("The password value is at most a 10-digit value, due to the maximum value of Int32.", "digits"); byte[] bytes = BitConverter.GetBytes(iterationNumber); if (BitConverter.IsLittleEndian) Array.Reverse((Array)bytes); byte[] array = Base32.FromBase32(secret); if (array.Length < 16) throw new ArgumentException("The length of the shared secret must be at least 128 bits (refer to https://tools.ietf.org/html/rfc4226#section-4).", "secret"); using (HMAC hMAC = GetHMACAlgorithm(array)) { byte[] array2 = hMAC.ComputeHash(bytes); int num = array2[array2.Length - 1] & 15; return ((((array2[num] & 127) << 24) | ((array2[num + 1] & 255) << 16) | ((array2[num + 2] & 255) << 8) | (array2[num + 3] & 255)) % (int)Math.Pow(10, (double)digits)).ToString(new string('0', digits)); } } private HMAC GetHMACAlgorithm(byte[] key) { switch (AuthenticatorAlgorithm) { case AuthenticatorAlgorithm.HMACSHA1: return new HMACSHA1(key); case AuthenticatorAlgorithm.HMACSHA256: return new HMACSHA256(key); case AuthenticatorAlgorithm.HMACSHA512: return new HMACSHA512(key); default: throw new InvalidOperationException("The supported algorithms are HMAC-SHA-1 for HOTP and TOTP, as well as HMAC-SHA-256 and HMAC-SHA-512 for TOTP."); } } } }