Repository: privacyidea/privacyidea

Issue:
TOTP token uses wrong attribute for manual sync
The TOTP token uses the wrong attribute for calculating the resync (wrong attriubte: ``timeWindow``).
It should use the ``syncWindow``.
The ``_autosync`` method (actually does it right)[https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/tokens/totptoken.py#L398]!

When the timeWindow is increased, a manual resync would work:

      update tokeninfo set Value="1800" where Key="timeWindow" and token_id=62;

This is the problematic code:
https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/tokens/totptoken.py#L475

We need to use ``self.get_sync_window()`` instead of ``self.timeWindow`` for both ``res1`` and ``res2``.

As we would use the default syncwindow of 1000 just like with the autoresync this would be 1000 count, i.e. 1000*30sec = 500 Minutes --- roughly 11 hours, which is fine.

~~~~python
        # check 2nd value
        hmac2Otp = HmacOtp(secretHOtp,
                           counter,
                           otplen,
                           self.get_hashlib(self.hashlib))
        log.debug("{0!s} in otpkey: {1!s} ".format(otp2, secretHOtp))
        res2 = hmac2Otp.checkOtp(otp2,
                                 int(self.get_sync_window()),
                                 symetric=True)  # TEST -remove the 10
        log.debug("res 2: {0!r}".format(res2))
        # check 1st value
        hmac2Otp = HmacOtp(secretHOtp,
                           counter - 1,
                           otplen,
                           self.get_hashlib(self.hashlib))
        log.debug("{0!s} in otpkey: {1!s} ".format(otp1, secretHOtp))
        res1 = hmac2Otp.checkOtp(otp1,
                                 int(self.get_sync_window()),
                                 symetric=True)  # TEST -remove the 10
~~~~

We also need to add some sensible tests!


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/tokens/totptoken.py b/privacyidea/lib/tokens/totptoken.py
--- a/privacyidea/lib/tokens/totptoken.py
+++ b/privacyidea/lib/tokens/totptoken.py
@@ -465,7 +465,8 @@ def resync(self, otp1, otp2, options=None):
         oCount = self.get_otp_count()
 
         log.debug("tokenCounter: {0!r}".format(oCount))
-        log.debug("now checking window {0!s}, timeStepping {1!s}".format(self.timewindow, self.timestep))
+        sync_window = self.get_sync_window()
+        log.debug("now checking window {0!s}, timeStepping {1!s}".format(sync_window, self.timestep))
         # check 2nd value
         hmac2Otp = HmacOtp(secretHOtp,
                            counter,
@@ -473,7 +474,7 @@ def resync(self, otp1, otp2, options=None):
                            self.get_hashlib(self.hashlib))
         log.debug("{0!s} in otpkey: {1!s} ".format(otp2, secretHOtp))
         res2 = hmac2Otp.checkOtp(otp2,
-                                 int(self.timewindow / self.timestep),
+                                 int(sync_window),
                                  symetric=True)  # TEST -remove the 10
         log.debug("res 2: {0!r}".format(res2))
         # check 1st value
@@ -483,7 +484,7 @@ def resync(self, otp1, otp2, options=None):
                            self.get_hashlib(self.hashlib))
         log.debug("{0!s} in otpkey: {1!s} ".format(otp1, secretHOtp))
         res1 = hmac2Otp.checkOtp(otp1,
-                                 int(self.timewindow / self.timestep),
+                                 int(sync_window),
                                  symetric=True)  # TEST -remove the 10
         log.debug("res 1: {0!r}".format(res1))
 


Test patch:
diff --git a/tests/test_lib_tokens_totp.py b/tests/test_lib_tokens_totp.py
--- a/tests/test_lib_tokens_totp.py
+++ b/tests/test_lib_tokens_totp.py
@@ -612,15 +612,24 @@ def test_23_resync(self):
         token = TotpTokenClass(db_token)
         token.update({"otpkey": self.otpkey,
                       "otplen": 6})
-        token.token.count = 47251400
         token.set_sync_window(1000)
-        # Successful resync
         # 705493 -> 47251649
         # 589836 -> 47251650
         # So the token might be at time 47251650,
         # but the server time is 47251600
+
+        # The server time is 2000*30 seconds further, the resync will fail
+        # 47253650 - 47251650 = 2000 ticks away
+        token.token.count = 47251400
+        r = token.resync("705493", "589836",
+                         options={"initTime": 47253650 * 30})
+        self.assertFalse(r)
+        # Successful resync
+        token.token.count = 47251400
+        # The server time is 200*30 seconds further
+        # 47251850 - 47251650 = 200 ticks away
         r = token.resync("705493", "589836",
-                         options={"initTime": 47251650 * 30})
+                         options={"initTime": 47251850 * 30})
         self.assertTrue(r is True, r)
         # resync fails
         token.token.count = 0