Skip to content

Commit

Permalink
feat: add .string_code method. Add docstring.
Browse files Browse the repository at this point in the history
  • Loading branch information
lepture committed Mar 24, 2023
1 parent bb02f14 commit d159b48
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 5 deletions.
26 changes: 24 additions & 2 deletions src/otpauth/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

class OTP(object, metaclass=ABCMeta):
TYPE: str

#: The supportted algorithms
ALGORITHMS = ["SHA1", "SHA256", "SHA512"]

def __init__(self, secret: bytes, digit: int = 6, algorithm: str = "SHA1"):
Expand All @@ -26,7 +28,13 @@ def b32_secret(self) -> str:
return self._b32_secret

@classmethod
def from_b32encode(cls, secret: t.AnyStr):
def from_b32encode(cls, secret: t.AnyStr, digit: int = 6, algorithm: str = "SHA1"):
"""Create the instance with a base32 encoded secret.
:param secret: A base32 encoded secret string or bytes.
:param digit: Number of digits in the OTP code.
:param algorithm: Hash algorithm used in HOTP.
"""
if isinstance(secret, str):
secret = secret.encode("utf-8")

Expand All @@ -36,7 +44,7 @@ def from_b32encode(cls, secret: t.AnyStr):
secret += b'=' * (-len(secret) % 8)
raw_secret = base64.b32decode(secret)

obj = cls(raw_secret)
obj = cls(raw_secret, digit, algorithm)
obj._b32_secret = b32_secret.decode("ascii")
return obj

Expand All @@ -46,6 +54,20 @@ def _get_base_uri(self, label: str, issuer: str) -> str:
_type = self.TYPE.lower()
return f"otpauth://{_type}/{label}?secret={self.b32_secret}&issuer={issuer}&algorithm={self.algorithm}&digits={self.digit}"

def string_code(self, code: int) -> str:
"""Add leading 0 if the code length does not match the defined length.
For instance, parameter ``digit=6``, but ``code=123``, this method would
return ``000123``::
>>> otp.string_code(123)
'000123'
:param code: The number that this OTP generated.
"""
code = str(code)
return "0" * (self.digit - len(code)) + code

@abstractmethod
def generate(self, *args, **kwargs) -> int:
...
Expand Down
16 changes: 14 additions & 2 deletions src/otpauth/rfc4226.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@


class HOTP(OTP):
"""Implementation of RFC4226, An HMAC-Based One-Time Password Algorithm.
:param secret: A secret in bytes
:param digit: Number of digits in the HOTP code.
:param algorithm: Hash algorithm used in HOTP.
"""

TYPE = "HOTP"

def generate(self, counter: int) -> int:
"""Generate a HOTP code.
"""Generate a HOTP code. The returning result is an integer.
To convert it into string with the correct digit length, developers
can use :meth:`string_code`::
int_code = hotp.generate(4)
str_code = hotp.string_code(int_code)
:param counter: HOTP is a counter based algorithm.
"""
Expand Down Expand Up @@ -41,7 +53,7 @@ def generate_hotp(secret: bytes, counter: int, digit: int=6, algorithm: str = "S
:param secret: A secret token for the authentication.
:param counter: HOTP is a counter based algorithm.
:param digit: Number of digits in the HOTP code.
:param hash_alg: Hash algorithm used in HOTP, default is hashlib.sha1.
:param algorithm: Hash algorithm used in HOTP.
"""
hash_alg = getattr(hashlib, algorithm.lower())
msg = struct.pack('>Q', counter)
Expand Down
15 changes: 14 additions & 1 deletion src/otpauth/rfc6238.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,27 @@


class TOTP(OTP):
"""Implementation of RFC6238, Time-Based One-Time Password Algorithm.
:param secret: A secret in bytes
:param digit: Number of digits in the HOTP code.
:param algorithm: Hash algorithm used in HOTP.
:param period: The password valid in "period" seconds.
"""

TYPE = "TOTP"

def __init__(self, secret: bytes, digit: int = 6, algorithm: str = "SHA1", period: int = 30):
super().__init__(secret, digit, algorithm)
self.period = period

def generate(self, timestamp: int = None) -> int:
"""Generate a TOTP code.
"""Generate a TOTP code. The returning result is an integer.
To convert it into string with the correct digit length, developers
can use :meth:`string_code`::
int_code = totp.generate()
str_code = totp.string_code(int_code)
:param timestamp: Create TOTP at this given timestamp, default is now.
"""
Expand Down

0 comments on commit d159b48

Please # to comment.