Last active
June 23, 2021 15:59
-
-
Save rubenhorn/eb07959c162c754533793cd9951e774c to your computer and use it in GitHub Desktop.
Simple TOTP implementation using Python
This file contains hidden or 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/python3 | |
import time, hashlib, hmac, base64 | |
secret = 'q5ma qo4x irgv cm6k auai kjyv ak57 5keu' | |
T_0 = 0 | |
X = 30 | |
d = 6 | |
K = base64.b32decode(secret.upper().replace(' ', '')) | |
def HMAC(K, C, block_size=64, output_size=20): | |
mac = hmac.new(K, C, hashlib.sha1).digest() | |
return mac | |
def DT(b): | |
offset = b[19] & 0xf | |
return b[offset:offset+4] | |
def HOTP(K, C): | |
global d | |
HS = HMAC(K, C.to_bytes(8, byteorder='big')) | |
DBC1 = int.from_bytes(DT(HS), byteorder='big') | |
DBC2 = DBC1 & 0x7fffffff | |
return DBC2 % (10**d) | |
def TOTP(K, timestamp): | |
global T_0, X | |
T = int((timestamp - T_0) / X) | |
return HOTP(K, T) | |
print(TOTP(K, int(time.time()))) |
This file contains hidden or 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/python3 | |
import time, hashlib, hmac | |
secret = 'q5ma qo4x irgv cm6k auai kjyv ak57 5keu' | |
T_0 = 0 | |
X = 30 | |
d = 6 | |
def b32tob(s): | |
ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' | |
values = [ALPHABET.index(d) for d in secret.upper().replace(' ', '').replace('=', 'A')] | |
count = int(len(s.replace(' ', '').replace('=', '')) * 5 / 8) | |
decoded = [0] * count | |
for i in range(len(values)): | |
bit_index = i * 5 | |
byte_index = int(bit_index / 8) | |
byte_bit_index = bit_index - byte_index * 8 | |
if byte_index < count: | |
decoded[byte_index] |= (values[i] << 3) >> byte_bit_index | |
if byte_index < count - 1: | |
decoded[byte_index+1] |= (values[i] << 3 + 8 - byte_bit_index) & 0xff | |
return bytes(bytearray(decoded)) | |
K = b32tob(secret) | |
def HMAC(K, C, block_size=64, output_size=20): | |
mac = hmac.new(K, C, hashlib.sha1).digest() | |
return mac | |
def DT(b): | |
offset = b[19] & 0xf | |
return b[offset:offset+4] | |
def HOTP(K, C): | |
global d | |
HS = HMAC(K, C.to_bytes(8, byteorder='big')) | |
DBC1 = int.from_bytes(DT(HS), byteorder='big') | |
DBC2 = DBC1 & 0x7fffffff | |
return DBC2 % (10**d) | |
def TOTP(K, timestamp): | |
global T_0, X | |
T = int((timestamp - T_0) / X) | |
return HOTP(K, T) | |
def format_OTP(otp): | |
formatted = '' | |
for i in range(d): | |
if i % 3 == 0: | |
formatted += ' ' | |
formatted += str(int(otp / 10**(d-i-1) % 10)) | |
return formatted.strip() | |
otp = TOTP(K, int(time.time())) | |
print(format_OTP(otp)) |
This file contains hidden or 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/python3 | |
import time, hashlib | |
secret = 'q5ma qo4x irgv cm6k auai kjyv ak57 5keu' | |
T_0 = 0 | |
X = 30 | |
d = 6 | |
def b32tob(s): | |
ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' | |
values = [ALPHABET.index(d) for d in secret.upper().replace(' ', '').replace('=', 'A')] | |
count = int(len(s.replace(' ', '').replace('=', '')) * 5 / 8) | |
decoded = [0] * count | |
for i in range(len(values)): | |
bit_index = i * 5 | |
byte_index = int(bit_index / 8) | |
byte_bit_index = bit_index - byte_index * 8 | |
if byte_index < count: | |
decoded[byte_index] |= (values[i] << 3) >> byte_bit_index | |
if byte_index < count - 1: | |
decoded[byte_index+1] |= (values[i] << 3 + 8 - byte_bit_index) & 0xff | |
return bytes(bytearray(decoded)) | |
K = b32tob(secret) | |
def sha1(b): | |
h = hashlib.new('sha1') | |
h.update(b) | |
return bytes.fromhex(h.hexdigest()) | |
def HMAC(K, C, block_size=64, output_size=20): | |
xor = lambda a,b: bytes([_a ^ _b for _a, _b in zip(a, b)]) | |
if len(K) > block_size: | |
K = sha1(K) | |
K = K + bytes(block_size - len(K)) | |
opad = xor(K, 0x5C.to_bytes(1, byteorder='big') * block_size) | |
ipad = xor(K, 0x36.to_bytes(1, byteorder='big') * block_size) | |
mac= sha1(opad + sha1(ipad + C)) | |
return mac | |
def DT(b): | |
offset = b[19] & 0xf | |
return b[offset:offset+4] | |
def HOTP(K, C): | |
global d | |
HS = HMAC(K, C.to_bytes(8, byteorder='big')) | |
DBC1 = int.from_bytes(DT(HS), byteorder='big') | |
DBC2 = DBC1 & 0x7fffffff | |
return DBC2 % (10**d) | |
def TOTP(K, timestamp): | |
global T_0, X | |
T = int((timestamp - T_0) / X) | |
return HOTP(K, T) | |
def format_OTP(otp): | |
formatted = '' | |
for i in range(d): | |
if i % 3 == 0: | |
formatted += ' ' | |
formatted += str(int(otp / 10**(d-i-1) % 10)) | |
return formatted.strip() | |
otp = TOTP(K, int(time.time())) | |
print(format_OTP(otp)) |
This file contains hidden or 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/python3 | |
import time, sys | |
secret = 'q5ma qo4x irgv cm6k auai kjyv ak57 5keu' | |
T_0 = 0 | |
X = 30 | |
d = 6 | |
SHA1_BLOCK_SIZE = int(512 / 8) | |
def b32tob(s): | |
ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' | |
values = [ALPHABET.index(d) for d in secret.upper().replace(' ', '').replace('=', 'A')] | |
count = int(len(s.replace(' ', '').replace('=', '')) * 5 / 8) | |
decoded = [0] * count | |
for i in range(len(values)): | |
bit_index = i * 5 | |
byte_index = int(bit_index / 8) | |
byte_bit_index = bit_index - byte_index * 8 | |
if byte_index < count: | |
decoded[byte_index] |= (values[i] << 3) >> byte_bit_index | |
if byte_index < count - 1: | |
decoded[byte_index+1] |= (values[i] << 3 + 8 - byte_bit_index) & 0xff | |
return bytes(bytearray(decoded)) | |
K = b32tob(secret) | |
def sha1(b): | |
global SHA1_BLOCK_SIZE | |
def i_to_b(i, size): | |
offsets = [i * 8 for i in range(size)] | |
if sys.byteorder == 'little': | |
offsets.reverse() | |
return b''.join([((i >> o) & 0xff).to_bytes(1, byteorder='big') for o in offsets]) | |
b_len = len(b) * 8 | |
b += b'\x80' | |
while (len(b) + 8) % SHA1_BLOCK_SIZE != 0: | |
b += b'\0' | |
b += i_to_b(b_len, 8) | |
def left_rotate(w, count): | |
return ((w << count) | (w >> (32 - count))) & 0xffffffff | |
H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] | |
M = [b[i * SHA1_BLOCK_SIZE:(i+1) * SHA1_BLOCK_SIZE] for i in range(int(len(b) / SHA1_BLOCK_SIZE))] | |
def F(t, B, C, D): | |
if t <= 19: | |
return (B & C) | (~B & D) | |
elif t <= 39: | |
return B ^ C ^ D | |
elif t <= 59: | |
return (B & C) | (B & D) | (C & D) | |
else: | |
return B ^ C ^ D | |
def K(t): | |
if t <= 19: | |
return 0x5A827999 | |
elif t <= 39: | |
return 0x6ED9EBA1 | |
elif t <= 59: | |
return 0x8F1BBCDC | |
else: | |
return 0xCA62C1D6 | |
for i in range(len(M)): | |
W = [0] * 80 | |
for t in range(16): | |
b = M[i][t*4:(t+1)*4] | |
w = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0) | |
W[t] = w | |
for t in range(16, 80): | |
W[t] = left_rotate(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) | |
(a, b, c, d, e) = H | |
for t in range(80): | |
temp = (left_rotate(a, 5) + F(t, b, c, d) + e + K(t) + W[t]) & 0xffffffff | |
e = d | |
d = c | |
c = left_rotate(b, 30) | |
b = a | |
a = temp | |
H = [(_a + _b) & 0xffffffff for _a, _b in zip(H, [a, b, c, d, e])] | |
return b''.join([_w.to_bytes(4, byteorder='big') for _w in H]) | |
def HMAC(K, C): | |
global SHA1_BLOCK_SIZE | |
xor = lambda a,b: bytes([_a ^ _b for _a, _b in zip(a, b)]) | |
if len(K) > SHA1_BLOCK_SIZE: | |
K = sha1(K) | |
K = K + bytes(SHA1_BLOCK_SIZE - len(K)) | |
opad = xor(K, 0x5C.to_bytes(1, byteorder='big') * SHA1_BLOCK_SIZE) | |
ipad = xor(K, 0x36.to_bytes(1, byteorder='big') * SHA1_BLOCK_SIZE) | |
mac= sha1(opad + sha1(ipad + C)) | |
return mac | |
def DT(b): | |
offset = b[19] & 0xf | |
return b[offset:offset+4] | |
def HOTP(K, C): | |
global d | |
HS = HMAC(K, C.to_bytes(8, byteorder='big')) | |
DBC1 = int.from_bytes(DT(HS), byteorder='big') | |
DBC2 = DBC1 & 0x7fffffff | |
return DBC2 % (10**d) | |
def TOTP(K, timestamp): | |
global T_0, X | |
T = int((timestamp - T_0) / X) | |
return HOTP(K, T) | |
def format_OTP(otp): | |
formatted = '' | |
for i in range(d): | |
if i % 3 == 0: | |
formatted += ' ' | |
formatted += str(int(otp / 10**(d-i-1) % 10)) | |
return formatted.strip() | |
otp = TOTP(K, int(time.time())) | |
print(format_OTP(otp)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment