U
    s@gM                     @   sf  d Z ddlmZm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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mZ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 Z&dd Z'G dd de(Z)eeG dd de)Z*eeG dd de(Z+dd Z,eeG dd de)e%Z-G dd de(Z.G d d! d!e(Z/dS )"zE
An implementation of the OpenSSH known_hosts database.

@since: 8.2
    )absolute_importdivisionN)Error
b2a_base64
a2b_base64)closing)sha1)implementer)IKnownHostEntry)HostKeyChangedUserRejectedKeyInvalidEntry)KeyBadKeyErrorFingerprintFormats)defer)log)nativeStringunicode)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   A/usr/lib/python3/dist-packages/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   _extractCommon-   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'   r-   r(   r   r   r   __init__Y   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}
        )r-   )r.   Z	keyObjectr   r   r   
matchesKey_   s    z_BaseEntry.matchesKeyN)__name__
__module____qualname____doc__r/   r0   r   r   r   r   r+   J   s   r+   c                       s<   e Zd ZdZ fddZedd Zdd Z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.
    @type _hostnames: L{list} of L{bytes}
    c                    s   || _ tt| ||| d S r,   )
_hostnamessuperr5   r/   )r.   r&   r'   r-   r(   	__class__r   r   r/   x   s    zPlainEntry.__init__c                 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".

        @type string: L{bytes}

        @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(   r.   r   r   r   r#   }   s    zPlainEntry.fromStringc                 C   s   t |tr|d}|| jkS )aT  
        Check to see if this entry matches a given hostname.

        @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}
        utf-8)
isinstancer   encoder6   r.   hostnamer   r   r   matchesHost   s    

zPlainEntry.matchesHostc                 C   s>   d | j| jt| j g}| jdk	r4|| j d |S )a  
        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.
        @rtype: L{bytes}
        r:   N    )joinr6   r'   r   r-   blobr(   appendr.   Zfieldsr   r   r   toString   s    	

zPlainEntry.toString)
r1   r2   r3   r4   r/   classmethodr#   rA   rG   __classcell__r   r   r8   r   r5   n   s   
r5   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)r.   r$   r   r   r   r/      s    zUnparsedEntry.__init__c                 C   s   dS z'
        Always returns False.
        Fr   r?   r   r   r   rA      s    zUnparsedEntry.matchesHostc                 C   s   dS rL   r   )r.   r)   r   r   r   r0      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   )rK   r"   r.   r   r   r   rG      s    zUnparsedEntry.toStringN)r1   r2   r3   r4   r/   rA   r0   rG   r   r   r   r   rJ      s
   rJ   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}
    )Z	digestmodr<   )hmacZHMACr   r=   r   r>   updateZdigest)r)   r$   hashr   r   r   _hmacedString   s
    


rQ   c                       sD   e Zd ZdZdZdZ fddZe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'   r-   r(   c                    s$   || _ || _tt| ||| d S r,   )rS   rT   r7   rR   r/   )r.   hostSalthostHashr'   r-   r(   r8   r   r   r/     s    zHashedEntry.__init__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.
        @type string: L{bytes}

        @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   )
r;   r$   Zstuffr'   r)   r(   ZsaltAndHashrU   rV   r.   r   r   r   r#     s      zHashedEntry.fromStringc                 C   s   t | j|| jk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}
        )rQ   rS   rT   r?   r   r   r   rA   5  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}
        rW   NrB   )
rX   rC   r   rS   rT   r'   r-   rD   r(   rE   rF   r   r   r   rG   E  s    	
zHashedEntry.toString)r1   r2   r3   r4   rX   ZcompareAttributesr/   rH   r#   rA   rG   rI   r   r   r8   r   rR      s   
 rR   c                   @   sX   e Zd ZdZdd Zedd Zdd Zdd	 Zd
d Z	dd Z
dd Ze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__}.
    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)r.   savePathr   r   r   r/   i  s    
