1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-30 05:58:58 +01:00

Added tests, added layer 55 json

This commit is contained in:
danogentili 2016-08-09 22:28:50 +02:00
parent f037212ac8
commit 90af9857ff
42 changed files with 40 additions and 2380 deletions

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
sudo: required
dist: trusty
group: edge
language: php
php:
- '7.0'
- nightly
- hhvm
- '5.6'
addons:
apt:
packages:
- python3
before_script:
- composer update --dev
script:
- ./testing.php

View File

@ -1,6 +1,6 @@
# MadelineProto
[![StyleCI](https://styleci.io/repos/61838413/shield)](https://styleci.io/repos/61838413)
[![Build Status](https://travis-ci.org/danog/MadelineProto.svg?branch=master)](https://travis-ci.org/danog/MadelineProto)
Licensed under AGPLv3.
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).

View File

@ -1,170 +0,0 @@
__author__ = 'agrigoryev'
import os
import struct
import json
import io
from numbers import Number
from binascii import hexlify
class TlConstructor:
def __init__(self, json_dict):
self.id = int(json_dict['id'])
self.type = json_dict['type']
self.predicate = json_dict['predicate']
self.params = []
# case of vector
for param in json_dict['params']:
if param['type'] == "Vector<long>":
param['type'] = "Vector t"
param['subtype'] = "long"
elif param['type'] == "vector<%Message>":
param['type'] = "vector"
param['subtype'] = "message"
elif param['type'] == "vector<future_salt>":
param['type'] = "vector"
param['subtype'] = "future_salt"
else:
param['subtype'] = None
self.params.append(param)
class TlMethod:
def __init__(self, json_dict):
self.id = int(json_dict['id'])
self.type = json_dict['type']
self.method = json_dict['method']
self.params = json_dict['params']
class TLObject(dict):
def __init__(self, tl_elem):
self.name = tl_elem.predicate
class TL:
def __init__(self, filename):
with open(filename, 'r') as f:
TL_dict = json.load(f)
# Read constructors
self.constructors = TL_dict['constructors']
self.constructor_id = {}
self.constructor_type = {}
for elem in self.constructors:
z = TlConstructor(elem)
self.constructor_id[z.id] = z
self.constructor_type[z.predicate] = z
self.methods = TL_dict['methods']
self.method_id = {}
self.method_name = {}
for elem in self.methods:
z = TlMethod(elem)
self.method_id[z.id] = z
self.method_name[z.method] = z
## Loading TL_schema (should be placed in the same directory as mtproto.py
tl = TL(os.path.join(os.path.dirname(__file__), "TL_schema.JSON"))
def serialize_obj(type_, **kwargs):
bytes_io = io.BytesIO()
try:
tl_constructor = tl.constructor_type[type_]
except KeyError:
raise Exception("Could not extract type: %s" % type_)
bytes_io.write(struct.pack('<i', tl_constructor.id))
for arg in tl_constructor.params:
serialize_param(bytes_io, type_=arg['type'], value=kwargs[arg['name']])
return bytes_io.getvalue()
def serialize_method(type_, **kwargs):
bytes_io = io.BytesIO()
try:
tl_method = tl.method_name[type_]
except KeyError:
raise Exception("Could not extract type: %s" % type_)
bytes_io.write(struct.pack('<i', tl_method.id))
for arg in tl_method.params:
serialize_param(bytes_io, type_=arg['type'], value=kwargs[arg['name']])
return bytes_io.getvalue()
def serialize_param(bytes_io, type_, value):
if type_ == "int":
assert isinstance(value, Number)
assert value.bit_length() <= 32
bytes_io.write(struct.pack('<i', value))
elif type_ == "long":
assert isinstance(value, Number)
bytes_io.write(struct.pack('<q', value))
elif type_ in ["int128", "int256"]:
assert isinstance(value, bytes)
bytes_io.write(value)
elif type_ == 'string' or type_ == 'bytes':
l = len(value)
if l < 254: # short string format
bytes_io.write(struct.pack('<b', l)) # 1 byte of string
bytes_io.write(value) # string
bytes_io.write(b'\x00'*((-l-1) % 4)) # padding bytes
else:
bytes_io.write(b'\xfe') # byte 254
bytes_io.write(struct.pack('<i', l)[:3]) # 3 bytes of string
bytes_io.write(value) # string
bytes_io.write(b'\x00'*(-l % 4)) # padding bytes
def deserialize(bytes_io, type_=None, subtype=None):
"""
:type bytes_io: io.BytesIO object
"""
assert isinstance(bytes_io, io.BytesIO)
# Built-in bare types
if type_ == 'int': x = struct.unpack('<i', bytes_io.read(4))[0]
elif type_ == '#': x = struct.unpack('<I', bytes_io.read(4))[0]
elif type_ == 'long': x = struct.unpack('<q', bytes_io.read(8))[0]
elif type_ == 'double': x = struct.unpack('<d', bytes_io.read(8))[0]
elif type_ == 'int128': x = bytes_io.read(16)
elif type_ == 'int256': x = bytes_io.read(32)
elif type_ == 'string' or type_ == 'bytes':
l = struct.unpack('<B', bytes_io.read(1))[0]
assert l <= 254 # In general, 0xFF byte is not allowed here
if l == 254:
# We have a long string
long_len = struct.unpack('<I', bytes_io.read(3)+b'\x00')[0]
x = bytes_io.read(long_len)
bytes_io.read(-long_len % 4) # skip padding bytes
else:
# We have a short string
x = bytes_io.read(l)
bytes_io.read(-(l+1) % 4) # skip padding bytes
assert isinstance(x, bytes)
elif type_ == 'vector':
assert subtype is not None
count = struct.unpack('<l', bytes_io.read(4))[0]
x = [deserialize(bytes_io, type_=subtype) for i in range(count)]
else:
# known types
try:
# Bare types
tl_elem = tl.constructor_type[type_]
except KeyError:
# Boxed types
Idata = bytes_io.read(4)
i = struct.unpack('<i', Idata)[0] # read type ID
try:
tl_elem = tl.constructor_id[i]
except KeyError:
# Unknown type
raise Exception("Could not extract type: %s" % type_)
base_boxed_types = ["Vector t", "Int", "Long", "Double", "String", "Int128", "Int256"]
if tl_elem.type in base_boxed_types:
x = deserialize(bytes_io, type_=tl_elem.predicate, subtype=subtype)
else: # other types
x = TLObject(tl_elem)
for arg in tl_elem.params:
x[arg['name']] = deserialize(bytes_io, type_=arg['type'], subtype=arg['subtype'])
return x

File diff suppressed because one or more lines are too long

View File

@ -1,5 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
$__all__ = ['Chat', 'User', 'Message', 'Contact'];

View File

@ -1,11 +0,0 @@
from . import chat, user, message, contact
from .chat import Chat
from .user import User
from .message import Message
from .contact import Contact
__all__ = ['Chat', 'User', 'Message', 'Contact']

View File

@ -1,16 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
class chat
{
public function __construct()
{
$this->_users = [];
}
public function add_user($user)
{
$this->_users += $user;
}
}

View File

@ -1,6 +0,0 @@
class Chat():
def __init__(self):
self._users = [] # users in this chatroom
def add_user(self, user):
self._users += user

View File

@ -1,7 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
class contact
{
}

View File

@ -1,3 +0,0 @@
class Contact():
pass

View File

@ -1,51 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
require_once 'os.php';
class file
{
public function __construct($path)
{
$this->_path = $path;
}
/**
* truncates the file and create new with :param bytes.
* :return number of bytes written.
*/
public function write_bytes($bytes)
{
// py2php.fixme "with" unsupported.
}
/**
* read the file as bytes. :return b'' on file not exist.
*/
public function read_bytes()
{
if (!(new exists($this->_path))) {
return '';
}
// py2php.fixme "with" unsupported.
}
/**
* tries to open with os default viewer.
*/
public function open()
{
new call((os::name == 'nt') ? 'cmd /c start "" "'.$this->_path.'"' : [platform::startswith('darwin') ? 'open' : 'xdg-open', $this->_path]);
}
/**
* try to remove the file.
*/
public function remove()
{
try {
os::remove($this->_path);
} catch (FileNotFoundError $e) {
}
}
}

View File

@ -1,32 +0,0 @@
from sys import platform
import os
from subprocess import call
from os.path import exists
class File():
def __init__(self, path):
self._path = path
def write_bytes(self, bytes):
''' truncates the file and create new with :param bytes.
:return number of bytes written'''
with open(self._path, 'w+b') as file:
return file.write(bytes)
def read_bytes(self):
''' read the file as bytes. :return b'' on file not exist '''
if not exists(self._path): return b''
# buf = b''
with open(self._path, 'r+b') as file:
return file.read()
# return buf
def open(self):
'''tries to open with os default viewer'''
call(('cmd /c start "" "'+ self._path +'"')if os.name is 'nt' else ('open' if platform.startswith('darwin') else 'xdg-open', self._path))
def remove(self):
''' try to remove the file '''
try:
os.remove(self._path)
except FileNotFoundError: pass

View File

@ -1 +0,0 @@
../libpy2php/

View File

@ -1,8 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
require_once 'crypt.php';
class message
{
}

View File

@ -1,4 +0,0 @@
import crypt
class Message():
pass

View File

@ -1,448 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
require_once 'os.php';
class TelepyShell
{
public $intro = 'Welcome to telepy interactive shell. Type help or ? for help.
';
public $prompt = '>';
public function preloop()
{
require_once 'classes/telepy.php';
$this->_telepy = new Telepy();
}
public function precmd($line)
{
$line = $line->lstrip();
$blank_pos = $line->find(' ');
if (($blank_pos < 0)) {
return $line->lower();
}
return array_slice($line, null, $blank_pos)->lower().' '.array_slice($line, ($blank_pos + 1), null);
}
public function completedefault(...$ignored)
{
pyjslib_printnl($ignored);
}
public function complete($text, $state)
{
$this->super()->complete($text, $state);
pyjslib_printnl('completing');
}
/**
* shell <command-line>
* lets you use external shell. !<command-line> for short-hand.
*/
public function do_shell($line)
{
pyjslib_printnl(os::popen($line)->read());
}
/**
* msg <peer>
* sends message to this peer.
*/
public function do_msg($arg)
{
}
/**
* fwd <user> <msg-no>
* forward message to user. You can see message numbers starting client with -N.
*/
public function do_fwd($arg)
{
}
/**
* chat_with_peer <peer>
* starts one on one chat session with this peer. /exit or /quit to end this mode.
*/
public function do_chat_with_peer($arg)
{
}
/**
* add_contact <phone-number> <first-name> <last-name>
* tries to add contact to contact-list by phone.
*/
public function do_add_contact($arg)
{
}
/**
* rename_contact <user> <first-name> <last-name>
* tries to rename contact. If you have another device it will be a fight.
*/
public function do_rename_contact($arg)
{
}
/**
* mark_read <peer>
* mark read all received messages with peer.
*/
public function do_mark_read($arg)
{
}
/**
* delete_msg <msg-no>
* deletes message (not completly, though).
*/
public function do_delete_msg($arg)
{
}
/**
* restore_msg <msg-no>
* restores delete message. Impossible for secret chats. Only possible short time (one hour, I think) after deletion.
*/
public function do_restore_msg($arg)
{
}
/**
* send_photo <peer> <photo-file-name>
* sends photo to peer.
*/
public function do_send_photo($arg)
{
}
/**
* send_video <peer> <video-file-name>
* sends video to peer.
*/
public function do_send_video($arg)
{
}
/**
* send_text <peer> <text-file-name>
* sends text file as plain messages.
*/
public function do_send_text($arg)
{
}
/**
* load_photo <msg-no>
* loads photo to download dir.
*/
public function do_load_photo($arg)
{
}
/**
* load_video <msg-no>
* loads video to download dir.
*/
public function do_load_video($arg)
{
}
/**
* load_video_thumb <msg-no>
* loads video thumbnail to download dir.
*/
public function do_load_video_thumb($arg)
{
}
/**
* load_audio <msg-no>
* loads audio to download dir.
*/
public function do_load_audio($arg)
{
}
/**
* load_document <msg-no>
* loads document to download dir.
*/
public function do_load_document($arg)
{
}
/**
* load_document_thumb <msg-no>
* loads document thumbnail to download dir.
*/
public function do_load_document_thumb($arg)
{
}
/**
* view_photo <msg-no>
* loads photo/video to download dir and starts system default viewer.
*/
public function do_view_photo($arg)
{
}
/**
* view_video <msg-no>.
*/
public function do_view_video($arg)
{
}
/**
* view_video_thumb <msg-no>.
*/
public function do_view_video_thumb($arg)
{
}
/**
* view_audio <msg-no>.
*/
public function do_view_audio($arg)
{
}
/**
* view_document <msg-no>.
*/
public function do_view_document($arg)
{
}
/**
* view_document_thumb <msg-no>.
*/
public function do_view_document_thumb($arg)
{
}
/**
* fwd_media <msg-no>
* send media in your message. Use this to prevent sharing info about author of media (though, it is possible to determine user_id from media itself, it is not possible get access_hash of this user).
*/
public function do_fwd_media($arg)
{
}
/**
* set_profile_photo <photo-file-name>
* sets userpic. Photo should be square, or server will cut biggest central square part.
*/
public function do_set_profile_photo($arg)
{
}
/**
* chat_info <chat>
* prints info about chat.
*/
public function do_chat_info($arg)
{
$arg = $arg->split();
if ((count($arg) == 1)) {
pyjslib_printnl(['chat_info called with ', $arg[0]]);
}
}
/**
* chat_add_user <chat> <user>
* add user to chat.
*/
public function do_chat_add_user($arg)
{
pyjslib_printnl($arg);
}
/**
* chat_del_user <chat> <user>
* remove user from chat.
*/
public function do_chat_del_user($arg)
{
}
/**
* chat_rename <chat> <new-name>
* rename chat room.
*/
public function do_chat_rename($arg)
{
$arg = $arg->split();
}
/**
* create_group_chat <chat topic> <user1> <user2> <user3> ...
* creates a groupchat with users, use chat_add_user to add more users.
*/
public function do_create_group_chat($chat_topic, $user1, $user2, $user3)
{
pyjslib_printnl($chat_topic);
pyjslib_printnl([$user1, $user2, $user3]);
}
/**
* chat_set_photo <chat> <photo-file-name>
* sets group chat photo. Same limits as for profile photos.
*/
public function do_chat_set_photo($chat, $photo)
{
}
/**
* search <peer> <pattern>
* searches pattern in messages with peer.
*/
public function do_search($pattern)
{
}
/**
* global_search <pattern>
* searches pattern in all messages.
*/
public function do_global_search($pattern)
{
}
/**
* create_secret_chat <user>
* creates secret chat with this user.
*/
public function do_create_secret_chat($user)
{
}
/**
* visualize_key <secret_chat>
* prints visualization of encryption key. You should compare it to your partner's one.
*/
public function do_visualize_key($secret_chat)
{
}
/**
* set_ttl <secret_chat> <ttl>
* sets ttl to secret chat. Though client does ignore it, client on other end can make use of it.
*/
public function do_set_ttl($secret_chat, $ttl)
{
}
/**
* accept_secret_chat <secret_chat>
* manually accept secret chat (only useful when starting with -E key).
*/
public function do_accept_secret_chat($secret_chat)
{
}
/**
* user_info <user>
* prints info about user.
*/
public function do_user_info($user)
{
}
/**
* history <peer> [limit]
* prints history (and marks it as read). Default limit = 40.
*/
public function do_history($peer, $limit = 40)
{
if (($peer == '')) {
pyjslib_printnl('no peer have specified');
return;
}
$args = $peer->split();
if (!in_array(count($args), [1, 2])) {
pyjslib_printnl(['not appropriate number of arguments : ', $peer]);
return;
}
if ((count($args) == 2)) {
if (!($args[1]->isdecimal()) || (pyjslib_int($args[1]) < 1)) {
pyjslib_printnl(['not a valid limit:', $args[1]]);
}
$limit = pyjslib_int($args[1]);
}
pyjslib_printnl($peer);
pyjslib_printnl($limit);
}
/**
* dialog_list
* prints info about your dialogs.
*/
public function do_dialog_list($ignored)
{
}
/**
* contact_list
* prints info about users in your contact list.
*/
public function do_contact_list($ignored)
{
}
/**
* suggested_contacts
* print info about contacts, you have max common friends.
*/
public function do_suggested_contacts($ignored)
{
}
/**
* stats
* just for debugging.
*/
public function do_stats($ignored)
{
}
/**
* export_card
* print your 'card' that anyone can later use to import your contact.
*/
public function do_export_card($card)
{
}
/**
* import_card <card>
* gets user by card. You can write messages to him after that.
*/
public function do_import_card($card)
{
}
/**
* quit_force
* quit without waiting for query ends.
*/
public function do_quit_force($ignored)
{
return true;
}
/**
* quit
* wait for all queries to end then quit.
*/
public function do_quit($ignored)
{
return true;
}
}

View File

@ -1,337 +0,0 @@
import os, cmd
class TelepyShell(cmd.Cmd):
intro='Welcome to telepy interactive shell. Type help or ? for help.\n'
prompt='>'
def preloop(self):
from classes.telepy import Telepy
self._telepy = Telepy()
def precmd(self, line):
# convert first word(command name) to lower and return it as line
line = line.lstrip()
blank_pos = line.find(' ')
if blank_pos < 0: return line.lower()
return line[:blank_pos].lower() + ' ' + line[blank_pos+1:]
def completedefault(self, *ignored):
print(ignored)
def complete(self, text, state):
self.super().complete(text, state)
print('completing')
def do_shell(self, line):
'''
shell <command-line>
lets you use external shell. !<command-line> for short-hand.
'''
print(os.popen(line).read())
#detailed commands
def do_msg(self, arg):
'''
msg <peer>
sends message to this peer
'''
pass
def do_fwd(self, arg):
'''
fwd <user> <msg-no>
forward message to user. You can see message numbers starting client with -N
'''
pass
def do_chat_with_peer(self, arg):
'''
chat_with_peer <peer>
starts one on one chat session with this peer. /exit or /quit to end this mode.
'''
pass
def do_add_contact(self, arg):
'''
add_contact <phone-number> <first-name> <last-name>
tries to add contact to contact-list by phone
'''
pass
def do_rename_contact(self, arg):
'''
rename_contact <user> <first-name> <last-name>
tries to rename contact. If you have another device it will be a fight
'''
pass
def do_mark_read(self, arg):
'''
mark_read <peer>
mark read all received messages with peer
'''
pass
def do_delete_msg(self, arg):
'''
delete_msg <msg-no>
deletes message (not completly, though)
'''
pass
def do_restore_msg(self, arg):
'''
restore_msg <msg-no>
restores delete message. Impossible for secret chats. Only possible short time (one hour, I think) after deletion
'''
pass
def do_send_photo(self, arg):
'''
send_photo <peer> <photo-file-name>
sends photo to peer
'''
pass
def do_send_video(self, arg):
'''
send_video <peer> <video-file-name>
sends video to peer
'''
pass
def do_send_text(self, arg):
'''
send_text <peer> <text-file-name>
sends text file as plain messages
'''
pass
def do_load_photo(self, arg):
'''
load_photo <msg-no>
loads photo to download dir
'''
pass
def do_load_video(self, arg):
'''
load_video <msg-no>
loads video to download dir
'''
pass
def do_load_video_thumb(self, arg):
'''
load_video_thumb <msg-no>
loads video thumbnail to download dir
'''
pass
def do_load_audio(self, arg):
'''
load_audio <msg-no>
loads audio to download dir
'''
pass
def do_load_document(self, arg):
'''
load_document <msg-no>
loads document to download dir
'''
pass
def do_load_document_thumb(self, arg):
'''
load_document_thumb <msg-no>
loads document thumbnail to download dir
'''
pass
def do_view_photo(self, arg):
'''
view_photo <msg-no>
loads photo/video to download dir and starts system default viewer
'''
pass
def do_view_video(self, arg):
'''
view_video <msg-no>
'''
pass
def do_view_video_thumb(self, arg):
'''
view_video_thumb <msg-no>
'''
pass
def do_view_audio(self, arg):
'''
view_audio <msg-no>
'''
pass
def do_view_document(self, arg):
'''
view_document <msg-no>
'''
pass
def do_view_document_thumb(self, arg):
'''
view_document_thumb <msg-no>
'''
pass
def do_fwd_media(self, arg):
'''
fwd_media <msg-no>
send media in your message. Use this to prevent sharing info about author of media (though, it is possible to determine user_id from media itself, it is not possible get access_hash of this user)
'''
pass
def do_set_profile_photo(self, arg):
'''
set_profile_photo <photo-file-name>
sets userpic. Photo should be square, or server will cut biggest central square part
'''
pass
def do_chat_info(self, arg):
'''
chat_info <chat>
prints info about chat
'''
arg=arg.split()
if len(arg) is 1:
print ('chat_info called with ', arg[0])
def do_chat_add_user(self,arg):
'''
chat_add_user <chat> <user>
add user to chat
'''
print(arg)
def do_chat_del_user(self,arg):
'''
chat_del_user <chat> <user>
remove user from chat
'''
pass
def do_chat_rename(self,arg):
'''
chat_rename <chat> <new-name>
rename chat room
'''
arg=arg.split()
def do_create_group_chat(self, chat_topic, user1, user2, user3):
'''
create_group_chat <chat topic> <user1> <user2> <user3> ...
creates a groupchat with users, use chat_add_user to add more users
'''
print(chat_topic)
print(user1,user2,user3)
pass
def do_chat_set_photo(self, chat, photo):
'''
chat_set_photo <chat> <photo-file-name>
sets group chat photo. Same limits as for profile photos.
'''
pass
def do_search(self, pattern):
'''
search <peer> <pattern>
searches pattern in messages with peer
'''
pass
def do_global_search(self, pattern):
'''
global_search <pattern>
searches pattern in all messages
'''
pass
def do_create_secret_chat(self, user):
'''
create_secret_chat <user>
creates secret chat with this user
'''
pass
def do_visualize_key(self, secret_chat):
'''
visualize_key <secret_chat>
prints visualization of encryption key. You should compare it to your partner's one
'''
pass
def do_set_ttl(self, secret_chat, ttl):
'''
set_ttl <secret_chat> <ttl>
sets ttl to secret chat. Though client does ignore it, client on other end can make use of it
'''
pass
def do_accept_secret_chat(self, secret_chat):
'''
accept_secret_chat <secret_chat>
manually accept secret chat (only useful when starting with -E key)
'''
pass
def do_user_info(self, user):
'''
user_info <user>
prints info about user
'''
pass
def do_history(self, peer, limit=40):
'''
history <peer> [limit]
prints history (and marks it as read). Default limit = 40
'''
if peer is '':
print('no peer have specified')
return
args = peer.split()
if len(args) not in (1,2) :
print('not appropriate number of arguments : ', peer)
return
if len(args) is 2:
if not args[1].isdecimal() or int(args[1]) < 1:
print('not a valid limit:', args[1])
limit = int(args[1])
print(peer)
print(limit)
def do_dialog_list(self, ignored):
'''
dialog_list
prints info about your dialogs
'''
pass
def do_contact_list(self, ignored):
'''
contact_list
prints info about users in your contact list
'''
pass
def do_suggested_contacts(self, ignored):
'''
suggested_contacts
print info about contacts, you have max common friends
'''
pass
def do_stats(self, ignored):
'''
stats
just for debugging
'''
pass
def do_export_card(self, card):
'''
export_card
print your 'card' that anyone can later use to import your contact
'''
pass
def do_import_card(self, card):
'''
import_card <card>
gets user by card. You can write messages to him after that.
'''
pass
def do_quit_force(self, ignored):
'''
quit_force
quit without waiting for query ends
'''
return True
def do_quit(self, ignored):
'''
quit
wait for all queries to end then quit
'''
#TODO:safely end queries
return True

View File

@ -1,28 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
class telepy
{
public function __construct()
{
try {
require_once 'configparser.php';
} catch (ImportError $e) {
require_once 'ConfigParser.php';
}
require_once 'mtproto.php';
$this->_config = $configparser->ConfigParser();
if (!($this->_config->read('credentials'))) {
pyjslib_printnl('File \'credentials\' seems to not exist.');
$exit(-1);
}
$ip = $this->_config->get('App data', 'ip_address');
$port = $this->_config->getint('App data', 'port');
$this->_session = $mtproto->Session($ip, $port);
$this->_session->create_auth_key();
$__temp22 = py2php_kwargs_method_call($this->_session, 'method_call', ['get_future_salts'], ['num' => 3]);
$this->_salt = $__temp22;
$future_salts = $__temp22;
}
}

View File

@ -1,22 +0,0 @@
class Telepy():
def __init__(self):
# Deal with py2 and py3 differences
try: # this only works in py2.7
import configparser
except ImportError:
import ConfigParser as configparser
import mtproto
self._config = configparser.ConfigParser()
# Check if credentials is correctly loaded (when it doesn't read anything it returns [])
if not self._config.read('credentials'):
print("File 'credentials' seems to not exist.")
exit(-1)
ip = self._config.get('App data', 'ip_address')
port = self._config.getint('App data', 'port')
self._session = mtproto.Session(ip, port)
self._session.create_auth_key()
self._salt = future_salts = self._session.method_call('get_future_salts', num=3)

View File

@ -1,16 +0,0 @@
<?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
require_once 'libpy2php.php';
class user
{
public $me = null;
// current connected user
public $friends = [];
// current connected user's friends
public function __construct($uid)
{
$this->uid = $uid;
}
}

View File

@ -1,11 +0,0 @@
class User():
me = None
''' current connected user '''
friends = []
''' current connected user's friends '''
def __init__(self, uid):
self.uid = uid

View File

@ -1,5 +0,0 @@
[App data]
api_id = 25628
api_hash = 1fe17cda7d355166cdaa71f04122873c
ip_address = 149.154.167.50
port = 443

View File

@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
# Author: Sammy Pfeiffer
# Author: Anton Grigoryev
# This file implements the AES 256 IGE cipher
# working in Python 2.7 and Python 3.4 (other versions untested)
# as it's needed for the implementation of Telegram API
# It's based on PyCryto
from __future__ import print_function
from Crypto.Util.strxor import strxor
from Crypto.Cipher import AES
# AES 256 IGE part
def ige_encrypt(message, key, iv):
return _ige(message, key, iv, operation="encrypt")
def ige_decrypt(message, key, iv):
return _ige(message, key, iv, operation="decrypt")
def _ige(message, key, iv, operation="decrypt"):
"""Given a key, given an iv, and message
do whatever operation asked in the operation field.
Operation will be checked for: "decrypt" and "encrypt" strings.
Returns the message encrypted/decrypted.
message must be a multiple by 16 bytes (for division in 16 byte blocks)
key must be 32 byte
iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
needed for IGE)"""
message = bytes(message)
if len(key) != 32:
raise ValueError("key must be 32 bytes long (was " +
str(len(key)) + " bytes)")
if len(iv) != 32:
raise ValueError("iv must be 32 bytes long (was " +
str(len(iv)) + " bytes)")
cipher = AES.new(key, AES.MODE_ECB, iv)
blocksize = cipher.block_size
if len(message) % blocksize != 0:
raise ValueError("message must be a multiple of 16 bytes (try adding " +
str(16 - len(message) % 16) + " bytes of padding)")
ivp = iv[0:blocksize]
ivp2 = iv[blocksize:]
ciphered = bytes()
for i in range(0, len(message), blocksize):
indata = message[i:i+blocksize]
if operation == "decrypt":
xored = strxor(indata, ivp2)
decrypt_xored = cipher.decrypt(xored)
outdata = strxor(decrypt_xored, ivp)
ivp = indata
ivp2 = outdata
elif operation == "encrypt":
xored = strxor(indata, ivp)
encrypt_xored = cipher.encrypt(xored)
outdata = strxor(encrypt_xored, ivp2)
ivp = outdata
ivp2 = indata
else:
raise ValueError("operation must be either 'decrypt' or 'encrypt'")
ciphered += outdata
return ciphered

View File

@ -1,6 +0,0 @@
#!/usr/bin/env python
import prime
import sys
pq = prime.primefactors(int(sys.argv[1]))
sys.stdout.write(str(pq[0]) + " " + str(pq[1]))
sys.stdout.flush()

View File

@ -1,270 +0,0 @@
# -*- coding: utf-8 -*-
"""
Created on Tue Sep 2 19:26:15 2014
@author: Anton Grigoryev
@author: Sammy Pfeiffer
"""
from binascii import crc32 as originalcrc32
from binascii import hexlify
from time import time
import io
import os.path
import socket
import struct
# pycrypto module
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes, bytes_to_long
# local modules
import crypt
import prime
import TL
def crc32(data):
return originalcrc32(data) & 0xffffffff
def vis(bs):
"""
Function to visualize byte streams. Split into bytes, print to console.
:param bs: BYTE STRING
"""
bs = bytearray(bs)
symbols_in_one_line = 8
n = len(bs) // symbols_in_one_line
i = 0
for i in range(n):
print(str(i*symbols_in_one_line)+" | "+" ".join(["%02X" % b for b in bs[i*symbols_in_one_line:(i+1)*symbols_in_one_line]])) # for every 8 symbols line
if not len(bs) % symbols_in_one_line == 0:
print(str((i+1)*symbols_in_one_line)+" | "+" ".join(["%02X" % b for b in bs[(i+1)*symbols_in_one_line:]])+"\n") # for last line
class Session:
""" Manages TCP Transport. encryption and message frames """
def __init__(self, ip, port, auth_key=None, server_salt=None):
# creating socket
self.sock = socket.socket()
self.sock.connect((ip, port))
self.number = 0
self.timedelta = 0
self.session_id = os.urandom(8)
self.auth_key = auth_key
self.auth_key_id = SHA.new(self.auth_key).digest()[-8:] if self.auth_key else None
self.sock.settimeout(5.0)
self.MAX_RETRY = 5;
self.AUTH_MAX_RETRY = 5;
def __del__(self):
# closing socket when session object is deleted
self.sock.close()
def send_message(self, message_data):
"""
Forming the message frame and sending message to server
:param message: byte string to send
"""
message_id = struct.pack('<Q', int((time()+self.timedelta)*2**30)*4)
if self.auth_key is None or self.server_salt is None:
# Unencrypted data send
message = (b'\x00\x00\x00\x00\x00\x00\x00\x00' +
message_id +
struct.pack('<I', len(message_data)) +
message_data)
else:
# Encrypted data send
encrypted_data = (self.server_salt +
self.session_id +
message_id +
struct.pack('<II', self.number, len(message_data)) +
message_data)
message_key = SHA.new(encrypted_data).digest()[-16:]
padding = os.urandom((-len(encrypted_data)) % 16)
aes_key, aes_iv = self.aes_calculate(message_key)
message = (self.auth_key_id + message_key +
crypt.ige_encrypt(encrypted_data+padding, aes_key, aes_iv))
step1 = struct.pack('<II', len(message)+12, self.number) + message
step2 = step1 + struct.pack('<I', crc32(step1))
self.sock.send(step2)
self.number += 1
def recv_message(self):
"""
Reading socket and receiving message from server. Check the CRC32.
"""
packet_length_data = self.sock.recv(4) # reads how many bytes to read
if len(packet_length_data) < 4:
raise Exception("Nothing in the socket!")
packet_length = struct.unpack("<I", packet_length_data)[0]
packet = self.sock.recv(packet_length - 4) # read the rest of bytes from socket
# check the CRC32
if not crc32(packet_length_data + packet[0:-4]) == struct.unpack('<I', packet[-4:])[0]:
raise Exception("CRC32 was not correct!")
x = struct.unpack("<I", packet[:4])
auth_key_id = packet[4:12]
if auth_key_id == b'\x00\x00\x00\x00\x00\x00\x00\x00':
# No encryption - Plain text
(message_id, message_length) = struct.unpack("<8sI", packet[12:24])
data = packet[24:24+message_length]
elif auth_key_id == self.auth_key_id:
message_key = packet[12:28]
encrypted_data = packet[28:-4]
aes_key, aes_iv = self.aes_calculate(message_key, direction="from server")
decrypted_data = crypt.ige_decrypt(encrypted_data, aes_key, aes_iv)
assert decrypted_data[0:8] == self.server_salt
assert decrypted_data[8:16] == self.session_id
message_id = decrypted_data[16:24]
seq_no = struct.unpack("<I", decrypted_data[24:28])[0]
message_data_length = struct.unpack("<I", decrypted_data[28:32])[0]
data = decrypted_data[32:32+message_data_length]
else:
raise Exception("Got unknown auth_key id")
return data
def method_call(self, method, **kwargs):
#print(kwargs)
for i in range(1, self.MAX_RETRY):
try:
self.send_message(TL.serialize_method(method, **kwargs))
server_answer = self.recv_message()
except socket.timeout:
print("Retry call method")
continue
return TL.deserialize(io.BytesIO(server_answer))
def create_auth_key(self):
nonce = os.urandom(16)
print("Requesting pq")
f = open(os.path.join(os.path.dirname(__file__), "rsa.pub"))
key = RSA.importKey(f.read())
ResPQ = self.method_call('req_pq', nonce=nonce)
server_nonce = ResPQ['server_nonce']
# TODO: selecting RSA public key based on this fingerprint
public_key_fingerprint = ResPQ['server_public_key_fingerprints'][0]
pq_bytes = ResPQ['pq']
pq = bytes_to_long(pq_bytes)
[p, q] = prime.primefactors(pq)
if p > q: (p, q) = (q, p)
assert p*q == pq and p < q
print("Factorization %d = %d * %d" % (pq, p, q))
p_bytes = long_to_bytes(p)
q_bytes = long_to_bytes(q)
new_nonce = os.urandom(32)
data = TL.serialize_obj('p_q_inner_data',
pq=pq_bytes,
p=p_bytes,
q=q_bytes,
nonce=nonce,
server_nonce=server_nonce,
new_nonce=new_nonce)
sha_digest = SHA.new(data).digest()
random_bytes = os.urandom(255-len(data)-len(sha_digest))
to_encrypt = sha_digest + data + random_bytes
encrypted_data = key.encrypt(to_encrypt, 0)[0]
print("Starting Diffie Hellman key exchange", len(to_encrypt))
server_dh_params = self.method_call('req_DH_params',
nonce=nonce,
server_nonce=server_nonce,
p=p_bytes,
q=q_bytes,
public_key_fingerprint=public_key_fingerprint,
encrypted_data=encrypted_data)
assert nonce == server_dh_params['nonce']
assert server_nonce == server_dh_params['server_nonce']
encrypted_answer = server_dh_params['encrypted_answer']
tmp_aes_key = SHA.new(new_nonce + server_nonce).digest() + SHA.new(server_nonce + new_nonce).digest()[0:12]
tmp_aes_iv = SHA.new(server_nonce + new_nonce).digest()[12:20] + SHA.new(new_nonce + new_nonce).digest() + new_nonce[0:4]
answer_with_hash = crypt.ige_decrypt(encrypted_answer, tmp_aes_key, tmp_aes_iv)
answer_hash = answer_with_hash[:20]
answer = answer_with_hash[20:]
# TODO: SHA hash assertion here
server_DH_inner_data = TL.deserialize(io.BytesIO(answer))
assert nonce == server_DH_inner_data['nonce']
assert server_nonce == server_DH_inner_data['server_nonce']
dh_prime_str = server_DH_inner_data['dh_prime']
g = server_DH_inner_data['g']
g_a_str = server_DH_inner_data['g_a']
server_time = server_DH_inner_data['server_time']
self.timedelta = server_time - time()
print("Server-client time delta = %.1f s" % self.timedelta)
dh_prime = bytes_to_long(dh_prime_str)
g_a = bytes_to_long(g_a_str)
assert prime.isprime(dh_prime)
retry_id = 0
b_str = os.urandom(256)
b = bytes_to_long(b_str)
g_b = pow(g, b, dh_prime)
g_b_str = long_to_bytes(g_b)
data = TL.serialize_obj('client_DH_inner_data',
nonce=nonce,
server_nonce=server_nonce,
retry_id=retry_id,
g_b=g_b_str)
data_with_sha = SHA.new(data).digest()+data
data_with_sha_padded = data_with_sha + os.urandom(-len(data_with_sha) % 16)
encrypted_data = crypt.ige_encrypt(data_with_sha_padded, tmp_aes_key, tmp_aes_iv)
for i in range(1, self.AUTH_MAX_RETRY): # retry when dh_gen_retry or dh_gen_fail
Set_client_DH_params_answer = self.method_call('set_client_DH_params',
nonce=nonce,
server_nonce=server_nonce,
encrypted_data=encrypted_data)
# print Set_client_DH_params_answer
auth_key = pow(g_a, b, dh_prime)
auth_key_str = long_to_bytes(auth_key)
auth_key_sha = SHA.new(auth_key_str).digest()
auth_key_aux_hash = auth_key_sha[:8]
new_nonce_hash1 = SHA.new(new_nonce+b'\x01'+auth_key_aux_hash).digest()[-16:]
new_nonce_hash2 = SHA.new(new_nonce+b'\x02'+auth_key_aux_hash).digest()[-16:]
new_nonce_hash3 = SHA.new(new_nonce+b'\x03'+auth_key_aux_hash).digest()[-16:]
assert Set_client_DH_params_answer['nonce'] == nonce
assert Set_client_DH_params_answer['server_nonce'] == server_nonce
if Set_client_DH_params_answer.name == 'dh_gen_ok':
assert Set_client_DH_params_answer['new_nonce_hash1'] == new_nonce_hash1
print("Diffie Hellman key exchange processed successfully")
self.server_salt = strxor(new_nonce[0:8], server_nonce[0:8])
self.auth_key = auth_key_str
self.auth_key_id = auth_key_sha[-8:]
print("Auth key generated")
return "Auth Ok"
elif Set_client_DH_params_answer.name == 'dh_gen_retry':
assert Set_client_DH_params_answer['new_nonce_hash2'] == new_nonce_hash2
print ("Retry Auth")
elif Set_client_DH_params_answer.name == 'dh_gen_fail':
assert Set_client_DH_params_answer['new_nonce_hash3'] == new_nonce_hash3
print("Auth Failed")
raise Exception("Auth Failed")
else: raise Exception("Response Error")
def aes_calculate(self, msg_key, direction="to server"):
x = 0 if direction == "to server" else 8
sha1_a = SHA.new(msg_key + self.auth_key[x:x+32]).digest()
sha1_b = SHA.new(self.auth_key[x+32:x+48] + msg_key + self.auth_key[48+x:64+x]).digest()
sha1_c = SHA.new(self.auth_key[x+64:x+96] + msg_key).digest()
sha1_d = SHA.new(msg_key + self.auth_key[x+96:x+128]).digest()
aes_key = sha1_a[0:8] + sha1_b[8:20] + sha1_c[4:16]
aes_iv = sha1_a[8:20] + sha1_b[0:8] + sha1_c[16:20] + sha1_d[0:8]
return aes_key, aes_iv

View File

@ -1,142 +0,0 @@
# NOTICE!!! This is copied from https://stackoverflow.com/questions/4643647/fast-prime-factorization-module
import random
def primesbelow(N):
# http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
#""" Input N>=6, Returns a list of primes, 2 <= p < N """
correction = N % 6 > 1
N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6]
sieve = [True] * (N // 3)
sieve[0] = False
for i in range(int(N ** .5) // 3 + 1):
if sieve[i]:
k = (3 * i + 1) | 1
sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1)
sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1)
return [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]]
smallprimeset = set(primesbelow(100000))
_smallprimeset = 100000
def isprime(n, precision=7):
# http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time
if n == 1 or n % 2 == 0:
return False
elif n < 1:
raise ValueError("Out of bounds, first argument must be > 0")
elif n < _smallprimeset:
return n in smallprimeset
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
for repeat in range(precision):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for r in range(s - 1):
x = pow(x, 2, n)
if x == 1: return False
if x == n - 1: break
else: return False
return True
# https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
def pollard_brent(n):
if n % 2 == 0: return 2
if n % 3 == 0: return 3
y, c, m = 87552211475113995, 330422027228888537, 226866727920975483
#random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1)
g, r, q = 1, 1, 1
while g == 1:
x = y
for i in range(r):
y = (pow(y, 2, n) + c) % n
k = 0
while k < r and g==1:
ys = y
for i in range(min(m, r-k)):
y = (pow(y, 2, n) + c) % n
q = q * abs(x-y) % n
g = gcd(q, n)
k += m
r *= 2
if g == n:
while True:
ys = (pow(ys, 2, n) + c) % n
g = gcd(abs(x - ys), n)
if g > 1:
break
return g
smallprimes = primesbelow(10000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
factors = []
limit = int(n ** .5) + 1
for checker in smallprimes:
if checker > limit: break
while n % checker == 0:
factors.append(checker)
n //= checker
limit = int(n ** .5) + 1
if checker > limit: break
if n < 2: return factors
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
def factorization(n):
factors = {}
for p1 in primefactors(n):
try:
factors[p1] += 1
except KeyError:
factors[p1] = 1
return factors
totients = {}
def totient(n):
if n == 0: return 1
try: return totients[n]
except KeyError: pass
tot = 1
for p, exp in factorization(n).items():
tot *= (p - 1) * p ** (exp - 1)
totients[n] = tot
return tot
def gcd(a, b):
if a == b: return a
while b > 0: a, b = b, a % b
return a
def lcm(a, b):
return abs(a * b) // gcd(a, b)

View File

@ -1,8 +0,0 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----

View File

@ -1,17 +0,0 @@
#CLI like interface
import argparse, getopt, os, io, struct, mtproto
from classes.shell import TelepyShell
if __name__ == '__main__':
parser = argparse.ArgumentParser('telepy',description='Python implementation of telegram API.')
parser.add_argument('command', nargs='?', choices=['cmd', 'dialog_list', 'contact_list'] + ['chat_' + sub for sub in ['info', 'add_user', 'add_user_to_chat', 'del_user', 'set_photo', 'rename']])
parser.add_argument('args', nargs='*')
#for command, args, help in (('info', 1, 'prints info about chat'), ('add_user', 2, 'add user to chat'), ('del_user', 2, 'remove user from chat'), ('set_photo', 1, 'sets group chat photo. Same limits as for profile photos.')):
# parser.add_argument('chat_' + command, nargs=args, help=help)
#parser.add_argument
args = parser.parse_args()
if args.command is None:
TelepyShell().cmdloop()

View File

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
import os
import io
import struct
# Deal with py2 and py3 differences
try: # this only works in py2.7
import configparser
except ImportError:
import ConfigParser as configparser
import mtproto
config = configparser.ConfigParser()
# Check if credentials is correctly loaded (when it doesn't read anything it returns [])
if not config.read('credentials'):
print("File 'credentials' seems to not exist.")
exit(-1)
ip = config.get('App data', 'ip_address')
port = config.getint('App data', 'port')
Session = mtproto.Session(ip, port)
Session.create_auth_key()
future_salts = Session.method_call('get_future_salts', num=3)
print(future_salts)

View File

@ -1,24 +0,0 @@
--------------------------
- BEGIN OF Serialization -
--------------------------
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'libpy2php');
require_once ('libpy2php.php');
--------------------------
- END OF Serialization -
--------------------------
--------------------------
- BEGIN OF Serialization -
--------------------------
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'libpy2php');
require_once ('libpy2php.php');
--------------------------
- END OF Serialization -
--------------------------

View File

@ -1,4 +0,0 @@
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'libpy2php');
require_once('libpy2php.php');

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
"""
MTProto data serialization and SHA hash test
@author: Anton Grigoryev
@author: Sammy Pfeiffer
"""
from Crypto.Hash import SHA
import io
import mtproto
# byte strings got from
# https://core.telegram.org/mtproto/samples-auth_key - step 4
z = io.BytesIO()
mtproto.serialize_obj(z, 'p_q_inner_data',
pq=b"\x17\xED\x48\x94\x1A\x08\xF9\x81",
p=b"\x49\x4C\x55\x3B",
q=b"\x53\x91\x10\x73",
nonce=b"\x3E\x05\x49\x82\x8C\xCA\x27\xE9\x66\xB3\x01\xA4\x8F\xEC\xE2\xFC",
server_nonce=b"\xA5\xCF\x4D\x33\xF4\xA1\x1E\xA8\x77\xBA\x4A\xA5\x73\x90\x73\x30",
new_nonce=b"\x31\x1C\x85\xDB\x23\x4A\xA2\x64\x0A\xFC\x4A\x76\xA7\x35\xCF\x5B\x1F\x0F\xD6\x8B\xD1\x7F\xA1\x81\xE1\x22\x9A\xD8\x67\xCC\x02\x4D")
x = z.getvalue()
if SHA.new(x).digest() == b'\xDB\x76\x1C\x27\x71\x8A\x23\x05\x04\x4F\x71\xF2\xAD\x95\x16\x29\xD7\x8B\x24\x49':
print("Test passed successfully")
else:
print("Test not passed")

View File

@ -1,66 +0,0 @@
# Author: Sammy Pfeiffer
# This file tests the AES 256 IGE cipher
# working in Python 2.7 and Python 3.4 (other versions untested)
# as it's needed for the implementation of Telegram API
from crypt import ige
# AES 256 IGE is using AES ECB internally, it implies (extract from PyCrypto.cipher.AES):
# key : byte string
# The secret key to use in the symmetric cipher.
# It must be 16 (*AES-128*), 24 (*AES-192*), or 32 (*AES-256*) bytes long.
# IV : byte string
# The initialization vector to use for encryption or decryption.
#
# It is ignored for `MODE_ECB` and `MODE_CTR`.
#
# For all other modes, it must be `block_size` bytes longs.
# message must be a multiple of 16 in size
msg_to_encrypt = "This is a secret message"
padding_needed = 16 - len(msg_to_encrypt) % 16
msg_to_encrypt_padded = msg_to_encrypt + str(0) * padding_needed
print("Encrypting: '" + str(msg_to_encrypt) + "'")
print("With padding: '" + str(msg_to_encrypt_padded) + "'")
# 32 bytes long key
aes_key = b'12345678901234567890123456789012'
print("With key for AES 256 ECB: '" + str(aes_key) + "'")
# Initialization Vector must be 32 bytes
aes_iv = b'01234567890123456789012345678901'
print("And initialization vector: '" + str(aes_iv) + "'")
encrypted_msg = _ige(msg_to_encrypt_padded, aes_key, aes_iv, operation="encrypt")
print("\nEncrypted msg: '" + str(encrypted_msg) + "'")
print("In hex: " + encrypted_msg.__repr__())
decrypted_msg = _ige(encrypted_msg, aes_key, aes_iv, operation="decrypt")
print("\nDecrypted msg: '" + str(decrypted_msg) + "'")
print("In hex: " + decrypted_msg.__repr__())
if msg_to_encrypt_padded == decrypted_msg:
print("Encrypt + Decrypt process, completed succesfully.")
# Let's test incorrect inputs
print("\n\nTesting incorrect inputs:")
# Message with length not multiple of 16
msg_not_multiple_of_16 = "6bytes"
print("Trying to encrypt: '" + msg_not_multiple_of_16 +
"' of size: " + str(len(msg_not_multiple_of_16)))
try:
encrypted_msg = _ige(msg_not_multiple_of_16, aes_key, aes_iv, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")
# key not being 32 bytes
aes_key_not_32_bytes = b'0123456789'
print("Trying to use key: '" + str(aes_key_not_32_bytes) + "'")
try:
encrypted_msg = _ige(msg_to_encrypt_padded, aes_key_not_32_bytes, aes_iv, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")
# iv not being 32 bytes
iv_key_not_32_bytes = b'0123456789'
print("Trying to use iv: '" + str(iv_key_not_32_bytes) + "'")
try:
encrypted_msg = _ige(msg_to_encrypt_padded, aes_key, iv_key_not_32_bytes, operation="encrypt")
except ValueError as ve:
print(" Correctly got ValueError: '" + str(ve) + "'")

View File

@ -1,9 +0,0 @@
from classes.file import File
from os.path import exists
f = File('text.txt')
assert f.write_bytes(b'testing bytes i/o'), 17
assert f.read_bytes(), b'testing bytes i/o'
f.open() # does it open any text editor on your system?
f.remove()
assert exists('text.txt'), False

View File

@ -1,185 +0,0 @@
# -*- coding: utf-8 -*-
# Author: Sammy Pfeiffer
# This file implements the AES 256 IGE cipher
# working in Python 2.7 and Python 3.4 (other versions untested)
# as it's needed for the implementation of Telegram API
# It's based on PyCryto
from __future__ import print_function
from Crypto.Util import number
from Crypto.Cipher import AES
MIN_SUPPORTED_PY3_VERSION = (3, 2, 0)
from sys import version_info
if version_info >= MIN_SUPPORTED_PY3_VERSION:
from binascii import hexlify
long = int
# Some color codes for printing
ENDC = '\033[0m' # To end a color
REDFAIL = '\033[91m' # RED
GREENOK = '\033[92m' # GREEN
def hex_string_to_str_bytes(val):
"""Given a String like
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
Convert it to it's byte representation, stored in py2 in a str, like:
tmp_aes_key_hex = '\xf0\x11(\x08\x87\xc7\xbb\x01\xdf\x0f\xc4\xe1x0\xe0\xb9\x1f\xbb\x8b\xe4\xb2&|\xb9\x85\xae%\xf3;RrS'
"""
return val.decode("hex")
def str_bytes_to_hex_string(val):
"""Given a str_bytes (so str()) like
tmp_aes_key_hex = '\xf0\x11(\x08\x87\xc7\xbb\x01\xdf\x0f\xc4\xe1x0\xe0\xb9\x1f\xbb\x8b\xe4\xb2&|\xb9\x85\xae%\xf3;RrS'
Convert it back to it's uppercase string representation, like:
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253" """
if version_info >= MIN_SUPPORTED_PY3_VERSION:
return str(hexlify(val).upper())
return val.encode("hex").upper()
def hex_string_to_long(val):
"""Given a String like
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
Convert it to int, which is actually long"""
return int(val, 16)
def xor_stuff(a, b):
"""XOR applied to every element of a with every element of b.
Depending on python version and depeding on input some arrangements need to be done."""
if version_info < MIN_SUPPORTED_PY3_VERSION:
if len(a) > len(b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])
else:
if type(a) == str and type(b) == bytes:# cipher.encrypt returns string
return bytes(ord(x) ^ y for x, y in zip(a, b))
elif type(a) == bytes and type(b) == str:
return bytes(x ^ ord(y) for x, y in zip(a, b))
else:
return bytes(x ^ y for x, y in zip(a, b))
def ige(message, key, iv, operation="decrypt"):
"""Given a key, given an iv, and message
do whatever operation asked in the operation field.
Operation will be checked for: "decrypt" and "encrypt" strings.
Returns the message encrypted/decrypted.
message must be a multiple by 16 bytes (for division in 16 byte blocks)
key must be 32 byte
iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
needed for IGE)"""
if type(message) == long:
message = number.long_to_bytes(message)
if type(key) == long:
key = number.long_to_bytes(key)
if type(iv) == long:
iv = number.long_to_bytes(iv)
if len(key) != 32:
raise ValueError("key must be 32 bytes long (was " +
str(len(key)) + " bytes)")
if len(iv) != 32:
raise ValueError("iv must be 32 bytes long (was " +
str(len(iv)) + " bytes)")
cipher = AES.new(key, AES.MODE_ECB, iv)
blocksize = cipher.block_size
if len(message) % blocksize != 0:
raise ValueError("message must be a multiple of 16 bytes (try adding " +
str(16 - len(message) % 16) + " bytes of padding)")
ivp = iv[0:blocksize]
ivp2 = iv[blocksize:]
ciphered = None
for i in range(0, len(message), blocksize):
indata = message[i:i+blocksize]
if operation == "decrypt":
xored = xor_stuff(indata, ivp2)
decrypt_xored = cipher.decrypt(xored)
outdata = xor_stuff(decrypt_xored, ivp)
ivp = indata
ivp2 = outdata
elif operation == "encrypt":
xored = xor_stuff(indata, ivp)
encrypt_xored = cipher.encrypt(xored)
outdata = xor_stuff(encrypt_xored, ivp2)
ivp = outdata
ivp2 = indata
else:
raise ValueError("operation must be either 'decrypt' or 'encrypt'")
if ciphered is None:
ciphered = outdata
else:
ciphered_ba = bytearray(ciphered)
ciphered_ba.extend(outdata)
if version_info >= MIN_SUPPORTED_PY3_VERSION:
ciphered = bytes(ciphered_ba)
else:
ciphered = str(ciphered_ba)
return ciphered
if __name__ == "__main__":
# Example data from https://core.telegram.org/mtproto/samples-auth_key#conversion-of-encrypted-answer-into-answer
encrypted_answer_str = "28A92FE20173B347A8BB324B5FAB2667C9A8BBCE6468D5B509A4CBDDC186240AC912CF7006AF8926DE606A2E74C0493CAA57741E6C82451F54D3E068F5CCC49B4444124B9666FFB405AAB564A3D01E67F6E912867C8D20D9882707DC330B17B4E0DD57CB53BFAAFA9EF5BE76AE6C1B9B6C51E2D6502A47C883095C46C81E3BE25F62427B585488BB3BF239213BF48EB8FE34C9A026CC8413934043974DB03556633038392CECB51F94824E140B98637730A4BE79A8F9DAFA39BAE81E1095849EA4C83467C92A3A17D997817C8A7AC61C3FF414DA37B7D66E949C0AEC858F048224210FCC61F11C3A910B431CCBD104CCCC8DC6D29D4A5D133BE639A4C32BBFF153E63ACA3AC52F2E4709B8AE01844B142C1EE89D075D64F69A399FEB04E656FE3675A6F8F412078F3D0B58DA15311C1A9F8E53B3CD6BB5572C294904B726D0BE337E2E21977DA26DD6E33270251C2CA29DFCC70227F0755F84CFDA9AC4B8DD5F84F1D1EB36BA45CDDC70444D8C213E4BD8F63B8AB95A2D0B4180DC91283DC063ACFB92D6A4E407CDE7C8C69689F77A007441D4A6A8384B666502D9B77FC68B5B43CC607E60A146223E110FCB43BC3C942EF981930CDC4A1D310C0B64D5E55D308D863251AB90502C3E46CC599E886A927CDA963B9EB16CE62603B68529EE98F9F5206419E03FB458EC4BD9454AA8F6BA777573CC54B328895B1DF25EAD9FB4CD5198EE022B2B81F388D281D5E5BC580107CA01A50665C32B552715F335FD76264FAD00DDD5AE45B94832AC79CE7C511D194BC42B70EFA850BB15C2012C5215CABFE97CE66B8D8734D0EE759A638AF013"
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
tmp_aes_iv_str = "3212D579EE35452ED23E0D0C92841AA7D31B2E9BDEF2151E80D15860311C85DB"
answer_str = "BA0D89B53E0549828CCA27E966B301A48FECE2FCA5CF4D33F4A11EA877BA4AA57390733002000000FE000100C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5BFE000100262AABA621CC4DF587DC94CF8252258C0B9337DFB47545A49CDD5C9B8EAE7236C6CADC40B24E88590F1CC2CC762EBF1CF11DCC0B393CAAD6CEE4EE5848001C73ACBB1D127E4CB93072AA3D1C8151B6FB6AA6124B7CD782EAF981BDCFCE9D7A00E423BD9D194E8AF78EF6501F415522E44522281C79D906DDB79C72E9C63D83FB2A940FF779DFB5F2FD786FB4AD71C9F08CF48758E534E9815F634F1E3A80A5E1C2AF210C5AB762755AD4B2126DFA61A77FA9DA967D65DFD0AFB5CDF26C4D4E1A88B180F4E0D0B45BA1484F95CB2712B50BF3F5968D9D55C99C0FB9FB67BFF56D7D4481B634514FBA3488C4CDA2FC0659990E8E868B28632875A9AA703BCDCE8FCB7AE551"
if version_info < MIN_SUPPORTED_PY3_VERSION:
# Crypto.Cipher.AES needs it's parameters to be 32byte str
# So we can either give 'str' type like this ONLY WORKS ON PYTHON2.7
encrypted_answer_hex = encrypted_answer_str.decode("hex")
tmp_aes_key_hex = tmp_aes_key_str.decode("hex")
tmp_aes_iv_hex = tmp_aes_iv_str.decode("hex")
answer_hex = answer_str.decode("hex")
decrypted_answer_in_str = ige(encrypted_answer_hex, tmp_aes_key_hex, tmp_aes_iv_hex)
print("decrypted_answer using string version of input: ")
print(decrypted_answer_in_str)
# Or give it long's representing the big numbers (ige will take care of the conversion)
encrypted_answer = int(encrypted_answer_str, 16)
tmp_aes_key = int(tmp_aes_key_str, 16)
tmp_aes_iv = int(tmp_aes_iv_str, 16)
answer = int(answer_str, 16)
decrypted_answer_in_int = ige(encrypted_answer, tmp_aes_key, tmp_aes_iv)
print("decrypted_answer using int version of input: ")
print(decrypted_answer_in_int)
if version_info < MIN_SUPPORTED_PY3_VERSION:
if decrypted_answer_in_str == decrypted_answer_in_int:
print("\nBoth str input and int input give the same result")
else:
print("\nDifferent result!!")
decrypt_ans_hex_str = str_bytes_to_hex_string(decrypted_answer_in_int)
print("Human friendly view of decrypted_answer:")
print(decrypt_ans_hex_str)
print("\nAnd we should expect inside of it:")
print(answer_str)
if answer_str in decrypt_ans_hex_str:
print("\n\nanswer_str is in decrypt_ans_hex_str!")
idx = decrypt_ans_hex_str.index(answer_str)
print(decrypt_ans_hex_str[:idx], end="")
print(GREENOK + decrypt_ans_hex_str[idx:idx+len(answer_str)] + ENDC, end="")
print(decrypt_ans_hex_str[idx+len(answer_str):])
print("There are " + str(idx/2) + " bytes at the start that are not part of the answer")
print("Plus " + str(len(decrypt_ans_hex_str[len(answer_str)+idx:]) / 2) + " at the end not forming part")
print("answer_str is: " + str(len(answer_str) / 2) + " bytes")
print("decrypt_ans_hex_str is: " + str(len(decrypt_ans_hex_str) / 2) + " bytes")
print("In total: " + str( (len(decrypt_ans_hex_str) - len(answer_str)) / 2) + " bytes that do not pertain")
else:
print("answer_str is not in decrypt_ans_hex_str :(")
print("This is because the header (SHA1(answer)) is included and is 20 bytes long.")
print("And in the end there are 0 to 15 bytes random to fill up the gap.")
print("This means that we can safely ignore the starting 20bytes and all the extra bytes in the end")
# answer_with_hash := SHA1(answer) + answer + (0-15 random bytes); such that the length be divisible by 16;
# This... divisible by 16 is because of the blocksize of AES-256-ECB (yay!)

View File

@ -1,334 +0,0 @@
# -*- coding: utf-8 -*-
# THIS MODULE HAS ALL THE CONVERSIONS WE NEED! (I think)
from Crypto.Util import number
def vis(bs):
"""
Function to visualize byte streams. Split into bytes, print to console.
:param bs: BYTE STRING
"""
bs = bytearray(bs)
symbols_in_one_line = 8
n = len(bs) // symbols_in_one_line
for i in range(n):
print(str(i*symbols_in_one_line)+" | "+" ".join(["%02X" % b for b in bs[i*symbols_in_one_line:(i+1)*symbols_in_one_line]])) # for every 8 symbols line
if not len(bs) % symbols_in_one_line == 0:
print(str((i+1)*symbols_in_one_line)+" | "+" ".join(["%02X" % b for b in bs[(i+1)*symbols_in_one_line:]])+"\n") # for last line
def hex_string_to_str_bytes(val):
"""Given a String like
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
Convert it to it's byte representation, stored in py2 in a str, like:
tmp_aes_key_hex = '\xf0\x11(\x08\x87\xc7\xbb\x01\xdf\x0f\xc4\xe1x0\xe0\xb9\x1f\xbb\x8b\xe4\xb2&|\xb9\x85\xae%\xf3;RrS'
"""
return val.decode("hex")
def str_bytes_to_hex_string(val):
"""Given a str_bytes (so str()) like
tmp_aes_key_hex = '\xf0\x11(\x08\x87\xc7\xbb\x01\xdf\x0f\xc4\xe1x0\xe0\xb9\x1f\xbb\x8b\xe4\xb2&|\xb9\x85\xae%\xf3;RrS'
Convert it back to it's uppercase string representation, like:
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253" """
return val.encode("hex").upper()
def hex_string_to_long(val):
"""Given a String like
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
Convert it to int, which is actually long"""
return int(val, 16)
def long_to_hex_string(val):
"""Given a long like:
Convert it to hex_string like:
from: http://stackoverflow.com/questions/4358285/is-there-a-faster-way-to-convert-an-arbitrary-large-integer-to-a-big-endian-seque/4358429#4358429
"""
# number.long_to_bytes(val)
# number.bytes_to_long()
# number.tobytes()
# number.bstr()
return
# Got from https://core.telegram.org/mtproto/samples-auth_key#conversion-of-encrypted-answer-into-answer
# They say they use AES 256 IGE
# Infinite Garble Extension (IGE) is a block cipher mode. (http://www.links.org/files/openssl-ige.pdf)
encrypted_answer_str = "28A92FE20173B347A8BB324B5FAB2667C9A8BBCE6468D5B509A4CBDDC186240AC912CF7006AF8926DE606A2E74C0493CAA57741E6C82451F54D3E068F5CCC49B4444124B9666FFB405AAB564A3D01E67F6E912867C8D20D9882707DC330B17B4E0DD57CB53BFAAFA9EF5BE76AE6C1B9B6C51E2D6502A47C883095C46C81E3BE25F62427B585488BB3BF239213BF48EB8FE34C9A026CC8413934043974DB03556633038392CECB51F94824E140B98637730A4BE79A8F9DAFA39BAE81E1095849EA4C83467C92A3A17D997817C8A7AC61C3FF414DA37B7D66E949C0AEC858F048224210FCC61F11C3A910B431CCBD104CCCC8DC6D29D4A5D133BE639A4C32BBFF153E63ACA3AC52F2E4709B8AE01844B142C1EE89D075D64F69A399FEB04E656FE3675A6F8F412078F3D0B58DA15311C1A9F8E53B3CD6BB5572C294904B726D0BE337E2E21977DA26DD6E33270251C2CA29DFCC70227F0755F84CFDA9AC4B8DD5F84F1D1EB36BA45CDDC70444D8C213E4BD8F63B8AB95A2D0B4180DC91283DC063ACFB92D6A4E407CDE7C8C69689F77A007441D4A6A8384B666502D9B77FC68B5B43CC607E60A146223E110FCB43BC3C942EF981930CDC4A1D310C0B64D5E55D308D863251AB90502C3E46CC599E886A927CDA963B9EB16CE62603B68529EE98F9F5206419E03FB458EC4BD9454AA8F6BA777573CC54B328895B1DF25EAD9FB4CD5198EE022B2B81F388D281D5E5BC580107CA01A50665C32B552715F335FD76264FAD00DDD5AE45B94832AC79CE7C511D194BC42B70EFA850BB15C2012C5215CABFE97CE66B8D8734D0EE759A638AF013"
tmp_aes_key_str = "F011280887C7BB01DF0FC4E17830E0B91FBB8BE4B2267CB985AE25F33B527253"
tmp_aes_iv_str = "3212D579EE35452ED23E0D0C92841AA7D31B2E9BDEF2151E80D15860311C85DB"
answer_str = "BA0D89B53E0549828CCA27E966B301A48FECE2FCA5CF4D33F4A11EA877BA4AA57390733002000000FE000100C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5BFE000100262AABA621CC4DF587DC94CF8252258C0B9337DFB47545A49CDD5C9B8EAE7236C6CADC40B24E88590F1CC2CC762EBF1CF11DCC0B393CAAD6CEE4EE5848001C73ACBB1D127E4CB93072AA3D1C8151B6FB6AA6124B7CD782EAF981BDCFCE9D7A00E423BD9D194E8AF78EF6501F415522E44522281C79D906DDB79C72E9C63D83FB2A940FF779DFB5F2FD786FB4AD71C9F08CF48758E534E9815F634F1E3A80A5E1C2AF210C5AB762755AD4B2126DFA61A77FA9DA967D65DFD0AFB5CDF26C4D4E1A88B180F4E0D0B45BA1484F95CB2712B50BF3F5968D9D55C99C0FB9FB67BFF56D7D4481B634514FBA3488C4CDA2FC0659990E8E868B28632875A9AA703BCDCE8FCB7AE551"
print("encrypted_answer_str:")
print(encrypted_answer_str)
print("tmp_aes_key_str:")
print(tmp_aes_key_str)
print("tmp_aes_iv_str:")
print(tmp_aes_iv_str)
print("answer_str:")
print(answer_str)
# Convert them to bytes (strings in py2 anyways)
# http://stackoverflow.com/questions/5649407/hexadecimal-string-to-byte-array-in-python
encrypted_answer_hex = encrypted_answer_str.decode("hex") # int(encrypted_answer_str, 16) for py3
tmp_aes_key_hex = tmp_aes_key_str.decode("hex")
tmp_aes_iv_hex = tmp_aes_iv_str.decode("hex")
answer_hex = answer_str.decode("hex")
print("encrypted_answer_hex:")
print(encrypted_answer_hex.__repr__())
print("tmp_aes_key_hex:")
print(tmp_aes_key_hex.__repr__())
print("tmp_aes_iv_hex:")
print(tmp_aes_iv_hex.__repr__())
print("answer_hex:")
print(answer_hex.__repr__())
# Re-convert them to string
encrypted_answer_hex_to_str = encrypted_answer_hex.encode("hex").upper() # int(encrypted_answer_str, 16) for py3
tmp_aes_key_hex_to_str = tmp_aes_key_hex.encode("hex").upper()
tmp_aes_iv_hex_to_str = tmp_aes_iv_hex.encode("hex").upper()
answer_hex_to_str = answer_hex.encode("hex").upper()
print("encrypted_answer_hex_to_str:")
print(encrypted_answer_hex_to_str)
print("tmp_aes_key_hex_to_str:")
print(tmp_aes_key_hex_to_str)
print("tmp_aes_iv_hex_to_str:")
print(tmp_aes_iv_hex_to_str)
print("answer_hex_to_str:")
print(answer_hex_to_str)
# Check if they are the same
if encrypted_answer_hex_to_str == encrypted_answer_str:
print("encrypted_answer_hex_to_str == encrypted_answer_str")
else:
print("encrypted_answer_hex_to_str != encrypted_answer_str")
encrypted_answer = int(encrypted_answer_str, 16) # int(encrypted_answer_str, 16) for py3
tmp_aes_key = int(tmp_aes_key_str, 16)
tmp_aes_iv = int(tmp_aes_iv_str, 16)
answer = int(answer_str, 16)
print("longtohexstring")
print(long_to_hex_string(tmp_aes_key))
# print("len(encrypted_answer): " + str(len(encrypted_answer)))
# print("len(answer): " + str(len(answer)))
# Got from testing.py
# encrypted_answer = 'L\xd7\xddI\x0b\xc3\xeay\xf1\x07]\x93\x7fY\x0cmVAX\x03\xeb\n}\x99\xd6\x99\xaa\xba\x05\x9d\xaaB\xe2\x97\xb3\xf2\xf8\xd8\x9f\xa6\x13\x177a\xb45A\x0f}\xb3\x99\xa3D?L\x94\xa3\xbcG\xe8\xf2\x14 \xb9.\x8b\xa0\xf1\xa5\xf1\x18\x9aZ2\x8f\xae\x05\xd9\x84H\xa3&\xad\x84\x82w\x9e\xe8\xba\x8a\x87QT\xdd\x12\x8c\x86\xde\xd8\x7fLM\xb9\x81H;JX\x85\x14\x1af!\xb20\xea)\xa8>(\xa9\xce5,\x96\x14\xd7P\x0c\xb3\x02\x9a\x16\xfc\x94\xacT\xa0\xd4\x82\xe5S1\xf4\xe1\x8cB\xad\x89\xc3C\xa6\rt)\xfa\x0b\xfe3\t\xdd\x02\xbe\xecP\xd6\xd7p\xf6\xf3\xb5\xdf6\xfc\x90l\xaa\x06\x8a\xc0XO\x96>\x85\x18\xebN\x08\x13x\xc0\x1ah\xbd\xedO\x99T\xfd\xed\x87C}\x89!\x99Oz\xfe\x927z~ &"\x0e/\x01N\x13\xfa\xd1\x96\x87\x0f\x83\x98d\x12X\xa7\x8c\xa8\x1c/\xbc\xab\xb2:\x07\xa6\x14\xfa\xe3\xd2\x8cG\xd6\x84\xe4[\x8f\x9e\x8f\xbb\x9c\x8a\x80\xbd\xcf\xaf!\xf7E\x1b\x1f\x91\x18\xc2\x8eBE\xfb\x84\xb6\xc5e5Q\xfd\xb8\xcb\xbc\xb4\x9f\xb7\x92\xfe\xae\xda,\xfaA\x94\x7fq\x1e\xd1\x05\xe8=\x9d#,\xe6\xb7y1\xe6\xc7!\xa0\x0bx\xd1\xb3\xad9\xc4\xdd\x99Y\xca\r\x07+\x903\x1e?\x1d_\x8b\xb0M\xff\x14\xc3:\x95\xa8\xee\xc1\xb5\xff\xfb1\x95\xe1\xcaT\xe4D\xcf\xd2%\x11\xeb@`Att\xbe\x11\xfc/\x05\x9a\xd2\x15\r\xb6\x9d\x88\xae\xa8\xd1q\xe5\x9b\x05A\x8d[\xbf\xaaN\x1b\xee\xbf#4\x1c\xd5\xa4\x1f\x0fo\xaf\xd0\x00g\xc1\x9a\x82\x00\x8c_\xd4\xac!{K\xca\x89x\xde\xf9\x8d\x19\xec\x12\x8epY\xdb\x9f\x98\xe6\x88\xe7\xc1\x92\x90\x17\x80\x03Ry\xf1n\x97e\xe2\x8c\xe9\x8c\xd6<\xba2:\x9f\x06g\x05\xaa#\xf4\xca1\x16o\xb5\x8b\xcd\xfe\x814h\xac\xcd\x0e\xd0\x1c\x0c\xc71\x11\xbe\xa5\xb3#\xcfh\x07)\x91\xc7\xc8iy!\x03\xc8\xf0\xb2\x02\xf3\xc7\xdf\xafXm\xf5\xaf\xdd\xc8\xeb\xb3n7\xe34\xa7R\x8c\xaf\xa3\xb7y\xe7\x12\x0f\x0c\xc2\xa8v\x12E\xc3u\xc8Y\x1fh.\xcf\x01\xae\x8c\x00"v\x99V\xad>\xaf\x08)\x83V*\x9b\xad\xc0\x9c\x94\xa5D[\x08s\x88\xd1\xcb\xf6\xf8j\xa1c\xc1yb\xda\x12\xa1~\xf6\xd1"\x14\x11a\x02\xc1\xd3\xf5'
# tmp_aes_key = b'\x82\xeb\x12\x0e\xbeT\x80>!\xaa\x01\xac\xc8\xe1u#d\x1b\x08\xf5G\xc7\xe5g\xa9\xc3\x1d*BC;6'
# tmp_aes_iv = b'r\xbb/\xe8\x0bb,T\x19\x17\xf20WsTf\x1d_C\x83|2h\xd3s\x82\xaeVW\x10v\xff'
from Crypto.Cipher import AES
# # try all modes
# aes_modes = [AES.MODE_CBC, AES.MODE_CFB, AES.MODE_CTR, AES.MODE_ECB, AES.MODE_OFB, AES.MODE_OPENPGP, AES.MODE_PGP]
# aes_modes_names = ["AES.MODE_CBC", "AES.MODE_CFB", "AES.MODE_CTR", "AES.MODE_ECB", "AES.MODE_OFB", "AES.MODE_OPENPGP", "AES.MODE_PGP"]
#
# working_modes = []
# working_modenames = []
# for mode, modename in zip(aes_modes, aes_modes_names):
# print("\n\nTrying mode: " + modename + "(" + str(mode) + ")")
# try:
# crypting_object = AES.new(tmp_aes_key, mode, tmp_aes_iv) # encrypter thing
# decrypted_answer = crypting_object.decrypt(encrypted_answer)
# print("decrypted_answer: ")
# print(decrypted_answer.__repr__())
# vis(decrypted_answer)
# working_modes.append(mode)
# working_modenames.append(modename)
# print("Which should look the same than: ")
# print(answer.__repr__())
# vis(answer)
# if answer == decrypted_answer:
# print("THEY ARE THE SAME!!")
# else:
# print("THEY ARE DIFFERENT :(((((")
# except Exception as e:
# print("Exception: " + str(e))
#
# print("\n\nModes " + str(working_modenames) + " (" + str(working_modes) + ") succesfully unencrypted the answer!")
# From http://stackoverflow.com/questions/17797582/java-aes-256-decrypt-with-ige
# public static final byte[] ige(final byte[] key, final byte[] IV,
# final byte[] Message) throws Exception {
def ige(key, iv, message, blocksize=16):#32):
"""given a key, ive and message, decrypt it. blocksize is the default one used in the javascript implementation"""
# print("len(key): " + str(len(key)))
# print("len(iv): " + str(len(iv)))
# print("len(message): " + str(len(message)))
# key = bytearray(key)
# iv = bytearray(iv)
# message = bytearray(message)
#
# final Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher = AES.new(key, AES.MODE_ECB, iv)
blocksize = cipher.block_size
# cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
#
# final int blocksize = cipher.getBlockSize();
#
# byte[] xPrev = Arrays.copyOfRange(IV, 0, blocksize);
xPrev = iv[0:blocksize]
# byte[] yPrev = Arrays.copyOfRange(IV, blocksize, IV.length);
yPrev = iv[blocksize:]
#
# byte[] decrypted = new byte[0];
decrypted = None
#
# byte[] y, x;
# y = bytearray()
# x = bytearray()
# for (int i = 0; i < Message.length; i += blocksize) {
def xor_strings(a, b): # xor two strings of different lengths
if len(a) > len(b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])
def add_strings(a, b):
if len(a) > len(b):
return "".join([chr(ord(x) + ord(y)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) + ord(y)) for (x, y) in zip(a, b[:len(a)])])
#return sum([a, b]) & 0xFFFFFFFF
for i in range(0, len(message), blocksize):
#print("i: " + str(i))
# x = java.util.Arrays.copyOfRange(Message, i, i + blocksize);
x = message[i:i+blocksize]
#print(" x: " + x.__repr__())
# y = xor(cipher.doFinal(xor(x, yPrev)), xPrev);
y = xor_strings(cipher.decrypt(xor_strings(x, yPrev)), xPrev)
#print(" y: " + y.__repr__())
#y = xor(cipher.decrypt(xor(x, yPrev)), xPrev)
# xPrev = x;
xPrev = x
# yPrev = y;
yPrev = y
#
# decrypted = sumBytes(decrypted, y);
if decrypted is None:
decrypted = y
else:
decrypted_ba = bytearray(decrypted)
decrypted_ba.extend(y)
decrypted = str(decrypted_ba)
# all this did not work
#decrypted = int(decrypted, 16) + int(y, 16)
#decrypted.append(y)
#add_strings(decrypted, y)
#decrypted = decrypted + y # this is wrong
#print(" decrypted: " + decrypted.__repr__())
# }
#
# return decrypted;
print("len(key): " + str(len(key)))
print("len(iv): " + str(len(iv)))
print("len(message): " + str(len(message)))
print("!!!!!!!!!!!!!!!!!!!!!! ---> cipher.block_size: " + str(cipher.block_size))
return decrypted
# }
#result = ige(tmp_aes_key, tmp_aes_iv, encrypted_answer)
result = ige(tmp_aes_key_hex, tmp_aes_iv_hex, encrypted_answer_hex)
print("result:")
print(result)
vis(result)
if answer == result:
print("THEY ARE THE SAME!!")
else:
print("THEY ARE DIFFERENT :(((((")
print("len(result): " + str(len(result)))
print("len(answer):" + str(len(answer)))
vis(encrypted_answer)
# Inspiration: http://passingcuriosity.com/2009/aes-encryption-in-python-with-m2crypto/
# sudo apt-get install swig
# sudo pip install M2Crypto
# This DOES NOT work. openssl wrapper m2crypto does not have IGE available
# from base64 import b64encode, b64decode
# from M2Crypto.EVP import Cipher
# ENC=1
# DEC=0
#
# def build_cipher(key, iv, op=ENC):
# """"""""
# #return Cipher(alg='aes_256_ecb', key=key, iv=iv, op=op)
# return Cipher(alg='aes_256 ige', key=key, iv=iv, op=op)
#
# def encryptor(key, iv=None):
# """"""
# # Decode the key and iv
# key = b64decode(key)
# if iv is None:
# iv = '\0' * 16
# else:
# iv = b64decode(iv)
#
# # Return the encryption function
# def encrypt(data):
# cipher = build_cipher(key, iv, ENC)
# v = cipher.update(data)
# v = v + cipher.final()
# del cipher
# v = b64encode(v)
# return v
# return encrypt
#
# def decryptor(key, iv=None):
# """"""
# # Decode the key and iv
# #key = b64decode(key)
# if iv is None:
# iv = '\0' * 16
# else:
# #iv = b64decode(iv)
# pass
#
# # Return the decryption function
# def decrypt(data):
# #data = b64decode(data)
# cipher = build_cipher(key, iv, DEC)
# v = cipher.update(data)
# v = v + cipher.final()
# del cipher
# return v
# return decrypt
#
# print("Decrypting with m2...")
# decryptor_m2= decryptor(tmp_aes_key, tmp_aes_iv)
# m2_decrypt_ans =decryptor_m2(encrypted_answer)
# vis(m2_decrypt_ans)
# Output was:
# Trying mode: AES.MODE_CBC(2)
# Exception: IV must be 16 bytes long
#
#
# Trying mode: AES.MODE_CFB(3)
# Exception: IV must be 16 bytes long
#
#
# Trying mode: AES.MODE_CTR(6)
# Exception: 'counter' keyword parameter is required with CTR mode
#
#
# Trying mode: AES.MODE_ECB(1)
# decrypted_answer:
# '\xb4\x87<\xc5&\xe2J\x0e\x96\x1a\x08\xeaSR\xc4!.\x17\x0b]ZI\xd6\xdc\xbd5\x87i\x11\x1b\x9d\x04\x93;\xc5\xd7C\xe1[\xb9\xaa\x95a)\x14\xfd\x8f\xe8\xf8\xc7\xb1~\xdf\xd3\xf7\xf8\xdc\xc4v\xae \xd7\xff\x91\x0c}:\xf45T~\xad\x00w\xde\x98\xe7\xd5b\x7f\xafZg\xd0\x01\x1e\xcaF\xc7\xe4,@kS\xc8|\xe4\xedv\xa3g\x121.\x00\x10X$\x00\xd0\xea\x12\xe0\xc0n\xdd\x9c&\xb3\xd9\x15\x97\xc67\xaeH\xef\xfb\xd9\x8eC\x8b\x99\xb6P\x9f&\x9d\x95a\x00\xc3\xac\xd7@\x95\\\x127\x97\xca:\x9b\xfbA\xad\xc5K \xc7\xe1erK\x16\xd3m\xd4\x1b\xd3\xbf\x9e\xc2\x15\xd8\xd6^\xa7r9\xffC\xb0(\xb5W\xa0\n=\x8a\x0b"\x18&\x95l\xc4oF\xd9\xff\x98\xb5\xaf\xbd\xb89\x80p\xffC\xa3\xcb\xff\x8a1p\xd0t0\x13\x89\x1dG\x1e+l\xab\xd9AN$L\xd8~\xedE\xf7\x8c\x93\x0c\xc1\xfd\xdb\xc9\x9d\xe9\x0c\x1d\xbb}8\xf6j\x95>\xae\xee-\x9e?k\xaf\xaer\xe4`\xf9\t\x11\xb4\xd8\x03q*\x83\x13\x02\x0e\xe8\x9d?\xef\x0f\xd3^\xd82o4U\xc4l\xde\x17iJ\xfe\xd4\x8c`\xc6{H\x97\x16\xb7g\xb9\xddeUc\xd4\xc8\xa782IeK\x81\xc3r=!\x92\x97\x0f\x92\xd4\xf6\x01\x93\xa5\xbacL\x8b\x1a\xd5\x1d4\x9a2\x87m6\xc9\xf1\xb0&K\xdfo\xee\xeaQ\xf9\xc3\xa6\xfd?\n\xad\xfc\x9e\xaf\x8c\xfdJd\x84\xa5\x8bBl\xb1\xa0g\x8c\xb47\xf4\xc0`\x88\xe8\x88\n\x85\x81r\xf4J\xe3}\x89]\x8b\xfb|\x10\x05-)\xe2\xba\x96BT:\x16F\xaf\xd8\xa9\xcfew>\xc4QE\x91M\xffc\x07d\x1c\xf2\xb0G\xe5\x04\x03\x9bZ\xa0w\xa4\xd42\x0ex\xc3@\xdd\x9c\x15X\x0ey\x0e+\x12\x13ro\xda\xc2a\xfbH\xd0\x7f\x96\xad\xa7b&\xe7\xca+h\x1b\x13!\xf2\xf0cUw\xd7\x0f\xcd\x10>\x91\xcb\x0e\xba\xc1\xdec\xe6\x11\xdb\xba=y\x97\xe9\xc5\xfcW\x9b\x91)\xf1\x19\x12\xc4L\x83\xee"\xc2S\x9at\xd4\x01({\x01\xdc2e\xe7K\x10C\xa8J\xa3a\x1c=#\x03\x9b\xb2\x8e\xe0\x95\x9a\xf4R\x8d\xcf\xef\x88\xef\xce|\xe7\x9a5\xfe>\x13\x9d\x13\xe9 \xfc[\x02\xe2QP\xd4\x93\xe3\x15uJ3\xe6\xe1B\x12\xbdy\x81G\x9a*\x93K'
#
#
# Trying mode: AES.MODE_OFB(5)
# Exception: IV must be 16 bytes long
#
#
# Trying mode: AES.MODE_OPENPGP(7)
# Exception: Length of IV must be 16 or 18 bytes for MODE_OPENPGP
#
#
# Trying mode: AES.MODE_PGP(4)
# Exception: MODE_PGP is not supported anymore
#
# Modes ['AES.MODE_ECB'] ([1]) succesfully unencrypted the answer!

View File

@ -83,4 +83,8 @@ class Connection
break;
}
}
public function read_message()
{
}
}

View File

@ -53,17 +53,17 @@ class PrimeModule
$d = ($n - 1);
$s = 0;
while (($d % 2) == 0) {
$d = floor($d / 2);
$d = intval($d / 2);
$s++;
}
$break = false;
foreach (pyjslib_range($precision) as $repeat) {
foreach (Tools::range($precision) as $repeat) {
$a = rand(2, ($n - 2));
$x = posmod(pow($a, $d), $n);
if (($x == 1) || ($x == ($n - 1))) {
continue;
}
foreach (pyjslib_range($s - 1) as $r) {
foreach (Tools::range($s - 1) as $r) {
$x = posmod(pow($x, 2), $n);
if (($x == 1)) {
return false;
@ -221,8 +221,7 @@ class PrimeModule
if (count($res) == 2) {
return $res;
}
$n = (int)$n->toString();
$factors = [];
$limit = sqrt($n) + 1;
foreach ($this->smallprimes as $checker) {
@ -242,13 +241,13 @@ class PrimeModule
return $factors;
}
while ($n > 1) {
if ($n->isprime()) {
if ($this->isprime($n)) {
$factors[] = $n;
break;
}
$factor = $this->pollard_brent($n);
$factors[] = $this->primefactors($factor);
$n = floor($n / $factor);
$n = intval($n / $factor);
}
if ($sort) {
$factors = sort($factors);
@ -303,6 +302,6 @@ class PrimeModule
public function lcm($a, $b)
{
return floor(abs(($a * $b)) / $this->gcd($a, $b));
return intval(abs(($a * $b)) / $this->gcd($a, $b));
}
}

View File

@ -31,7 +31,10 @@ class Session extends Tools
'protocol' => 'tcp',
'api_id' => 25628,
'api_hash' => '1fe17cda7d355166cdaa71f04122873c',
'tl_schema' => 'https://core.telegram.org/schema/mtproto-json',
'tl_schema' => [
'https://core.telegram.org/schema/mtproto-json',
__DIR__.'/telegram_layer55.json',
],
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS

View File

@ -16,7 +16,10 @@ class TL
{
public function __construct($filename)
{
$TL_dict = json_decode(file_get_contents($filename), true);
$TL_dict = [];
foreach ($filename as $file) {
$TL_dict = array_replace(json_decode(file_get_contents($file), true), $TL_dict);
}
$this->constructors = $TL_dict['constructors'];
$this->constructor_id = [];
$this->constructor_type = [];

File diff suppressed because one or more lines are too long

1
testing.php Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env php
<?php
require_once 'vendor/autoload.php';