U
    hq,                     @   s   d dl Z d dlZd dlZd dlmZ d dlmZ ddlmZ ddl	m
Z
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZ dZdZdZdZG dd deZG dd dZdS )    N)sha256)deque   )AES)SecurityErrorInvalidBufferError)BinaryReader)	TLMessage)	TLRequest)InvokeAfterMsgRequest)
GzipPacked)BadServerSaltBadMsgNotificationi     i,  
   c                   @   s&   e Zd ZdZedddZdd ZdS )_OpaqueRequestzN
    Wraps a serialized request into a type that can be serialized again.
    datac                 C   s
   || _ d S Nr   )selfr    r   A/tmp/pip-unpacked-wheel-c81u5j2r/telethon/network/mtprotostate.py__init__   s    z_OpaqueRequest.__init__c                 C   s   | j S r   r   r   r   r   r   _bytes"   s    z_OpaqueRequest._bytesN)__name__
__module____qualname____doc__bytesr   r   r   r   r   r   r      s   r   c                   @   sr   e Zd ZdZdd Zdd Zdd Zedd	 Zd
dddZ	dd Z
dd Zdd Zdd Zdd Zdd Zd
S )MTProtoStatea
  
    `telethon.network.mtprotosender.MTProtoSender` needs to hold a state
    in order to be able to encrypt and decrypt incoming/outgoing messages,
    as well as generating the message IDs. Instances of this class hold
    together all the required information.

    It doesn't make sense to use `telethon.sessions.abstract.Session` for
    the sender because the sender should *not* be concerned about storing
    this information to disk, as one may create as many senders as they
    desire to any other data center, or some CDN. Using the same session
    for all these is not a good idea as each need their own authkey, and
    the concept of "copying" sessions with the unnecessary entities or
    updates state for these connections doesn't make sense.

    While it would be possible to have a `MTProtoPlainState` that does no
    encryption so that it was usable through the `MTProtoLayer` and thus
    avoid the need for a `MTProtoPlainSender`, the `MTProtoLayer` is more
    focused to efficiency and this state is also more advanced (since it
    supports gzipping and invoking after other message IDs). There are too
    many methods that would be needed to make it convenient to use for the
    authentication process, at which point the `MTProtoPlainSender` is better.
    c                 C   sR   || _ |t | _d| _d| _d  | _ | _| _tt	d| _
d| _d| _|   d S )Nr   )maxlen)auth_keyr   _logtime_offsetsaltid	_sequence_last_msg_idr   MAX_RECENT_MSG_IDS_recent_remote_ids_highest_remote_id_ignore_countreset)r   r"   loggersr   r   r   r   >   s    
zMTProtoState.__init__c                 C   s>   t dtdd | _d| _d| _| j  d| _	d| _
dS )z#
        Resets the state.
        q   r   N)structunpackosurandomr&   r'   r(   r*   clearr+   r,   r   r   r   r   r-   J   s    
zMTProtoState.resetc                 C   s   |   |_dS )za
        Updates the message ID to a new one,
        used when the time offset changed.
        N)_get_new_msg_idmsg_id)r   messager   r   r   update_message_idV   s    zMTProtoState.update_message_idc                 C   s   |rdnd}t || ||d    }t | |d |d  |  }|dd |dd  |dd  }|dd |dd  |dd  }||fS )	z
        Calculate the key based on Telegram guidelines for MTProto 2,
        specifying whether it's the client or not. See
        https://core.telegram.org/mtproto/description#defining-aes-key-and-initialization-vector
        r   r0   $   (   L   N       )r   digest)r"   msg_keyclientxZsha256aZsha256baes_keyaes_ivr   r   r   	_calc_key]   s     $$zMTProtoState._calc_keyN)after_idc             	   C   sj   |   }| |}|dkr(t||}nt|tt|t|}|t	d||t
| || |S )zj
        Writes a message containing the given data into buffer.

        Returns the message id.
        Nz<qii)r6   _get_seq_nor   Zgzip_if_smallerr   r   r   writer1   packlen)r   bufferr   content_relatedrF   r7   Zseq_nobodyr   r   r   write_data_as_messagem   s    

z"MTProtoState.write_data_as_messagec                 C   s   t d| j| j| }tt|d  d d }t| jj	dd | | 
 }|dd }| | jj	|d\}}t d	| jj}|| t|| || S )
z
        Encrypts the given message data using the current authorization key
        following MTProto 2.0 guidelines core.telegram.org/mtproto/description.
        z<qq      X   x   r0   r=   T<Q)r1   rI   r%   r&   r3   r4   rJ   r   r"   keyr?   rE   key_idr   Zencrypt_ige)r   r   paddingZmsg_key_larger@   rC   rD   rU   r   r   r   encrypt_message_data   s    z!MTProtoState.encrypt_message_datac                 C   s  t   }t|dk rt|td|dd d }|| jjkrHtd|dd }| | jj	|d\}}t
|dd ||}t| jj	dd	 | }|| dd krtd
t|}|  | | jkrtd| }	|	d dkrtd|	| jkr(|	| jkr(| jd|	 |   dS | }
|  | }|jtjtjfkrp| js| js| |	 n`|	d? }|| j | }|tkr| jd|	 |   dS | tkr| jd|	 |   dS | j|	 |	| _d| _ t!|	|
|S )zQ
        Inverse of `encrypt_message_data` for incoming server messages.
        r0   rS   Nr   z'Server replied with an invalid auth keyr=   F`      z0Received msg_key doesn't match with expected onez<Server replied with a wrong session ID (see FAQ for details)r      zServer sent an even msg_idz,Server resent the older message %d, ignoringr>   zIServer sent a very old message with ID %d, ignoring (see FAQ for details)zIServer sent a very new message with ID %d, ignoring (see FAQ for details))"timerJ   r   r1   r2   r"   rU   r   rE   rT   r   Zdecrypt_iger   r?   r   Z	read_longr&   r+   r*   r#   warning_count_ignoredZread_intZtgread_objectZCONSTRUCTOR_IDr   r   r$   update_time_offsetMSG_TOO_OLD_DELTAMSG_TOO_NEW_DELTAappendr,   r	   )r   rM   nowrU   r@   rC   rD   Zour_keyreaderZremote_msg_idZremote_sequenceobjZremote_msg_timeZ
time_deltar   r   r   decrypt_message_data   sZ    
z!MTProtoState.decrypt_message_datac                 C   s$   |  j d7  _ | j tkr tdd S )NrZ   z1Too many messages had to be ignored consecutively)r,   MAX_CONSECUTIVE_IGNOREDr   r   r   r   r   r]      s    
zMTProtoState._count_ignoredc                 C   sT   t   | j }t|t| d }t|d> |d> B }| j|krJ| jd }|| _|S )z
        Generates a new unique message ID based on the current
        time (in ms) since epoch, applying a known time offset.
        g    eAr>   r      )r[   r$   intr(   )r   rb   ZnanosecondsZ
new_msg_idr   r   r   r6      s    

zMTProtoState._get_new_msg_idc                 C   sX   |   }| j}tt }|d? }|| | _| j|krRd| _| jd|||| j | jS )zd
        Updates the time offset to the correct
        one given a known valid message ID.
        r>   r   z<Updated time offset (old offset %d, bad %d, good %d, new %d))r6   r$   rh   r[   r(   r#   debug)r   Zcorrect_msg_idbadoldrb   correctr   r   r   r^      s    

   zMTProtoState.update_time_offsetc                 C   s2   |r$| j d d }|  j d7  _ |S | j d S dS )z
        Generates the next sequence number depending on whether
        it should be for a content-related query or not.
        r   rZ   N)r'   )r   rL   resultr   r   r   rG     s
    zMTProtoState._get_seq_no)r   r   r   r   r   r-   r9   staticmethodrE   rN   rW   re   r]   r6   r^   rG   r   r   r   r   r    '   s   
Qr    )r3   r1   r[   hashlibr   collectionsr   Zcryptor   errorsr   r   
extensionsr   Ztl.corer	   Ztl.tlobjectr
   Ztl.functionsr   Ztl.core.gzippackedr   Ztl.typesr   r   r)   r`   r_   rf   r   r    r   r   r   r   <module>   s$   