@amrutjk public class Totp : Otp
{
private const long unixEpochTicks = 621355968000000000L;
private const long ticksToSeconds = 10000000L;
private readonly int step;
private readonly int totpSize;
private readonly TimeCorrection correctedTime;
public Totp(byte[] secretKey, int step = 30, OtpHashMode mode = OtpHashMode.Sha1, int totpSize = 6, TimeCorrection timeCorrection = null)
: base(secretKey, mode)
{
VerifyParameters(step, totpSize);
this.step = step;
this.totpSize = totpSize;
// we never null check the corrected time object. Since it's readonly, we'll ensure that it isn't null here and provide neatral functionality in this case.
correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance;
}
public Totp(IKeyProvider key, int step = 30, OtpHashMode mode = OtpHashMode.Sha1, int totpSize = 6, TimeCorrection timeCorrection = null)
: base(key, mode)
{
VerifyParameters(step, totpSize);
this.step = step;
this.totpSize = totpSize;
// we never null check the corrected time object. Since it's readonly, we'll ensure that it isn't null here and provide neatral functionality in this case.
correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance;
}
private static void VerifyParameters(int step, int totpSize)
{
if (!(step > 0))
{
throw new ArgumentOutOfRangeException(nameof(step));
}
if (!(totpSize > 0))
{
throw new ArgumentOutOfRangeException(nameof(totpSize));
}
if (!(totpSize <= 10))
{
throw new ArgumentOutOfRangeException(nameof(totpSize));
}
}
public string ComputeTotp(DateTime timestamp)
{
return ComputeTotpFromSpecificTime(correctedTime.GetCorrectedTime(timestamp));
}
public string ComputeTotp()
{
return ComputeTotpFromSpecificTime(correctedTime.CorrectedUtcNow);
}
private string ComputeTotpFromSpecificTime(DateTime timestamp)
{
long window = CalculateTimeStepFromTimestamp(timestamp);
return Compute(window, hashMode);
}
public bool VerifyTotp(string totp, out long timeStepMatched, VerificationWindow window = null)
{
return VerifyTotpForSpecificTime(correctedTime.CorrectedUtcNow, totp, window, out timeStepMatched);
}
public bool VerifyTotp(DateTime timestamp, string totp, out long timeStepMatched, VerificationWindow window = null)
{
return VerifyTotpForSpecificTime(correctedTime.GetCorrectedTime(timestamp), totp, window, out timeStepMatched);
}
private bool VerifyTotpForSpecificTime(DateTime timestamp, string totp, VerificationWindow window, out long timeStepMatched)
{
long initialStep = CalculateTimeStepFromTimestamp(timestamp);
return Verify(initialStep, totp, out timeStepMatched, window);
}
private long CalculateTimeStepFromTimestamp(DateTime timestamp)
{
long unixTimestamp = (timestamp.Ticks - unixEpochTicks) / ticksToSeconds;
long window = unixTimestamp / step;
return window;
}
public int RemainingSeconds()
{
return RemainingSecondsForSpecificTime(correctedTime.CorrectedUtcNow);
}
public int RemainingSeconds(DateTime timestamp)
{
return RemainingSecondsForSpecificTime(correctedTime.GetCorrectedTime(timestamp));
}
private int RemainingSecondsForSpecificTime(DateTime timestamp)
{
return step - (int)(((timestamp.Ticks - unixEpochTicks) / ticksToSeconds) % step);
}
protected override string Compute(long counter, OtpHashMode mode)
{
byte[] data = KeyUtilities.GetBigEndianBytes(counter);
long otp = CalculateOtp(data, mode);
return Digits(otp, totpSize);
}
}