Search code examples
c#.nettotp

Otp.NET Verification Window not understandable


I've a problem with Otp.NET lib for TOTP generation/verification. Due to some reasons I've a requirement where I need to generate totp codes valid up to some days.

But following test code does not work:

using OtpNet;
using System;

// Secret key for TOTP (must be consistent across all tests)
byte[] secretKey = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");

// Create a Totp instance with an optional time offset
Totp totpGenerator = new Totp(secretKey);

// Simulate generating TOTP at different dates
DateTime generationTime = DateTime.Now;
string totpCode = totpGenerator.ComputeTotp(generationTime);
Console.WriteLine($"TOTP generated on {generationTime}: {totpCode}");

TestTotpValidity(totpCode, generationTime.AddDays(5)); // 5 days later. this should be valid but is invalid.

TestTotpValidity(totpCode, generationTime.AddDays(-5)); // 5 days earlier. this should be not valid but is valid.

TestTotpValidity(totpCode, generationTime.AddDays(7));  // 7 days later. this should be valid but is invalid.
TestTotpValidity(totpCode, generationTime.AddDays(-7));  // 7 days earlier. this should be not valid but is valid.

TestTotpValidity(totpCode, generationTime.AddDays(8));  // 8 days later. This should be invalid and is invalid.
TestTotpValidity(totpCode, generationTime.AddDays(-8));  // 8 days earlier. this should be not valid and is invalid.

void TestTotpValidity(string totpCode, DateTime verificationTime)
{
    var validator = new Totp(secretKey);
    long timeStepMatched;
    bool isValid = validator.VerifyTotp(verificationTime, totpCode, out timeStepMatched, new VerificationWindow(previous: 2, future: 2880 * 7)); // Custom verification window. 2880 steps / day -> 4 days

    Console.ForegroundColor = isValid ? ConsoleColor.DarkGreen : ConsoleColor.DarkRed;
    Console.WriteLine($"Verifying TOTP on {verificationTime} (time step matched {timeStepMatched}): {isValid}");
    Console.ResetColor();
}

This is the console output:

TOTP generated on 05/16/2024 10:08:22: 935002
39;49mVerifying TOTP on 05/21/2024 10:08:22 (time step matched 0): False
[39;49m[39;49mVerifying TOTP on 05/11/2024 10:08:22 (time step matched 57195136): True
[39;49m[39;49mVerifying TOTP on 05/23/2024 10:08:22 (time step matched 0): False
[39;49m[39;49mVerifying TOTP on 05/09/2024 10:08:22 (time step matched 57195136): True
[39;49m[39;49mVerifying TOTP on 05/24/2024 10:08:22 (time step matched 0): False
[39;49m[39;49mVerifying TOTP on 05/08/2024 10:08:22 (time step matched 0): False
[39;49m

I'm not understanding something from their doc or am I doing something wrong?

Please, some help about this would be worth.

Here you have dotnet fiddle: https://dotnetfiddle.net/A1dOEm

Edit: Now instances for generation and each validation are different so should be valid anytime. I want to accept only once but I would like to test that validation window is well defined. In this example, TOTP code generated today should be valid until 7 days ahead if I don't misunderstood something from documentation.


Solution

  • First of all, thanks to ralf comment above which is key.

    As there a two times(creation,validation) here there might be a problem what previous/future means here.

    Here you can find corrected fiddle with corrected VerificationWindow.

    It was the opposite I understood before.

    If you want your code to be valid from now to 7 days ahead, VerificationWindow must be 7 days past, not 7 days future.

    using System;
    using OtpNet;
    
    byte[] secretKey = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");
    Totp totpGenerator = new Totp(secretKey);
    
    // Generate TOTP code using the current time
    DateTime generationTime = DateTime.Now;
    string totpCode = totpGenerator.ComputeTotp();
    Console.WriteLine($"TOTP generated on {generationTime}: {totpCode}");
    
    // Test the validity of the TOTP code from 7 days in the past to 9 days in the future
    for (var days = -7; days < 10; days++)
    {
        TestTotpValidity(totpCode, generationTime.AddDays(days));
    }
    
    void TestTotpValidity(string totpCode, DateTime verificationTime)
    {
        var validator = new Totp(secretKey);
        long timeStepMatched;
        
        // 2880 steps per day, window of 7 days in the past and 2 steps in the future
        bool isValid = validator.VerifyTotp(verificationTime, totpCode, out timeStepMatched, new VerificationWindow(previous: 2880 * 7, future: 2));
        Console.WriteLine($"Verifying TOTP on {verificationTime} (time step matched {timeStepMatched}): {isValid}");
    }
    
    TOTP generated on 05/16/2024 12:38:52: 065465
    Verifying TOTP on 05/09/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/10/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/11/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/12/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/13/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/14/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/15/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/16/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/17/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/18/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/19/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/20/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/21/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/22/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/23/2024 12:38:52 (time step matched 57195437): True
    Verifying TOTP on 05/24/2024 12:38:52 (time step matched 0): False
    Verifying TOTP on 05/25/2024 12:38:52 (time step matched 0): False
    

    https://dotnetfiddle.net/7frFel