o
    ‹Íf_  ã                   @   sÖ   d Z ddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
mZmZ ddlmZ ddlmZ ddlmZ edƒr[ed	ƒr[dd
lmZmZmZmZmZ ddlmZmZmZmZ ndZdd„ ZG dd„ deƒZ dS )z-
Tests for L{twisted.conch.scripts.ckeygen}.
é    N)ÚStringIO)ÚprivateECDSA_opensshÚprivateEd25519_openssh_newÚprivateRSA_opensshÚprivateRSA_openssh_encryptedÚpublicRSA_openssh)ÚFilePath)ÚrequireModule)ÚTestCaseÚcryptographyÚpyasn1)Ú_saveKeyÚchangePassPhraseÚdisplayPublicKeyÚenumrepresentationÚprintFingerprint)ÚBadFingerPrintFormatÚBadKeyErrorÚFingerprintFormatsÚKeyzBcryptography and pyasn1 required for twisted.conch.scripts.ckeygenc                     s   t ˆ ƒ‰ ‡ fdd„}|S )a@  
    Return a callable to patch C{getpass.getpass}.  Yields a passphrase each
    time called. Use case is to provide an old, then new passphrase(s) as if
    requested interactively.

    @param passphrases: The list of passphrases returned, one per each call.

    @return: A callable to patch C{getpass.getpass}.
    c                    s   t ˆ ƒS ©N)Únext©Ú_©Úpassphrases© úA/usr/lib/python3/dist-packages/twisted/conch/test/test_ckeygen.pyÚfakeGetpass7   ó   z makeGetpass.<locals>.fakeGetpass)Úiter)r   r   r   r   r   ÚmakeGetpass+   s   
r!   c                   @   s"  e Zd ZdZdd„ ZdGdd„Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd „ Zd!d"„ Zd#d$„ Zd%d&„ Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ Zd/d0„ Zd1d2„ Zd3d4„ Zd5d6„ Zd7d8„ Zd9d:„ Zd;d<„ Z d=d>„ Z!d?d@„ Z"dAdB„ Z#dCdD„ Z$dEdF„ Z%dS )HÚKeyGenTestszN
    Tests for various functions used to implement the I{ckeygen} script.
    c                 C   s   t ƒ | _|  td| j¡ dS )zX
        Patch C{sys.stdout} so tests can make assertions about what's printed.
        ÚstdoutN)r   r#   ÚpatchÚsys©Úselfr   r   r   ÚsetUpB   s   zKeyGenTests.setUpNc                 C   sê   |   ¡ }dd|d|dg}|d ur| d|g¡ |d ur"| d|g¡ zt |¡ W n ty;   d|d< t |¡ Y nw t |¡}t |d	 ¡}|d
krU|  | ¡ d¡ n|dkrb|  | ¡ d¡ n
|  | ¡ | 	¡ ¡ |  
| ¡ ¡ d S )NÚckeygenú-tú-fz--no-passphrasez-bz--private-key-subtypeÚckeygen3r   z.pubÚecdsaÚECÚed25519ÚEd25519)ÚmktempÚextendÚ
subprocessÚcallÚFileNotFoundErrorr   ÚfromFileÚassertEqualÚtypeÚupperÚ
assertTrueÚisPublic)r'   ÚkeyTypeÚkeySizeÚprivateKeySubtypeÚfilenameÚargsÚprivKeyÚpubKeyr   r   r   Ú_testrunI   s(   þ
zKeyGenTests._testrunc                 C   sª   |   dd¡ | j dddd |   d¡ | j ddd |   d¡ |   dd¡ | j dddd |   d¡ | j ddd |   dd¡ | j dddd |   d¡ | j ddd d S )	Nr-   Ú384Úv1)r>   r/   ÚdsaÚ2048Úrsa)rC   r&   r   r   r   Útest_keygeneration_   s   



zKeyGenTests.test_keygenerationc                 C   s¾   |   ¡ }|  tj¡K ttjdƒ,}ztjdddd|g|d W n ty5   tjdddd|g|d Y nw W d   ƒ n1 s@w   Y  W d   ƒ d S W d   ƒ d S 1 sXw   Y  d S )NÚrbr)   r*   Úfoor+   )Ústderrr,   )	r1   ÚassertRaisesr3   ÚCalledProcessErrorÚopenÚosÚdevnullÚ
check_callr5   )r'   r?   rQ   r   r   r   Útest_runBadKeytypen   s&   
þ
þÿ€ûÿ"ÿzKeyGenTests.test_runBadKeytypec                 C   ó"   t ddiƒ}|  |d tj¡ dS )z˜
        L{enumrepresentation} takes a dictionary as input and returns a
        dictionary with its attributes changed to enum representation.
        Úformatúmd5-hexN)r   ÚassertIsr   ÚMD5_HEX©r'   Úoptionsr   r   r   Útest_enumrepresentation{   s   z#KeyGenTests.test_enumrepresentationc                 C   rT   )zF
        Test for format L{FingerprintFormats.SHA256-BASE64}.
        rU   úsha256-base64N)r   rW   r   ÚSHA256_BASE64rY   r   r   r   Útest_enumrepresentationsha256ƒ   s   z)KeyGenTests.test_enumrepresentationsha256c                 C   sN   |   t¡}tddiƒ W d  ƒ n1 sw   Y  |  d|jjd ¡ dS )z9
        Test for unsupported fingerprint format
        rU   ú
sha-base64Nú*Unsupported fingerprint format: sha-base64r   )rM   r   r   r7   Ú	exceptionr@   )r'   Úemr   r   r   Ú test_enumrepresentationBadFormatŠ   s   ÿÿz,KeyGenTests.test_enumrepresentationBadFormatc                 C   ó:   |   ¡ }t|ƒ t¡ t|ddœƒ |  | j ¡ d¡ dS )z¹
        L{printFingerprint} writes a line to standard out giving the number of
        bits of the key, its fingerprint, and the basename of the file from it
        was read.
        rV   ©r?   rU   z:2048 85:25:04:32:58:55:96:9f:57:ee:fb:a8:1a:ea:69:da temp
N©r1   r   Ú
setContentr   r   r7   r#   Úgetvalue©r'   r?   r   r   r   Útest_printFingerprint”   s   þz!KeyGenTests.test_printFingerprintc                 C   rd   )zŽ
        L{printFigerprint} will print key fingerprint in
        L{FingerprintFormats.SHA256-BASE64} format if explicitly specified.
        r\   re   z72048 FBTCOoknq0mHy+kpfnY9tDdcAJuWtCpuQMaV3EsvbUI= temp
