import os from hashlib import sha1 try: import rsa import rsa.core except ImportError: rsa = None raise ImportError('Missing module "rsa", please install via pip.') from ..extensions import BinaryWriter # {fingerprint: Crypto.PublicKey.RSA._RSAobj} dictionary _server_keys = {} def get_byte_array(integer): """Return the variable length bytes corresponding to the given int""" # Operate in big endian (unlike most of Telegram API) since: # > "...pq is a representation of a natural number # (in binary *big endian* format)..." # > "...current value of dh_prime equals # (in *big-endian* byte order)..." # Reference: https://core.telegram.org/mtproto/auth_key return int.to_bytes( integer, length=(integer.bit_length() + 8 - 1) // 8, # 8 bits per byte, byteorder='big', signed=False ) def _compute_fingerprint(key): """For a given Crypto.RSA key, computes its 8-bytes-long fingerprint in the same way that Telegram does. """ with BinaryWriter() as writer: writer.tgwrite_bytes(get_byte_array(key.n)) writer.tgwrite_bytes(get_byte_array(key.e)) # Telegram uses the last 8 bytes as the fingerprint return sha1(writer.get_bytes()).digest()[-8:] def add_key(pub): """Adds a new public key to be used when encrypting new data is needed""" global _server_keys key = rsa.PublicKey.load_pkcs1(pub) _server_keys[_compute_fingerprint(key)] = key def encrypt(fingerprint, data): """Given the fingerprint of a previously added RSA key, encrypt its data in the way Telegram requires us to do so (sha1(data) + data + padding) """ global _server_keys key = _server_keys.get(fingerprint, None) if not key: return None # len(sha1.digest) is always 20, so we're left with 255 - 20 - x padding to_encrypt = sha1(data).digest() + data + os.urandom(235 - len(data)) # rsa module rsa.encrypt adds 11 bits for padding which we don't want # rsa module uses rsa.transform.bytes2int(to_encrypt), easier way: payload = int.from_bytes(to_encrypt, 'big') encrypted = rsa.core.encrypt_int(payload, key.e, key.n) # rsa module uses transform.int2bytes(encrypted, keylength), easier: block = encrypted.to_bytes(256, 'big') return block # Add default keys for pub in ( '''-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ 8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB -----END RSA PUBLIC KEY-----''', '''-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB -----END RSA PUBLIC KEY-----''', '''-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+ DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB 1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB -----END RSA PUBLIC KEY-----''', '''-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc /n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB -----END RSA PUBLIC KEY-----''' ): add_key(pub)