j256 / two-factor-auth

Two Factor Authentication Java code implementing the Time-based One-time Password Algorithm

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

windowMillis setting is not correct

bino7 opened this issue · comments

commented

in function validateCurrentNumber(String base32Secret, int authNumber, int windowMillis, long timeMillis,
int timeStepSeconds) throws GeneralSecurityException

for (long millis = from; millis <= to; millis += timeStepMillis) {

should be updated to

for (long millis = from; millis <= to; millis += 1000) {

commented

Sorry, I don't quite understand. Can you either make a PR or show a unit test that fails because of the bad code?

I ran into a problem when using small window periods and I initially thought I agreed with the reporter of this issue that the for loop increment was wrong, but after some testing the problem isn't with the for loop's increment, but the initialisation of the from and to values, specifically when the window period is less than half the step time.

If, for instance, the step is the default of 30 seconds and the window time is 10 seconds, the for loop will only run once. This has the unintended side-effect that a client-generated code will only be valid from 10 seconds after it is valid from - because the for loop will generate the code from 10 seconds ago and as it only runs once, the current code won't be valid until at least 10 seconds have passed and the for loop is now generating the current code.

One solution would be to change the for loop to loop every 1000 millis, as in the initial report, but this would generate a lot of identical codes unnecessarily.

A better solution would be to change the behaviour of the from and to values in the case where the window period is less than half the step period to ensure that the current code, at the very least, is always generated immediately.

The first possible approach would be that if the window period is less than half the step period, to then force the window period to be half the step period, but this wouldn't ever allow for small window periods on previous codes.

Another approach would be to simply set the to value to be the from value plus the step period, thereby ensuring the for loop at least always generates the current code. The from value is fine as-is and abides by the intent of the window period in that the old code is only valid for that limited period.

The code that works for me and allows for smaller window periods is:

      final long timeStepMillis = timeStepSeconds * 1000L;
      long from = timeMillis;
      long to = timeMillis;
      if (windowMillis > 0) {
        from -= windowMillis;
        to += windowMillis;
        if (timeStepMillis / windowMillis > 2) {
          to = from + timeStepMillis;
        }
      }
commented

Wow. Took me a while to understand but now I do. I've been able to reproduce this in tests.