Created
August 21, 2014 13:36
-
-
Save bbolli/d1ec564f31764326c137 to your computer and use it in GitHub Desktop.
HOTP/TOTP one time password (RFC 4226/RFC 6238) implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""HOTP/TOTP one time password (RFC 4226/RFC 6238) implementation""" | |
import hashlib | |
import hmac | |
import struct | |
def hotp(secret, counter, digits=6, alg=hashlib.sha1): | |
"""Calculate the HOTP value for the given secret and counter. | |
Secret and test values from appendix D of RFC 4226. | |
>>> s = '12345678901234567890' | |
>>> hotp(s, 0) | |
'755224' | |
>>> hotp(s, 1) | |
'287082' | |
>>> hotp(s, 2) | |
'359152' | |
>>> hotp(s, 3) | |
'969429' | |
>>> hotp(s, 4) | |
'338314' | |
>>> hotp(s, 5) | |
'254676' | |
>>> hotp(s, 6) | |
'287922' | |
>>> hotp(s, 7) | |
'162583' | |
>>> hotp(s, 8) | |
'399871' | |
>>> hotp(s, 9) | |
'520489' | |
>>> hotp(s, 4, digits=7) # check for leading zeros | |
'0338314' | |
""" | |
c = struct.pack('>Q', counter) | |
hash = hmac.new(secret, c, alg).digest() | |
offset = ord(hash[-1]) & 0x0F | |
result = hash[offset:offset+4] | |
result = struct.unpack('>L', result)[0] & 0x7FFFFFFF | |
result = result % 10 ** digits | |
return '%0*d' % (digits, result) | |
def _totp_counter(ts, timebase=0, timestep=30): | |
"""Calculate the TOTP counter for a given timestamp. | |
>>> ts = 1400000000; ts -= ts % 30 # let it start on a timestep | |
>>> base = _totp_counter(ts) | |
>>> base == _totp_counter(ts + 29) | |
True | |
>>> base != _totp_counter(ts + 30) | |
True | |
>>> base != _totp_counter(ts - 1) | |
True | |
>>> base != _totp_counter(ts, timebase=90) | |
True | |
>>> base == _totp_counter(ts + 90, timebase=90) | |
True | |
>>> base != _totp_counter(ts, timestep=60) | |
True | |
>>> base == _totp_counter(ts * 2, timestep=60) | |
True | |
""" | |
return int((ts - timebase) / timestep) | |
def totp(secret, ts=None, timebase=0, timestep=30, digits=6, alg=hashlib.sha1): | |
"""Calculate the TOTP value for a given timestamp.""" | |
if ts is None: | |
import time | |
ts = time.time() | |
return hotp(secret, _totp_counter(ts, timebase, timestep), digits, alg) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment