U
     T‚g¡O  ã                   @  sˆ  d Z ddlmZ ddlZddlZddlmZmZm	Z	 ddl
mZ ddlmZ ddlmZmZmZ ddlmZ dd	lmZmZmZ dd
lmZ ddlmZ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) ddl*m+Z+ e#ƒ Z,dd„ Z-dd„ Z.G dd„ dƒZ/eeƒG dd„ de/ƒƒZ0eeƒG dd„ dƒƒZ1dd„ Z2eeƒG dd „ d e/e+ƒƒZ3G d!d"„ d"ƒZ4G d#d$„ d$ƒZ5dS )%zE
An implementation of the OpenSSH known_hosts database.

@since: 8.2
é    )ÚannotationsN)ÚErrorÚ
a2b_base64Ú
b2a_base64)Úclosing)Úsha1)ÚIOÚCallableÚLiteral)Úimplementer)ÚHostKeyChangedÚInvalidEntryÚUserRejectedKey)ÚIKnownHostEntry)ÚBadKeyErrorÚFingerprintFormatsÚKey)Údefer)ÚDeferred)ÚLogger)ÚnativeString)ÚFilePath)ÚsecureRandom)ÚFancyEqMixinc                 C  s   t | ƒ ¡ S )z½
    Encode a binary string as base64 with no trailing newline.

    @param s: The string to encode.
    @type s: L{bytes}

    @return: The base64-encoded string.
    @rtype: L{bytes}
    )r   Ústrip)Ús© r   úC/tmp/pip-unpacked-wheel-1_7di8ic/twisted/conch/client/knownhosts.pyÚ
_b64encode$   s    
r   c           	      C  sz   |   dd¡}t|ƒdkrtƒ ‚|\}}}|  dd¡}t|ƒdkrT|\}}| d¡}n|d }d}t t|ƒ¡}||||fS )aµ  
    Extract common elements of base64 keys from an entry in a hosts file.

    @param string: A known hosts file entry (a single line).
    @type string: L{bytes}

    @return: a 4-tuple of hostname data (L{bytes}), ssh key type (L{bytes}), key
        (L{Key}), and comment (L{bytes} or L{None}).  The hostname data is
        simply the beginning of the line up to the first occurrence of
        whitespace.
    @rtype: L{tuple}
    Né   é   é   ó   
r   )ÚsplitÚlenr   Úrstripr   Ú
fromStringr   )	ÚstringÚelementsÚ	hostnamesÚkeyTypeZkeyAndCommentZsplitkeyZ	keyStringÚcommentÚkeyr   r   r   Ú_extractCommon1   s    
r-   c                   @  s    e Zd ZdZdd„ Zdd„ ZdS )Ú
_BaseEntryaª  
    Abstract base of both hashed and non-hashed entry objects, since they
    represent keys and key types the same way.

    @ivar keyType: The type of the key; either ssh-dss or ssh-rsa.
    @type keyType: L{bytes}

    @ivar publicKey: The server public key indicated by this line.
    @type publicKey: L{twisted.conch.ssh.keys.Key}

    @ivar comment: Trailing garbage after the key line.
    @type comment: L{bytes}
    c                 C  s   || _ || _|| _d S ©N)r*   Ú	publicKeyr+   )Úselfr*   r0   r+   r   r   r   Ú__init__\   s    z_BaseEntry.__init__c                 C  s
   | j |kS )a  
        Check to see if this entry matches a given key object.

        @param keyObject: A public key object to check.
        @type keyObject: L{Key}

        @return: C{True} if this entry's key matches C{keyObject}, C{False}
            otherwise.
        @rtype: L{bool}
        )r0   )r1   Z	keyObjectr   r   r   Ú
matchesKeya   s    z_BaseEntry.matchesKeyN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r2   r3   r   r   r   r   r.   M   s   r.   c                      s^   e Zd ZdZdddddœ‡ fdd„Zedd dœd	d
„ƒZdddœdd„Zddœdd„Z‡  Z	S )Ú
PlainEntryz¬
    A L{PlainEntry} is a representation of a plain-text entry in a known_hosts
    file.

    @ivar _hostnames: the list of all host-names associated with this entry.
    zlist[bytes]Úbytesr   )r)   r*   r0   r+   c                   s   || _ tƒ  |||¡ d S r/   )Ú
_hostnamesÚsuperr2   )r1   r)   r*   r0   r+   ©Ú	__class__r   r   r2   x   s    zPlainEntry.__init__©r'   Úreturnc                 C  s(   t |ƒ\}}}}| | d¡|||ƒ}|S )a¦  
        Parse a plain-text entry in a known_hosts file, and return a
        corresponding L{PlainEntry}.

        @param string: a space-separated string formatted like "hostname
            key-type base64-key-data comment".

        @raise DecodeError: if the key is not valid encoded as valid base64.

        @raise InvalidEntry: if the entry does not have the right number of
            elements and is therefore invalid.

        @raise BadKeyError: if the key, once decoded from base64, is not
            actually an SSH key.

        @return: an IKnownHostEntry representing the hostname and key in the
            input line.

        @rtype: L{PlainEntry}
        ó   ,)r-   r#   )Úclsr'   r)   r*   r,   r+   r1   r   r   r   r&   ~   s    zPlainEntry.fromStringzbytes | strÚbool)Úhostnamer?   c                 C  s   t |tƒr| d¡}|| jkS )a  
        Check to see if this entry matches a given hostname.

        @param hostname: A hostname or IP address literal to check against this
            entry.

        @return: C{True} if this entry is for the given hostname or IP address,
            C{False} otherwise.
        úutf-8)Ú
isinstanceÚstrÚencoder:   ©r1   rC   r   r   r   ÚmatchesHost˜   s    


zPlainEntry.matchesHost©r?   c                 C  s>   d  | j¡| jt| j ¡ ƒg}| jdk	r4| | j¡ d  |¡S )zù
        Implement L{IKnownHostEntry.toString} by recording the comma-separated
        hostnames, key type, and base-64 encoded key.

        @return: The string representation of this entry, with unhashed hostname
            information.
        r@   Nó    )Újoinr:   r*   r   r0   Úblobr+   Úappend©r1   Úfieldsr   r   r   ÚtoString¦   s    	
ý
zPlainEntry.toString)
r4   r5   r6   r7   r2   Úclassmethodr&   rI   rQ   Ú__classcell__r   r   r<   r   r8   o   s   r8   c                   @  s0   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
S )ÚUnparsedEntryzŒ
    L{UnparsedEntry} is an entry in a L{KnownHostsFile} which can't actually be
    parsed; therefore it matches no keys and no hosts.
    c                 C  s
   || _ dS )zv
        Create an unparsed entry from a line in a known_hosts file which cannot
        otherwise be parsed.
        N)Ú_string)r1   r'   r   r   r   r2   ¿   s    zUnparsedEntry.__init__c                 C  s   dS ©z'
        Always returns False.
        Fr   rH   r   r   r   rI   Æ   s    zUnparsedEntry.matchesHostc                 C  s   dS rV   r   )r1   r,   r   r   r   r3   Ì   s    zUnparsedEntry.matchesKeyc                 C  s   | j  d¡S )a  
        Returns the input line, without its newline if one was given.

        @return: The string representation of this entry, almost exactly as was
            used to initialize this entry but without a trailing newline.
        @rtype: L{bytes}
        r"   )rU   r%   ©r1   r   r   r   rQ   Ò   s    zUnparsedEntry.toStringN)r4   r5   r6   r7   r2   rI   r3   rQ   r   r   r   r   rT   ¸   s
   rT   c                 C  s4   t j| td�}t|tƒr"| d¡}| |¡ | ¡ S )zù
    Return the SHA-1 HMAC hash of the given key and string.

    @param key: The HMAC key.
    @type key: L{bytes}

    @param string: The string to be hashed.
    @type string: L{bytes}

    @return: The keyed hash value.
    @rtype: L{bytes}
    )Ú	digestmodrD   )ÚhmacÚHMACr   rE   rF   rG   ÚupdateÚdigest)r,   r'   Úhashr   r   r   Ú_hmacedStringÝ   s
    


r^   c                      s\   e Zd ZdZdZdZdddddddœ‡ fd	d
„Zedd dœdd„ƒZdd„ Z	dd„ Z
‡  ZS )ÚHashedEntrya�  
    A L{HashedEntry} is a representation of an entry in a known_hosts file
    where the hostname has been hashed and salted.

    @ivar _hostSalt: the salt to combine with a hostname for hashing.

    @ivar _hostHash: the hashed representation of the hostname.

    @cvar MAGIC: the 'hash magic' string used to identify a hashed line in a
    known_hosts file as opposed to a plaintext one.
    s   |1|)Ú	_hostSaltÚ	_hostHashr*   r0   r+   r9   r   zbytes | NoneÚNone)ÚhostSaltÚhostHashr*   r0   r+   r?   c                   s    || _ || _tƒ  |||¡ d S r/   )r`   ra   r;   r2   )r1   rc   rd   r*   r0   r+   r<   r   r   r2     s    zHashedEntry.__init__r>   c           
      C  s^   t |ƒ\}}}}|t| jƒd…  d¡}t|ƒdkr:tƒ ‚|\}}| t|ƒt|ƒ|||ƒ}	|	S )a  
        Load a hashed entry from a string representing a line in a known_hosts
        file.

        @param string: A complete single line from a I{known_hosts} file,
            formatted as defined by OpenSSH.

        @raise DecodeError: if the key, the hostname, or the is not valid
            encoded as valid base64

        @raise InvalidEntry: if the entry does not have the right number of
            elements and is therefore invalid, or the host/hash portion
            contains more items than just the host and hash.

        @raise BadKeyError: if the key, once decoded from base64, is not
            actually an SSH key.

        @return: The newly created L{HashedEntry} instance, initialized with
            the information from C{string}.
        Nó   |r   )r-   r$   ÚMAGICr#   r   r   )
rA   r'   Ústuffr*   r,   r+   ZsaltAndHashrc   rd   r1   r   r   r   r&     s    zHashedEntry.fromStringc                 C  s   t  t| j|ƒ| j¡S )a…  
        Implement L{IKnownHostEntry.matchesHost} to compare the hash of the
        input to the stored hash.

        @param hostname: A hostname or IP address literal to check against this
            entry.
        @type hostname: L{bytes}

        @return: C{True} if this entry is for the given hostname or IP address,
            C{False} otherwise.
        @rtype: L{bool}
        )rY   Úcompare_digestr^   r`   ra   rH   r   r   r   rI   -  s    
 ÿzHashedEntry.matchesHostc                 C  sR   | j d t| jƒt| jƒg¡ | jt| j ¡ ƒg}| jdk	rH| 	| j¡ d |¡S )zï
        Implement L{IKnownHostEntry.toString} by base64-encoding the salt, host
        hash, and key.

        @return: The string representation of this entry, with the hostname part
            hashed.
        @rtype: L{bytes}
        re   NrK   )
rf   rL   r   r`   ra   r*   r0   rM   r+   rN   rO   r   r   r   rQ   >  s    
ÿü
zHashedEntry.toString)r4   r5   r6   r7   rf   ZcompareAttributesr2   rR   r&   rI   rQ   rS   r   r   r<   r   r_   ñ   s   r_   c                   @  sŒ   e Zd ZdZdddœdd„Zeddœdd	„ƒZd
d„ Zdd„ Zddddddœdd„Z	ddddœdd„Z
ddœdd„Zedd dœdd„ƒZdS )ÚKnownHostsFileaz  
    A structured representation of an OpenSSH-format ~/.ssh/known_hosts file.

    @ivar _added: A list of L{IKnownHostEntry} providers which have been added
        to this instance in memory but not yet saved.

    @ivar _clobber: A flag indicating whether the current contents of the save
        path will be disregarded and potentially overwritten or not.  If
        C{True}, this will be done.  If C{False}, entries in the save path will
        be read and new entries will be saved by appending rather than
        overwriting.
    @type _clobber: L{bool}

    @ivar _savePath: See C{savePath} parameter of L{__init__}.
    zFilePath[str]rb   )ÚsavePathr?   c                 C  s   g | _ || _d| _dS )a$  
        Create a new, empty KnownHostsFile.

        Unless you want to erase the current contents of C{savePath}, you want
        to use L{KnownHostsFile.fromPath} instead.

        @param savePath: The L{FilePath} to which to save new entries.
        @type savePath: L{FilePath}
        TN)Ú_addedÚ	_savePathÚ_clobber)r1   rj   r   r   r   r2   c  s    
zKnownHostsFile.__init__rJ   c                 C  s   | j S )z<
        @see: C{savePath} parameter of L{__init__}
        )rl   rW   r   r   r   rj   q  s    zKnownHostsFile.savePathc                 c  s°   | j D ]
}|V  q| jrdS z| j ¡ }W n tk
r@   Y dS X |�` |D ]T}z&| tj¡rjt |¡}n
t	 |¡}W n" t
ttfk
r˜   t|ƒ}Y nX |V  qLW 5 Q R X dS )aK  
        Iterate over the host entries in this file.

        @return: An iterable the elements of which provide L{IKnownHostEntry}.
            There is an element for each entry in the file as well as an element
            for each added but not yet saved entry.
        @rtype: iterable of L{IKnownHostEntry} providers
        N)rk   rm   rl   ÚopenÚOSErrorÚ
