Created
April 29, 2019 07:16
-
-
Save livingstonetech/32ec98d94e809bbf1150a71d6210fe19 to your computer and use it in GitHub Desktop.
Reference code to calculate TOTP, given a base32 secret.
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
#!/usr/bin/env python3 | |
""" | |
Gist to calculate Time-based One Time Password (TOTP). | |
This function is designed to be used in two modes: | |
- Current Mode | |
No timestamp supplied, and the current timestamp is considered. | |
This mode gives the totp at the present moment in time. | |
- Historic Mode | |
Here an explicit timestamp is supplied. This is to calculate a TOTP | |
that had occured at a moment in history. | |
""" | |
import hashlib | |
import hmac | |
import time | |
import base64 | |
import math | |
def calculate_totp(secret, unix_ts=None, power=6): | |
""" | |
The main function to calculate TOTP. | |
Parameters: | |
secret (str): Base32 secret for TOTP as string | |
unix_ts (int): Current or arbitrary unix timestamp as integer | |
power (int): Power of modulus. Essentially, the number of digits the | |
TOTP must have | |
Returns: | |
final_totp (int): Final calculated OTP as an integer. | |
Returns None if fails | |
""" | |
print("[+] Checking secret...") | |
if secret is None or secret == "": | |
print("---> [!] Secret is None or empty") | |
print("---> [!] Exiting...") | |
return None | |
try: | |
print("[+] Trying to convert secret to bytes") | |
secret_bytes = base64.b32decode(secret) | |
except Exception as e: | |
print("---> [!] Something unforeseen happened.") | |
print("---> [!] Error: {}".format(e)) | |
print("---> [!] Exiting...") | |
return None | |
print("[+] Verified secret.") | |
if unix_ts is None: | |
print("[+] Mode:\tCurrent Mode") | |
unix_ts = int(time.time()) | |
else: | |
print("[+] Mode:\tHistoric Mode") | |
print("[+] Using time:\t{}".format(int(unix_ts))) | |
current_time = math.floor(int(unix_ts) / 30) | |
print("[+] Calculating HMAC-SHA1 sum...") | |
try: | |
current_time_bytes = str(current_time).encode() | |
hmac_sha1_sum = hmac.new(secret_bytes, | |
msg=current_time_bytes, | |
digestmod=hashlib.sha1) | |
print("---> [+] HMAC-SHA1 sum:\t{}".format(hmac_sha1_sum.hexdigest())) | |
except Exception as e: | |
print("---> [!] Something unforeseen happened.") | |
print("---> [!] Error: {}".format(e)) | |
print("---> [!] Exiting...") | |
return None | |
hmac_sha1_sum_int = int(hmac_sha1_sum.hexdigest(), 16) | |
print("---> [+] HMAC-SHA1 sum (int):\t{}".format(hmac_sha1_sum_int)) | |
mod_power = 10 ** power | |
print("[+] Calculating TOTP with power:\t{}".format(mod_power)) | |
temp_totp = hmac_sha1_sum_int % mod_power | |
print("[+] Checking if padding is required") | |
if temp_totp < (mod_power / 10): | |
print("---> [+] Padding OTP") | |
while temp_totp < (mod_power / 10): | |
temp_totp = temp_totp * 10 | |
else: | |
print("---> [+] No padding required...") | |
final_otp = temp_totp | |
return final_otp | |
if __name__ == "__main__": | |
print("\n\n-------TESTS-------\n\n") | |
CURRENT_TIME_TOTP = calculate_totp("RPFYC5FREUVZ22I7") | |
print("[+] Current Time TOTP: {}".format(CURRENT_TIME_TOTP)) | |
ARBITRARY_TIME_TOTP = calculate_totp("RPFYC5FREUVZ22I7", | |
unix_ts=829974000) | |
print("[+] Arbitrary Time TOTP: {}".format(ARBITRARY_TIME_TOTP)) | |
# Output = 719686 | |
ARBITRARY_POWER_TOTP = calculate_totp("RPFYC5FREUVZ22I7", | |
power=8) | |
print("[+] Arbitrary Time TOTP: {}".format(ARBITRARY_POWER_TOTP)) | |
# Output = 25683649 | |
ARBITRARY_ALL_TOTP = calculate_totp("RPFYC5FREUVZ22I7", | |
unix_ts=829974000, | |
power=4) | |
print("[+] Arbitrary All TOTP: {}".format(ARBITRARY_ALL_TOTP)) | |
# Output = 9686 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample Output