Nrf   ri   r   r   r   Útest_printFingerprintsha256¢   s   þz'KeyGenTests.test_printFingerprintsha256c                 C   sf   |   ¡ }t|ƒ t¡ |  t¡}t|ddœƒ W d  ƒ n1 s"w   Y  |  d|jj	d ¡ dS )zx
        L{printFigerprint} raises C{keys.BadFingerprintFormat} when unsupported
        formats are requested.
        r_   re   Nr`   r   )
r1   r   rg   r   rM   r   r   r7   ra   r@   )r'   r?   rb   r   r   r   Ú)test_printFingerprintBadFingerPrintFormat¯   s   ÿÿz5KeyGenTests.test_printFingerprintBadFingerPrintFormatc                 C   óš   t |  ¡ ƒ}| ¡  | d¡j}t t¡}t||dddœƒ |  	| j
 ¡ d||f ¡ |  	| | d¡ ¡ dd¡|¡ |  	t | d¡ ¡ ¡| ¡ ¡ dS )z–
        L{_saveKey} writes the private and public parts of a key to two
        different files and writes a report of this to standard out.
        Úid_rsaÚ
passphraserV   ©r?   ÚpassrU   úºYour identification has been saved in %s
Your public key has been saved in %s.pub
The key fingerprint in <FingerprintFormats=MD5_HEX> is:
85:25:04:32:58:55:96:9f:57:ee:fb:a8:1a:ea:69:da
Nú
id_rsa.pub©r   r1   ÚmakedirsÚchildÚpathr   Ú
fromStringr   r   r7   r#   rh   Ú
getContentÚpublic©r'   Úbaser?   Úkeyr   r   r   Útest_saveKey¼   s"   
ýþÿÿzKeyGenTests.test_saveKeyc                 C   rm   )z³
        L{_saveKey} writes the private and public parts of a key to two
        different files and writes a report of this to standard out.
        Test with ECDSA key.
        Úid_ecdsaro   rV   rp   zºYour identification has been saved in %s
Your public key has been saved in %s.pub
The key fingerprint in <FingerprintFormats=MD5_HEX> is:
1e:ab:83:a6:f2:04:22:99:7c:64:14:d2:ab:fa:f5:16
Nzid_ecdsa.pub)r   r1   ru   rv   rw   r   rx   r   r   r7   r#   rh   ry   rz   r{   r   r   r   Útest_saveKeyECDSAÔ   s"   
ýþÿÿzKeyGenTests.test_saveKeyECDSAc                 C   rm   )zµ
        L{_saveKey} writes the private and public parts of a key to two
        different files and writes a report of this to standard out.
        Test with Ed25519 key.
        Ú
id_ed25519ro   rV   rp   zºYour identification has been saved in %s
Your public key has been saved in %s.pub
The key fingerprint in <FingerprintFormats=MD5_HEX> is:
ab:ee:c8:ed:e5:01:1b:45:b7:8d:b2:f0:8f:61:1c:14
Nzid_ed25519.pub)r   r1   ru   rv   rw   r   rx   r   r   r7   r#   rh   ry   rz   r{   r   r   r   Útest_saveKeyEd25519í   s$   
ýþþÿzKeyGenTests.test_saveKeyEd25519c                 C   rm   )zŠ
        L{_saveKey} will generate key fingerprint in
        L{FingerprintFormats.SHA256-BASE64} format if explicitly specified.
        rn   ro   r\   rp   z½Your identification has been saved in %s
Your public key has been saved in %s.pub
The key fingerprint in <FingerprintFormats=SHA256_BASE64> is:
FBTCOoknq0mHy+kpfnY9tDdcAJuWtCpuQMaV3EsvbUI=
Nrs   rt   r{   r   r   r   Útest_saveKeysha256  s&   
ÿýþÿÿzKeyGenTests.test_saveKeysha256c                 C   s~   t |  ¡ ƒ}| ¡  | d¡j}t t¡}|  t	¡}t
||dddœƒ W d  ƒ n1 s.w   Y  |  d|jjd ¡ dS )zq
        L{_saveKey} raises C{keys.BadFingerprintFormat} when unsupported
        formats are requested.
        rn   ro   r_   rp   Nr`   r   )r   r1   ru   rv   rw   r   rx   r   rM   r   r   r7   ra   r@   )r'   r|   r?   r}   rb   r   r   r   Ú test_saveKeyBadFingerPrintformat!  s   

