First attempt at TelegramClient. Added fixes and doc

This commit is contained in:
Lonami
2016-09-04 11:07:18 +02:00
parent c863537b7b
commit 39a23559f0
19 changed files with 256 additions and 65 deletions

0
crypto/__init__.py Normal file
View File

65
crypto/aes.py Normal file
View File

@@ -0,0 +1,65 @@
# This file is based on TLSharp
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/AES.cs
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 = [0] * len(cipher_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[0:16]
plain_text[block_index * 16:block_index * 16 + 16] = plain_text_block[:16]
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"""
# TODO: Random padding
padding = bytes(16 - len(plain_text) % 16)
plain_text += padding
iv1 = iv[:len(iv)//2]
iv2 = iv[len(iv)//2:]
aes = pyaes.AES(key)
blocks_count = len(plain_text) // 16
cipher_text = [0] * len(plain_text)
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[0:16]
iv2 = plain_text[block_index * 16:block_index * 16 + 16]
cipher_text[block_index * 16:block_index * 16 + 16] = cipher_text_block[:16]
return bytes(cipher_text)

30
crypto/auth_key.py Executable file
View File

@@ -0,0 +1,30 @@
# This file is based on TLSharp
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/AuthKey.cs
import utils.helpers as utils
from utils.binary_writer import BinaryWriter
from utils.binary_reader import BinaryReader
class AuthKey:
def __init__(self, gab=None, data=None):
if gab:
self.key = utils.get_byte_array(gab, signed=False)
elif data:
self.key = data
else:
raise AssertionError('Either a gab integer or data bytes array must be provided')
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.sha1(writer.get_bytes())[4:20]
return new_nonce_hash

64
crypto/factorizator.py Executable file
View File

@@ -0,0 +1,64 @@
# This file is based on TLSharp
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/Factorizator.cs
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

62
crypto/rsa.py Executable file
View File

@@ -0,0 +1,62 @@
# This file is based on TLSharp
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/RSA.cs
from utils.binary_writer import BinaryWriter
import utils.helpers as utils
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(utils.generate_random_bytes(235 - length))
cipher_text = utils.get_byte_array(
pow(int.from_bytes(writer.get_bytes(), byteorder='big'), self.e, self.m),
signed=False)
if len(cipher_text) == 256:
return cipher_text
else:
padding = bytes(256 - len(cipher_text))
return padding + cipher_text
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)