commit 9466f0269607098304c03f87e2ed116b93a2c01d
parent d989a19f764c5838e6a4ae698af2e32af567a3fe
Author: BlackDex <black.dex@gmail.com>
Date: Sat, 12 Oct 2019 15:28:28 +0200
Recoded TOTP time drift validation
Diffstat:
1 file changed, 26 insertions(+), 37 deletions(-)
diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs
@@ -118,43 +118,32 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, conn: &
let current_time: u64 = SystemTime::now().duration_since(UNIX_EPOCH)
.expect("Earlier than 1970-01-01 00:00:00 UTC").as_secs();
- // First check the current time for a valid token.
- let time_step_now = (current_time / 30) as i32;
- let generated_now = totp_raw_custom_time(&decoded_secret, 6, 0, 30, current_time, &HashType::SHA1);
- if generated_now == totp_code && time_step_now > twofactor.last_used {
- twofactor.last_used = time_step_now;
- twofactor.save(&conn)?;
- return Ok(());
- } else if generated_now == totp_code && time_step_now <= twofactor.last_used {
- warn!("This or a future TOTP code has already been used!");
- err!("Invalid TOTP code!");
- }
-
- // Check for time drifted codes
- // First check the previous TOTP code
- let time_step_prev = ((current_time - 30) / 30) as i32;
- let generated_prev = totp_raw_custom_time(&decoded_secret, 6, 0, 30, current_time - 30, &HashType::SHA1);
- if generated_prev == totp_code && time_step_prev > twofactor.last_used {
- info!("TOTP Time drift detected. Token is valide for one step on the past.");
- twofactor.last_used = time_step_prev;
- twofactor.save(&conn)?;
- return Ok(());
- } else if generated_prev == totp_code && time_step_prev <= twofactor.last_used {
- warn!("This or a future TOTP code has already been used!");
- err!("Invalid TOTP code!");
- }
-
- // Second check the next TOTP code
- let time_step_next = ((current_time + 30) / 30) as i32;
- let generated_next = totp_raw_custom_time(&decoded_secret, 6, 0, 30, current_time + 30, &HashType::SHA1);
- if generated_next == totp_code && time_step_next > twofactor.last_used {
- info!("TOTP Time drift detected. Token is valide for one step on the future.");
- twofactor.last_used = time_step_next;
- twofactor.save(&conn)?;
- return Ok(());
- } else if generated_next == totp_code && time_step_next <= twofactor.last_used {
- warn!("This or a previous TOTP code has already been used!");
- err!("Invalid TOTP code!");
+ // The amount of steps back and forward in time
+ let steps = 1;
+ for step in -steps..=steps {
+
+ let time_step = (current_time / 30) as i32 + step;
+ // We need to calculate the time offsite and cast it as an i128.
+ // Else we can't do math with it on a default u64 variable.
+ let time_offset: i128 = (step * 30).into();
+ let generated = totp_raw_custom_time(&decoded_secret, 6, 0, 30, (current_time as i128 + time_offset) as u64, &HashType::SHA1);
+
+ // Check the the given code equals the generated and if the time_step is larger then the one last used.
+ if generated == totp_code && time_step > twofactor.last_used {
+
+ // If the step does not equals 0 the time is drifted either server or client side.
+ if step != 0 {
+ info!("TOTP Time drift detected. The step offset is {}", step);
+ }
+
+ // Save the last used time step so only totp time steps higher then this one are allowed.
+ twofactor.last_used = time_step;
+ twofactor.save(&conn)?;
+ return Ok(());
+ } else if generated == totp_code && time_step <= twofactor.last_used {
+ warn!("This or a TOTP code within {} steps back and forward has already been used!", steps);
+ err!("Invalid TOTP Code!");
+ }
}
// Else no valide code received, deny access