zKnownHostsFile.__init__c                 C   s   | j S )z<
        @see: C{savePath} parameter of L{__init__}
        )r[   rM   r   r   r   r]   x  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)rZ   r\   r[   openIOError
startswithrR   rX   r#   r5   DecodeErrorr   r   rJ   )r.   entryfpliner   r   r   iterentries  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)
	enumeratere   r!   rZ   rA   r'   sshTyper0   r[   r   )r.   r@   r)   Zlineidxrb   rd   pathr   r   r   
hasHostKey  s    
zKnownHostsFile.hasHostKeyc                    s.   t j } 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}
        c                    s   | rB s>d tf     | S  fdd} }|dkrhd}dt t|jtjdf }	|
t }||S d S )NzZWarning: Permanently added the %s host key for IP address '%s' to the list of known hosts.c                    s2   | r(       | S t d S r,   )
addHostKeysaver   )Zresponse)r@   ipr)   r.   r   r   promptResponse  s    zGKnownHostsFile.verifyHostKey.<locals>.gotHasKey.<locals>.promptResponseZECZECDSAzThe 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)ri   warntyper   rj   rk   Zfingerprintr   ZSHA256_BASE64promptr>   sysgetdefaultencodingaddCallback)resultrm   Zkeytyperq   Zproceedr@   rl   r)   r.   uir   r   	gotHasKey  s(    	z/KnownHostsFile.verifyHostKey.<locals>.gotHasKey)r   ZmaybeDeferredri   rt   )r.   rw   r@   rl   r)   Zhhkrx   r   rv   r   verifyHostKey  s    !zKnownHostsFile.verifyHostKeyc                 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   rg   rR   rQ   rZ   rE   )r.   r@   r)   Zsaltr'   rb   r   r   r   rj     s      zKnownHostsFile.addHostKeyc              	   C   sx   | j  }| s|  | jr&d}nd}| j |2}| jrd|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.
        wbZabr   c                 S   s   g | ]}|  qS r   )rG   ).0rb   r   r   r   
<listcomp>$  s     z'KnownHostsFile.save.<locals>.<listcomp>FN)	r[   parentisdirmakedirsr\   r^   rZ   writerC   )r.   pmodeZhostsFileObjr   r   r   rk     s    
zKnownHostsFile.savec                 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.
        @type path: L{FilePath}

        @return: A L{KnownHostsFile} initialized with entries from C{path}.
        @rtype: L{KnownHostsFile}
        F)r\   )r;   rh   Z
knownHostsr   r   r   fromPath*  s    zKnownHostsFile.fromPathN)r1   r2   r3   r4   r/   propertyr]   re   ri   ry   rj   rk   rH   r   r   r   r   r   rY   X  s   
 "9rY   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )		ConsoleUIz
    A UI object that can ask true/false questions and post notifications on the
    console, to be used during key verification.
    c                 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
        N)opener)r.   r   r   r   r   r/   C  s    zConsoleUI.__init__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   yesTs   noFs   Please type 'yes' or 'no': )r   r   r   readliner   lower)ZignoredfZanswerr.   textr   r   body\  s    
zConsoleUI.prompt.<locals>.body)r   Zsucceedrt   )r.   r   dr   r   r   r   rq   N  s    
zConsoleUI.promptc              	   C   s@   z&t |  }|| W 5 Q R X W n   t  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.
        @type text: L{bytes}
        N)r   r   r   r   err)r.   r   r   r   r   r   ro   j  s
    zConsoleUI.warnN)r1   r2   r3   r4   r/   rq   ro   r   r   r   r   r   >  s   r   )0r4   Z
__future__r   r   rN   Zbinasciir   ra   r   r   
contextlibr   Zhashlibr   rr   Zzope.interfacer	   Ztwisted.conch.interfacesr
   Ztwisted.conch.errorr   r   r   Ztwisted.conch.ssh.keysr   r   r   Ztwisted.internetr   Ztwisted.pythonr   Ztwisted.python.compatr   r   Ztwisted.python.randbytesr   Ztwisted.python.utilr   r   r*   objectr+   r5   rJ   rQ   rR   rY   r   r   r   r   r   <module>   s8   $N(\ g