Initial YakPanel commit
This commit is contained in:
66
class/pyotp/otp.py
Normal file
66
class/pyotp/otp.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
from .compat import str
|
||||
|
||||
class OTP(object):
|
||||
"""
|
||||
Base class for OTP handlers.
|
||||
"""
|
||||
def __init__(self, s, digits=6, digest=hashlib.sha1):
|
||||
"""
|
||||
:param s: secret in base32 format
|
||||
:type s: str
|
||||
:param digits: number of integers in the OTP. Some apps expect this to be 6 digits, others support more.
|
||||
:type digits: int
|
||||
:param digest: digest function to use in the HMAC (expected to be sha1)
|
||||
:type digest: callable
|
||||
"""
|
||||
self.digits = digits
|
||||
self.digest = digest
|
||||
self.secret = s
|
||||
|
||||
def generate_otp(self, input):
|
||||
"""
|
||||
:param input: the HMAC counter value to use as the OTP input.
|
||||
Usually either the counter, or the computed integer based on the Unix timestamp
|
||||
:type input: int
|
||||
"""
|
||||
if input < 0:
|
||||
raise ValueError('input must be positive integer')
|
||||
hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
|
||||
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 byte_secret(self):
|
||||
missing_padding = len(self.secret) % 8
|
||||
if missing_padding != 0:
|
||||
self.secret += '=' * (8 - missing_padding)
|
||||
return base64.b32decode(self.secret, casefold=True)
|
||||
|
||||
@staticmethod
|
||||
def int_to_bytestring(i, padding=8):
|
||||
"""
|
||||
Turns an integer to the OATH specified
|
||||
bytestring, which is fed to the HMAC
|
||||
along with the secret
|
||||
"""
|
||||
result = bytearray()
|
||||
while i != 0:
|
||||
result.append(i & 0xFF)
|
||||
i >>= 8
|
||||
# It's necessary to convert the final result from bytearray to bytes
|
||||
# because the hmac functions in python 2.6 and 3.3 don't work with
|
||||
# bytearray
|
||||
return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))
|
||||
Reference in New Issue
Block a user