Skip to content

Instantly share code, notes, and snippets.

@MayankFawkes
Last active November 19, 2023 16:01
Show Gist options
  • Save MayankFawkes/53049919390234ec253f8d0c04b261e7 to your computer and use it in GitHub Desktop.
Save MayankFawkes/53049919390234ec253f8d0c04b261e7 to your computer and use it in GitHub Desktop.
python otp management without database, with overtime-handler
# -*- coding: utf-8 -*-
'''
>>> otp = HashOTP()
>>> otp_code = h.now("MY NAME IS MAYANK")
123456
>>> verify = h.verify("MY NAME IS MAYANK", "123456")
True
'''
import hashlib, time
import hmac
from functools import wraps
from contextlib import contextmanager
__author__ = "Mayank Gupta"
class HashOTP:
def __init__(self, digits:int=6, interval:int=10*60, offset_interval:int=5*60):
self.interval = interval
self.digits = digits
self.offset_interval = offset_interval
self._overtime = 0
@contextmanager
def overtime(self):
try:
self._overtime = 1
yield 1
finally:
self._overtime = 0
def verify(self, key:str, otp:int):
stamp = time.time()
if self.now(key) == str(otp):
return True
with self.overtime():
start_time = self._getmsg() * self.interval
if self.now(key) == str(otp) and (stamp - start_time) <= (self.interval + self.offset_interval):
return True
return False
def now(self, key:str) -> str:
key = self._enc_key(key=key)
msg = self._getmsg()
hasher = hmac.new(key, self.int_to_bytestring(msg), hashlib.sha256)
hmac_hash = bytearray(hasher.digest())
offset = hmac_hash[-1] & 0xf
code = ((hmac_hash[offset] & 0x7f) << 24 |
(hmac_hash[offset + 1] & 0xff) << 16 |
(hmac_hash[offset + 2] & 0xff) << 8 |
(hmac_hash[offset + 3] & 0xff))
str_code = str(code % 10 ** self.digits)
while len(str_code) < self.digits:
str_code = '0' + str_code
return str_code
@staticmethod
def int_to_bytestring(i: int, padding: int = 8) -> bytes:
result = bytearray()
while i != 0:
result.append(i & 0xFF)
i = i >> 8
return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))
def _getmsg(self):
return (int(time.time()) // self.interval) - self._overtime
def _enc_key(self, key:str):
key = key.encode()
key_hash = hashlib.sha1(key)
return key_hash.digest()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment