windowMillis setting is not correct
bino7 opened this issue · comments
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) {
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;
}
}
Wow. Took me a while to understand but now I do. I've been able to reproduce this in tests.