VerifyTotp is returning false randomly even Step time is not expired
Anitha-cmd opened this issue · 10 comments
HI , VerifyTotp is returning false randomly even Step time is not expired. Please suggest us what should we have to do now.
Ensure that your server and client system times are in sync.
Ensure that your server and client system times are in sync.
I'm seeing this too, but running locally with one clock. This is for version 1.2.2. I haven't tested the new version yet.
I saw on Stack Overflow someone else having this issue.. I modified their test loop, results of the first 200 loops.
Is there something I'm missing?
0
verify-:False-4
verify-:False-33
verify-:False-63
verify-:False-93
100
verify-:False-122
verify-:False-152
verify-:False-182
200
for (int i = 0; i < 10000; i++) {
var clientSecret = "USER1";
var _secretKey = "HIHI-SECRET";
var bytes = System.Text.Encoding.UTF8.GetBytes($"{clientSecret}-{_secretKey}-{i}");
var totp1 = new OtpNet.Totp(bytes, step: 30);
var t = DateTime.UtcNow;
var result = totp1.ComputeTotp(t);
// Console.WriteLine($"{t} - TOTP code: {result}");
Thread.Sleep(1000);
var input = result;
long timeStepMatched;
var totp2 = new OtpNet.Totp(bytes, step: 30);
bool verify = totp2.VerifyTotp(DateTime.UtcNow, input, out var window);
if (!verify) {
Console.WriteLine("{0}-:{1}-{2}", "verify", verify, i);
}
if (i % 100 == 0) {
Console.WriteLine($"{i}");
}
}
Console.WriteLine("Done Testing");
}
@markbauer1975 , please see https://github.com/kspearrin/Otp.NET#expanded-time-window.
You have not specified a Verification Window, so you are most likely hitting the 30-second boundary. This is evidenced by your output showing up at almost exactly 30 second intervals. The first one at 4 seconds is due to the time you started the execution - probably at 0:26 seconds after the top of a minute (4 seconds prior to a 0:30 boundary) or 0:56 seconds (4 seconds prior to the top of a minute).
The documentation explains that absence of the Verification Window essentially means you only consider the current point-in-time boundary, so any time you create a code and verify it crossing a "top of the minute" or 0:30 "bottom of the minute" boundary, it will fail.
Add a window that includes previous step = 1 and it should not fail using only the 1 second delay specified in your loop.
Better yet, use the RFC-specified window per the docs:
totp.VerifyTotp(totpCode, out timeWindowUsed, VerificationWindow.RfcSpecifiedNetworkDelay);
or using your code:
bool verify = totp2.VerifyTotp(DateTime.UtcNow, input, out var window, VerificationWindow.RfcSpecifiedNetworkDelay);
Ahh Thank you! Sorry for the bad code example. After adding the VerificationWindow I don't see any errors in that test.
Our application code does have that window variable. I was trying to trace down a few instances where I've seen a valid code not verify. With this ruled out, it must be something else in our code. It doesn't seem to be a time issue.
Thanks again...
The following problem assume a default expiry duration of 30 seconds is used.
If I create a new token at 10:15:00 current time, it gets expired at 10:15:30 current time, which is as expected, but when I create a new token at let's say 10:15:28 current time, it is expired after 2 seconds at 10:15:30 time mark, instead of expiring after 30 seconds.
I want my tokens to expire after exact 30 (or N) seconds, no matter I generate them at "top of the minute" or "bottom of the minute" boundaries. Is it possible?
HI , VerifyTotp is returning false randomly even Step time is not expired. Please suggest us what should we have to do now.
please tell me you solved it?
@kspearrin @markbauer1975 @damiarnold @Anitha-cmd
can you help me the below code ,,My problem is that the first time it's false and the second time I create code it's true,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OtpNet;
using Microsoft.Extensions.Logging;
namespace DmzApi.Helper.HtopCustom
{
public class HtopCustomHelper : IHtopCustomHelper
{
private readonly ILogger _logger;
public HtopCustomHelper(ILogger<HtopCustomHelper> logger)
{
_logger = logger;
}
public bool verifyCode(string code,string key,int sec)
{
var result = false;
try
{
// var bytes = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");
//var key = "MGI3OTNiZDg0OWVkZWM5NGZjNmYxZTJlNTEwMzM4MTZmZjRhZmIwZDhlYmVmMGJiMGM3NzllZjc1MDY5OGFmN2ZmYmFjYWEzZDY5YTBhYzg1YzQyZDQ3MTQ1NTIxZWY5NWE2N2QxOWYwYzczMWU0NzJiYjE1ODEyYWYxMjk3MTk=";
byte[] data = System.Convert.FromBase64String(key);
string decodedString = System.Text.Encoding.UTF8.GetString(data);
//var totp = new Totp(Encoding.ASCII.GetBytes(key), step: 30, OtpHashMode.Sha512);
_logger.LogInformation("finally get bytes:" + System.Text.Encoding.UTF8.GetBytes(decodedString));
byte[] serkey = System.Text.Encoding.UTF8.GetBytes(decodedString);
_logger.LogInformation("verifyCode finally serkey get bytes:" + serkey);
var totp = new Totp(serkey, step: sec);
Console.WriteLine("verifyCode");
Console.WriteLine(decodedString);
//var totp = new Totp(Encoding.ASCII.GetBytes(key), step: 60, OtpHashMode.Sha512);
//var totp = new Totp(Encoding.ASCII.GetBytes(key), step: 1, OtpHashMode.Sha512);
long time;
result = totp.VerifyTotp(code, out time, null);
_logger.LogInformation("time:" + time);
}
catch (Exception ex)
{
throw ex;
}
return result;
}
public (string, string) createCode(string mobile,int _step)
{
var code = "";
var base64StringKey = "";
try
{
//var bytes = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");
// var key = "MGI3OTNiZDg0OWVkZWM5NGZjNmYxZTJlNTEwMzM4MTZmZjRhZmIwZDhlYmVmMGJiMGM3NzllZjc1MDY5OGFmN2ZmYmFjYWEzZDY5YTBhYzg1YzQyZDQ3MTQ1NTIxZWY5NWE2N2QxOWYwYzczMWU0NzJiYjE1ODEyYWYxMjk3MTk=";
//var totp = new Totp(Encoding.ASCII.GetBytes(key), step: 60, OtpHashMode.Sha512);
// var totp = new Totp(Encoding.ASCII.GetBytes(key), step: 30, OtpHashMode.Sha512);
var now = DateTime.Now;
//var now = DateTime.Now;
var key = KeyGeneration.GenerateRandomKey(20);
// var data = now.Ticks.ToString() + mobile;
//var data = now + mobile;
var data = now + mobile;
// base64StringKey = ticks;
// var plainTextBytes = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ticks));
_logger.LogInformation("createCode");
_logger.LogInformation("now:" + now);
base64StringKey = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(data));
byte[] serkey = System.Text.Encoding.UTF8.GetBytes(data);
_logger.LogInformation("createCode finally serkey get bytes:" + serkey);
var totp = new Totp(serkey, step: _step);
code = totp.ComputeTotp();
//code = totp.ComputeTotp();
}
catch (Exception ex)
{
throw ex;
}
return (code, base64StringKey);
}
}
}
my expired time is 90 second
my expired time is 90 second
when the randomly return false , the time output is 0 , But it's still within the effective time
Ensure that your server and client system times are in sync.
How to recalculate the remaining time of each re-generated code, I am using the current timestamp as the key encryption.