diff --git a/bwb/common.py b/bwb/common.py new file mode 100644 index 0000000..3e20bcc --- /dev/null +++ b/bwb/common.py @@ -0,0 +1,129 @@ +import base58 +import base64 +import pyotp +import random +import string + +from Crypto.Cipher import AES +from Crypto.Random import get_random_bytes +from Crypto.Hash import SHA256, SHA512, MD5 +from Crypto.Util.Padding import pad, unpad + +OTP_LOOKAHEAD = 25 + +class common: + def __init__(self): + self.otp = None + self.secret = '' + self.otp_count = 0 + + def init(self, secret=None): + secret = secret or ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(12)) + encoded = secret.encode() + b32 = base64.b32encode(encoded) + self.otp = pyotp.HOTP(b32) + self.secret = secret + self.otp_count = 0 + return secret + + def get_otp(self, target): + if not self.otp: + raise RuntimeError('bwb not init yet') + if not isinstance(target, int): + raise ValueError('invalid target') + if int(target) <= 999999: + raise ValueError('invalid target') + + code = self.otp.at(self.otp_count) + self.otp_count += 1 + return str(int(target) % int(code)).zfill(6) + + def get_otp_cast(self): + if not self.otp: + raise RuntimeError('bwb not init yet') + + code = self.otp.at(self.otp_count) + self.otp_count += 1 + return code + + def check_otp(self, code): + if not self.otp: + return False + + for i in range(OTP_LOOKAHEAD): + count = self.otp_count + i + if self.otp.at(count) == str(code): # broadcast + self.otp_count = count + 1 + return True + elif self.TELEGRAM_ID % int(self.otp.at(count)) == int(code): + self.otp_count = count + 1 + return True + return False + + def check_auth(self, text): + try: + int(text[:6]) + except ValueError: + return False, None + + if self.check_otp(text[:6]): + return True, text[6:] + else: + return False, text[6:] + + def to_b58(self, text): + return 'l' + base58.b58encode(text.encode()).decode() + + def from_b58(self, text): + if not text.startswith('l'): + return False + try: + return base58.b58decode(text[1:]).decode() + except BaseException as e: + return False + + def enc(self, text): + if not self.secret: + raise RuntimeError('bwb not init yet') + + key = SHA256.new(self.secret.encode()).digest() + iv = get_random_bytes(4) + cipher = AES.new(key, AES.MODE_CBC, iv * 4) + ct = cipher.encrypt(pad(text.encode(), AES.block_size)) + return 'I' + base58.b58encode(iv + ct).decode() + + def dec(self, ciphertext): + if not self.secret: + return False # so we can run every message through + if not ciphertext.startswith('I'): + return False + + try: + ciphertext = base58.b58decode(ciphertext) + key = SHA256.new(self.secret.encode()).digest() + iv = ciphertext[:4] + ct = ciphertext[4:] + cipher = AES.new(key, AES.MODE_CBC, iv * 4) + pt = unpad(cipher.decrypt(ct), AES.block_size) + return pt.decode() + except BaseException as e: + return False + + def wrap(self, text, target=None, b58=False, enc=False): + if target: + code = self.get_otp(target) + else: + code = self.get_otp_cast() + + text = code + text + + if enc: + return self.enc(text) + elif b58: + return self.to_b58(text) + else: + return text + + def parse(self, text): + decoded = self.dec(text) or self.from_b58(text) or text + return self.check_auth(decoded) # returns tuple diff --git a/bwb/jason.py b/bwb/jason.py new file mode 100644 index 0000000..31baa2d --- /dev/null +++ b/bwb/jason.py @@ -0,0 +1,6 @@ +from .common import common + +class user(common): + TELEGRAM_ID = 172033414 + +bwb = user() diff --git a/bwb/tanner.py b/bwb/tanner.py new file mode 100644 index 0000000..a35b51f --- /dev/null +++ b/bwb/tanner.py @@ -0,0 +1,6 @@ +from .common import common + +class user(common): + TELEGRAM_ID = 79316791 + +bwb = user() diff --git a/bwb/tdev.py b/bwb/tdev.py new file mode 100644 index 0000000..398978a --- /dev/null +++ b/bwb/tdev.py @@ -0,0 +1,6 @@ +from .common import common + +class user(common): + TELEGRAM_ID = 559772740 + +bwb = user() diff --git a/setup.py b/setup.py index 26f9b4f..f870d1e 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools +import setuptools setuptools.setup(name='bwb', description='bwb',