Squashed 'utils_v2/' content from commit ac9e5c3
git-subtree-dir: utils_v2 git-subtree-split: ac9e5c331d9da4be516c6149cbdc9b5ebc19aa64
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Tuesday, 10th Sept., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide a way to generate and verify OTPs.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
01. https://pyauth.github.io/pyotp/#
|
||||
02. https://en.wikipedia.org/wiki/Google_Authenticator
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# To work with OTPs:
|
||||
import pyotp
|
||||
import base64
|
||||
import hashlib
|
||||
|
||||
# To work with date and time:
|
||||
import time
|
||||
import datetime
|
||||
|
||||
# For debugging:
|
||||
from icecream import IceCreamDebugger
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** CLASSES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
class HashedOTP:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
secret
|
||||
):
|
||||
|
||||
"""
|
||||
Used to generate and verify HMAC-based OTPs.
|
||||
:param secret: The key to use to generate and verify OTPs.
|
||||
"""
|
||||
|
||||
# Note down the input variables:
|
||||
self.__secret = secret
|
||||
self.__otp_client = pyotp.HOTP(secret)
|
||||
|
||||
@staticmethod
|
||||
def generate_secret(message = None):
|
||||
|
||||
"""
|
||||
Generate a secret key to then use to generate and verify the OTPs.
|
||||
You may override the random generator by giving a "message" of any length.
|
||||
:param message: A custom value to convert into a key. Avoid using this for better security, but this can be used
|
||||
to generate keys based on user identifiers. THERE IS NO RANDOMNESS IF YOU USE THIS FEATURE. IT IS FOR
|
||||
CONVENIENCE ONLY. NOT RECOMMENDED.
|
||||
:return: The key (as a string) that can be used to generate and verify the OTPs.
|
||||
"""
|
||||
|
||||
# If the user wants to generate a key from a custom input:
|
||||
if message:
|
||||
|
||||
# Ensure we have a bytes object:
|
||||
if not isinstance(message, (str, bytes)): message = str(message)
|
||||
if isinstance(message, str): message = message.encode("utf-8")
|
||||
|
||||
# Hash the bytes object:
|
||||
sha256_hash = hashlib.sha256()
|
||||
sha256_hash.update(message)
|
||||
hashed_key = sha256_hash.digest()
|
||||
|
||||
# Convert to base-32:
|
||||
return base64.b32encode(hashed_key).decode("utf-8")
|
||||
|
||||
# If the user wants a totally random key:
|
||||
else: return pyotp.random_base32()
|
||||
|
||||
def generate_otp(self, count: int):
|
||||
|
||||
"""
|
||||
Generates the OTP at a particular step.
|
||||
:param count: The step at which the OTP needs to be generated.
|
||||
:return: The OTP string (6 digits).
|
||||
"""
|
||||
|
||||
return str(self.__otp_client.at(count))
|
||||
|
||||
def verify_otp(self, otp, count: int):
|
||||
|
||||
"""
|
||||
Verifies the claimed OTP.
|
||||
:param otp: The OTP as claimed by the end user.
|
||||
:param count: The step at which the OTP needs to be verified.
|
||||
:return: True if the OTP is valid, else False.
|
||||
"""
|
||||
|
||||
return self.__otp_client.verify(otp, counter = count)
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
Reference in New Issue
Block a user