Skip to content

TOTP invalid sometimes in same or previous period #32

Closed
@sax

Description

I have an issue where sometimes I generate a TOTP which is immediately invalid, even when I check against the previous time period. My understanding is that per the grace period doc if I use the same time, simple subtraction should allow my TOTP to pass.

Here is a test that demonstrates the problem:

Mix.install([:nimble_totp])
ExUnit.start()

defmodule Fixture do
  @spec generate_totp(binary(), pos_integer()) :: binary()
  def generate_totp(secret, period, time \\ System.os_time(:second)) do
    NimbleTOTP.verification_code(secret, period: period, time: time)
  end

  @spec valid_totp?(binary(), binary(), integer()) :: boolean()
  def valid_totp?(secret, totp, period, time \\ System.os_time(:second)) do
    (NimbleTOTP.valid?(secret, totp, period: period, time: time) ||
       NimbleTOTP.valid?(secret, totp, period: period, time: time - period))
    |> if do
      true
    else
      false
    end
  end
end

defmodule NimbleTOTPTest do
  use ExUnit.Case

  test "is valid within the period in which the totp was generated" do
    time = System.os_time(:second)
    totp = Fixture.generate_totp("abc123", 100, time)
    assert Fixture.valid_totp?("abc123", totp, 100, time - 10)
  end

  test "is valid within the next time period following expiration" do
    time = System.os_time(:second)
    totp = Fixture.generate_totp("abc123", 100, time - 102)
    assert Fixture.valid_totp?("abc123", totp, 100, time)
  end

  test "is invalid after two time periods" do
    time = System.os_time(:second)
    totp = Fixture.generate_totp("abc123", 100, time - 205)
    refute Fixture.valid_totp?("abc123", totp, 100, time)
  end
end

If I save that file as nimble_totp_test.exs and run the following command:

while elixir -r nimble_totp_test.exs; do
  echo
done

It seems like the only failures happen the time is divisible by 10.

  1) test is valid within the period in which the totp was generated (NimbleTOTPTest)
     nimble_totp_test.exs:25
     Expected truthy, got false
     code: assert Fixture.valid_totp?("abc123", totp, 100, time - 10)
     arguments:

         # 1
         "abc123"

         # 2
         "537101"

         # 3
         100

         # 4
         1730929790

     stacktrace:
       nimble_totp_test.exs:28: (test)



  2) test is valid within the next time period following expiration (NimbleTOTPTest)
     nimble_totp_test.exs:31
     Expected truthy, got false
     code: assert Fixture.valid_totp?("abc123", totp, 100, time)
     arguments:

         # 1
         "abc123"

         # 2
         "982784"

         # 3
         100

         # 4
         1730929800

     stacktrace:
       nimble_totp_test.exs:34: (test)

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions