
    \dM                        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+dS )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                 D    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)ss    ?lib/python3.11/site-packages/twisted/conch/client/knownhosts.py
_b64encoder       s     a==       c                 f   |                      dd          }t          |          dk    rt                      |\  }}}|                     dd          }t          |          dk    r|\  }}|                    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}}nn(0%Iw""4++H
8}}%	7..''QK	
.I..
/
/CgsG++r   c                       e Zd ZdZd Z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                 0    || _         || _        || _        d S 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                   D     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                 \    || _         t                                          |||           d S r1   )
_hostnamessuperr4   )r3   r&   r'   r2   r+   	__class__s        r   r4   zPlainEntry.__init__u   s,    #)W55555r   c                 p    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+   r3   s          r   r#   zPlainEntry.fromStringy   s@    0 ,:&+A+A(	7Cs9??4(('3@@r   c                 h    t          |t                    r|                    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   s4     h$$ 	0w//H4?**r   c                     d                     | j                  | j        t          | j                                                  g}| j        |                    | 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}
        rD   N    )joinr@   r'   r   r2   blobr+   appendr3   fieldss     r   toStringzPlainEntry.toString   sj     IIdo&&Lt~**,,--

 <#MM$,'''yy   r   )
r8   r9   r:   r;   r4   classmethodr#   rM   rU   __classcell__rB   s   @r   r>   r>   k   s{         6 6 6 6 6   [6+ + + ! ! ! ! ! ! !r   r>   c                   *    e Zd ZdZd Zd Zd Z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                     || _         dS )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                     dS z'
        Always returns False.
        Fr<   rK   s     r   rM   zUnparsedEntry.matchesHost   	     ur   c                     dS r^   r<   )r3   r,   s     r   r7   zUnparsedEntry.matchesKey   r_   r   c                 6    | 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      sZ         
      * * * * *r   rZ   c                     t          j        | t                    }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}
    )	digestmodrG   )hmacHMACr   rH   rI   rJ   updatedigest)r,   r$   hashs      r   _hmacedStringrj      s[     9SD)))D&# (w''KK;;==r   c                   L     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                 j    || _         || _        t                                          |||           d S r1   )rm   rn   rA   r4   )r3   hostSalthostHashr'   r2   r+   rB   s         r   r4   zHashedEntry.__init__  s3    !!)W55555r   c                 *   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   )
rE   r$   stuffr'   r,   r+   saltAndHashrp   rq   r3   s
             r   r#   zHashedEntry.fromString  s    . (6f'='=$wWC	NN,,-33D99{q  .. ((s:h''H)=)=wWUUr   c                 \    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,     "$.(33T^
 
 	
r   c                 L   | j         d                    t          | j                  t          | j                  g          z   | j        t          | j                                                  g}| j        |	                    | 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}
        rs   NrO   )
rt   rP   r   rm   rn   r'   r2   rQ   r+   rR   rS   s     r   rU   zHashedEntry.toString8  s     JiiDN33Z5O5OPQQRLt~**,,--	
 <#MM$,'''yy   r   )r8   r9   r:   r;   rt   compareAttributesr4   rV   r#   rM   rU   rW   rX   s   @r   rl   rl      s        
 
 EU6 6 6 6 6
   [<
 
 
"! ! ! ! ! ! !r   rl   c                   b    e Zd ZdZd Zed             Zd Zd Zd Z	d Z
d Ze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                 0    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)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 ]}|V  | j        rdS 	 | j                                        }n# t          $ r Y dS w xY w|5  |D ]}	 |                    t          j                  rt                              |          }nt                              |          }n,# t          t          t          f$ r t          |          }Y nw xY w|V  	 ddd           dS # 1 swxY w Y   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)r~   r   r   openOSError
startswithrl   rt   r#   r>   DecodeErrorr
   r   rZ   )r3   entryfplines       r   iterentrieszKnownHostsFile.iterentriesr  sr      [ 	 	EKKKK= 	F	$$&&BB 	 	 	FF	  		 		  0{'899 < + 6 6t < < * 5 5d ; ;#\;? 0 0 0)$//EEE0		 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		sF   5 
AAC"AB#"C"#&C	C"CC""C&)C&c                 `   t          |                                 t          | j                             D ]w\  }}|                    |          r]|j        |                                k    r@|                    |          r dS |dk     rd}d}n|dz   }| j        }t          |||          xd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)
	enumerater   r!   r~   rM   r'   sshTyper7   r   r	   )r3   rL   r,   lineidxr   r   paths          r   
hasHostKeyzKnownHostsFile.hasHostKey  s    " ((8(8(:(:S=M=M<MNN 	< 	<NGU  ** <u}/M/M##C(( <44 {{##&{#~(d;;;ur   c                 ~     t          j         j                  } f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           
      t   | r}                               se	                    d                                dt                    d                                                                           | S fd}                                }|dk    rd}dt                    dt                    d	|d
                    t          j                  d	}		                    |
                    t          j                                        }|                    |          S )NzWarning: Permanently added the z host key for IP address 'z' to the list of known hosts.c                     | rB                                                                                                 | S t                      r1   )
addHostKeysaver   )responserL   ipr,   r3   s    r   promptResponsezGKnownHostsFile.verifyHostKey.<locals>.gotHasKey.<locals>.promptResponse  sQ     0#666C00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  sY    ';r3//  GGG 88::::|B'7'7'7'79  
 OOB,,,IIKKK0 0 0 0 0 0 0 0 ((**d??%G %X....$R((((/A/OPPPP	  ))FMM#2H2J2J$K$KLL**>:::r   )r   executer   r   )r3   r   rL   r   r,   hhkr   s   `````  r   verifyHostKeyzKnownHostsFile.verifyHostKey  s`    ( mDOXs;;(	; (	; (	; (	; (	; (	; (	; (	; (	;T y)))r   c                     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   r   rl   rj   r~   rR   )r3   rL   r,   saltr'   r   s         r   r   zKnownHostsFile.addHostKey  sX    " B++--D-h"?"?#tTT5!!!r   c                    | j                                         }|                                s|                                 | j        rd}nd}| j                             |          5 }| j        rA|                    d                    d | j        D                       dz              g | _        ddd           n# 1 swxY w Y   d| _        dS )zM
        Save this L{KnownHostsFile} to the path it was loaded from.
        wbabr   c                 6    g | ]}|                                 S r<   )rU   ).0r   s     r   
<listcomp>z'KnownHostsFile.save.<locals>.<listcomp>  s"    JJJU 0 0JJJr   NF)	r   parentisdirmakedirsr   r   r~   writerP   )r3   pmodehostsFileObjs       r   r   zKnownHostsFile.save  s    N!!##wwyy 	JJLLL= 	DDD^  && 	!,{ !""JJJJdkJJJKKeS   !	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! s   (A	B==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     SYY
#
r   N)r8   r9   r:   r;   r4   propertyr   r   r   r   r   r   rV   r   r<   r   r   r|   r|   L  s               X  >  B@* @* @*D  .  *   [  r   r|   c                   $    e Zd ZdZd Zd Z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                     || _         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)r3   r   s     r   r4   zConsoleUI.__init__9  s     r   c                 d     t          j        d          } f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                 z   t                                                    5 }|                               	 |                                                                                                }|dk    r	 d d d            dS |dk    r	 d d d            dS |                    d           v# 1 swxY w Y   d S )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17799F''#	@ 	@ 	@ 	@ 	@ 	@ 	@ 	@  5$	@ 	@ 	@ 	@ 	@ 	@ 	@ 	@  >???@	@ 	@ 	@ 	@ 	@ 	@ 	@ 	@ 	@ 	@s   AB0B0B00B47B4)r   succeedr   )r3   r   dr   s   ``  r   r   zConsoleUI.promptC  sM     M$
	@ 
	@ 
	@ 
	@ 
	@ 
	@ }}T"""r   c                     	 t          |                                           5 }|                    |           ddd           dS # 1 swxY w Y   dS # t          $ r t                              d           Y dS 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`  s    	6'' 1                  	6 	6 	6KK4555555	6s3   !A AA A

A A
A $A;:A;N)r8   r9   r:   r;   r4   r   r   r<   r   r   r   r   3  sK         
  # # #:6 6 6 6 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 A A A A A A A A             & & & & & & M M M M M M M M M M 4 4 4 4 4 4 G G G G G G G G G G " " " " " " ! ! ! ! ! ! . . . . . . 1 1 1 1 1 1 , , , , , ,fhh
! 
! 
!, , ,8+ + + + + + + +D _I! I! I! I! I! I! I! I!X _!* !* !* !* !* !* !* !*H  ( _W! W! W! W! W!*l W! W! W!td d d d d d d dN96 96 96 96 96 96 96 96 96 96r   