startswithr_   rf   r&   r8   ÚDecodeErrorr   r   rT   )r1   ÚentryÚfpÚliner   r   r   Úiterentriesx  s"    	
zKnownHostsFile.iterentriesc                 C  sx   t |  ¡ t| jƒ ƒD ]\\}}| |¡r|j| ¡ kr| |¡rF dS |dk rXd}d}n|d }| j}t	|||ƒ‚qdS )a  
        Check for an entry with matching hostname and key.

        @param hostname: A hostname or IP address literal to check for.
        @type hostname: L{bytes}

        @param key: The public key to check for.
        @type key: L{Key}

        @return: C{True} if the given hostname and key are present in this file,
            C{False} if they are not.
        @rtype: L{bool}

        @raise HostKeyChanged: if the host key found for the given hostname
            does not match the given key.
        Tr   Nr!   F)
Ú	enumerateru   r$   rk   rI   r*   ÚsshTyper3   rl   r   )r1   rC   r,   Zlineidxrr   rt   Úpathr   r   r   Ú
hasHostKey—  s    
zKnownHostsFile.hasHostKeyÚ	ConsoleUIr9   r   úDeferred[bool])ÚuirC   Úipr,   r?   c                   s6   t  ˆjˆ ˆ¡}dddœ‡ ‡‡‡‡fdd„}| |¡S )a¹  
        Verify the given host key for the given IP and host, asking for
        confirmation from, and notifying, the given UI about changes to this
        file.

        @param ui: The user interface to request an IP address from.

        @param hostname: The hostname that the user requested to connect to.

        @param ip: The string representation of the IP address that is actually
        being connected to.

        @param key: The public key of the server.

        @return: a L{Deferred} that fires with True when the key has been
            verified, or fires with an errback when the key either cannot be
            verified or has changed.
        @rtype: L{Deferred}
        rB   zbool | Deferred[bool])Úresultr?   c                   sÄ   | rRˆ  ˆˆ¡sNdˆ ¡ › dˆ ¡ › d�}ˆ | d¡¡ ˆ ˆˆ¡ ˆ ¡  | S dddœ‡ ‡‡‡fdd„}ˆ ¡ }|d	kr€d
}dtˆ ƒtˆƒ|ˆjt	j
d�f }ˆ | t ¡ ¡¡}| |¡S d S )NzWarning: Permanently added the z host key for IP address 'z' to the list of known hosts.
rD   rB   )Úresponser?   c                   s2   | r(ˆ  ˆ ˆ¡ ˆ  ˆˆ¡ ˆ ¡  | S tƒ ‚d S r/   )Ú
addHostKeyÚsaver   )r   )rC   r}   r,   r1   r   r   ÚpromptResponseÞ  s    zGKnownHostsFile.verifyHostKey.<locals>.gotHasKey.<locals>.promptResponseZECZECDSAz‘The authenticity of host '%s (%s)' can't be established.
%s key fingerprint is SHA256:%s.
Are you sure you want to continue connecting (yes/no)? )Úformat)ry   ÚtypeÚdecodeÚwarnrG   r€   r�   r   Úfingerprintr   ZSHA256_BASE64ÚpromptÚsysÚgetdefaultencodingÚaddCallback)r~   Z
addMessager‚   Zkeytyperˆ   Úproceed©rC   r}   r,   r1   r|   r   r   Ú	gotHasKeyÐ  s,    ÿ	üüÿz/KnownHostsFile.verifyHostKey.<locals>.gotHasKey)r   Úexecutery   r‹   )r1   r|   rC   r}   r,   ZhhkrŽ   r   r�   r   ÚverifyHostKey¸  s    +zKnownHostsFile.verifyHostKeyr_   )rC   r,   r?   c                 C  s6   t dƒ}| ¡ }t|t||ƒ||dƒ}| j |¡ |S )aï  
        Add a new L{HashedEntry} to the key database.

        Note that you still need to call L{KnownHostsFile.save} if you wish
        these changes to be persisted.

        @param hostname: A hostname or IP address literal to associate with the
            new entry.
        @type hostname: L{bytes}

        @param key: The public key to associate with the new entry.
        @type key: L{Key}

        @return: The L{HashedEntry} that was added.
        @rtype: L{HashedEntry}
        é   N)r   rw   r_   r^   rk   rN   )r1   rC   r,   Zsaltr*   rr   r   r   r   r€   ý  s
    zKnownHostsFile.addHostKeyc              	   C  sv   | j  ¡ }| ¡ s| ¡  | jr$dnd}| j  |¡�2}| jrb| d dd„ | jD ƒ¡d ¡ g | _W 5 Q R X d| _dS )zM
        Save this L{KnownHostsFile} to the path it was loaded from.
        ÚwÚar"   c                 S  s   g | ]}|  ¡ ‘qS r   )rQ   )Ú.0rr   r   r   r   Ú
