Telethon/telethon/_crypto/aes.py
2021-09-11 17:48:23 +02:00

112 lines
3.1 KiB
Python

"""
AES IGE implementation in Python.
If available, cryptg will be used instead, otherwise
if available, libssl will be used instead, otherwise
the Python implementation will be used.
"""
import os
import pyaes
import logging
from . import libssl
__log__ = logging.getLogger(__name__)
try:
import cryptg
__log__.info('cryptg detected, it will be used for encryption')
except ImportError:
cryptg = None
if libssl.encrypt_ige and libssl.decrypt_ige:
__log__.info('libssl detected, it will be used for encryption')
else:
__log__.info('cryptg module not installed and libssl not found, '
'falling back to (slower) Python encryption')
class AES:
"""
Class that servers as an interface to encrypt and decrypt
text through the AES IGE mode.
"""
@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.
"""
if cryptg:
return cryptg.decrypt_ige(cipher_text, key, iv)
if libssl.decrypt_ige:
return libssl.decrypt_ige(cipher_text, key, iv)
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.
"""
padding = len(plain_text) % 16
if padding:
plain_text += os.urandom(16 - padding)
if cryptg:
return cryptg.encrypt_ige(plain_text, key, iv)
if libssl.encrypt_ige:
return libssl.encrypt_ige(plain_text, key, iv)
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)