import time import datetime import math import hmac import base64 import qrcode from PIL import Image from pyzbar import pyzbar from hashlib import sha1 ''' depend: qrcode,pillow,PIL,pyzbar ''' class GoogleAuthenticator(object): def __init__(self,secretKey=None,digits=6,interval=30): self.secretKey = secretKey self.digits = digits self.interval = interval def __str_extend(self,old_str,length,extend_str): new_strs = None if len(old_str) < length: clen = length - len(old_str) s0 = ''.join([extend_str for i in range(clen)]) new_strs = s0 + old_str elif len(old_str) == length: new_strs = old_str return new_strs def __str_split(self,old_str,split_len,prefix='',suffix=''): array = [] str_len = len(old_str) for i in range(0,str_len,4): if i + 4 < str_len: array.append(prefix+old_str[i:i+4]+suffix) else: array.append(prefix+old_str[i:str_len]+suffix) return array def __base32tohex(self,base32str): base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" bits = "" for i in range(len(base32str)): char = str(base32str[i]).upper() idx = int(base32chars.index(char)) bit = self.__str_extend(format(idx,'b'),5,'0') if bit is None: raise Exception('bit={},len={}'.format(bit,len(bit))) bits += bit bitArray = self.__str_split(bits,4,'0b') hexstr = ''.join([format(int(i,2),'x') for i in bitArray]) return hexstr def __get_HexSecret(self): hexstr = self.__base32tohex(self.secretKey) return hexstr def __byte_secret(self): missing_padding = len(self.secretKey) % 8 if missing_padding != 0: self.secret += '=' * (8 - missing_padding) return base64.b32decode(self.secretKey, casefold=True) def __int_to_bytestring(self,i, padding=8): result = bytearray() while i != 0: result.append(i & 0xFF) i >>= 8 return bytes(bytearray(reversed(result)).rjust(padding, b'\0')) def __timecode(self, for_time): i = time.mktime(for_time.timetuple()) return int(i / self.interval) def get_QR_url(self): base_url = "https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=200x200&chld=M|0&cht=qr&chl=" QR_url = base_url + "otpauth://totp/user@host.com\%3Fsecret%3D" + self.secretKey return QR_url def get_QR_code(self): qr = qrcode.QRCode(version=1,error_correction=qrcode.constants.ERROR_CORRECT_L,box_size=10,border=4) data = self.get_QR_url() qr.add_data(data=data) qr.make(fit=True) img = qr.make_image(fill_color="green", back_color="white") img.show() def QR2url(self,QR_path): base_url = 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=200x200&chld=M|0&cht=qr&chl=' ourl = pyzbar.decode(Image.open(QR_path), symbols=[pyzbar.ZBarSymbol.QRCODE])[0].data.decode("utf-8") QR_url = base_url + ourl return QR_url def QR2code(self,QR_path): QR_url = pyzbar.decode(Image.open(QR_path), symbols=[pyzbar.ZBarSymbol.QRCODE])[0].data.decode("utf-8") i = QR_url.find('\%3Fsecret%3D') l = len('\%3Fsecret%3D') if i == -1: i = QR_url.find('?secret=') l = len('?secret=') if i == -1: raise Exception('QR is error') j = QR_url.find('?',i+1) if j == -1: j = QR_url.find('\%3F',i+1) if j == -1: sk = QR_url[i+l:] else: sk = QR_url[i+l:j] self.secretKey = sk timestamp = self.__timecode(datetime.datetime.now()) str_code = self.generate_otp(timestamp) return str_code def generate_otp(self,timestamp): key = self.secretKey hasher = hmac.new(self.__byte_secret(), self.__int_to_bytestring(timestamp), sha1) # print(hasher.hexdigest()) 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 def getTotp(self): timestamp = self.__timecode(datetime.datetime.now()) str_code = self.generate_otp(timestamp) return str_code def at(self, for_time, counter_offset=0): if not isinstance(for_time, datetime.datetime): for_time = datetime.datetime.fromtimestamp(int(for_time)) return self.generate_otp(self.timecode(for_time) + counter_offset) def verifyTotp(self,str_code): gTotp = self.getTotp() if gTotp == str_code: return True else: return False