<listcomp>   s     z'KnownHostsFile.save.<locals>.<listcomp>FN)	rl   ÚparentÚisdirÚmakedirsrm   rn   rk   ÚwriterL   )r1   ÚpÚmodeZhostsFileObjr   r   r   r�     s    
ÿzKnownHostsFile.save)rx   r?   c                 C  s   | |ƒ}d|_ |S )a­  
        Create a new L{KnownHostsFile}, potentially reading existing known
        hosts information from the given file.

        @param path: A path object to use for both reading contents from and
            later saving to.  If no file exists at this path, it is not an
            error; a L{KnownHostsFile} with no entries is returned.

        @return: A L{KnownHostsFile} initialized with entries from C{path}.
        F)rm   )rA   rx   Z
knownHostsr   r   r   ÚfromPath%  s    zKnownHostsFile.fromPathN)r4   r5   r6   r7   r2   Úpropertyrj   ru   ry   r�   r€   r�   rR   rœ   r   r   r   r   ri   R  s   !Eri   c                   @  s>   e Zd ZdZddœdd„Zdddœd	d
„Zdddœdd„ZdS )rz   z†
    A UI object that can ask true/false questions and post notifications on the
    console, to be used during key verification.
    zCallable[[], IO[bytes]]©Úopenerc                 C  s
   || _ dS )aA  
        @param opener: A no-argument callable which should open a console
            binary-mode file-like object to be used for reading and writing.
            This initializes the C{opener} attribute.
        @type opener: callable taking no arguments and returning a read/write
            file-like object
        Nrž   )r1   rŸ   r   r   r   r2   <  s    zConsoleUI.__init__r9   r{   )Útextr?   c                   s"   t  d¡}‡ ‡fdd„}| |¡S )a¿  
        Write the given text as a prompt to the console output, then read a
        result from the console input.

        @param text: Something to present to a user to solicit a yes or no
            response.
        @type text: L{bytes}

        @return: a L{Deferred} which fires with L{True} when the user answers
            'yes' and L{False} when the user answers 'no'.  It may errback if
            there were any I/O errors.
        Nc              	     sn   t ˆ  ¡ ƒ�X}| ˆ¡ | ¡  ¡  ¡ }|dkr>W 5 Q R £ dS |dkrTW 5 Q R £ dS | d¡ qW 5 Q R X d S )Ns   yesT>   ó    ó   noFs   Please type 'yes' or 'no': )r   rŸ   r™   Úreadliner   Úlower)ZignoredÚfZanswer©r1   r    r   r   ÚbodyU  s    
zConsoleUI.prompt.<locals>.body)r   Zsucceedr‹   )r1   r    Údr§   r   r¦   r   rˆ   F  s    
zConsoleUI.promptrb   c              	   C  sJ   z&t |  ¡ ƒ�}| |¡ W 5 Q R X W n tk
rD   t d¡ Y nX dS )z¹
        Notify the user (non-interactively) of the provided text, by writing it
        to the console.

        @param text: Some information the user is to be made aware of.
        zFailed to write to consoleN)r   rŸ   r™   Ú	ExceptionÚlogZfailure)r1   r    r¥   r   r   r   r†   c  s
    zConsoleUI.warnN)r4   r5   r6   r7   r2   rˆ   r†   r   r   r   r   rz   6  s   
rz   )6r7   Ú
__future__r   rY   r‰   Úbinasciir   rq   r   r   Ú
contextlibr   Úhashlibr   Útypingr   r	   r
   Zzope.interfacer   Ztwisted.conch.errorr   r   r   Ztwisted.conch.interfacesr   Ztwisted.conch.ssh.keysr   r   r   Ztwisted.internetr   Ztwisted.internet.deferr   Ztwisted.loggerr   Ztwisted.python.compatr   Ztwisted.python.filepathr   Ztwisted.python.randbytesr   Ztwisted.python.utilr   rª   r   r-   r.   r8   rT   r^   r_   ri   rz   r   r   r   r   Ú<module>   s@   "H$` e