Otp.NET icon indicating copy to clipboard operation
Otp.NET copied to clipboard

VerifyTotp is returning false randomly even Step time is not expired

Open Anitha-cmd opened this issue 1 year ago • 10 comments

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

Anitha-cmd avatar Apr 10 '23 08:04 Anitha-cmd

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

kspearrin avatar Apr 10 '23 14:04 kspearrin

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

}

markbauer1975 avatar Apr 13 '23 22:04 markbauer1975

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

damiarnold avatar Apr 13 '23 23:04 damiarnold

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

markbauer1975 avatar Apr 14 '23 14:04 markbauer1975

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?

nabeel-servcorp avatar Jul 20 '23 01:07 nabeel-servcorp

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 avatar Oct 03 '23 14:10 lewisli666

@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<HtopCustomHelper> _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 avatar Oct 03 '23 14:10 lewisli666

my expired time is 90 second

lewisli666 avatar Oct 03 '23 14:10 lewisli666

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 avatar Oct 03 '23 15:10 lewisli666

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.

lewisli666 avatar Oct 04 '23 02:10 lewisli666