pyauth / pyotp

Python One-Time Password Library
https://pyauth.github.io/pyotp/
Other
2.96k stars 323 forks source link

Support resync in TOTP validation #11

Open jjude opened 9 years ago

jjude commented 9 years ago

Consider this test:

import pyotp
from time import sleep

key = pyotp.random_base32()
for i in range(1,10):
  otp = pyotp.TOTP(key).now()
  sleep(5)
  print pyotp.TOTP(key).verify(otp)

Sample output is:

True
False
True
True
True
True
True
False
True

I have seen this in production. I don't know why it fails sometimes. Any idea why? And how I can go about fixing it? Thank you, Joseph

thanatos commented 9 years ago

Looking at the implementation, I think I can answer this. The Wikipedia article on TOTP notes, emphasis mine,

Both the server and the client compute the token, then the server checks if the token supplied by the client matches the locally generated token. Some servers allow codes that should have been generated before or after the current time in order to account for slight clock skews, network latencies and user delays.

pyotp, simply, does not (see here). It would be great if this could be an option, I think.

AndCycle commented 9 years ago

the only totp python package that have time skew support is SpookyOTP, this should be easy to make it support, if anyone do the task.

kislyuk commented 9 years ago

I am repurposing this issue for skew support, and changing the title accordingly. If anyone has a preferred API for how this would work, please comment here.

kislyuk commented 9 years ago

From https://tools.ietf.org/html/rfc6238#section-6:

   Because of possible clock drifts between a client and a validation
   server, we RECOMMEND that the validator be set with a specific limit
   to the number of time steps a prover can be "out of synch" before
   being rejected.

   This limit can be set both forward and backward from the calculated
   time step on receipt of the OTP value.  If the time step is
   30 seconds as recommended, and the validator is set to only accept
   two time steps backward, then the maximum elapsed time drift would be
   around 89 seconds, i.e., 29 seconds in the calculated time step and
   60 seconds for two backward time steps.

   This would mean the validator could perform a validation against the
   current time and then two further validations for each backward step
   (for a total of 3 validations).  Upon successful validation, the
   validation server can record the detected clock drift for the token
   in terms of the number of time steps.  When a new OTP is received
   after this step, the validator can validate the OTP with the current
   timestamp adjusted with the recorded number of time-step clock drifts
   for the token.

   Also, it is important to note that the longer a prover has not sent
   an OTP to a validation system, the longer (potentially) the
   accumulated clock drift between the prover and the verifier.  In such
   cases, the automatic resynchronization described above may not work
   if the drift exceeds the allowed threshold.  Additional
   authentication measures should be used to safely authenticate the
   prover and explicitly resynchronize the clock drift between the
   prover and the validator.

It seems unambiguous that a "max_skew" parameter is needed, in number of time steps. The questions I have are:

tilkinsc commented 7 years ago

Was this resolved in a commit?

kislyuk commented 7 years ago

There is a new parameter, verify(valid_window=...), which allows for a fixed amount of skew, just like max_skew above. There is no resync capability yet.

tilkinsc commented 7 years ago

Why would there need to be resync capabilities? Your computer isn't going to jump a second ahead or a second behind. The reason why it initially failed was because such a reason and TOTP implementation requests that there is a TTL component. Is this really necessary for the library to have (especially since you have to take latency into account and have the user give you the ms delay and fix and yada yada)

kislyuk commented 7 years ago

Please read the RFC, it explains the use case for clock skew support and resynchronization.

legkoszkur commented 11 months ago

Still have similar issue in 2023, always my first probe fails even my keys are the same and interval is setup on 30.

kislyuk commented 11 months ago

Clock skew is supported via the valid_window keyword argument.

Because resync is stateful, I don't think this library will fully support resync functionality directly. I think what we need in PyOTP to support resync is the ability to return where in the valid window the code was accepted, and documentation on how to use that.