KEYCLOAK-18880 TimeBasedOTP should use look-around to mitigate clock skew
Previously the TimeBasedOTP only looked behind to mitigate clock skew. We now look around (look ahead + look behind) to better accommodate clock skew.
This commit is contained in:
parent
1c2752300b
commit
5898f9c390
1 changed files with 20 additions and 8 deletions
|
@ -42,10 +42,10 @@ public class TimeBasedOTP extends HmacOTP {
|
|||
* @param algorithm the encryption algorithm
|
||||
* @param numberDigits the number of digits for tokens
|
||||
* @param timeIntervalInSeconds the number of seconds a token is valid
|
||||
* @param lookAheadWindow the number of previous intervals that should be used to validate tokens.
|
||||
* @param lookAroundWindow the number of previous and following intervals that should be used to validate tokens.
|
||||
*/
|
||||
public TimeBasedOTP(String algorithm, int numberDigits, int timeIntervalInSeconds, int lookAheadWindow) {
|
||||
super(numberDigits, algorithm, lookAheadWindow);
|
||||
public TimeBasedOTP(String algorithm, int numberDigits, int timeIntervalInSeconds, int lookAroundWindow) {
|
||||
super(numberDigits, algorithm, lookAroundWindow);
|
||||
this.clock = new Clock(timeIntervalInSeconds);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,9 @@ public class TimeBasedOTP extends HmacOTP {
|
|||
String steps = Long.toHexString(T).toUpperCase();
|
||||
|
||||
// Just get a 16 digit string
|
||||
while (steps.length() < 16)
|
||||
while (steps.length() < 16) {
|
||||
steps = "0" + steps;
|
||||
}
|
||||
|
||||
return generateOTP(secretKey, steps, this.numberDigits, this.algorithm);
|
||||
}
|
||||
|
@ -71,17 +72,21 @@ public class TimeBasedOTP extends HmacOTP {
|
|||
*
|
||||
* @param token OTP string to validate
|
||||
* @param secret Shared secret
|
||||
* @return
|
||||
* @return true of the token is valid
|
||||
*/
|
||||
public boolean validateTOTP(String token, byte[] secret) {
|
||||
long currentInterval = this.clock.getCurrentInterval();
|
||||
|
||||
for (int i = this.lookAheadWindow; i >= 0; --i) {
|
||||
String steps = Long.toHexString(currentInterval - i).toUpperCase();
|
||||
for (int i = 0; i <= (lookAheadWindow * 2); i++) {
|
||||
long delta = clockSkewIndexToDelta(i);
|
||||
long adjustedInterval = currentInterval + delta;
|
||||
|
||||
String steps = Long.toHexString(adjustedInterval).toUpperCase();
|
||||
|
||||
// Just get a 16 digit string
|
||||
while (steps.length() < 16)
|
||||
while (steps.length() < 16) {
|
||||
steps = "0" + steps;
|
||||
}
|
||||
|
||||
String candidate = generateOTP(new String(secret), steps, this.numberDigits, this.algorithm);
|
||||
|
||||
|
@ -93,6 +98,13 @@ public class TimeBasedOTP extends HmacOTP {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* maps 0, 1, 2, 3, 4, 5, 6, 7, ... to 0, -1, 1, -2, 2, -3, 3, ...
|
||||
*/
|
||||
public static long clockSkewIndexToDelta(int idx) {
|
||||
return (idx + 1) / 2 * (1 - (idx % 2) * 2);
|
||||
}
|
||||
|
||||
public void setCalendar(Calendar calendar) {
|
||||
this.clock.setCalendar(calendar);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue