Merge branch 'master' into docs

This commit is contained in:
Dan 2018-03-11 17:19:45 +01:00
commit 5af4a0a49a
8 changed files with 350 additions and 31 deletions

View File

@ -29,6 +29,7 @@ destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
---functions---
req_pq#60469778 nonce:int128 = ResPQ;
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params;

View File

@ -8,7 +8,9 @@ you have to change are the target chats (username, id) and file paths for sendin
- [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py)
- [**get_history.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_history.py)
- [**get_participants.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants.py)
- [**inline_bots.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/inline_bots.py)
- [**updates.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/updates.py)
- [**simple_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/simple_echo.py)
- [**advanced_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo.py)
- [**advanced_echo2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo2.py)
- [**welcome_bot.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/welcome_bot.py)

52
examples/welcome_bot.py Normal file
View File

@ -0,0 +1,52 @@
from pyrogram import Client, Emoji
from pyrogram.api import types
"""
This is the Welcome Bot in @PyrogramChat
The code is commented to help you understand each part
It also uses the Emoji module to easily add emojis in your text messages
"""
# Your Supergroup ID
SUPERGROUP_ID = 1387666944
def update_handler(client, update, users, chats):
# Supergroup messages are contained in the "UpdateNewChannelMessage" update type
if isinstance(update, types.UpdateNewChannelMessage):
message = update.message
# When a user joins, a "MessageService" is received
if isinstance(message, types.MessageService):
# Check if the message is sent to your SUPERGROUP_ID
if message.to_id.channel_id == SUPERGROUP_ID:
# A "MessageService" contains the "action" field.
# The action for user joins is "MessageActionChatAddUser" if the user
# joined using the username, otherwise is "MessageActionChatJoinedByLink" if
# the user joined a private group by link
if isinstance(message.action, (types.MessageActionChatAddUser, types.MessageActionChatJoinedByLink)):
# Now send the welcome message. Extra info about a user (such as the first_name, username, ...)
# are contained in the users dictionary and can be accessed by the user ID
client.send_message(
SUPERGROUP_ID,
"{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s "
"group chat, [{}](tg://user?id={})!".format(
Emoji.SPARKLES, # Add an emoji
users[message.from_id].first_name,
users[message.from_id].id
),
reply_to_message_id=message.id,
disable_web_page_preview=True
)
def main():
client = Client("example")
client.set_update_handler(update_handler)
client.start()
client.idle()
if __name__ == "__main__":
main()

View File

@ -23,7 +23,7 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() == "ascii" else "\xe8"
)
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.6.1"
__version__ = "0.6.2"
from .api.errors import Error
from .client import ChatAction

View File

@ -256,15 +256,16 @@ class Client:
if isinstance(entity, Chat):
chat_id = entity.id
peer_id = -chat_id
if chat_id in self.peers_by_id:
if peer_id in self.peers_by_id:
continue
input_peer = InputPeerChat(
chat_id=chat_id
)
self.peers_by_id[chat_id] = input_peer
self.peers_by_id[peer_id] = input_peer
if isinstance(entity, Channel):
channel_id = entity.id
@ -571,6 +572,8 @@ class Client:
elif confirm in ("n", "2"):
self.phone_number = input("Enter phone number: ")
self.phone_number = self.phone_number.strip("+")
try:
r = self.send(
functions.auth.SendCode(
@ -886,16 +889,19 @@ class Client:
if isinstance(peer_id, types.PeerUser):
peer_id = peer_id.user_id
elif isinstance(peer_id, types.PeerChat):
peer_id = peer_id.chat_id
peer_id = -peer_id.chat_id
elif isinstance(peer_id, types.PeerChannel):
peer_id = int("-100" + str(peer_id.channel_id))
try:
try: # User
return self.peers_by_id[peer_id]
except KeyError:
try:
return self.peers_by_id[int("-100" + str(peer_id))]
try: # Chat
return self.peers_by_id[-peer_id]
except KeyError:
try: # Channel
return self.peers_by_id[int("-100" + str(peer_id))]
except (KeyError, ValueError):
raise PeerIdInvalid
def get_me(self):
@ -1014,7 +1020,8 @@ class Client:
parse_mode: str = "",
ttl_seconds: int = None,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send photos.
Args:
@ -1047,6 +1054,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
@ -1054,7 +1072,7 @@ class Client:
:class:`pyrogram.Error`
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(photo)
file = self.save_file(photo, progress=progress)
while True:
try:
@ -1085,7 +1103,8 @@ class Client:
performer: str = None,
title: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send audio files.
For sending voice messages, use the :obj:`send_voice` method instead.
@ -1124,6 +1143,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
@ -1131,7 +1161,7 @@ class Client:
:class:`pyrogram.Error`
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(audio)
file = self.save_file(audio, progress=progress)
while True:
try:
@ -1167,7 +1197,8 @@ class Client:
caption: str = "",
parse_mode: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send general files.
Args:
@ -1195,6 +1226,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
@ -1202,7 +1244,7 @@ class Client:
:class:`pyrogram.Error`
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(document)
file = self.save_file(document, progress=progress)
while True:
try:
@ -1231,7 +1273,8 @@ class Client:
chat_id: int or str,
sticker: str,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send .webp stickers.
Args:
@ -1251,13 +1294,24 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
Raises:
:class:`pyrogram.Error`
"""
file = self.save_file(sticker)
file = self.save_file(sticker, progress=progress)
while True:
try:
@ -1290,9 +1344,11 @@ class Client:
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
supports_streaming: bool = None,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send video files.
Args:
@ -1322,6 +1378,11 @@ class Client:
height (:obj:`int`, optional):
Video height.
thumb (:obj:`str`, optional):
Video thumbnail.
Pass a file path as string to send an image that exists on your local machine.
Thumbnail should have 90 or less pixels of width and 90 or less pixels of height.
supports_streaming (:obj:`bool`, optional):
Pass True, if the uploaded video is suitable for streaming.
@ -1332,6 +1393,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
@ -1339,7 +1411,8 @@ class Client:
:class:`pyrogram.Error`
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(video)
file = self.save_file(video, progress=progress)
file_thumb = None if thumb is None else self.save_file(thumb)
while True:
try:
@ -1349,6 +1422,7 @@ class Client:
media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"],
file=file,
thumb=file_thumb,
attributes=[
types.DocumentAttributeVideo(
supports_streaming=supports_streaming,
@ -1377,7 +1451,8 @@ class Client:
parse_mode: str = "",
duration: int = 0,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send audio files.
Args:
@ -1408,6 +1483,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
@ -1415,7 +1501,7 @@ class Client:
:class:`pyrogram.Error`
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(voice)
file = self.save_file(voice, progress=progress)
while True:
try:
@ -1449,7 +1535,8 @@ class Client:
duration: int = 0,
length: int = 1,
disable_notification: bool = None,
reply_to_message_id: int = None):
reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send video messages.
Args:
@ -1475,13 +1562,24 @@ class Client:
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns:
On success, the sent Message is returned.
Raises:
:class:`pyrogram.Error`
"""
file = self.save_file(video_note)
file = self.save_file(video_note, progress=progress)
while True:
try:
@ -1511,6 +1609,7 @@ class Client:
else:
return r
# TODO: Add progress parameter
def send_media_group(self,
chat_id: int or str,
media: list,
@ -1965,7 +2064,11 @@ class Client:
)
# TODO: Remove redundant code
def save_file(self, path: str, file_id: int = None, file_part: int = 0):
def save_file(self,
path: str,
file_id: int = None,
file_part: int = 0,
progress: callable = None):
part_size = 512 * 1024
file_size = os.path.getsize(path)
file_total_parts = math.ceil(file_size / part_size)
@ -2005,6 +2108,9 @@ class Client:
md5_sum.update(chunk)
file_part += 1
if progress:
progress(min(file_part * part_size, file_size), file_size)
except Exception as e:
log.error(e)
else:
@ -2102,7 +2208,7 @@ class Client:
offset += limit
if progress:
progress(offset, size)
progress(min(offset, size), size)
r = session.send(
functions.upload.GetFile(
@ -2466,10 +2572,10 @@ class Client:
progress (:obj:`callable`):
Pass a callback function to view the download progress.
The function must accept two arguments (progress, total).
The function must accept two arguments (current, total).
Other Parameters:
progress (:obj:`int`):
current (:obj:`int`):
The amount of bytes downloaded so far.
total (:obj:`int`):
@ -2648,3 +2754,39 @@ class Client:
reply_to_msg_id=reply_to_message_id
)
)
def get_messages(self,
chat_id: int or str,
message_ids: list):
"""Use this method to get messages that belong to a specific chat.
You can retrieve up to 200 messages at once.
Args:
chat_id (:obj:`int` | :obj:`str`):
Unique identifier for the target chat or username of the target channel/supergroup
(in the format @username). For your personal cloud storage (Saved Messages) you can
simply use "me" or "self". Phone numbers that exist in your Telegram address book are also supported.
message_ids (:obj:`list`):
A list of Message identifiers in the chat specified in *chat_id*.
Returns:
List of the requested messages
Raises:
:class:`pyrogram.Error`
"""
peer = self.resolve_peer(chat_id)
message_ids = [types.InputMessageID(i) for i in message_ids]
if isinstance(peer, types.InputPeerChannel):
rpc = functions.channels.GetMessages(
channel=peer,
id=message_ids
)
else:
rpc = functions.messages.GetMessages(
id=message_ids
)
return self.send(rpc)

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram.api.types import InputPhoneContact as RawInputPhoneContact
from pyrogram.session.internals import MsgId
class InputPhoneContact:
@ -36,7 +37,7 @@ class InputPhoneContact:
def __new__(cls, phone: str, first_name: str, last_name: str = ""):
return RawInputPhoneContact(
client_id=0,
client_id=MsgId(),
phone="+" + phone.strip("+"),
first_name=first_name,
last_name=last_name

View File

@ -23,10 +23,16 @@ PublicKey = namedtuple("PublicKey", ["m", "e"])
class RSA:
# To get modulus and exponent:
#
# [RSA PUBLIC KEY]:
# grep -v -- - public.key | tr -d \\n | base64 -d | openssl asn1parse -inform DER -i
#
# [PUBLIC KEY]:
# openssl rsa -pubin -in key -text -noout
server_public_keys = {
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers
# -4344800451088585951
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers #1
# -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
# lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
@ -48,6 +54,108 @@ class RSA:
), # Modulus
int("010001", 16) # Exponent
),
# 847625836280919973
0x10bc35f3509f7b7a5 - (1 << 64): PublicKey( # Telegram servers #2
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAruw2yP/BCcsJliRoW5eB
# VBVle9dtjJw+OYED160Wybum9SXtBBLXriwt4rROd9csv0t0OHCaTmRqBcQ0J8fx
# hN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvd
# l84Kd9ORYjDEAyFnEA7dD556OptgLQQ2e2iVNq8NZLYTzLp5YpOdO1doK+ttrltg
# gTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnSLj16yE5HvJQn0CNpRdENvRUXe6tBP78O
# 39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wFXGF710w9lwCGNbmNxNYhtIkdqfsEcwR5
# JwIDAQAB
# -----END PUBLIC KEY-----
int(
"AEEC36C8FFC109CB099624685B97815415657BD76D8C9C3E398103D7AD16C9BB"
"A6F525ED0412D7AE2C2DE2B44E77D72CBF4B7438709A4E646A05C43427C7F184"
"DEBF72947519680E651500890C6832796DD11F772C25FF8F576755AFE055B0A3"
"752C696EB7D8DA0D8BE1FAF38C9BDD97CE0A77D3916230C4032167100EDD0F9E"
"7A3A9B602D04367B689536AF0D64B613CCBA7962939D3B57682BEB6DAE5B6081"
"30B2E52ACA78BA023CF6CE806B1DC49C72CF928A7199D22E3D7AC84E47BC9427"
"D0236945D10DBD15177BAB413FBF0EDFDA09F014C7A7DA088DDE9759702CA760"
"AF2B8E4E97CC055C617BD74C3D97008635B98DC4D621B4891DA9FB0473047927",
16
), # Modulus
int("010001", 16) # Exponent
),
# 1562291298945373506
0x115ae5fa8b5529542 - (1 << 64): PublicKey( # Telegram servers #3
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvfLHfYH2r9R70w8prHbl
# Wt/nDkh+XkgpflqQVcnAfSuTtO05lNPspQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOO
# KPi0OfJXoRVylFzAQG/j83u5K3kRLbae7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ
# 3TDS2pQOCtovG4eDl9wacrXOJTG2990VjgnIKNA0UMoP+KF03qzryqIt3oTvZq03
# DyWdGK+AZjgBLaDKSnC6qD2cFY81UryRWOab8zKkWAnhw2kFpcqhI0jdV5QaSCEx
# vnsjVaX0Y1N0870931/5Jb9ICe4nweZ9kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV
# /wIDAQAB
# -----END PUBLIC KEY-----
int(
"BDF2C77D81F6AFD47BD30F29AC76E55ADFE70E487E5E48297E5A9055C9C07D2B"
"93B4ED3994D3ECA5098BF18D978D54F8B7C713EB10247607E69AF9EF44F38E28"
"F8B439F257A11572945CC0406FE3F37BB92B79112DB69EEDF2DC71584A661638"
"EA5BECB9E23585074B80D57D9F5710DD30D2DA940E0ADA2F1B878397DC1A72B5"
"CE2531B6F7DD158E09C828D03450CA0FF8A174DEACEBCAA22DDE84EF66AD370F"
"259D18AF806638012DA0CA4A70BAA83D9C158F3552BC9158E69BF332A45809E1"
"C36905A5CAA12348DD57941A482131BE7B2355A5F4635374F3BD3DDF5FF925BF"
"4809EE27C1E67D9120C5FE08A9DE458B1B4A3C5D0A428437F2BECA81F4E2D5FF",
16
), # Modulus
int("010001", 16) # Exponent
),
# -5859577972006586033
0xaeae98e13cd7f94f - (1 << 64): PublicKey( # Telegram servers #4
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ditzm+mPND6xkhzwFI
# z6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGrzqTDHkO30R8VeRM/Kz2f4nR05GIFiITl
# 4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+th6knSU0yLtNKuQVP6voMrnt9MV1X92L
# GZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvSUwwc+yi1/gGaybwlzZwqXYoPOhwMebzK
# Uk0xW14htcJrRrq+PXXQbRzTMynseCoPIoke0dtCodbA3qQxQovE16q9zz4Otv2k
# 4j63cz53J+mhkVWAeWxVGI0lltJmWtEYK6er8VqqWot3nqmWMXogrgRLggv/Nbbo
# oQIDAQAB
# -----END PUBLIC KEY-----
int(
"B3F762B739BE98F343EB1921CF0148CFA27FF7AF02B6471213FED9DAA0098976"
"E667750324F1ABCEA4C31E43B7D11F1579133F2B3D9FE27474E462058884E5E1"
"B123BE9CBBC6A443B2925C08520E7325E6F1A6D50E117EB61EA49D2534C8BB4D"
"2AE4153FABE832B9EDF4C5755FDD8B19940B81D1D96CF433D19E6A22968A85DC"
"80F0312F596BD2530C1CFB28B5FE019AC9BC25CD9C2A5D8A0F3A1C0C79BCCA52"
"4D315B5E21B5C26B46BABE3D75D06D1CD33329EC782A0F22891ED1DB42A1D6C0"
"DEA431428BC4D7AABDCF3E0EB6FDA4E23EB7733E7727E9A1915580796C55188D"
"2596D2665AD1182BA7ABF15AAA5A8B779EA996317A20AE044B820BFF35B6E8A1",
16
), # Modulus
int("010001", 16) # Exponent
),
# 6491968696586960280
0x15a181b2235057d98 - (1 << 64): PublicKey( # Telegram servers #5
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q0
# 5shjg8/4p6047bn6/m8yPy1RBsvIyvuDuGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xb
# nfxL5BXHplJhMtADXKM9bWB11PU1Eioc3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA
# 9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvifRLJbY08/Gp66KpQvy7g8w7VB8wlgePe
# xW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqePji9NP3tJUFQjcECqcm0yV7/2d0t/pbC
# m+ZH1sadZspQCEPPrtbkQBlvHb4OLiIWPGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6M
# AQIDAQAB
# -----END PUBLIC KEY-----
int(
"BE6A71558EE577FF03023CFA17AAB4E6C86383CFF8A7AD38EDB9FAFE6F323F2D"
"5106CBC8CAFB83B869CFFD1CCF121CD743D509E589E68765C96601E813DC5B9D"
"FC4BE415C7A6526132D0035CA33D6D6075D4F535122A1CDFE017041F1088D141"
"9F65C8E5490EE613E16DBF662698C0F54870F0475FA893FC41EB55B08FF1AC21"
"1BC045DED31BE27D12C96D8D3CFC6A7AE8AA50BF2EE0F30ED507CC2581E3DEC5"
"6DE94F5DC0A7ABEE0BE990B893F2887BD2C6310A1E0A9E3E38BD34FDED254150"
"8DC102A9C9B4C95EFFD9DD2DFE96C29BE647D6C69D66CA500843CFAED6E44019"
"6F1DBE0E2E22163C61CA48C79116FA77216726749A976A1C4B0944B5121E8C01",
16
), # Modulus
int("010001", 16) # Exponent
),
# 6427105915145367799
0x15931aac70e0d30f7 - (1 << 64): PublicKey( # CDN DC-121
# -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEA+Lf3PvgE1yxbJUCMaEAkV0QySTVpnaDjiednB5RbtNWjCeqSVakY
@ -70,6 +178,8 @@ class RSA:
), # Modulus
int("010001", 16) # Exponent
),
# 2685959930972952888
0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140
# -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o

View File

@ -91,8 +91,19 @@ class Auth:
# Step 1; Step 2
nonce = int.from_bytes(urandom(16), "little", signed=True)
log.debug("Send req_pq: {}".format(nonce))
res_pq = self.send(functions.ReqPq(nonce))
res_pq = self.send(functions.ReqPqMulti(nonce))
log.debug("Got ResPq: {}".format(res_pq.server_nonce))
log.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints))
for i in res_pq.server_public_key_fingerprints:
if i in RSA.server_public_keys:
log.debug("Using fingerprint: {}".format(i))
public_key_fingerprint = i
break
else:
log.debug("Fingerprint unknown: {}".format(i))
else:
raise Exception("Public key not found")
# Step 3
pq = int.from_bytes(res_pq.pq, "big")
@ -118,7 +129,7 @@ class Auth:
sha = sha1(data).digest()
padding = urandom(- (len(data) + len(sha)) % 255)
data_with_hash = sha + data + padding
encrypted_data = RSA.encrypt(data_with_hash, res_pq.server_public_key_fingerprints[0])
encrypted_data = RSA.encrypt(data_with_hash, public_key_fingerprint)
log.debug("Done encrypt data with RSA")
@ -130,7 +141,7 @@ class Auth:
server_nonce,
int.to_bytes(p, 4, "big"),
int.to_bytes(q, 4, "big"),
res_pq.server_public_key_fingerprints[0],
public_key_fingerprint,
encrypted_data
)
)