Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VerifyTotp is returning false randomly even Step time is not expired #43

Open
Anitha-cmd opened this issue Apr 10, 2023 · 10 comments
Open

Comments

@Anitha-cmd
Copy link

HI , VerifyTotp is returning false randomly even Step time is not expired. Please suggest us what should we have to do now.

@kspearrin
Copy link
Owner

Ensure that your server and client system times are in sync.

@markbauer1975
Copy link

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?

@kspearrin @Anitha-cmd

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");

}

@damiarnold
Copy link

damiarnold commented Apr 13, 2023

@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);

@markbauer1975
Copy link

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...

@nabeel-servcorp
Copy link

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?

@lewisli666
Copy link

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?

@lewisli666
Copy link

@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);

    }


}

}

@lewisli666
Copy link

my expired time is 90 second

@lewisli666
Copy link

lewisli666 commented Oct 3, 2023

my expired time is 90 second

when the randomly return false , the time output is 0 , But it's still within the effective time

@lewisli666
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants