Ϫf-m dZddlmZddlZddlZddlZddlZddlZddlmZddl m Z ddl m Z ddl m Z mZmZmZmZddlmZdd lmZdd lmZed rdd lmZmZmZmZmZmZdd lm Z m!Z!m"Z"m#Z#ndZ$ddZ%GddeZ&y)z- Tests for L{twisted.conch.scripts.ckeygen}. ) annotationsN)StringIO)Callable)NoReturn)privateECDSA_opensshprivateEd25519_openssh_newprivateRSA_opensshprivateRSA_openssh_encryptedpublicRSA_openssh)FilePath) requireModule)TestCase cryptography)_getKeyOrDefault_saveKeychangePassPhrasedisplayPublicKeyenumrepresentationprintFingerprint)BadFingerPrintFormat BadKeyErrorFingerprintFormatsKeyz7cryptography required for twisted.conch.scripts.ckeygenc*t|dfd }|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}. ctSN)next)_passphrasesIters A/usr/lib/python3/dist-packages/twisted/conch/test/test_ckeygen.py fakeGetpassz makeGetpass..fakeGetpass=sO$$)robjectreturnstr)iter) passphrasesr!rs @r makeGetpassr(1s;'O% r"cdeZdZdZd+dZ d, d-dZd+dZd+dZd+dZd+dZ d+d Z d+d Z d+d Z d+d Z d+d Zd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZ 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+y). KeyGenTestszN Tests for various functions used to implement the I{ckeygen} script. cdt|_|jtd|jy)zX Patch C{sys.stdout} so tests can make assertions about what's printed. stdoutN)rr,patchsysselfs r setUpzKeyGenTests.setUpHs!j  3$++.r"Nc|j}dd|d|dg}||jd|g||jd|g tj|t j |}t j |d z}|d k(r!|j|jd nT|d k(r!|j|jd n.|j|j|j|j|jy#t$rd|d<tj|YwxYw)Nckeygen-t-fz--no-passphrasez-bz--private-key-subtypeckeygen3r.pubecdsaECed25519Ed25519) mktempextend subprocesscallFileNotFoundErrorrfromFile assertEqualtypeupper assertTrueisPublic)r0keyTypekeySizeprivateKeySubtypefilenameargsprivKeypubKeys r _testrunzKeyGenTests._testrunOs ;;=4$:KL   KKw (  ( KK02CD E " OOD !,,x(h/0 g    W\\^T 2  !   W\\^Y 7   W\\^W]]_ = )*! " DG OOD ! "sD #EEc|jdd|jddd|jd|jdd|jd|jdd|jddd|jd|jdd|jdd|jddd|jd|jddy) Nr8384v1)rIr:dsa2048rsa)rNr/s r test_keygenerationzKeyGenTests.test_keygenerationjs gu% gu = g g 6 i  eV$ eVt < e et 4 eV$ eVt < e et 4r"c z|j}|jtj5t t j d5} tjdddd|g|ddddddy#t$rtjdddd|g|Y8wxYw#1swY=xYw#1swYyxYw)Nrbr3r4foor5)stderrr6) r< assertRaisesr>CalledProcessErroropenosdevnull check_callr@)r0rJr^s r test_runBadKeytypezKeyGenTests.test_runBadKeytypeys;;=   z<< = (bjj$' (7())"D%x@&( ( ( ( )())#T5$A&((  ( ( ( (sAB1 B% A:)B1:%B"B%!B""B%%B. *B11B:cdtddi}|j|dtjy)z L{enumrepresentation} takes a dictionary as input and returns a dictionary with its attributes changed to enum representation. formatmd5-hexN)rassertIsrMD5_HEXr0optionss r test_enumrepresentationz#KeyGenTests.test_enumrepresentations- %h %:; gh');)C)CDr"cdtddi}|j|dtjy)zF Test for format L{FingerprintFormats.SHA256-BASE64}. rb sha256-base64N)rrdr SHA256_BASE64rfs r test_enumrepresentationsha256z)KeyGenTests.test_enumrepresentationsha256s-%h%@A gh');)I)IJr"c|jt5}tddiddd|jdjj dy#1swY3xYw)z9 Test for unsupported fingerprint format rb sha-base64N*Unsupported fingerprint format: sha-base64r)rZrrrB exceptionrK)r0ems r test_enumrepresentationBadFormatz,KeyGenTests.test_enumrepresentationBadFormatsY  3 4 9 ,7 8 9  8",,:K:KA:N  9 9s AAc|j}t|jtt |dd|j |j jdy)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. rcrJrbz:2048 85:25:04:32:58:55:96:9f:57:ee:fb:a8:1a:ea:69:da temp Nr<r setContentr rrBr,getvaluer0rJs r test_printFingerprintz!KeyGenTests.test_printFingerprintsQ ;;=%%&78h)DE  KK " I r"c|j}t|jtt |dd|j |j jdy)z L{printFigerprint} will print key fingerprint in L{FingerprintFormats.SHA256-BASE64} format if explicitly specified. rjrtz72048 FBTCOoknq0mHy+kpfnY9tDdcAJuWtCpuQMaV3EsvbUI= temp Nrurxs r test_printFingerprintsha256z'KeyGenTests.test_printFingerprintsha256sQ ;;=%%&78h/JK  KK " F r"c"|j}t|jt|j t 5}t |ddddd|jdjjdy#1swY3xYw)zx L{printFigerprint} raises C{keys.BadFingerprintFormat} when unsupported formats are requested. rnrtNror) r<r rvr rZrrrBrprK)r0rJrqs r )test_printFingerprintBadFingerPrintFormatz5KeyGenTests.test_printFingerprintBadFingerPrintFormats| ;;=%%&78   3 4 M (lK L M  8",,:K:KA:N  M Ms BBc|j}t|dzjtt |dd|j |j jdy)zn L{printFingerprint} checks if the filename with the '.pub' suffix exists in ~/.ssh. r7rcrtz>2048 85:25:04:32:58:55:96:9f:57:ee:fb:a8:1a:ea:69:da temp.pub Nrurxs r #test_printFingerprintSuffixAppendedz/KeyGenTests.test_printFingerprintSuffixAppendedsV ;;=F"#../@Ah)DE  KK " M r"c`t|j}|j|jdj}t j t}t||ddd|j|jjd|d|d|j|j |jdjdd||jt j |jd j|jy) 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 passphrasercrJpassrb&Your identification has been saved in # Your public key has been saved in m.pub The key fingerprint in is: 85:25:04:32:58:55:96:9f:57:ee:fb:a8:1a:ea:69:da N id_rsa.pubr r<makedirschildpathr fromStringr rrBr,rw getContentpublicr0baserJkeys r test_saveKeyzKeyGenTests.test_saveKeys  & ::h',,nn/08\YWX  KK "DLX W   NN4::h/::
>@ A3::< r"c`t|j}|j|jdj}t j t}t||ddd|j|jjd|d|d|j|j |jdjdd||jt j |jd j|jy) 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_ecdsarrcrrrzm.pub The key fingerprint in is: 1e:ab:83:a6:f2:04:22:99:7c:64:14:d2:ab:fa:f5:16 Nz id_ecdsa.pub)r r<rrrrrrrrBr,rwrrrs r test_saveKeyECDSAzKeyGenTests.test_saveKeyECDSAs  & ::j)..nn128\YWX  KK "DLX W   NN4::j1<<>l SUX   NN4::n5@@B CSZZ\ r"c`t|j}|j|jdj}t j t}t||ddd|j|jjd|d|d|j|j |jdjdd||jt j |jd j|jy) 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_ed25519rrcrrrzm.pub The key fingerprint in is: ab:ee:c8:ed:e5:01:1b:45:b7:8d:b2:f0:8f:61:1c:14 Nzid_ed25519.pub)r r<rrrrrrrrBr,rwrrrs r test_saveKeyEd25519zKeyGenTests.test_saveKeyEd25519s  & ::l+00nn788\YWX  KK "DLX W   NN4::l3>>@$ U    NN4::&67BBD Eszz| r"c`t|j}|j|jdj}t j t}t||ddd|j|jjd|d|d|j|j |jdjdd||jt j |jd j|jy) z L{_saveKey} will generate key fingerprint in L{FingerprintFormats.SHA256-BASE64} format if explicitly specified. rrrjrrrzp.pub The key fingerprint in is: FBTCOoknq0mHy+kpfnY9tDdcAJuWtCpuQMaV3EsvbUI= Nrrrs r test_saveKeysha256zKeyGenTests.test_saveKeysha256s  & ::h',,nn/0 h X   KK "AI( T   NN4::h/::
>@ A3::< r"ct|j}|j|jdj}t j t}|jt5}t||dddddd|jdjjdy#1swY3xYw)zq L{_saveKey} raises C{keys.BadFingerprintFormat} when unsupported formats are requested. rrrnrNror)r r<rrrrrr rZrrrBrprK)r0rrJrrqs r test_saveKeyBadFingerPrintformatz,KeyGenTests.test_saveKeyBadFingerPrintformat9s  & ::h',,nn/0   3 4  %||T    8",,:K:KA:N   s 3B66B?c^t|j}|j|jdj}t j t}t||ddd|j|j |jdjdd|y)q L{_saveKey} will choose an empty string for the passphrase if no-passphrase is C{True}. rTrcrJz no-passphraserbNr") r r<rrrrrr rrBrrs r test_saveKeyEmptyPassphrasez'KeyGenTests.test_saveKeyEmptyPassphraseKs  & ::h',,nn/0 hS   NN4::h/::(I(I(KTRTWXr"c\t|j}|j|jdj}t j t}t||ddd|j|j |jdjd|y)rrTrcrN) r r<rrrrrrrrBrrs r "test_saveKeyEd25519EmptyPassphrasez.KeyGenTests.test_saveKeyEd25519EmptyPassphraseis  & ::l+00nn78 hS   NN4::l3>>@$ G r"ct|j}|j|jdjgddl}d fd }|j |jjjdfdtjt}t|dddd ||jdj}|j|dd }|j||y) zd When no path is specified, it will ask for the path used to store the key. custom_keyrNc(jdyNappendrK input_promptss r mock_inputz6KeyGenTests.test_saveKeyNoFilename..mock_input   $r"_inputSaveFilecSr)rkeyPaths r z4KeyGenTests.test_saveKeyNoFilename..sgr"Trcrr"rKr#r$r%)r r<rrrtwisted.conch.scripts.ckeygenr-conchscriptsr3rrr rrrB) r0rtwistedrrpersistedKeyContent persistedKeyrrs @@r test_saveKeyNoFilenamez"KeyGenTests.test_saveKeyNoFilenameys  & **\*//#% ,  7==((002BDUVnn/0  J  #jj6AAC~~&94E  l+r"cPd d}t|j}|j|jdj}|j t jddtjt}|ddd}|jtt|||y) zs When the specified file exists, it will ask the user for confirmation before overwriting. cdgS)Nnr)rKs r rz6KeyGenTests.test_saveKeyFileExists..mock_inputs 5Lr"rexistscy)NTr)rs r rz4KeyGenTests.test_saveKeyFileExists..r"TrcrN)rKr#r$z list[str]) r r<rrrr-r]rrr rZ SystemExitr)r0rrrrrgs r test_saveKeyFileExistsz"KeyGenTests.test_saveKeyFileExistss{   & **\*// 277Hn5nn/0&S *hWjIr"ct|j}|j|jdj}t j t}t||dddd|j|jjd|d|d|jdj}|j|j |d d||j|jd |jt j |jd j|jy ) zi L{_saveKey} can be told to write the new private key file in OpenSSH v1 format. rrrcrQ)rJrrbprivate-key-subtyperrrN$-----BEGIN OPENSSH PRIVATE KEY----- r)r r<rrrrrr rrBr,rwrrE startswithr)r0rrJrprivateKeyContents r test_saveKeySubtypeV1z!KeyGenTests.test_saveKeySubtypeV1s  & ::h',,nn/0 $$#'+     KK "DLX W !JJx0;;= (94NPST   ( ()Q R   NN4::l3>>@ A3::< r"c^|j}tjt}t |j t td|i|jjjdjd}|j||jdy)zl L{displayPublicKey} prints out the public key associated with a given private key. rJ asciiopensshN)r<rrr r rvr rr,rwstripencoderBtoStringr0rJrM displayeds r test_displayPublicKeyz!KeyGenTests.test_displayPublicKeys ;;= 12%%&89*h/0KK((*006==gF  FOOI$>?r"c`|j}tjt}t |j t t|dd|jjjdjd}|j||jdy)z L{displayPublicKey} prints out the public key associated with a given private key using the given passphrase when it's encrypted. encryptedrJrrrrN)r<rrr r rvr rr,rwrrrBrrs r test_displayPublicKeyEncryptedz*KeyGenTests.test_displayPublicKeyEncrypteds ;;= 12%%&BCh DEKK((*006==gF  FOOI$>?r"c|j}tjt}t |j t |jtddtd|i|jjjdjd}|j||jdy)z L{displayPublicKey} prints out the public key associated with a given private key, asking for the passphrase when it's encrypted. getpasscy)Nrr)xs r rzLKeyGenTests.test_displayPublicKeyEncryptedPassphrasePrompt..rr"rJrrrN)r<rrr r rvr r-rrr,rwrrrBrrs r .test_displayPublicKeyEncryptedPassphrasePromptz:KeyGenTests.test_displayPublicKeyEncryptedPassphrasePrompts ;;= 12%%&BC 7I'<=*h/0KK((*006==gF  FOOI$>?r"c|j}t|jt|j t t |ddy)z L{displayPublicKey} fails with a L{BadKeyError} when trying to decrypt an encrypted key with the wrong password. wrongrN)r<r rvr rZrrrxs r $test_displayPublicKeyWrongPassphrasez0KeyGenTests.test_displayPublicKeyWrongPassphrases> ;;=%%&BC  )'+R r"ctddd}|jtd||j}t |j t td|i|j|jjjdd|jt t |jy)zt L{changePassPhrase} allows a user to change the passphrase of a private key interactively. rnewpassrrJr;Your identification has been saved with the new passphrase.Nr(r-rr<r rvr rrBr,rwrassertNotEqualr)r0 oldNewConfirmrJs r test_changePassphrasez!KeyGenTests.test_changePassphrases $KIF  7I}5;;=%%&BC*h/0  KK " ( ( . I   ((8*<*G*G*I r"ctdd}|jtd||j}t |j t t|dd|j|jjjdd|jt t |jy)z L{changePassPhrase} allows a user to change the passphrase of a private key, providing the old passphrase and prompting for new one. rrrrrrNr)r0 newConfirmrJs r test_changePassphraseWithOldz(KeyGenTests.test_changePassphraseWithOld s !I6  7Iz2;;=%%&BCh DE  KK " ( ( . I   ((8*<*G*G*I r"cJ|j}t|jtt |ddd|j |j jjdd|jtt|jy)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)rJrrrrN) r<r rvr rrBr,rwrrrrxs r test_changePassphraseWithBothz)KeyGenTests.test_changePassphraseWithBoth!s ;;=%%&BC!;< P   KK " ( ( . I   ((8*<*G*G*I r"c,|j}t|jt|j t t |dd}|jdt||jtt|jy)z L{changePassPhrase} exits if passed an invalid old passphrase when trying to change the passphrase of a private key. rrz1Could not change passphrase: old passphrase errorN) r<r rvr rZrrrBr%rr0rJerrors r $test_changePassphraseWrongPassphrasez0KeyGenTests.test_changePassphraseWrongPassphrase4sz ;;=%%&BC!! (x*Q   ?U  5x7I7T7T7VWr"cj|jtdtd|j}t |j t |jttd|i}|jdt||jt t |jy)z L{changePassPhrase} exits if no passphrase is specified for the C{getpass} call and the key is encrypted. rrrJzMCould not change passphrase: Passphrase must be provided for an encrypted keyN) r-rr(r<r rvr rZrrrBr%rrs r !test_changePassphraseEmptyGetPassz-KeyGenTests.test_changePassphraseEmptyGetPassCs 7I{27;;=%%&BC!!*.>X@VW  # J 5x7I7T7T7VWr"c|j}t|jd|jtt d|i}d}|j |t||j dt|jy)zc L{changePassPhrase} exits if the file specified points to an invalid key. sfoobarrJz?Could not change passphrase: cannot guess the type of b'foobar'N) r<r rvrZrrrBr%r)r0rJrexpecteds r test_changePassphraseBadKeyz'KeyGenTests.test_changePassphraseBadKeySss ;;=%%i0!!*.>X@VWW 3u:. HX$6$A$A$CDr"cb|j}t|jtdd}|j t d||j tt|dd}|jdt||jtt|jy)z L{changePassPhrase} doesn't modify the key file if an unexpected error happens when trying to create the key with the new passphrase. ctd)Noops) RuntimeErrorrKkwargss r rz>KeyGenTests.test_changePassphraseCreateError..toStringhs v& &r"rrrJrz!Could not change passphrase: oopsN)rKr#rr#r$r r<r rvr r-rrZrrrBr%r)r0rJrrs r test_changePassphraseCreateErrorz,KeyGenTests.test_changePassphraseCreateError`s ;;=%%&89 ' 3 H-!!  !l ;  .toStringsr"rrrz9Could not change passphrase: cannot guess the type of b''N)rKr#rr#r$r%r)r0rJrrrs r %test_changePassphraseEmptyStringErrorz1KeyGenTests.test_changePassphraseEmptyStringErrorws ;;=%%&89  3 H-!!  !l ;  O 3u:. +Xh-?-J-J-LMr"c,|j}t|jt|j t t |dd}|jdt||jtt|jy)z L{changePassPhrase} exits when trying to change the passphrase on a public key, and doesn't change the file. rrz.Could not change passphrase: key not encryptedN) r<r rvr rZrrrBr%rrs r test_changePassphrasePublicKeyz*KeyGenTests.test_changePassphrasePublicKeysv ;;=%%&78!! (xF*S  I3u:V *HX,>,I,I,KLr"ctddd}|jtd||j}t |j t t|dd|j|jjjddt |j}|jt ||j|jdy ) zq L{changePassPhrase} can be told to write the new private key file in OpenSSH v1 format. rrrrQ)rJrrrrN)r(r-rr<r rvr rrBr,rwrrrrEr)r0rrJrs r test_changePassphraseSubtypeV1z*KeyGenTests.test_changePassphraseSubtypeV1s $KIF  7I}5;;=%%&BChtLM  KK " ( ( . I %X.99; 8:KL   ( ()Q R r"c8gdfd }ddi}t||}|j|dd|j|jtj j dd|jdt|jdgy) zg L{options} will default to "~/.ssh/id_rsa" if the user doesn't specify a key. c(jdyrrrs r rz5KeyGenTests.test_useDefaultForKey..mock_inputrr"rJrz.sshrNr)rrBrEendswithr]rjoinlen)r0rrgrJrs @r test_useDefaultForKeyz!KeyGenTests.test_useDefaultForKeys $&  r"#GZ8  J   ))"'',,vx*HIJ C ./ "}-r"cddi}|jtt|}|jd|jdy)z Ensure FileNotFoundError is handled, whether the user has supplied a bad path, or has no key at the default path. rJ/foo/bar+could not be opened, please specify a file.rN)rZrrassertInrKr0rgexcs r 'test_displayPublicKeyHandleFileNotFoundz3KeyGenTests.test_displayPublicKeyHandleFileNotFounds: z* ,r.iortypingrrtwisted.conch.test.keydatarrr r r twisted.python.filepathr twisted.python.reflectr twisted.trial.unittestrrrrrrrrtwisted.conch.ssh.keysrrrrskipr(r*rr"r r*su#  -0+  ED$_ R(_ Rr"