Tolerate up to 0.5Hz difference in OTP timestamps
authorDan Fuhry <dan@enanocms.org>
Wed, 11 Jan 2017 13:02:34 +0000
changeset 12 31387f4022e5
parent 11 b9eb748ac1e4
child 13 8a8cdc21aa15
Tolerate up to 0.5Hz difference in OTP timestamps I've received complaints of OTP validation failures during the trial rollout at Datto. I dumped a few OTPs along with the times of validation and found that the user's Yubikey was running its oscillator at 8.32Hz. This commit adds tolerance for up to 0.5Hz of variation to the OTP's timestamp field.
yms/backend.php
yms/libotp.php
--- a/yms/backend.php	Mon Apr 11 11:23:30 2016 -0400
+++ b/yms/backend.php	Wed Jan 11 13:02:34 2017 +0000
@@ -347,22 +347,20 @@
     return 'REPLAYED_OTP';
   }
   
-  // update DB
-  $q = $db->sql_query("UPDATE " . table_prefix . "yms_yubikeys SET session_count = {$otp['session']}, token_count = {$otp['count']}, access_time = " . time() . ", token_time = {$otp['timestamp']} WHERE id = $yubikey_id;");
-  if ( !$q )
-    $db->_die();
-  
   // check timestamp
   if ( $otp['session'] == $session_count )
   {
     $expect_delta = time() - $access_time;
-    // 8Hz Yubikey internal clock
-    $actual_delta = intval(( $otp['timestamp'] - $token_time ) / 8);
-    $fuzz = 150;
+    // Tolerate up to a 0.5Hz deviance from 8Hz. I've observed Yubikey
+    // clocks running at 8.32Hz
+    $actual_delta = $otp['timestamp'] - $token_time;
+    $fuzz = 150 + round(($actual_delta / 7.5) - ($actual_delta / 8.5));
+    // Now that we've calculated fuzz, convert the actual delta to quasi-seconds
+    $actual_delta /= 8;
     if ( !yms_within($expect_delta, $actual_delta, $fuzz) )
     {
       // if we have a likely wraparound, just pass it
-      if ( !($token_time > 0xe80000 && $otp['timestamp'] < 0x800000) )
+      if ( !($token_time > 0xe80000 && $otp['timestamp'] < 0x080000) )
       {
         return 'BAD_OTP';
       }
@@ -370,6 +368,11 @@
     // $debug_array = array('ts_debug_delta_expected' => $expect_delta, 'ts_debug_delta_received' => $actual_delta);
   }
   
+  // update DB
+  $q = $db->sql_query("UPDATE " . table_prefix . "yms_yubikeys SET session_count = {$otp['session']}, token_count = {$otp['count']}, access_time = " . time() . ", token_time = {$otp['timestamp']} WHERE id = $yubikey_id;");
+  if ( !$q )
+    $db->_die();
+  
   // looks like we're good
   return 'OK';
 }
--- a/yms/libotp.php	Mon Apr 11 11:23:30 2016 -0400
+++ b/yms/libotp.php	Wed Jan 11 13:02:34 2017 +0000
@@ -78,7 +78,5 @@
 
 function yms_within($test, $control, $fuzz)
 {
-  $min = $control - $fuzz;
-  $max = $control + $fuzz;
-  return $test > $min && $test < $max;
+  return abs($control - $test) <= $fuzz;
 }