
    ϪfM                        d 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 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!  e       Z"d Z#d Z$ G d d      Z% ee       G d de%             Z& ee       G d d             Z'd Z( ee       G d de%e!             Z) G d d      Z* G d d      Z+y)zE
An implementation of the OpenSSH known_hosts database.

@since: 8.2
    N)Error
a2b_base64
b2a_base64)closing)sha1)implementer)HostKeyChangedInvalidEntryUserRejectedKey)IKnownHostEntry)BadKeyErrorFingerprintFormatsKey)defer)Logger)nativeString)secureRandom)FancyEqMixinc                 4    t        |       j                         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)ss    A/usr/lib/python3/dist-packages/twisted/conch/client/knownhosts.py
_b64encoder       s     a=      c                 &   | j                  dd      }t        |      dk7  r
t               |\  }}}|j                  dd      }t        |      dk(  r|\  }}|j                  d      }n|d   }d}t	        j
                  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keyTypekeyAndCommentsplitkey	keyStringcommentkeys	            r   _extractCommonr-   -   s     ||D!$H
8}n(0%Iw""4+H
8}%	7..'QK	
..I.
/CgsG++r   c                       e Zd ZdZd Zd Zy)
_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                 .    || _         || _        || _        y N)r'   	publicKeyr+   )selfr'   r2   r+   s       r   __init__z_BaseEntry.__init__X   s    "r   c                      | 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}
        )r2   )r3   	keyObjects     r   
matchesKeyz_BaseEntry.matchesKey]   s     ~~**r   N)__name__
__module____qualname____doc__r4   r7    r   r   r/   r/   I   s    
+r   r/   c                   >     e Zd ZdZ fdZed        Zd Zd Z x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                 6    || _         t        | 	  |||       y r1   )
_hostnamessuperr4   )r3   r&   r'   r2   r+   	__class__s        r   r4   zPlainEntry.__init__u   s    #)W5r   c                 Z    t        |      \  }}}} | |j                  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+   r3   s          r   r#   zPlainEntry.fromStringy   s5    0 ,:&+A(	7C9??4('3@r   c                 `    t        |t              r|j                  d      }|| j                  v 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)
isinstancestrencoder@   r3   hostnames     r   matchesHostzPlainEntry.matchesHost   s+     h$w/H4??**r   c                    dj                  | j                        | j                  t        | j                  j                               g}| j                  |j                  | j                         dj                  |      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}
        rD       )joinr@   r'   r   r2   blobr+   appendr3   fieldss     r   toStringzPlainEntry.toString   sb     IIdoo&LLt~~**,-

 <<#MM$,,'yy  r   )
r8   r9   r:   r;   r4   classmethodr#   rM   rU   __classcell__rB   s   @r   r>   r>   k   s+    6  6+ !r   r>   c                   (    e Zd ZdZd Zd Zd Zd Zy)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                     || _         y)zv
        Create an unparsed entry from a line in a known_hosts file which cannot
        otherwise be parsed.
        N)_string)r3   r$   s     r   r4   zUnparsedEntry.__init__   s    
 r   c                      yz'
        Always returns False.
        Fr<   rK   s     r   rM   zUnparsedEntry.matchesHost        r   c                      yr^   r<   )r3   r,   s     r   r7   zUnparsedEntry.matchesKey   r_   r   c                 8    | j                   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   )r\   r"   r3   s    r   rU   zUnparsedEntry.toString   s     ||""5))r   N)r8   r9   r:   r;   r4   rM   r7   rU   r<   r   r   rZ   rZ      s    
*r   rZ   c                     t        j                  | t              }t        |t              r|j                  d      }|j                  |       |j                         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}
    )	digestmodrG   )hmacHMACr   rH   rI   rJ   updatedigest)r,   r$   hashs      r   _hmacedStringrj      sD     99SD)D&#w'KK;;=r   c                   F     e Zd ZdZdZdZ fdZed        Zd Z	d Z
 x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'   r2   r+   c                 D    || _         || _        t        |   |||       y r1   )rm   rn   rA   r4   )r3   hostSalthostHashr'   r2   r+   rB   s         r   r4   zHashedEntry.__init__  s"    !!)W5r   c                     t        |      \  }}}}|t        | j                        d j                  d      }t        |      dk7  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   )
rE   r$   stuffr'   r,   r+   saltAndHashrp   rq   r3   s
             r   r#   zHashedEntry.fromString  sw    . (6f'=$wWC		N,-33D9{q . ((:h'H)=wWUr   c                 j    t        j                  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}
        )re   compare_digestrj   rm   rn   rK   s     r   rM   zHashedEntry.matchesHost'  s+     ""$..(3T^^
 	
r   c                 Z   | j                   dj                  t        | j                        t        | j                        g      z   | j
                  t        | j                  j                               g}| j                  |j                  | j                         dj                  |      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}
        rs   rO   )
rt   rP   r   rm   rn   r'   r2   rQ   r+   rR   rS   s     r   rU   zHashedEntry.toString8  s     JJiiDNN3Z5OPQRLLt~~**,-	
 <<#MM$,,'yy  r   )r8   r9   r:   r;   rt   compareAttributesr4   rV   r#   rM   rU   rW   rX   s   @r   rl   rl      s8    
 EU6
  <
"!r   rl   c                   T    e Zd ZdZd Zed        Zd Zd Zd Z	d Z
d Zed	        Zy
)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                 .    g | _         || _        d| _        y)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)r3   savePaths     r   r4   zKnownHostsFile.__init__]  s     !r   c                     | j                   S )z<
        @see: C{savePath} parameter of L{__init__}
        )r   rb   s    r   r   zKnownHostsFile.savePathk  s    
 ~~r   c              #     K   | j                   D ]  }|  | j                  ry	 | j                  j                         }|5  |D ]Q  }	 |j                  t        j                        rt        j                  |      }nt        j                  |      }| S 	 ddd       y# t        $ r Y yw xY w# t        t        t        f$ r t        |      }Y @w xY w# 1 sw Y   yxY ww)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)r~   r   r   openOSError
startswithrl   rt   r#   r>   DecodeErrorr
   r   rZ   )r3   entryfplines       r   iterentrieszKnownHostsFile.iterentriesr  s      [[ 	EK	 ==	$$&B  		 0{'8'89 + 6 6t < * 5 5d ; 		 		  		 $\;? 0)$/E0		 		se   #C#B#  C#C	A
B2C	C##	B/,C#.B//C#2CCCCC C#c                 D   t        | j                         t        | j                               D ]o  \  }}|j	                  |      s|j
                  |j                         k(  s6|j                  |      r y|dk  rd}d}n|dz   }| j                  }t        |||       y)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)
	enumerater   r!   r~   rM   r'   sshTyper7   r   r	   )r3   rL   r,   lineidxr   r   paths          r   
hasHostKeyzKnownHostsFile.hasHostKey  s    " ((8(8(:S=M<MN 	<NGU  *u}}/M##C( {##&{#~~(d;;	< r   c                      t        j                   j                        } fd}|j                  |      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           
         | rej                        sQ	j                  dj                         dt              d       j	                         j                          | S fd}j                         }|dk(  rd}dt              dt              d	|d
j                  t        j                        d	}	j                  |j                  t        j                                     }|j                  |      S )NzWarning: Permanently added the z host key for IP address 'z' to the list of known hosts.c                     | r6j                         j                         j                          | S t               r1   )
addHostKeysaver   )responserL   ipr,   r3   s    r   promptResponsezGKnownHostsFile.verifyHostKey.<locals>.gotHasKey.<locals>.promptResponse  s9    #6C0		'-//r   ECECDSAzThe authenticity of host 'z (z)' can't be established.
z key fingerprint is SHA256:)formatz9.
Are you sure you want to continue connecting (yes/no)? )r   warntyper   r   r   fingerprintr   SHA256_BASE64promptrJ   sysgetdefaultencodingaddCallback)
resultr   keytyper   proceedrL   r   r,   r3   uis
        r   	gotHasKeyz/KnownHostsFile.verifyHostKey.<locals>.gotHasKey  s    r3/GG 88:|B'79
 OOB,IIK0 ((*d?%G %X.$R(/A/O/OP	  ))FMM#2H2H2J$KL**>::r   )r   executer   r   )r3   r   rL   r   r,   hhkr   s   `````  r   verifyHostKeyzKnownHostsFile.verifyHostKey  s7    ( mmDOOXs;(	; (	;T y))r   c                     t        d      }|j                         }t        |t        ||      ||d      }| j                  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   r   rl   rj   r~   rR   )r3   rL   r,   saltr'   r   s         r   r   zKnownHostsFile.addHostKey  sI    " B++-D-h"?#tT5!r   c           
         | j                   j                         }|j                         s|j                          | j                  rd}nd}| j                   j                  |      5 }| j                  rP|j                  dj                  | j                  D cg c]  }|j                          c}      dz          g | _        ddd       d| _        yc c}w # 1 sw Y   d| _        yxY w)zM
        Save this L{KnownHostsFile} to the path it was loaded from.
        wbabr   NF)
r   parentisdirmakedirsr   r   r~   writerP   rU   )r3   pmodehostsFileObjr   s        r   r   zKnownHostsFile.save  s     NN!!#wwyJJL==DD^^  & 	!,{{""JJdkkJU 0JKeS !	!   K	! s   '1CC/CCC)c                 $     | |      }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   )rE   r   
knownHostss      r   fromPathzKnownHostsFile.fromPath   s     Y
#
r   N)r8   r9   r:   r;   r4   propertyr   r   r   r   r   r   rV   r   r<   r   r   r|   r|   L  sP       >B@*D.*  r   r|   c                   "    e Zd ZdZd Zd Zd Zy)	ConsoleUIz
    A UI object that can ask true/false questions and post notifications on the
    console, to be used during key verification.
    c                     || _         y)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)r3   r   s     r   r4   zConsoleUI.__init__9  s     r   c                 ^     t        j                  d      } fd}|j                  |      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                 ,   t        j                               5 }|j                         	 |j                         j	                         j                         }|dk(  r
	 d d d        y|dk(  r
	 d d d        y|j                  d       \# 1 sw Y   y xY w)NTs   yess   noFs   Please type 'yes' or 'no': )r   r   r   readliner   lower)ignoredfanswerr3   texts      r   bodyzConsoleUI.prompt.<locals>.bodyR  s    ' 	@1ZZ\//1779F'#	@ 	@  5$	@ 	@  >? 	@ 	@s   AB
)B
8B

B)r   succeedr   )r3   r   dr   s   ``  r   r   zConsoleUI.promptC  s*     MM$
	@ }}T""r   c                     	 t        | j                               5 }|j                  |       ddd       y# 1 sw Y   yxY w# t        $ r t        j                  d       Y yw xY w)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}
        NzFailed to write to console)r   r   r   	Exceptionlogfailure)r3   r   r   s      r   r   zConsoleUI.warn`  sR    	6' 1   	6KK45	6s(   A 6A ?A A A#"A#N)r8   r9   r:   r;   r4   r   r   r<   r   r   r   r   3  s    
#:6r   r   ),r;   re   r   binasciir   r   r   r   
contextlibr   hashlibr   zope.interfacer   twisted.conch.errorr	   r
   r   twisted.conch.interfacesr   twisted.conch.ssh.keysr   r   r   twisted.internetr   twisted.loggerr   twisted.python.compatr   twisted.python.randbytesr   twisted.python.utilr   r   r   r-   r/   r>   rZ   rj   rl   r|   r   r<   r   r   <module>r      s   
  
 A A   & M M 4 G G " ! . 1 ,h
!,8+ +D _I! I! I!X _!* !* !*H( _W!*l W! W!td dN96 96r   