þÿÿz,KeyGenTests.test_saveKeyBadFingerPrintformatc                 C   s`   t |  ¡ ƒ}| ¡  | d¡j}t t¡}t||dddœƒ |  	| | d¡ 
¡ dd¡|¡ dS )úq
        L{_saveKey} will choose an empty string for the passphrase if
        no-passphrase is C{True}.
        rn   TrV   ©r?   zno-passphraserU   Nó    )r   r1   ru   rv   rw   r   rx   r   r   r7   ry   r{   r   r   r   Útest_saveKeyEmptyPassphrase3  s   
ÿÿz'KeyGenTests.test_saveKeyEmptyPassphrasec                 C   ó^   t |  ¡ ƒ}| ¡  | d¡j}t t¡}t||dddœƒ |  	| | d¡ 
¡ d¡|¡ dS )r…   r   TrV   r†   N)r   r1   ru   rv   rw   r   rx   r   r   r7   ry   r{   r   r   r   Ú test_saveKeyECDSAEmptyPassphraseC  s   
ÿ"z,KeyGenTests.test_saveKeyECDSAEmptyPassphrasec                 C   r‰   )r…   r   TrV   r†   N)r   r1   ru   rv   rw   r   rx   r   r   r7   ry   r{   r   r   r   Ú"test_saveKeyEd25519EmptyPassphraseQ  s   
ÿÿz.KeyGenTests.test_saveKeyEd25519EmptyPassphrasec                    sŒ   t |  ¡ ƒ}| ¡  | d¡j‰ ddl}|  |jjj	d‡ fdd„¡ t
 t¡}t|dddd	œƒ | d¡ ¡ }| |dd
¡}|  ||¡ dS )zd
        When no path is specified, it will ask for the path used to store the
        key.
        Ú
custom_keyr   NÚ_inputSaveFilec                    s   ˆ S r   r   r   ©ÚkeyPathr   r   Ú<lambda>l  ó    z4KeyGenTests.test_saveKeyNoFilename.<locals>.<lambda>TrV   r†   r‡   )r   r1   ru   rv   rw   Útwisted.conch.scripts.ckeygenr$   ÚconchÚscriptsr)   r   rx   r   r   ry   r7   )r'   r|   Útwistedr}   ÚpersistedKeyContentÚpersistedKeyr   rŽ   r   Útest_saveKeyNoFilenamea  s   
z"KeyGenTests.test_saveKeyNoFilenamec                 C   s°   t |  ¡ ƒ}| ¡  | d¡j}t t¡}t||ddddœƒ |  	| j
 ¡ d||f ¡ | d¡ ¡ }|  	| |dd¡|¡ |  | d¡¡ |  	t | d	¡ ¡ ¡| ¡ ¡ dS )
zi
        L{_saveKey} can be told to write the new private key file in OpenSSH
        v1 format.
        rn   ro   rV   rE   )r?   rq   rU   úprivate-key-subtyperr   Nó$   -----BEGIN OPENSSH PRIVATE KEY-----
rs   )r   r1   ru   rv   rw   r   rx   r   r   r7   r#   rh   ry   r:   Ú
startswithrz   )r'   r|   r?   r}   ÚprivateKeyContentr   r   r   Útest_saveKeySubtypeV1t  s4   
üþ	ýþÿÿz!KeyGenTests.test_saveKeySubtypeV1c                 C   sf   |   ¡ }t t¡}t|ƒ t¡ td|iƒ | j 	¡  
d¡}t|tƒr(| d¡}|  || d¡¡ dS )zl
        L{displayPublicKey} prints out the public key associated with a given
        private key.
        r?   Ú
ÚasciiÚopensshN)r1   r   rx   r   r   rg   r   r   r#   rh   ÚstripÚ
isinstanceÚstrÚencoder7   ÚtoString©r'   r?   rB   Ú	displayedr   r   r   Útest_displayPublicKey–  s   


z!KeyGenTests.test_displayPublicKeyc                 C   sh   |   ¡ }t t¡}t|ƒ t¡ t|ddœƒ | j 	¡  
d¡}t|tƒr)| d¡}|  || d¡¡ dS )z›
        L{displayPublicKey} prints out the public key associated with a given
        private key using the given passphrase when it's encrypted.
        Ú	encrypted©r?   rq   rž   rŸ   r    N)r1   r   rx   r   r   rg   r   r   r#   rh   r¡   r¢   r£   r¤   r7   r¥   r¦   r   r   r   Útest_displayPublicKeyEncrypted¤  s   


z*KeyGenTests.test_displayPublicKeyEncryptedc                 C   sx   |   ¡ }t t¡}t|ƒ t¡ |  tddd„ ¡ t	d|iƒ | j
 ¡  d¡}t|tƒr1| d¡}|  || d¡¡ dS )	z›
        L{displayPublicKey} prints out the public key associated with a given
        private key, asking for the passphrase when it's encrypted.
        Úgetpassc                 S   ó   dS )Nr©   r   )Úxr   r   r   r   º  r‘   zLKeyGenTests.test_displayPublicKeyEncryptedPassphrasePrompt.<locals>.<lambda>r?   rž   rŸ   r    N)r1   r   rx   r   r   rg   r   r$   r¬   r   r#   rh   r¡   r¢   r£   r¤   r7   r¥   r¦   r   r   r   Ú.test_displayPublicKeyEncryptedPassphrasePrompt²  s   


z:KeyGenTests.test_displayPublicKeyEncryptedPassphrasePromptc                 C   s.   |   ¡ }t|ƒ t¡ |  tt|ddœ¡ dS )zŠ
        L{displayPublicKey} fails with a L{BadKeyError} when trying to decrypt
        an encrypted key with the wrong password.
        Úwrongrª   N)r1   r   rg   r   rM   r   r   ri   r   r   r   Ú$test_displayPublicKeyWrongPassphraseÁ  s
   ÿz0KeyGenTests.test_displayPublicKeyWrongPassphrasec                 C   sl   t dddƒ}|  td|¡ |  ¡ }t|ƒ t¡ td|iƒ |  | j	 
¡  d¡d¡ |  tt|ƒ ¡ ¡ dS )zt
        L{changePassPhrase} allows a user to change the passphrase of a
        private key interactively.
        r©   Únewpassr¬   r?   rž   ú;Your identification has been saved with the new passphrase.N©r!   r$   r¬   r1   r   rg   r   r   r7   r#   rh   r¡   ÚassertNotEqualry   )r'   ÚoldNewConfirmr?   r   r   r   Útest_changePassphraseÌ  s   þÿz!KeyGenTests.test_changePassphrasec                 C   sl   t ddƒ}|  td|¡ |  ¡ }t|ƒ t¡ t|ddœƒ |  | j	 
¡  d¡d¡ |  tt|ƒ ¡ ¡ dS )zž
        L{changePassPhrase} allows a user to change the passphrase of a
        private key, providing the old passphrase and prompting for new one.
        r²   r¬   r©   rª   rž   r³   Nr´   )r'   Ú
newConfirmr?   r   r   r   Útest_changePassphraseWithOldà  s   
þÿz(KeyGenTests.test_changePassphraseWithOldc                 C   sV   |   ¡ }t|ƒ t¡ t|dddœƒ |  | j ¡  d¡d¡ |  	tt|ƒ 
¡ ¡ dS )z¢
        L{changePassPhrase} allows a user to change the passphrase of a private
        key by providing both old and new passphrases without prompting.
        r©   Ú
newencrypt)r?   rq   r²   rž   r³   N)r1   r   rg   r   r   r7   r#   rh   r¡   rµ   ry   ri   r   r   r   Útest_changePassphraseWithBothô  s   
ÿþÿz)KeyGenTests.test_changePassphraseWithBothc                 C   óR   |   ¡ }t|ƒ t¡ |  tt|ddœ¡}|  dt|ƒ¡ |  tt|ƒ 	¡ ¡ dS )zŽ
        L{changePassPhrase} exits if passed an invalid old passphrase when
        trying to change the passphrase of a private key.
        r°   rª   z1Could not change passphrase: old passphrase errorN)
r1   r   rg   r   rM   Ú
SystemExitr   r7   r£   ry   ©r'   r?   Úerrorr   r   r   Ú$test_changePassphraseWrongPassphrase  s   ÿÿz0KeyGenTests.test_changePassphraseWrongPassphrasec                 C   sb   |   tdtdƒ¡ |  ¡ }t|ƒ t¡ |  tt	d|i¡}|  
dt|ƒ¡ |  
tt|ƒ ¡ ¡ dS )zƒ
        L{changePassPhrase} exits if no passphrase is specified for the
        C{getpass} call and the key is encrypted.
        r¬   Ú r?   zMCould not change passphrase: Passphrase must be provided for an encrypted keyN)r$   r¬   r!   r1   r   rg   r   rM   r½   r   r7   r£   ry   r¾   r   r   r   Ú!test_changePassphraseEmptyGetPass  s   ýz-KeyGenTests.test_changePassphraseEmptyGetPassc                 C   sT   |   ¡ }t|ƒ d¡ |  ttd|i¡}d}|  |t|ƒ¡ |  dt|ƒ ¡ ¡ dS )zc
        L{changePassPhrase} exits if the file specified points to an invalid
        key.
        s   foobarr?   z?Could not change passphrase: cannot guess the type of b'foobar'N)	r1   r   rg   rM   r½   r   r7   r£   ry   )r'   r?   r¿   Úexpectedr   r   r   Útest_changePassphraseBadKey&  s   z'KeyGenTests.test_changePassphraseBadKeyc                 C   sh   |   ¡ }t|ƒ t¡ dd„ }|  td|¡ |  tt|ddœ¡}|  	dt
|ƒ¡ |  	tt|ƒ ¡ ¡ dS )zŸ
        L{changePassPhrase} doesn't modify the key file if an unexpected error
        happens when trying to create the key with the new passphrase.
        c                  _   s   t dƒ‚)NÚoops)ÚRuntimeError©r@   Úkwargsr   r   r   r¥   ;  r   z>KeyGenTests.test_changePassphraseCreateError.<locals>.toStringr¥   rº   ©r?   r²   z!Could not change passphrase: oopsN©r1   r   rg   r   r$   r   rM   r½   r   r7   r£   ry   )r'   r?   r¥   r¿   r   r   r   Ú test_changePassphraseCreateError3  s   ýz,KeyGenTests.test_changePassphraseCreateErrorc                 C   sl   |   ¡ }t|ƒ t¡ dd„ }|  td|¡ |  tt|ddœ¡}d}|  	|t
|ƒ¡ |  	tt|ƒ ¡ ¡ dS )zq
        L{changePassPhrase} doesn't modify the key file if C{toString} returns
        an empty string.
        c                  _   r­   )NrÁ   r   rÇ   r   r   r   r¥   R  s   zCKeyGenTests.test_changePassphraseEmptyStringError.<locals>.toStringr¥   rº   rÉ   z9Could not change passphrase: cannot guess the type of b''NrÊ   )r'   r?   r¥   r¿   rÃ   r   r   r   Ú%test_changePassphraseEmptyStringErrorJ  s   ýz1KeyGenTests.test_changePassphraseEmptyStringErrorc                 C   r¼   )z†
        L{changePassPhrase} exits when trying to change the passphrase on a
        public key, and doesn't change the file.
        rq   rÉ   z.Could not change passphrase: key not encryptedN)
r1   r   rg   r   rM   r½   r   r7   r£   ry   r¾   r   r   r   Útest_changePassphrasePublicKeyb  s   ÿz*KeyGenTests.test_changePassphrasePublicKeyc                 C   s‚   t dddƒ}|  td|¡ |  ¡ }t|ƒ t¡ t|ddœƒ |  | j	 
¡  d¡d¡ t|ƒ ¡ }|  t|¡ |  | d¡¡ d	S )
zq
        L{changePassPhrase} can be told to write the new private key file in
        OpenSSH v1 format.
        r©   r²   r¬   rE   )r?   r™   rž   r³   rš   N)r!   r$   r¬   r1   r   rg   r   r   r7   r#   rh   r¡   ry   rµ   r:   r›   )r'   r¶   r?   rœ   r   r   r   Útest_changePassphraseSubtypeV1o  s   þÿz*KeyGenTests.test_changePassphraseSubtypeV1)NN)&Ú__name__Ú
__module__Ú__qualname__Ú__doc__r(   rC   rI   rS   r[   r^   rc   rj   rk   rl   r~   r€   r‚   rƒ   r„   rˆ   rŠ   r‹   r˜   r   r¨   r«   r¯   r±   r·   r¹   r»   rÀ   rÂ   rÄ   rË   rÌ   rÍ   rÎ   r   r   r   r   r"   =   sH    

"r"   )!rÒ   r¬   r3   r%   rP   Úior   Útwisted.conch.test.keydatar   r   r   r   r   Útwisted.python.filepathr   Útwisted.python.reflectr	   Útwisted.trial.unittestr
   r’   r   r   r   r   r   Útwisted.conch.ssh.keysr   r   r   r   Úskipr!   r"   r   r   r   r   Ú<module>   s    