Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import base64
- import calendar
- import datetime
- import hashlib
- import hmac
- import sys
- import time
- from typing import Any
- """
- Copyright (C) 2011-2021 Mark Percival <[email protected]>,
- Nathan Reynolds <[email protected]>, Andrey Kislyuk <[email protected]>,
- and PyOTP contributors
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- """
- def int_to_bytestring(i: int, padding: int = 8) -> bytes:
- """
- 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"))
- def timecode(for_time: datetime.datetime, interval: int = 30) -> int:
- """
- Accepts either a timezone naive (`for_time.tzinfo is None`) or
- a timezone aware datetime as argument and returns the
- corresponding counter value (timecode).
- """
- if for_time.tzinfo:
- return int(calendar.timegm(for_time.utctimetuple()) / interval)
- else:
- return int(time.mktime(for_time.timetuple()) / interval)
- def byte_secret(secret) -> bytes:
- missing_padding = len(secret) % 8
- if missing_padding != 0:
- secret += "=" * (8 - missing_padding)
- return base64.b32decode(secret, casefold=True)
- def generate_otp(
- secret: bytes, input: int, digest: Any = hashlib.sha1, digits: int = 6
- ) -> str:
- """
- :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
- """
- if input < 0:
- raise ValueError("input must be positive integer")
- hasher = hmac.new(secret, int_to_bytestring(input), 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**digits)
- while len(str_code) < digits:
- str_code = "0" + str_code
- return str_code
- def easy_totp(secret):
- return generate_otp(byte_secret(secret), timecode(datetime.datetime.now()))
- if __name__ == "__main__":
- for secret in sys.argv[1:]:
- print(easy_totp(secret))
Add Comment
Please, Sign In to add comment