mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-23 09:53:45 +03:00
123 lines
4.3 KiB
Python
123 lines
4.3 KiB
Python
"""
|
|
This module holds several utilities regarding RSA and server fingerprints.
|
|
"""
|
|
import os
|
|
import struct
|
|
from hashlib import sha1
|
|
try:
|
|
import rsa
|
|
import rsa.core
|
|
except ImportError:
|
|
rsa = None
|
|
raise ImportError('Missing module "rsa", please install via pip.')
|
|
|
|
from ..tl import TLObject
|
|
|
|
|
|
# {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):
|
|
"""
|
|
Given a RSA key, computes its fingerprint like Telegram does.
|
|
|
|
:param key: the Crypto.RSA key.
|
|
:return: its 8-bytes-long fingerprint.
|
|
"""
|
|
n = TLObject.serialize_bytes(get_byte_array(key.n))
|
|
e = TLObject.serialize_bytes(get_byte_array(key.e))
|
|
# Telegram uses the last 8 bytes as the fingerprint
|
|
return struct.unpack('<q', sha1(n + e).digest()[-8:])[0]
|
|
|
|
|
|
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):
|
|
"""
|
|
Encrypts the given data known the fingerprint to be used
|
|
in the way Telegram requires us to do so (sha1(data) + data + padding)
|
|
|
|
:param fingerprint: the fingerprint of the RSA key.
|
|
:param data: the data to be encrypted.
|
|
:return:
|
|
the cipher text, or None if no key matching this fingerprint is found.
|
|
"""
|
|
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)
|