using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TwoStepsAuthenticator { /// /// Implementation of RFC 4226 Counter-Based One-Time Password Algorithm /// public class CounterAuthenticator : Authenticator { private readonly int WindowSize; public CounterAuthenticator(int windowSize = 10) { if (windowSize <= 0) { throw new ArgumentException("look-ahead window size must be positive"); } this.WindowSize = windowSize; } /// /// Generates One-Time-Password. /// /// Shared Secret /// Current Counter /// OTP public string GetCode(string secret, ulong counter) { return GetCodeInternal(secret, counter); } /// /// Checks if the passed code is valid. /// /// Shared Secret /// OTP /// Current Counter Position /// true if any code from counter to counter + WindowSize matches public bool CheckCode(string secret, string code, ulong counter) { ulong successfulSequenceNumber = 0uL; return CheckCode(secret, code, counter, out successfulSequenceNumber); } /// /// Checks if the passed code is valid. /// /// Shared Secret /// OTP /// Current Counter Position /// Matching counter value if successful /// true if any code from counter to counter + WindowSize matches public bool CheckCode(string secret, string code, ulong counter, out ulong usedCounter) { var codeMatch = false; ulong successfulSequenceNumber = 0uL; for (uint i = 0; i <= WindowSize; i++) { ulong checkCounter = counter + i; if (ConstantTimeEquals(GetCode(secret, checkCounter), code)) { codeMatch = true; successfulSequenceNumber = checkCounter; } } usedCounter = successfulSequenceNumber; return codeMatch; } } }