Totally refactored source files location

Now it *should* be easier to turn Telethon
into a pip package
This commit is contained in:
Lonami
2016-09-17 20:42:34 +02:00
parent 27ec7292d8
commit 51a531225f
42 changed files with 518 additions and 531 deletions

View File

@@ -0,0 +1,4 @@
from .aes import AES
from .rsa import RSA, RSAServerKey
from .auth_key import AuthKey
from .factorizator import Factorizator

66
telethon/crypto/aes.py Normal file
View File

@@ -0,0 +1,66 @@
import os
import pyaes
class AES:
@staticmethod
def decrypt_ige(cipher_text, key, iv):
"""Decrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector"""
iv1 = iv[:len(iv)//2]
iv2 = iv[len(iv)//2:]
aes = pyaes.AES(key)
plain_text = []
blocks_count = len(cipher_text) // 16
cipher_text_block = [0] * 16
for block_index in range(blocks_count):
for i in range(16):
cipher_text_block[i] = cipher_text[block_index * 16 + i] ^ iv2[i]
plain_text_block = aes.decrypt(cipher_text_block)
for i in range(16):
plain_text_block[i] ^= iv1[i]
iv1 = cipher_text[block_index * 16:block_index * 16 + 16]
iv2 = plain_text_block[:]
plain_text.extend(plain_text_block[:])
return bytes(plain_text)
@staticmethod
def encrypt_ige(plain_text, key, iv):
"""Encrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector"""
# Add random padding if and only if it's not evenly divisible by 16 already
if len(plain_text) % 16 != 0:
padding_count = 16 - len(plain_text) % 16
plain_text += os.urandom(padding_count)
iv1 = iv[:len(iv)//2]
iv2 = iv[len(iv)//2:]
aes = pyaes.AES(key)
cipher_text = []
blocks_count = len(plain_text) // 16
for block_index in range(blocks_count):
plain_text_block = list(plain_text[block_index * 16:block_index * 16 + 16])
for i in range(16):
plain_text_block[i] ^= iv1[i]
cipher_text_block = aes.encrypt(plain_text_block)
for i in range(16):
cipher_text_block[i] ^= iv2[i]
iv1 = cipher_text_block[:]
iv2 = plain_text[block_index * 16:block_index * 16 + 16]
cipher_text.extend(cipher_text_block[:])
return bytes(cipher_text)

22
telethon/crypto/auth_key.py Executable file
View File

@@ -0,0 +1,22 @@
from telethon.utils import BinaryWriter, BinaryReader
import telethon.helpers as utils
class AuthKey:
def __init__(self, data):
self.key = data
with BinaryReader(utils.sha1(self.key)) as reader:
self.aux_hash = reader.read_long(signed=False)
reader.read(4)
self.key_id = reader.read_long(signed=False)
def calc_new_nonce_hash(self, new_nonce, number):
"""Calculates the new nonce hash based on the current class fields' values"""
with BinaryWriter() as writer:
writer.write(new_nonce)
writer.write_byte(number)
writer.write_long(self.aux_hash, signed=False)
new_nonce_hash = utils.calc_msg_key(writer.get_bytes())
return new_nonce_hash

62
telethon/crypto/factorizator.py Executable file
View File

@@ -0,0 +1,62 @@
from random import randint
class Factorizator:
@staticmethod
def find_small_multiplier_lopatin(what):
"""Finds the small multiplier by using Lopatin's method"""
g = 0
for i in range(3):
q = (randint(0, 127) & 15) + 17
x = randint(0, 1000000000) + 1
y = x
lim = 1 << (i + 18)
for j in range(1, lim):
a, b, c = x, x, q
while b != 0:
if (b & 1) != 0:
c += a
if c >= what:
c -= what
a += a
if a >= what:
a -= what
b >>= 1
x = c
z = y - x if x < y else x - y
g = Factorizator.gcd(z, what)
if g != 1:
break
if (j & (j - 1)) == 0:
y = x
if g > 1:
break
p = what // g
return min(p, g)
@staticmethod
def gcd(a, b):
"""Calculates the greatest common divisor"""
while a != 0 and b != 0:
while b & 1 == 0:
b >>= 1
while a & 1 == 0:
a >>= 1
if a > b:
a -= b
else:
b -= a
return a if b == 0 else b
@staticmethod
def factorize(pq):
"""Factorizes the given number and returns both the divisor and the number divided by the divisor"""
divisor = Factorizator.find_small_multiplier_lopatin(pq)
return divisor, pq // divisor

58
telethon/crypto/rsa.py Executable file
View File

@@ -0,0 +1,58 @@
from telethon.utils import BinaryWriter
import telethon.helpers as utils
import os
class RSAServerKey:
def __init__(self, fingerprint, m, e):
self.fingerprint = fingerprint
self.m = m
self.e = e
def encrypt(self, data, offset=None, length=None):
"""Encrypts the given data with the current key"""
if offset is None:
offset = 0
if length is None:
length = len(data)
with BinaryWriter() as writer:
# Write SHA
writer.write(utils.sha1(data[offset:offset+length]))
# Write data
writer.write(data[offset:offset+length])
# Add padding if required
if length < 235:
writer.write(os.urandom(235 - length))
result = int.from_bytes(writer.get_bytes(), byteorder='big')
result = pow(result, self.e, self.m)
# If the result byte count is less than 256, since the byte order is big,
# the non-used bytes on the left will be 0 and act as padding,
# without need of any additional checks
return int.to_bytes(result, length=256, byteorder='big', signed=False)
class RSA:
_server_keys = {
'216be86c022bb4c3':
RSAServerKey('216be86c022bb4c3', int('C150023E2F70DB7985DED064759CFECF0AF328E69A41DAF4D6F01B538135A6F9'
'1F8F8B2A0EC9BA9720CE352EFCF6C5680FFC424BD634864902DE0B4BD6D49F4E'
'580230E3AE97D95C8B19442B3C0A10D8F5633FECEDD6926A7F6DAB0DDB7D457F'
'9EA81B8465FCD6FFFEED114011DF91C059CAEDAF97625F6C96ECC74725556934'
'EF781D866B34F011FCE4D835A090196E9A5F0E4449AF7EB697DDB9076494CA5F'
'81104A305B6DD27665722C46B60E5DF680FB16B210607EF217652E60236C255F'
'6A28315F4083A96791D7214BF64C1DF4FD0DB1944FB26A2A57031B32EEE64AD1'
'5A8BA68885CDE74A5BFC920F6ABF59BA5C75506373E7130F9042DA922179251F',
16), int('010001', 16))
}
@staticmethod
def encrypt(fingerprint, data, offset=None, length=None):
"""Encrypts the given data given a fingerprint"""
if fingerprint.lower() not in RSA._server_keys:
return None
key = RSA._server_keys[fingerprint.lower()]
return key.encrypt(data, offset, length)