test_transport.py 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for ssh/transport.py and the classes therein.
  5. """
  6. from __future__ import absolute_import, division
  7. import struct
  8. import binascii
  9. try:
  10. import pyasn1
  11. except ImportError:
  12. pyasn1 = None
  13. try:
  14. import cryptography
  15. except ImportError:
  16. cryptography = None
  17. if pyasn1 is not None and cryptography is not None:
  18. dependencySkip = None
  19. from twisted.conch.ssh import transport, keys, factory
  20. from twisted.conch.test import keydata
  21. from cryptography.hazmat.backends import default_backend
  22. from cryptography.hazmat.primitives.asymmetric import ec
  23. from cryptography.exceptions import UnsupportedAlgorithm
  24. else:
  25. if pyasn1 is None:
  26. dependencySkip = "Cannot run without PyASN1"
  27. elif cryptography is None:
  28. dependencySkip = "can't run without cryptography"
  29. class transport: # fictional modules to make classes work
  30. class SSHTransportBase: pass
  31. class SSHServerTransport: pass
  32. class SSHClientTransport: pass
  33. class factory:
  34. class SSHFactory:
  35. pass
  36. from hashlib import md5, sha1, sha256, sha384, sha512
  37. from twisted.trial import unittest
  38. from twisted.internet import defer
  39. from twisted.protocols import loopback
  40. from twisted.python import randbytes
  41. from twisted.python.randbytes import insecureRandom
  42. from twisted.python.compat import iterbytes, _bytesChr as chr
  43. from twisted.conch.ssh import address, service, common, _kex
  44. from twisted.test import proto_helpers
  45. from twisted.conch.error import ConchError
  46. class MockTransportBase(transport.SSHTransportBase):
  47. """
  48. A base class for the client and server protocols. Stores the messages
  49. it receives instead of ignoring them.
  50. @ivar errors: a list of tuples: (reasonCode, description)
  51. @ivar unimplementeds: a list of integers: sequence number
  52. @ivar debugs: a list of tuples: (alwaysDisplay, message, lang)
  53. @ivar ignoreds: a list of strings: ignored data
  54. """
  55. def connectionMade(self):
  56. """
  57. Set up instance variables.
  58. """
  59. transport.SSHTransportBase.connectionMade(self)
  60. self.errors = []
  61. self.unimplementeds = []
  62. self.debugs = []
  63. self.ignoreds = []
  64. self.gotUnsupportedVersion = None
  65. def _unsupportedVersionReceived(self, remoteVersion):
  66. """
  67. Intercept unsupported version call.
  68. @type remoteVersion: L{str}
  69. """
  70. self.gotUnsupportedVersion = remoteVersion
  71. return transport.SSHTransportBase._unsupportedVersionReceived(
  72. self, remoteVersion)
  73. def receiveError(self, reasonCode, description):
  74. """
  75. Store any errors received.
  76. @type reasonCode: L{int}
  77. @type description: L{str}
  78. """
  79. self.errors.append((reasonCode, description))
  80. def receiveUnimplemented(self, seqnum):
  81. """
  82. Store any unimplemented packet messages.
  83. @type seqnum: L{int}
  84. """
  85. self.unimplementeds.append(seqnum)
  86. def receiveDebug(self, alwaysDisplay, message, lang):
  87. """
  88. Store any debug messages.
  89. @type alwaysDisplay: L{bool}
  90. @type message: L{str}
  91. @type lang: L{str}
  92. """
  93. self.debugs.append((alwaysDisplay, message, lang))
  94. def ssh_IGNORE(self, packet):
  95. """
  96. Store any ignored data.
  97. @type packet: L{str}
  98. """
  99. self.ignoreds.append(packet)
  100. class MockCipher(object):
  101. """
  102. A mocked-up version of twisted.conch.ssh.transport.SSHCiphers.
  103. """
  104. outCipType = b'test'
  105. encBlockSize = 6
  106. inCipType = b'test'
  107. decBlockSize = 6
  108. inMACType = b'test'
  109. outMACType = b'test'
  110. verifyDigestSize = 1
  111. usedEncrypt = False
  112. usedDecrypt = False
  113. outMAC = (None, b'', b'', 1)
  114. inMAC = (None, b'', b'', 1)
  115. keys = ()
  116. def encrypt(self, x):
  117. """
  118. Called to encrypt the packet. Simply record that encryption was used
  119. and return the data unchanged.
  120. """
  121. self.usedEncrypt = True
  122. if (len(x) % self.encBlockSize) != 0:
  123. raise RuntimeError("length %i modulo blocksize %i is not 0: %i" %
  124. (len(x), self.encBlockSize, len(x) % self.encBlockSize))
  125. return x
  126. def decrypt(self, x):
  127. """
  128. Called to decrypt the packet. Simply record that decryption was used
  129. and return the data unchanged.
  130. """
  131. self.usedDecrypt = True
  132. if (len(x) % self.encBlockSize) != 0:
  133. raise RuntimeError("length %i modulo blocksize %i is not 0: %i" %
  134. (len(x), self.decBlockSize, len(x) % self.decBlockSize))
  135. return x
  136. def makeMAC(self, outgoingPacketSequence, payload):
  137. """
  138. Make a Message Authentication Code by sending the character value of
  139. the outgoing packet.
  140. """
  141. return chr(outgoingPacketSequence)
  142. def verify(self, incomingPacketSequence, packet, macData):
  143. """
  144. Verify the Message Authentication Code by checking that the packet
  145. sequence number is the same.
  146. """
  147. return chr(incomingPacketSequence) == macData
  148. def setKeys(self, ivOut, keyOut, ivIn, keyIn, macIn, macOut):
  149. """
  150. Record the keys.
  151. """
  152. self.keys = (ivOut, keyOut, ivIn, keyIn, macIn, macOut)
  153. class MockCompression:
  154. """
  155. A mocked-up compression, based on the zlib interface. Instead of
  156. compressing, it reverses the data and adds a 0x66 byte to the end.
  157. """
  158. def compress(self, payload):
  159. return payload[::-1] # reversed
  160. def decompress(self, payload):
  161. return payload[:-1][::-1]
  162. def flush(self, kind):
  163. return b'\x66'
  164. class MockService(service.SSHService):
  165. """
  166. A mocked-up service, based on twisted.conch.ssh.service.SSHService.
  167. @ivar started: True if this service has been started.
  168. @ivar stopped: True if this service has been stopped.
  169. """
  170. name = b"MockService"
  171. started = False
  172. stopped = False
  173. protocolMessages = {0xff: "MSG_TEST", 71: "MSG_fiction"}
  174. def logPrefix(self):
  175. return "MockService"
  176. def serviceStarted(self):
  177. """
  178. Record that the service was started.
  179. """
  180. self.started = True
  181. def serviceStopped(self):
  182. """
  183. Record that the service was stopped.
  184. """
  185. self.stopped = True
  186. def ssh_TEST(self, packet):
  187. """
  188. A message that this service responds to.
  189. """
  190. self.transport.sendPacket(0xff, packet)
  191. class MockFactory(factory.SSHFactory):
  192. """
  193. A mocked-up factory based on twisted.conch.ssh.factory.SSHFactory.
  194. """
  195. services = {
  196. b'ssh-userauth': MockService}
  197. def getPublicKeys(self):
  198. """
  199. Return the public keys that authenticate this server.
  200. """
  201. return {
  202. b'ssh-rsa': keys.Key.fromString(keydata.publicRSA_openssh),
  203. b'ssh-dsa': keys.Key.fromString(keydata.publicDSA_openssh)}
  204. def getPrivateKeys(self):
  205. """
  206. Return the private keys that authenticate this server.
  207. """
  208. return {
  209. b'ssh-rsa': keys.Key.fromString(keydata.privateRSA_openssh),
  210. b'ssh-dsa': keys.Key.fromString(keydata.privateDSA_openssh)}
  211. def getPrimes(self):
  212. """
  213. Diffie-Hellman primes that can be used for key exchange algorithms
  214. that use group exchange to establish a prime / generator group.
  215. @return: The primes and generators.
  216. @rtype: L{dict} mapping the key size to a C{list} of
  217. C{(generator, prime)} tuple.
  218. """
  219. # In these tests, we hardwire the prime values to those defined by the
  220. # diffie-hellman-group14-sha1 key exchange algorithm, to avoid requiring
  221. # a moduli file when running tests.
  222. # See OpenSSHFactory.getPrimes.
  223. return {
  224. 2048: ((3, _kex.getDHGeneratorAndPrime(
  225. b'diffie-hellman-group14-sha1')[1]),),
  226. 4096: ((5, 7),)}
  227. class MockOldFactoryPublicKeys(MockFactory):
  228. """
  229. The old SSHFactory returned mappings from key names to strings from
  230. getPublicKeys(). We return those here for testing.
  231. """
  232. def getPublicKeys(self):
  233. """
  234. We used to map key types to public key blobs as strings.
  235. """
  236. keys = MockFactory.getPublicKeys(self)
  237. for name, key in keys.items()[:]:
  238. keys[name] = key.blob()
  239. return keys
  240. class MockOldFactoryPrivateKeys(MockFactory):
  241. """
  242. The old SSHFactory returned mappings from key names to cryptography key
  243. objects from getPrivateKeys(). We return those here for testing.
  244. """
  245. def getPrivateKeys(self):
  246. """
  247. We used to map key types to cryptography key objects.
  248. """
  249. keys = MockFactory.getPrivateKeys(self)
  250. for name, key in keys.items()[:]:
  251. keys[name] = key.keyObject
  252. return keys
  253. class TransportTestCase(unittest.TestCase):
  254. """
  255. Base class for transport test cases.
  256. """
  257. klass = None
  258. if dependencySkip:
  259. skip = dependencySkip
  260. def setUp(self):
  261. self.transport = proto_helpers.StringTransport()
  262. self.proto = self.klass()
  263. self.packets = []
  264. def secureRandom(len):
  265. """
  266. Return a consistent entropy value
  267. """
  268. return b'\x99' * len
  269. self.patch(randbytes, 'secureRandom', secureRandom)
  270. def stubSendPacket(messageType, payload):
  271. self.packets.append((messageType, payload))
  272. self.proto.makeConnection(self.transport)
  273. # we just let the kex packet go into the transport
  274. self.proto.sendPacket = stubSendPacket
  275. def finishKeyExchange(self, proto):
  276. """
  277. Deliver enough additional messages to C{proto} so that the key exchange
  278. which is started in L{SSHTransportBase.connectionMade} completes and
  279. non-key exchange messages can be sent and received.
  280. """
  281. proto.dataReceived(b"SSH-2.0-BogoClient-1.2i\r\n")
  282. proto.dispatchMessage(
  283. transport.MSG_KEXINIT, self._A_KEXINIT_MESSAGE)
  284. proto._keySetup(b"foo", b"bar")
  285. # SSHTransportBase can't handle MSG_NEWKEYS, or it would be the right
  286. # thing to deliver next. _newKeys won't work either, because
  287. # sendKexInit (probably) hasn't been called. sendKexInit is
  288. # responsible for setting up certain state _newKeys relies on. So,
  289. # just change the key exchange state to what it would be when key
  290. # exchange is finished.
  291. proto._keyExchangeState = proto._KEY_EXCHANGE_NONE
  292. def simulateKeyExchange(self, sharedSecret, exchangeHash):
  293. """
  294. Finish a key exchange by calling C{_keySetup} with the given arguments.
  295. Also do extra whitebox stuff to satisfy that method's assumption that
  296. some kind of key exchange has actually taken place.
  297. """
  298. self.proto._keyExchangeState = self.proto._KEY_EXCHANGE_REQUESTED
  299. self.proto._blockedByKeyExchange = []
  300. self.proto._keySetup(sharedSecret, exchangeHash)
  301. class DHGroupExchangeSHA1Mixin:
  302. """
  303. Mixin for diffie-hellman-group-exchange-sha1 tests.
  304. """
  305. kexAlgorithm = b'diffie-hellman-group-exchange-sha1'
  306. hashProcessor = sha1
  307. class DHGroupExchangeSHA256Mixin:
  308. """
  309. Mixin for diffie-hellman-group-exchange-sha256 tests.
  310. """
  311. kexAlgorithm = b'diffie-hellman-group-exchange-sha256'
  312. hashProcessor = sha256
  313. class ECDHMixin:
  314. """
  315. Mixin for elliptic curve diffie-hellman tests.
  316. """
  317. kexAlgorithm = b'ecdh-sha2-nistp256'
  318. hashProcessor = sha256
  319. class BaseSSHTransportBaseCase:
  320. """
  321. Base case for TransportBase tests.
  322. """
  323. klass = MockTransportBase
  324. class BaseSSHTransportTests(BaseSSHTransportBaseCase, TransportTestCase):
  325. """
  326. Test TransportBase. It implements the non-server/client specific
  327. parts of the SSH transport protocol.
  328. """
  329. _A_KEXINIT_MESSAGE = (
  330. b"\xAA" * 16 +
  331. common.NS(b'diffie-hellman-group14-sha1') +
  332. common.NS(b'ssh-rsa') +
  333. common.NS(b'aes256-ctr') +
  334. common.NS(b'aes256-ctr') +
  335. common.NS(b'hmac-sha1') +
  336. common.NS(b'hmac-sha1') +
  337. common.NS(b'none') +
  338. common.NS(b'none') +
  339. common.NS(b'') +
  340. common.NS(b'') +
  341. b'\x00' + b'\x00\x00\x00\x00')
  342. def test_sendVersion(self):
  343. """
  344. Test that the first thing sent over the connection is the version
  345. string.
  346. """
  347. # the other setup was done in the setup method
  348. self.assertEqual(self.transport.value().split(b'\r\n', 1)[0],
  349. b"SSH-2.0-Twisted")
  350. def test_sendPacketPlain(self):
  351. """
  352. Test that plain (unencrypted, uncompressed) packets are sent
  353. correctly. The format is::
  354. uint32 length (including type and padding length)
  355. byte padding length
  356. byte type
  357. bytes[length-padding length-2] data
  358. bytes[padding length] padding
  359. """
  360. proto = MockTransportBase()
  361. proto.makeConnection(self.transport)
  362. self.finishKeyExchange(proto)
  363. self.transport.clear()
  364. message = ord('A')
  365. payload = b'BCDEFG'
  366. proto.sendPacket(message, payload)
  367. value = self.transport.value()
  368. self.assertEqual(value, b'\x00\x00\x00\x0c\x04ABCDEFG\x99\x99\x99\x99')
  369. def test_sendPacketEncrypted(self):
  370. """
  371. Test that packets sent while encryption is enabled are sent
  372. correctly. The whole packet should be encrypted.
  373. """
  374. proto = MockTransportBase()
  375. proto.makeConnection(self.transport)
  376. self.finishKeyExchange(proto)
  377. proto.currentEncryptions = testCipher = MockCipher()
  378. message = ord('A')
  379. payload = b'BC'
  380. self.transport.clear()
  381. proto.sendPacket(message, payload)
  382. self.assertTrue(testCipher.usedEncrypt)
  383. value = self.transport.value()
  384. self.assertEqual(
  385. value,
  386. # Four byte length prefix
  387. b'\x00\x00\x00\x08'
  388. # One byte padding length
  389. b'\x04'
  390. # The actual application data
  391. b'ABC'
  392. # "Random" padding - see the secureRandom monkeypatch in setUp
  393. b'\x99\x99\x99\x99'
  394. # The MAC
  395. b'\x02')
  396. def test_sendPacketCompressed(self):
  397. """
  398. Test that packets sent while compression is enabled are sent
  399. correctly. The packet type and data should be encrypted.
  400. """
  401. proto = MockTransportBase()
  402. proto.makeConnection(self.transport)
  403. self.finishKeyExchange(proto)
  404. proto.outgoingCompression = MockCompression()
  405. self.transport.clear()
  406. proto.sendPacket(ord('A'), b'B')
  407. value = self.transport.value()
  408. self.assertEqual(
  409. value,
  410. b'\x00\x00\x00\x0c\x08BA\x66\x99\x99\x99\x99\x99\x99\x99\x99')
  411. def test_sendPacketBoth(self):
  412. """
  413. Test that packets sent while compression and encryption are
  414. enabled are sent correctly. The packet type and data should be
  415. compressed and then the whole packet should be encrypted.
  416. """
  417. proto = MockTransportBase()
  418. proto.makeConnection(self.transport)
  419. self.finishKeyExchange(proto)
  420. proto.currentEncryptions = testCipher = MockCipher()
  421. proto.outgoingCompression = MockCompression()
  422. message = ord('A')
  423. payload = b'BC'
  424. self.transport.clear()
  425. proto.sendPacket(message, payload)
  426. self.assertTrue(testCipher.usedEncrypt)
  427. value = self.transport.value()
  428. self.assertEqual(
  429. value,
  430. # Four byte length prefix
  431. b'\x00\x00\x00\x0e'
  432. # One byte padding length
  433. b'\x09'
  434. # Compressed application data
  435. b'CBA\x66'
  436. # "Random" padding - see the secureRandom monkeypatch in setUp
  437. b'\x99\x99\x99\x99\x99\x99\x99\x99\x99'
  438. # The MAC
  439. b'\x02')
  440. def test_getPacketPlain(self):
  441. """
  442. Test that packets are retrieved correctly out of the buffer when
  443. no encryption is enabled.
  444. """
  445. proto = MockTransportBase()
  446. proto.makeConnection(self.transport)
  447. self.finishKeyExchange(proto)
  448. self.transport.clear()
  449. proto.sendPacket(ord('A'), b'BC')
  450. proto.buf = self.transport.value() + b'extra'
  451. self.assertEqual(proto.getPacket(), b'ABC')
  452. self.assertEqual(proto.buf, b'extra')
  453. def test_getPacketEncrypted(self):
  454. """
  455. Test that encrypted packets are retrieved correctly.
  456. See test_sendPacketEncrypted.
  457. """
  458. proto = MockTransportBase()
  459. proto.sendKexInit = lambda: None # don't send packets
  460. proto.makeConnection(self.transport)
  461. self.transport.clear()
  462. proto.currentEncryptions = testCipher = MockCipher()
  463. proto.sendPacket(ord('A'), b'BCD')
  464. value = self.transport.value()
  465. proto.buf = value[:MockCipher.decBlockSize]
  466. self.assertIsNone(proto.getPacket())
  467. self.assertTrue(testCipher.usedDecrypt)
  468. self.assertEqual(proto.first, b'\x00\x00\x00\x0e\x09A')
  469. proto.buf += value[MockCipher.decBlockSize:]
  470. self.assertEqual(proto.getPacket(), b'ABCD')
  471. self.assertEqual(proto.buf, b'')
  472. def test_getPacketCompressed(self):
  473. """
  474. Test that compressed packets are retrieved correctly. See
  475. test_sendPacketCompressed.
  476. """
  477. proto = MockTransportBase()
  478. proto.makeConnection(self.transport)
  479. self.finishKeyExchange(proto)
  480. self.transport.clear()
  481. proto.outgoingCompression = MockCompression()
  482. proto.incomingCompression = proto.outgoingCompression
  483. proto.sendPacket(ord('A'), b'BCD')
  484. proto.buf = self.transport.value()
  485. self.assertEqual(proto.getPacket(), b'ABCD')
  486. def test_getPacketBoth(self):
  487. """
  488. Test that compressed and encrypted packets are retrieved correctly.
  489. See test_sendPacketBoth.
  490. """
  491. proto = MockTransportBase()
  492. proto.sendKexInit = lambda: None
  493. proto.makeConnection(self.transport)
  494. self.transport.clear()
  495. proto.currentEncryptions = MockCipher()
  496. proto.outgoingCompression = MockCompression()
  497. proto.incomingCompression = proto.outgoingCompression
  498. proto.sendPacket(ord('A'), b'BCDEFG')
  499. proto.buf = self.transport.value()
  500. self.assertEqual(proto.getPacket(), b'ABCDEFG')
  501. def test_ciphersAreValid(self):
  502. """
  503. Test that all the supportedCiphers are valid.
  504. """
  505. ciphers = transport.SSHCiphers(b'A', b'B', b'C', b'D')
  506. iv = key = b'\x00' * 16
  507. for cipName in self.proto.supportedCiphers:
  508. self.assertTrue(ciphers._getCipher(cipName, iv, key))
  509. def test_sendKexInit(self):
  510. """
  511. Test that the KEXINIT (key exchange initiation) message is sent
  512. correctly. Payload::
  513. bytes[16] cookie
  514. string key exchange algorithms
  515. string public key algorithms
  516. string outgoing ciphers
  517. string incoming ciphers
  518. string outgoing MACs
  519. string incoming MACs
  520. string outgoing compressions
  521. string incoming compressions
  522. bool first packet follows
  523. uint32 0
  524. """
  525. value = self.transport.value().split(b'\r\n', 1)[1]
  526. self.proto.buf = value
  527. packet = self.proto.getPacket()
  528. self.assertEqual(packet[0:1], chr(transport.MSG_KEXINIT))
  529. self.assertEqual(packet[1:17], b'\x99' * 16)
  530. (keyExchanges, pubkeys, ciphers1, ciphers2, macs1, macs2,
  531. compressions1, compressions2, languages1, languages2,
  532. buf) = common.getNS(packet[17:], 10)
  533. self.assertEqual(
  534. keyExchanges, b','.join(self.proto.supportedKeyExchanges))
  535. self.assertEqual(pubkeys, b','.join(self.proto.supportedPublicKeys))
  536. self.assertEqual(ciphers1, b','.join(self.proto.supportedCiphers))
  537. self.assertEqual(ciphers2, b','.join(self.proto.supportedCiphers))
  538. self.assertEqual(macs1, b','.join(self.proto.supportedMACs))
  539. self.assertEqual(macs2, b','.join(self.proto.supportedMACs))
  540. self.assertEqual(compressions1,
  541. b','.join(self.proto.supportedCompressions))
  542. self.assertEqual(compressions2,
  543. b','.join(self.proto.supportedCompressions))
  544. self.assertEqual(languages1, b','.join(self.proto.supportedLanguages))
  545. self.assertEqual(languages2, b','.join(self.proto.supportedLanguages))
  546. self.assertEqual(buf, b'\x00' * 5)
  547. def test_receiveKEXINITReply(self):
  548. """
  549. Immediately after connecting, the transport expects a KEXINIT message
  550. and does not reply to it.
  551. """
  552. self.transport.clear()
  553. self.proto.dispatchMessage(
  554. transport.MSG_KEXINIT, self._A_KEXINIT_MESSAGE)
  555. self.assertEqual(self.packets, [])
  556. def test_sendKEXINITReply(self):
  557. """
  558. When a KEXINIT message is received which is not a reply to an earlier
  559. KEXINIT message which was sent, a KEXINIT reply is sent.
  560. """
  561. self.finishKeyExchange(self.proto)
  562. del self.packets[:]
  563. self.proto.dispatchMessage(
  564. transport.MSG_KEXINIT, self._A_KEXINIT_MESSAGE)
  565. self.assertEqual(len(self.packets), 1)
  566. self.assertEqual(self.packets[0][0], transport.MSG_KEXINIT)
  567. def test_sendKexInitTwiceFails(self):
  568. """
  569. A new key exchange cannot be started while a key exchange is already in
  570. progress. If an attempt is made to send a I{KEXINIT} message using
  571. L{SSHTransportBase.sendKexInit} while a key exchange is in progress
  572. causes that method to raise a L{RuntimeError}.
  573. """
  574. self.assertRaises(RuntimeError, self.proto.sendKexInit)
  575. def test_sendKexInitBlocksOthers(self):
  576. """
  577. After L{SSHTransportBase.sendKexInit} has been called, messages types
  578. other than the following are queued and not sent until after I{NEWKEYS}
  579. is sent by L{SSHTransportBase._keySetup}.
  580. RFC 4253, section 7.1.
  581. """
  582. # sendKexInit is called by connectionMade, which is called in setUp.
  583. # So we're in the state already.
  584. disallowedMessageTypes = [
  585. transport.MSG_SERVICE_REQUEST,
  586. transport.MSG_KEXINIT,
  587. ]
  588. # Drop all the bytes sent by setUp, they're not relevant to this test.
  589. self.transport.clear()
  590. # Get rid of the sendPacket monkey patch, we are testing the behavior
  591. # of sendPacket.
  592. del self.proto.sendPacket
  593. for messageType in disallowedMessageTypes:
  594. self.proto.sendPacket(messageType, b'foo')
  595. self.assertEqual(self.transport.value(), b"")
  596. self.finishKeyExchange(self.proto)
  597. # Make the bytes written to the transport cleartext so it's easier to
  598. # make an assertion about them.
  599. self.proto.nextEncryptions = MockCipher()
  600. # Pseudo-deliver the peer's NEWKEYS message, which should flush the
  601. # messages which were queued above.
  602. self.proto._newKeys()
  603. self.assertEqual(self.transport.value().count(b"foo"), 2)
  604. def test_sendDebug(self):
  605. """
  606. Test that debug messages are sent correctly. Payload::
  607. bool always display
  608. string debug message
  609. string language
  610. """
  611. self.proto.sendDebug(b"test", True, b'en')
  612. self.assertEqual(
  613. self.packets,
  614. [(transport.MSG_DEBUG,
  615. b"\x01\x00\x00\x00\x04test\x00\x00\x00\x02en")])
  616. def test_receiveDebug(self):
  617. """
  618. Test that debug messages are received correctly. See test_sendDebug.
  619. """
  620. self.proto.dispatchMessage(
  621. transport.MSG_DEBUG,
  622. b'\x01\x00\x00\x00\x04test\x00\x00\x00\x02en')
  623. self.assertEqual(self.proto.debugs, [(True, b'test', b'en')])
  624. def test_sendIgnore(self):
  625. """
  626. Test that ignored messages are sent correctly. Payload::
  627. string ignored data
  628. """
  629. self.proto.sendIgnore(b"test")
  630. self.assertEqual(
  631. self.packets, [(transport.MSG_IGNORE,
  632. b'\x00\x00\x00\x04test')])
  633. def test_receiveIgnore(self):
  634. """
  635. Test that ignored messages are received correctly. See
  636. test_sendIgnore.
  637. """
  638. self.proto.dispatchMessage(transport.MSG_IGNORE, b'test')
  639. self.assertEqual(self.proto.ignoreds, [b'test'])
  640. def test_sendUnimplemented(self):
  641. """
  642. Test that unimplemented messages are sent correctly. Payload::
  643. uint32 sequence number
  644. """
  645. self.proto.sendUnimplemented()
  646. self.assertEqual(
  647. self.packets, [(transport.MSG_UNIMPLEMENTED,
  648. b'\x00\x00\x00\x00')])
  649. def test_receiveUnimplemented(self):
  650. """
  651. Test that unimplemented messages are received correctly. See
  652. test_sendUnimplemented.
  653. """
  654. self.proto.dispatchMessage(transport.MSG_UNIMPLEMENTED,
  655. b'\x00\x00\x00\xff')
  656. self.assertEqual(self.proto.unimplementeds, [255])
  657. def test_sendDisconnect(self):
  658. """
  659. Test that disconnection messages are sent correctly. Payload::
  660. uint32 reason code
  661. string reason description
  662. string language
  663. """
  664. disconnected = [False]
  665. def stubLoseConnection():
  666. disconnected[0] = True
  667. self.transport.loseConnection = stubLoseConnection
  668. self.proto.sendDisconnect(0xff, b"test")
  669. self.assertEqual(
  670. self.packets,
  671. [(transport.MSG_DISCONNECT,
  672. b"\x00\x00\x00\xff\x00\x00\x00\x04test\x00\x00\x00\x00")])
  673. self.assertTrue(disconnected[0])
  674. def test_receiveDisconnect(self):
  675. """
  676. Test that disconnection messages are received correctly. See
  677. test_sendDisconnect.
  678. """
  679. disconnected = [False]
  680. def stubLoseConnection():
  681. disconnected[0] = True
  682. self.transport.loseConnection = stubLoseConnection
  683. self.proto.dispatchMessage(transport.MSG_DISCONNECT,
  684. b'\x00\x00\x00\xff\x00\x00\x00\x04test')
  685. self.assertEqual(self.proto.errors, [(255, b'test')])
  686. self.assertTrue(disconnected[0])
  687. def test_dataReceived(self):
  688. """
  689. Test that dataReceived parses packets and dispatches them to
  690. ssh_* methods.
  691. """
  692. kexInit = [False]
  693. def stubKEXINIT(packet):
  694. kexInit[0] = True
  695. self.proto.ssh_KEXINIT = stubKEXINIT
  696. self.proto.dataReceived(self.transport.value())
  697. self.assertTrue(self.proto.gotVersion)
  698. self.assertEqual(self.proto.ourVersionString,
  699. self.proto.otherVersionString)
  700. self.assertTrue(kexInit[0])
  701. def test_service(self):
  702. """
  703. Test that the transport can set the running service and dispatches
  704. packets to the service's packetReceived method.
  705. """
  706. service = MockService()
  707. self.proto.setService(service)
  708. self.assertEqual(self.proto.service, service)
  709. self.assertTrue(service.started)
  710. self.proto.dispatchMessage(0xff, b"test")
  711. self.assertEqual(self.packets, [(0xff, b"test")])
  712. service2 = MockService()
  713. self.proto.setService(service2)
  714. self.assertTrue(service2.started)
  715. self.assertTrue(service.stopped)
  716. self.proto.connectionLost(None)
  717. self.assertTrue(service2.stopped)
  718. def test_avatar(self):
  719. """
  720. Test that the transport notifies the avatar of disconnections.
  721. """
  722. disconnected = [False]
  723. def logout():
  724. disconnected[0] = True
  725. self.proto.logoutFunction = logout
  726. self.proto.avatar = True
  727. self.proto.connectionLost(None)
  728. self.assertTrue(disconnected[0])
  729. def test_isEncrypted(self):
  730. """
  731. Test that the transport accurately reflects its encrypted status.
  732. """
  733. self.assertFalse(self.proto.isEncrypted('in'))
  734. self.assertFalse(self.proto.isEncrypted('out'))
  735. self.assertFalse(self.proto.isEncrypted('both'))
  736. self.proto.currentEncryptions = MockCipher()
  737. self.assertTrue(self.proto.isEncrypted('in'))
  738. self.assertTrue(self.proto.isEncrypted('out'))
  739. self.assertTrue(self.proto.isEncrypted('both'))
  740. self.proto.currentEncryptions = transport.SSHCiphers(b'none', b'none',
  741. b'none', b'none')
  742. self.assertFalse(self.proto.isEncrypted('in'))
  743. self.assertFalse(self.proto.isEncrypted('out'))
  744. self.assertFalse(self.proto.isEncrypted('both'))
  745. self.assertRaises(TypeError, self.proto.isEncrypted, 'bad')
  746. def test_isVerified(self):
  747. """
  748. Test that the transport accurately reflects its verified status.
  749. """
  750. self.assertFalse(self.proto.isVerified('in'))
  751. self.assertFalse(self.proto.isVerified('out'))
  752. self.assertFalse(self.proto.isVerified('both'))
  753. self.proto.currentEncryptions = MockCipher()
  754. self.assertTrue(self.proto.isVerified('in'))
  755. self.assertTrue(self.proto.isVerified('out'))
  756. self.assertTrue(self.proto.isVerified('both'))
  757. self.proto.currentEncryptions = transport.SSHCiphers(b'none', b'none',
  758. b'none', b'none')
  759. self.assertFalse(self.proto.isVerified('in'))
  760. self.assertFalse(self.proto.isVerified('out'))
  761. self.assertFalse(self.proto.isVerified('both'))
  762. self.assertRaises(TypeError, self.proto.isVerified, 'bad')
  763. def test_loseConnection(self):
  764. """
  765. Test that loseConnection sends a disconnect message and closes the
  766. connection.
  767. """
  768. disconnected = [False]
  769. def stubLoseConnection():
  770. disconnected[0] = True
  771. self.transport.loseConnection = stubLoseConnection
  772. self.proto.loseConnection()
  773. self.assertEqual(self.packets[0][0], transport.MSG_DISCONNECT)
  774. self.assertEqual(self.packets[0][1][3:4],
  775. chr(transport.DISCONNECT_CONNECTION_LOST))
  776. def test_badVersion(self):
  777. """
  778. Test that the transport disconnects when it receives a bad version.
  779. """
  780. def testBad(version):
  781. self.packets = []
  782. self.proto.gotVersion = False
  783. disconnected = [False]
  784. def stubLoseConnection():
  785. disconnected[0] = True
  786. self.transport.loseConnection = stubLoseConnection
  787. for c in iterbytes(version + b'\r\n'):
  788. self.proto.dataReceived(c)
  789. self.assertTrue(disconnected[0])
  790. self.assertEqual(self.packets[0][0], transport.MSG_DISCONNECT)
  791. self.assertEqual(
  792. self.packets[0][1][3:4],
  793. chr(transport.DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED))
  794. testBad(b'SSH-1.5-OpenSSH')
  795. testBad(b'SSH-3.0-Twisted')
  796. testBad(b'GET / HTTP/1.1')
  797. def test_dataBeforeVersion(self):
  798. """
  799. Test that the transport ignores data sent before the version string.
  800. """
  801. proto = MockTransportBase()
  802. proto.makeConnection(proto_helpers.StringTransport())
  803. data = (b"""here's some stuff beforehand
  804. here's some other stuff
  805. """ + proto.ourVersionString + b"\r\n")
  806. [proto.dataReceived(c) for c in iterbytes(data)]
  807. self.assertTrue(proto.gotVersion)
  808. self.assertEqual(proto.otherVersionString, proto.ourVersionString)
  809. def test_compatabilityVersion(self):
  810. """
  811. Test that the transport treats the compatibility version (1.99)
  812. as equivalent to version 2.0.
  813. """
  814. proto = MockTransportBase()
  815. proto.makeConnection(proto_helpers.StringTransport())
  816. proto.dataReceived(b"SSH-1.99-OpenSSH\n")
  817. self.assertTrue(proto.gotVersion)
  818. self.assertEqual(proto.otherVersionString, b"SSH-1.99-OpenSSH")
  819. def test_supportedVersionsAreAllowed(self):
  820. """
  821. If an unusual SSH version is received and is included in
  822. C{supportedVersions}, an unsupported version error is not emitted.
  823. """
  824. proto = MockTransportBase()
  825. proto.supportedVersions = (b"9.99", )
  826. proto.makeConnection(proto_helpers.StringTransport())
  827. proto.dataReceived(b"SSH-9.99-OpenSSH\n")
  828. self.assertFalse(proto.gotUnsupportedVersion)
  829. def test_unsupportedVersionsCallUnsupportedVersionReceived(self):
  830. """
  831. If an unusual SSH version is received and is not included in
  832. C{supportedVersions}, an unsupported version error is emitted.
  833. """
  834. proto = MockTransportBase()
  835. proto.supportedVersions = (b"2.0", )
  836. proto.makeConnection(proto_helpers.StringTransport())
  837. proto.dataReceived(b"SSH-9.99-OpenSSH\n")
  838. self.assertEqual(b"9.99", proto.gotUnsupportedVersion)
  839. def test_badPackets(self):
  840. """
  841. Test that the transport disconnects with an error when it receives
  842. bad packets.
  843. """
  844. def testBad(packet, error=transport.DISCONNECT_PROTOCOL_ERROR):
  845. self.packets = []
  846. self.proto.buf = packet
  847. self.assertIsNone(self.proto.getPacket())
  848. self.assertEqual(len(self.packets), 1)
  849. self.assertEqual(self.packets[0][0], transport.MSG_DISCONNECT)
  850. self.assertEqual(self.packets[0][1][3:4], chr(error))
  851. testBad(b'\xff' * 8) # big packet
  852. testBad(b'\x00\x00\x00\x05\x00BCDE') # length not modulo blocksize
  853. oldEncryptions = self.proto.currentEncryptions
  854. self.proto.currentEncryptions = MockCipher()
  855. testBad(b'\x00\x00\x00\x08\x06AB123456', # bad MAC
  856. transport.DISCONNECT_MAC_ERROR)
  857. self.proto.currentEncryptions.decrypt = lambda x: x[:-1]
  858. testBad(b'\x00\x00\x00\x08\x06BCDEFGHIJK') # bad decryption
  859. self.proto.currentEncryptions = oldEncryptions
  860. self.proto.incomingCompression = MockCompression()
  861. def stubDecompress(payload):
  862. raise Exception('bad compression')
  863. self.proto.incomingCompression.decompress = stubDecompress
  864. testBad(b'\x00\x00\x00\x04\x00BCDE', # bad decompression
  865. transport.DISCONNECT_COMPRESSION_ERROR)
  866. self.flushLoggedErrors()
  867. def test_unimplementedPackets(self):
  868. """
  869. Test that unimplemented packet types cause MSG_UNIMPLEMENTED packets
  870. to be sent.
  871. """
  872. seqnum = self.proto.incomingPacketSequence
  873. def checkUnimplemented(seqnum=seqnum):
  874. self.assertEqual(self.packets[0][0],
  875. transport.MSG_UNIMPLEMENTED)
  876. self.assertEqual(self.packets[0][1][3:4], chr(seqnum))
  877. self.proto.packets = []
  878. seqnum += 1
  879. self.proto.dispatchMessage(40, b'')
  880. checkUnimplemented()
  881. transport.messages[41] = b'MSG_fiction'
  882. self.proto.dispatchMessage(41, b'')
  883. checkUnimplemented()
  884. self.proto.dispatchMessage(60, b'')
  885. checkUnimplemented()
  886. self.proto.setService(MockService())
  887. self.proto.dispatchMessage(70, b'')
  888. checkUnimplemented()
  889. self.proto.dispatchMessage(71, b'')
  890. checkUnimplemented()
  891. def test_multipleClasses(self):
  892. """
  893. Test that multiple instances have distinct states.
  894. """
  895. proto = self.proto
  896. proto.dataReceived(self.transport.value())
  897. proto.currentEncryptions = MockCipher()
  898. proto.outgoingCompression = MockCompression()
  899. proto.incomingCompression = MockCompression()
  900. proto.setService(MockService())
  901. proto2 = MockTransportBase()
  902. proto2.makeConnection(proto_helpers.StringTransport())
  903. proto2.sendIgnore(b'')
  904. self.assertNotEqual(proto.gotVersion, proto2.gotVersion)
  905. self.assertNotEqual(proto.transport, proto2.transport)
  906. self.assertNotEqual(proto.outgoingPacketSequence,
  907. proto2.outgoingPacketSequence)
  908. self.assertNotEqual(proto.incomingPacketSequence,
  909. proto2.incomingPacketSequence)
  910. self.assertNotEqual(proto.currentEncryptions,
  911. proto2.currentEncryptions)
  912. self.assertNotEqual(proto.service, proto2.service)
  913. class BaseSSHTransportDHGroupExchangeBaseCase(BaseSSHTransportBaseCase):
  914. """
  915. Diffie-Hellman group exchange tests for TransportBase.
  916. """
  917. def test_getKey(self):
  918. """
  919. Test that _getKey generates the correct keys.
  920. """
  921. self.proto.kexAlg = self.kexAlgorithm
  922. self.proto.sessionID = b'EF'
  923. k1 = self.hashProcessor(
  924. b'AB' + b'CD' + b'K' + self.proto.sessionID).digest()
  925. k2 = self.hashProcessor(b'ABCD' + k1).digest()
  926. self.assertEqual(self.proto._getKey(b'K', b'AB', b'CD'), k1 + k2)
  927. class BaseSSHTransportDHGroupExchangeSHA1Tests(
  928. BaseSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA1Mixin,
  929. TransportTestCase):
  930. """
  931. diffie-hellman-group-exchange-sha1 tests for TransportBase.
  932. """
  933. class BaseSSHTransportDHGroupExchangeSHA256Tests(
  934. BaseSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA256Mixin,
  935. TransportTestCase):
  936. """
  937. diffie-hellman-group-exchange-sha256 tests for TransportBase.
  938. """
  939. class BaseSSHTransportEllipticCurveTests(
  940. BaseSSHTransportDHGroupExchangeBaseCase, ECDHMixin,
  941. TransportTestCase):
  942. """
  943. ecdh-sha2-nistp256 tests for TransportBase
  944. """
  945. class ServerAndClientSSHTransportBaseCase:
  946. """
  947. Tests that need to be run on both the server and the client.
  948. """
  949. def checkDisconnected(self, kind=None):
  950. """
  951. Helper function to check if the transport disconnected.
  952. """
  953. if kind is None:
  954. kind = transport.DISCONNECT_PROTOCOL_ERROR
  955. self.assertEqual(self.packets[-1][0], transport.MSG_DISCONNECT)
  956. self.assertEqual(self.packets[-1][1][3:4], chr(kind))
  957. def connectModifiedProtocol(self, protoModification,
  958. kind=None):
  959. """
  960. Helper function to connect a modified protocol to the test protocol
  961. and test for disconnection.
  962. """
  963. if kind is None:
  964. kind = transport.DISCONNECT_KEY_EXCHANGE_FAILED
  965. proto2 = self.klass()
  966. protoModification(proto2)
  967. proto2.makeConnection(proto_helpers.StringTransport())
  968. self.proto.dataReceived(proto2.transport.value())
  969. if kind:
  970. self.checkDisconnected(kind)
  971. return proto2
  972. def test_disconnectIfCantMatchKex(self):
  973. """
  974. Test that the transport disconnects if it can't match the key
  975. exchange
  976. """
  977. def blankKeyExchanges(proto2):
  978. proto2.supportedKeyExchanges = []
  979. self.connectModifiedProtocol(blankKeyExchanges)
  980. def test_disconnectIfCantMatchKeyAlg(self):
  981. """
  982. Like test_disconnectIfCantMatchKex, but for the key algorithm.
  983. """
  984. def blankPublicKeys(proto2):
  985. proto2.supportedPublicKeys = []
  986. self.connectModifiedProtocol(blankPublicKeys)
  987. def test_disconnectIfCantMatchCompression(self):
  988. """
  989. Like test_disconnectIfCantMatchKex, but for the compression.
  990. """
  991. def blankCompressions(proto2):
  992. proto2.supportedCompressions = []
  993. self.connectModifiedProtocol(blankCompressions)
  994. def test_disconnectIfCantMatchCipher(self):
  995. """
  996. Like test_disconnectIfCantMatchKex, but for the encryption.
  997. """
  998. def blankCiphers(proto2):
  999. proto2.supportedCiphers = []
  1000. self.connectModifiedProtocol(blankCiphers)
  1001. def test_disconnectIfCantMatchMAC(self):
  1002. """
  1003. Like test_disconnectIfCantMatchKex, but for the MAC.
  1004. """
  1005. def blankMACs(proto2):
  1006. proto2.supportedMACs = []
  1007. self.connectModifiedProtocol(blankMACs)
  1008. def test_getPeer(self):
  1009. """
  1010. Test that the transport's L{getPeer} method returns an
  1011. L{SSHTransportAddress} with the L{IAddress} of the peer.
  1012. """
  1013. self.assertEqual(self.proto.getPeer(),
  1014. address.SSHTransportAddress(
  1015. self.proto.transport.getPeer()))
  1016. def test_getHost(self):
  1017. """
  1018. Test that the transport's L{getHost} method returns an
  1019. L{SSHTransportAddress} with the L{IAddress} of the host.
  1020. """
  1021. self.assertEqual(self.proto.getHost(),
  1022. address.SSHTransportAddress(
  1023. self.proto.transport.getHost()))
  1024. class ServerSSHTransportBaseCase(ServerAndClientSSHTransportBaseCase):
  1025. """
  1026. Base case for SSHServerTransport tests.
  1027. """
  1028. klass = transport.SSHServerTransport
  1029. def setUp(self):
  1030. TransportTestCase.setUp(self)
  1031. self.proto.factory = MockFactory()
  1032. self.proto.factory.startFactory()
  1033. def tearDown(self):
  1034. TransportTestCase.tearDown(self)
  1035. self.proto.factory.stopFactory()
  1036. del self.proto.factory
  1037. class ServerSSHTransportTests(ServerSSHTransportBaseCase, TransportTestCase):
  1038. """
  1039. Tests for SSHServerTransport.
  1040. """
  1041. def test_KEXINITMultipleAlgorithms(self):
  1042. """
  1043. Receiving a KEXINIT packet listing multiple supported algorithms will
  1044. set up the first common algorithm found in the client's preference
  1045. list.
  1046. """
  1047. self.proto.dataReceived(
  1048. b'SSH-2.0-Twisted\r\n\x00\x00\x01\xf4\x04\x14'
  1049. b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99'
  1050. b'\x99\x00\x00\x00bdiffie-hellman-group1-sha1,diffie-hellman-g'
  1051. b'roup-exchange-sha1,diffie-hellman-group-exchange-sha256\x00'
  1052. b'\x00\x00\x0fssh-dss,ssh-rsa\x00\x00\x00\x85aes128-ctr,aes128-'
  1053. b'cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc,cast128-ctr,c'
  1054. b'ast128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc\x00'
  1055. b'\x00\x00\x85aes128-ctr,aes128-cbc,aes192-ctr,aes192-cbc,aes25'
  1056. b'6-ctr,aes256-cbc,cast128-ctr,cast128-cbc,blowfish-ctr,blowfis'
  1057. b'h-cbc,3des-ctr,3des-cbc\x00\x00\x00\x12hmac-md5,hmac-sha1\x00'
  1058. b'\x00\x00\x12hmac-md5,hmac-sha1\x00\x00\x00\tnone,zlib\x00\x00'
  1059. b'\x00\tnone,zlib\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  1060. b'\x00\x00\x99\x99\x99\x99')
  1061. # Even if as server we prefer diffie-hellman-group-exchange-sha256 the
  1062. # client preference is used, after skipping diffie-hellman-group1-sha1
  1063. self.assertEqual(self.proto.kexAlg,
  1064. b'diffie-hellman-group-exchange-sha1')
  1065. self.assertEqual(self.proto.keyAlg,
  1066. b'ssh-dss')
  1067. self.assertEqual(self.proto.outgoingCompressionType,
  1068. b'none')
  1069. self.assertEqual(self.proto.incomingCompressionType,
  1070. b'none')
  1071. ne = self.proto.nextEncryptions
  1072. self.assertEqual(ne.outCipType, b'aes128-ctr')
  1073. self.assertEqual(ne.inCipType, b'aes128-ctr')
  1074. self.assertEqual(ne.outMACType, b'hmac-md5')
  1075. self.assertEqual(ne.inMACType, b'hmac-md5')
  1076. def test_ignoreGuessPacketKex(self):
  1077. """
  1078. The client is allowed to send a guessed key exchange packet
  1079. after it sends the KEXINIT packet. However, if the key exchanges
  1080. do not match, that guess packet must be ignored. This tests that
  1081. the packet is ignored in the case of the key exchange method not
  1082. matching.
  1083. """
  1084. kexInitPacket = b'\x00' * 16 + (
  1085. b''.join([common.NS(x) for x in
  1086. [b','.join(y) for y in
  1087. [self.proto.supportedKeyExchanges[::-1],
  1088. self.proto.supportedPublicKeys,
  1089. self.proto.supportedCiphers,
  1090. self.proto.supportedCiphers,
  1091. self.proto.supportedMACs,
  1092. self.proto.supportedMACs,
  1093. self.proto.supportedCompressions,
  1094. self.proto.supportedCompressions,
  1095. self.proto.supportedLanguages,
  1096. self.proto.supportedLanguages]]])) + (
  1097. b'\xff\x00\x00\x00\x00')
  1098. self.proto.ssh_KEXINIT(kexInitPacket)
  1099. self.assertTrue(self.proto.ignoreNextPacket)
  1100. self.proto.ssh_DEBUG(b"\x01\x00\x00\x00\x04test\x00\x00\x00\x00")
  1101. self.assertTrue(self.proto.ignoreNextPacket)
  1102. self.proto.ssh_KEX_DH_GEX_REQUEST_OLD(b'\x00\x00\x08\x00')
  1103. self.assertFalse(self.proto.ignoreNextPacket)
  1104. self.assertEqual(self.packets, [])
  1105. self.proto.ignoreNextPacket = True
  1106. self.proto.ssh_KEX_DH_GEX_REQUEST(b'\x00\x00\x08\x00' * 3)
  1107. self.assertFalse(self.proto.ignoreNextPacket)
  1108. self.assertEqual(self.packets, [])
  1109. def test_ignoreGuessPacketKey(self):
  1110. """
  1111. Like test_ignoreGuessPacketKex, but for an incorrectly guessed
  1112. public key format.
  1113. """
  1114. kexInitPacket = b'\x00' * 16 + (
  1115. b''.join([common.NS(x) for x in
  1116. [b','.join(y) for y in
  1117. [self.proto.supportedKeyExchanges,
  1118. self.proto.supportedPublicKeys[::-1],
  1119. self.proto.supportedCiphers,
  1120. self.proto.supportedCiphers,
  1121. self.proto.supportedMACs,
  1122. self.proto.supportedMACs,
  1123. self.proto.supportedCompressions,
  1124. self.proto.supportedCompressions,
  1125. self.proto.supportedLanguages,
  1126. self.proto.supportedLanguages]]])) + (
  1127. b'\xff\x00\x00\x00\x00')
  1128. self.proto.ssh_KEXINIT(kexInitPacket)
  1129. self.assertTrue(self.proto.ignoreNextPacket)
  1130. self.proto.ssh_DEBUG(b"\x01\x00\x00\x00\x04test\x00\x00\x00\x00")
  1131. self.assertTrue(self.proto.ignoreNextPacket)
  1132. self.proto.ssh_KEX_DH_GEX_REQUEST_OLD(b'\x00\x00\x08\x00')
  1133. self.assertFalse(self.proto.ignoreNextPacket)
  1134. self.assertEqual(self.packets, [])
  1135. self.proto.ignoreNextPacket = True
  1136. self.proto.ssh_KEX_DH_GEX_REQUEST(b'\x00\x00\x08\x00' * 3)
  1137. self.assertFalse(self.proto.ignoreNextPacket)
  1138. self.assertEqual(self.packets, [])
  1139. def assertKexDHInitResponse(self, kexAlgorithm):
  1140. """
  1141. Test that the KEXDH_INIT packet causes the server to send a
  1142. KEXDH_REPLY with the server's public key and a signature.
  1143. @param kexAlgorithm: The key exchange algorithm to use.
  1144. @type kexAlgorithm: L{str}
  1145. """
  1146. self.proto.supportedKeyExchanges = [kexAlgorithm]
  1147. self.proto.supportedPublicKeys = [b'ssh-rsa']
  1148. self.proto.dataReceived(self.transport.value())
  1149. g, p = _kex.getDHGeneratorAndPrime(kexAlgorithm)
  1150. e = pow(g, 5000, p)
  1151. self.proto.ssh_KEX_DH_GEX_REQUEST_OLD(common.MP(e))
  1152. y = common.getMP(b'\x00\x00\x00\x40' + b'\x99' * 64)[0]
  1153. f = common._MPpow(self.proto.g, y, self.proto.p)
  1154. sharedSecret = common._MPpow(e, y, self.proto.p)
  1155. h = sha1()
  1156. h.update(common.NS(self.proto.ourVersionString) * 2)
  1157. h.update(common.NS(self.proto.ourKexInitPayload) * 2)
  1158. h.update(common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob()))
  1159. h.update(common.MP(e))
  1160. h.update(f)
  1161. h.update(sharedSecret)
  1162. exchangeHash = h.digest()
  1163. signature = self.proto.factory.privateKeys[b'ssh-rsa'].sign(
  1164. exchangeHash)
  1165. self.assertEqual(
  1166. self.packets,
  1167. [(transport.MSG_KEXDH_REPLY,
  1168. common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob())
  1169. + f + common.NS(signature)),
  1170. (transport.MSG_NEWKEYS, b'')])
  1171. def test_checkBad_KEX_ECDH_INIT_CurveName(self):
  1172. """
  1173. Test that if the server receives a KEX_DH_GEX_REQUEST_OLD message
  1174. and the key exchange algorithm is not set, we raise a ConchError.
  1175. """
  1176. self.proto.kexAlg = b'bad-curve'
  1177. self.proto.keyAlg = b'ssh-rsa'
  1178. self.assertRaises(UnsupportedAlgorithm,
  1179. self.proto._ssh_KEX_ECDH_INIT,
  1180. common.NS(b'unused-key'))
  1181. def test_checkBad_KEX_INIT_CurveName(self):
  1182. """
  1183. Test that if the server received a bad name for a curve
  1184. we raise an UnsupportedAlgorithm error.
  1185. """
  1186. kexmsg = (
  1187. b"\xAA" * 16 +
  1188. common.NS(b'ecdh-sha2-nistp256') +
  1189. common.NS(b'ssh-rsa') +
  1190. common.NS(b'aes256-ctr') +
  1191. common.NS(b'aes256-ctr') +
  1192. common.NS(b'hmac-sha1') +
  1193. common.NS(b'hmac-sha1') +
  1194. common.NS(b'none') +
  1195. common.NS(b'none') +
  1196. common.NS(b'') +
  1197. common.NS(b'') +
  1198. b'\x00' + b'\x00\x00\x00\x00')
  1199. self.proto.ssh_KEXINIT(kexmsg)
  1200. self.assertRaises(AttributeError)
  1201. self.assertRaises(UnsupportedAlgorithm)
  1202. def test_KEXDH_INIT_GROUP14(self):
  1203. """
  1204. KEXDH_INIT messages are processed when the
  1205. diffie-hellman-group14-sha1 key exchange algorithm is requested.
  1206. """
  1207. self.assertKexDHInitResponse(b'diffie-hellman-group14-sha1')
  1208. def test_keySetup(self):
  1209. """
  1210. Test that _keySetup sets up the next encryption keys.
  1211. """
  1212. self.proto.kexAlg = b'diffie-hellman-group14-sha1'
  1213. self.proto.nextEncryptions = MockCipher()
  1214. self.simulateKeyExchange(b'AB', b'CD')
  1215. self.assertEqual(self.proto.sessionID, b'CD')
  1216. self.simulateKeyExchange(b'AB', b'EF')
  1217. self.assertEqual(self.proto.sessionID, b'CD')
  1218. self.assertEqual(self.packets[-1], (transport.MSG_NEWKEYS, b''))
  1219. newKeys = [self.proto._getKey(c, b'AB', b'EF')
  1220. for c in iterbytes(b'ABCDEF')]
  1221. self.assertEqual(
  1222. self.proto.nextEncryptions.keys,
  1223. (newKeys[1], newKeys[3], newKeys[0], newKeys[2], newKeys[5],
  1224. newKeys[4]))
  1225. def test_ECDH_keySetup(self):
  1226. """
  1227. Test that _keySetup sets up the next encryption keys.
  1228. """
  1229. self.proto.kexAlg = b'ecdh-sha2-nistp256'
  1230. self.proto.nextEncryptions = MockCipher()
  1231. self.simulateKeyExchange(b'AB', b'CD')
  1232. self.assertEqual(self.proto.sessionID, b'CD')
  1233. self.simulateKeyExchange(b'AB', b'EF')
  1234. self.assertEqual(self.proto.sessionID, b'CD')
  1235. self.assertEqual(self.packets[-1], (transport.MSG_NEWKEYS, b''))
  1236. newKeys = [self.proto._getKey(c, b'AB', b'EF')
  1237. for c in iterbytes(b'ABCDEF')]
  1238. self.assertEqual(
  1239. self.proto.nextEncryptions.keys,
  1240. (newKeys[1], newKeys[3], newKeys[0], newKeys[2], newKeys[5],
  1241. newKeys[4]))
  1242. def test_NEWKEYS(self):
  1243. """
  1244. Test that NEWKEYS transitions the keys in nextEncryptions to
  1245. currentEncryptions.
  1246. """
  1247. self.test_KEXINITMultipleAlgorithms()
  1248. self.proto.nextEncryptions = transport.SSHCiphers(b'none', b'none',
  1249. b'none', b'none')
  1250. self.proto.ssh_NEWKEYS(b'')
  1251. self.assertIs(self.proto.currentEncryptions,
  1252. self.proto.nextEncryptions)
  1253. self.assertIsNone(self.proto.outgoingCompression)
  1254. self.assertIsNone(self.proto.incomingCompression)
  1255. self.proto.outgoingCompressionType = b'zlib'
  1256. self.simulateKeyExchange(b'AB', b'CD')
  1257. self.proto.ssh_NEWKEYS(b'')
  1258. self.assertIsNotNone(self.proto.outgoingCompression)
  1259. self.proto.incomingCompressionType = b'zlib'
  1260. self.simulateKeyExchange(b'AB', b'EF')
  1261. self.proto.ssh_NEWKEYS(b'')
  1262. self.assertIsNotNone(self.proto.incomingCompression)
  1263. def test_SERVICE_REQUEST(self):
  1264. """
  1265. Test that the SERVICE_REQUEST message requests and starts a
  1266. service.
  1267. """
  1268. self.proto.ssh_SERVICE_REQUEST(common.NS(b'ssh-userauth'))
  1269. self.assertEqual(self.packets, [(transport.MSG_SERVICE_ACCEPT,
  1270. common.NS(b'ssh-userauth'))])
  1271. self.assertEqual(self.proto.service.name, b'MockService')
  1272. def test_disconnectNEWKEYSData(self):
  1273. """
  1274. Test that NEWKEYS disconnects if it receives data.
  1275. """
  1276. self.proto.ssh_NEWKEYS(b"bad packet")
  1277. self.checkDisconnected()
  1278. def test_disconnectSERVICE_REQUESTBadService(self):
  1279. """
  1280. Test that SERVICE_REQUESTS disconnects if an unknown service is
  1281. requested.
  1282. """
  1283. self.proto.ssh_SERVICE_REQUEST(common.NS(b'no service'))
  1284. self.checkDisconnected(transport.DISCONNECT_SERVICE_NOT_AVAILABLE)
  1285. class ServerSSHTransportDHGroupExchangeBaseCase(ServerSSHTransportBaseCase):
  1286. """
  1287. Diffie-Hellman group exchange tests for SSHServerTransport.
  1288. """
  1289. def test_KEX_DH_GEX_REQUEST_OLD(self):
  1290. """
  1291. Test that the KEX_DH_GEX_REQUEST_OLD message causes the server
  1292. to reply with a KEX_DH_GEX_GROUP message with the correct
  1293. Diffie-Hellman group.
  1294. """
  1295. self.proto.supportedKeyExchanges = [self.kexAlgorithm]
  1296. self.proto.supportedPublicKeys = [b'ssh-rsa']
  1297. self.proto.dataReceived(self.transport.value())
  1298. self.proto.ssh_KEX_DH_GEX_REQUEST_OLD(b'\x00\x00\x04\x00')
  1299. dhGenerator, dhPrime = self.proto.factory.getPrimes().get(2048)[0]
  1300. self.assertEqual(
  1301. self.packets,
  1302. [(transport.MSG_KEX_DH_GEX_GROUP,
  1303. common.MP(dhPrime) + b'\x00\x00\x00\x01\x03')])
  1304. self.assertEqual(self.proto.g, 3)
  1305. self.assertEqual(self.proto.p, dhPrime)
  1306. def test_KEX_DH_GEX_REQUEST_OLD_badKexAlg(self):
  1307. """
  1308. Test that if the server receives a KEX_DH_GEX_REQUEST_OLD message
  1309. and the key exchange algorithm is not set, we raise a ConchError.
  1310. """
  1311. self.proto.kexAlg = None
  1312. self.assertRaises(ConchError, self.proto.ssh_KEX_DH_GEX_REQUEST_OLD,
  1313. None)
  1314. def test_KEX_DH_GEX_REQUEST(self):
  1315. """
  1316. Test that the KEX_DH_GEX_REQUEST message causes the server to reply
  1317. with a KEX_DH_GEX_GROUP message with the correct Diffie-Hellman
  1318. group.
  1319. """
  1320. self.proto.supportedKeyExchanges = [self.kexAlgorithm]
  1321. self.proto.supportedPublicKeys = [b'ssh-rsa']
  1322. self.proto.dataReceived(self.transport.value())
  1323. self.proto.ssh_KEX_DH_GEX_REQUEST(b'\x00\x00\x04\x00\x00\x00\x08\x00' +
  1324. b'\x00\x00\x0c\x00')
  1325. dhGenerator, dhPrime = self.proto.factory.getPrimes().get(2048)[0]
  1326. self.assertEqual(
  1327. self.packets,
  1328. [(transport.MSG_KEX_DH_GEX_GROUP,
  1329. common.MP(dhPrime) + b'\x00\x00\x00\x01\x03')])
  1330. self.assertEqual(self.proto.g, 3)
  1331. self.assertEqual(self.proto.p, dhPrime)
  1332. def test_KEX_DH_GEX_INIT_after_REQUEST_OLD(self):
  1333. """
  1334. Test that the KEX_DH_GEX_INIT message after the client sends
  1335. KEX_DH_GEX_REQUEST_OLD causes the server to send a KEX_DH_GEX_INIT
  1336. message with a public key and signature.
  1337. """
  1338. self.test_KEX_DH_GEX_REQUEST_OLD()
  1339. e = pow(self.proto.g, 3, self.proto.p)
  1340. y = common.getMP(b'\x00\x00\x01\x00' + b'\x99' * 256)[0]
  1341. f = common._MPpow(self.proto.g, y, self.proto.p)
  1342. sharedSecret = common._MPpow(e, y, self.proto.p)
  1343. h = self.hashProcessor()
  1344. h.update(common.NS(self.proto.ourVersionString) * 2)
  1345. h.update(common.NS(self.proto.ourKexInitPayload) * 2)
  1346. h.update(common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob()))
  1347. h.update(b'\x00\x00\x04\x00')
  1348. h.update(common.MP(self.proto.p))
  1349. h.update(common.MP(self.proto.g))
  1350. h.update(common.MP(e))
  1351. h.update(f)
  1352. h.update(sharedSecret)
  1353. exchangeHash = h.digest()
  1354. self.proto.ssh_KEX_DH_GEX_INIT(common.MP(e))
  1355. self.assertEqual(
  1356. self.packets[1:],
  1357. [(transport.MSG_KEX_DH_GEX_REPLY,
  1358. common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob()) +
  1359. f + common.NS(self.proto.factory.privateKeys[b'ssh-rsa'].sign(
  1360. exchangeHash))),
  1361. (transport.MSG_NEWKEYS, b'')])
  1362. def test_KEX_DH_GEX_INIT_after_REQUEST(self):
  1363. """
  1364. Test that the KEX_DH_GEX_INIT message after the client sends
  1365. KEX_DH_GEX_REQUEST causes the server to send a KEX_DH_GEX_INIT message
  1366. with a public key and signature.
  1367. """
  1368. self.test_KEX_DH_GEX_REQUEST()
  1369. e = pow(self.proto.g, 3, self.proto.p)
  1370. y = common.getMP(b'\x00\x00\x01\x00' + b'\x99' * 256)[0]
  1371. f = common._MPpow(self.proto.g, y, self.proto.p)
  1372. sharedSecret = common._MPpow(e, y, self.proto.p)
  1373. h = self.hashProcessor()
  1374. h.update(common.NS(self.proto.ourVersionString) * 2)
  1375. h.update(common.NS(self.proto.ourKexInitPayload) * 2)
  1376. h.update(common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob()))
  1377. h.update(b'\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x0c\x00')
  1378. h.update(common.MP(self.proto.p))
  1379. h.update(common.MP(self.proto.g))
  1380. h.update(common.MP(e))
  1381. h.update(f)
  1382. h.update(sharedSecret)
  1383. exchangeHash = h.digest()
  1384. self.proto.ssh_KEX_DH_GEX_INIT(common.MP(e))
  1385. self.assertEqual(
  1386. self.packets[1],
  1387. (transport.MSG_KEX_DH_GEX_REPLY,
  1388. common.NS(self.proto.factory.publicKeys[b'ssh-rsa'].blob()) +
  1389. f + common.NS(self.proto.factory.privateKeys[b'ssh-rsa'].sign(
  1390. exchangeHash))))
  1391. class ServerSSHTransportDHGroupExchangeSHA1Tests(
  1392. ServerSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA1Mixin,
  1393. TransportTestCase):
  1394. """
  1395. diffie-hellman-group-exchange-sha1 tests for SSHServerTransport.
  1396. """
  1397. class ServerSSHTransportDHGroupExchangeSHA256Tests(
  1398. ServerSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA256Mixin,
  1399. TransportTestCase):
  1400. """
  1401. diffie-hellman-group-exchange-sha256 tests for SSHServerTransport.
  1402. """
  1403. class ClientSSHTransportBaseCase(ServerAndClientSSHTransportBaseCase):
  1404. """
  1405. Base case for SSHClientTransport tests.
  1406. """
  1407. klass = transport.SSHClientTransport
  1408. def verifyHostKey(self, pubKey, fingerprint):
  1409. """
  1410. Mock version of SSHClientTransport.verifyHostKey.
  1411. """
  1412. self.calledVerifyHostKey = True
  1413. self.assertEqual(pubKey, self.blob)
  1414. self.assertEqual(fingerprint.replace(b':', b''),
  1415. binascii.hexlify(md5(pubKey).digest()))
  1416. return defer.succeed(True)
  1417. def setUp(self):
  1418. TransportTestCase.setUp(self)
  1419. self.blob = keys.Key.fromString(keydata.publicRSA_openssh).blob()
  1420. self.privObj = keys.Key.fromString(keydata.privateRSA_openssh)
  1421. self.calledVerifyHostKey = False
  1422. self.proto.verifyHostKey = self.verifyHostKey
  1423. class ClientSSHTransportTests(ClientSSHTransportBaseCase, TransportTestCase):
  1424. """
  1425. Tests for SSHClientTransport.
  1426. """
  1427. def test_KEXINITMultipleAlgorithms(self):
  1428. """
  1429. Receiving a KEXINIT packet listing multiple supported
  1430. algorithms will set up the first common algorithm, ordered after our
  1431. preference.
  1432. """
  1433. self.proto.dataReceived(
  1434. b'SSH-2.0-Twisted\r\n\x00\x00\x01\xf4\x04\x14'
  1435. b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99'
  1436. b'\x99\x00\x00\x00bdiffie-hellman-group1-sha1,diffie-hellman-g'
  1437. b'roup-exchange-sha1,diffie-hellman-group-exchange-sha256\x00'
  1438. b'\x00\x00\x0fssh-dss,ssh-rsa\x00\x00\x00\x85aes128-ctr,aes128-'
  1439. b'cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc,cast128-ctr,c'
  1440. b'ast128-cbc,blowfish-ctr,blowfish-cbc,3des-ctr,3des-cbc\x00'
  1441. b'\x00\x00\x85aes128-ctr,aes128-cbc,aes192-ctr,aes192-cbc,aes25'
  1442. b'6-ctr,aes256-cbc,cast128-ctr,cast128-cbc,blowfish-ctr,blowfis'
  1443. b'h-cbc,3des-ctr,3des-cbc\x00\x00\x00\x12hmac-md5,hmac-sha1\x00'
  1444. b'\x00\x00\x12hmac-md5,hmac-sha1\x00\x00\x00\tzlib,none\x00\x00'
  1445. b'\x00\tzlib,none\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  1446. b'\x00\x00\x99\x99\x99\x99')
  1447. # Even if client prefer diffie-hellman-group1-sha1, we will go for
  1448. # diffie-hellman-group-exchange-sha256 as this what we prefer and is
  1449. # also supported by the server.
  1450. self.assertEqual(self.proto.kexAlg,
  1451. b'diffie-hellman-group-exchange-sha256')
  1452. self.assertEqual(self.proto.keyAlg,
  1453. b'ssh-rsa')
  1454. self.assertEqual(self.proto.outgoingCompressionType,
  1455. b'none')
  1456. self.assertEqual(self.proto.incomingCompressionType,
  1457. b'none')
  1458. ne = self.proto.nextEncryptions
  1459. self.assertEqual(ne.outCipType, b'aes256-ctr')
  1460. self.assertEqual(ne.inCipType, b'aes256-ctr')
  1461. self.assertEqual(ne.outMACType, b'hmac-sha1')
  1462. self.assertEqual(ne.inMACType, b'hmac-sha1')
  1463. def test_notImplementedClientMethods(self):
  1464. """
  1465. verifyHostKey() should return a Deferred which fails with a
  1466. NotImplementedError exception. connectionSecure() should raise
  1467. NotImplementedError().
  1468. """
  1469. self.assertRaises(NotImplementedError, self.klass().connectionSecure)
  1470. def _checkRaises(f):
  1471. f.trap(NotImplementedError)
  1472. d = self.klass().verifyHostKey(None, None)
  1473. return d.addCallback(self.fail).addErrback(_checkRaises)
  1474. def assertKexInitResponseForDH(self, kexAlgorithm):
  1475. """
  1476. Test that a KEXINIT packet with a group1 or group14 key exchange
  1477. results in a correct KEXDH_INIT response.
  1478. @param kexAlgorithm: The key exchange algorithm to use
  1479. @type kexAlgorithm: L{str}
  1480. """
  1481. self.proto.supportedKeyExchanges = [kexAlgorithm]
  1482. # Imitate reception of server key exchange request contained
  1483. # in data returned by self.transport.value()
  1484. self.proto.dataReceived(self.transport.value())
  1485. self.assertEqual(common.MP(self.proto.x)[5:], b'\x99' * 64)
  1486. # Data sent to server should be a transport.MSG_KEXDH_INIT
  1487. # message containing our public key.
  1488. self.assertEqual(
  1489. self.packets, [(transport.MSG_KEXDH_INIT, self.proto.e)])
  1490. def test_KEXINIT_group14(self):
  1491. """
  1492. KEXINIT messages requesting diffie-hellman-group14-sha1 result in
  1493. KEXDH_INIT responses.
  1494. """
  1495. self.assertKexInitResponseForDH(b'diffie-hellman-group14-sha1')
  1496. def test_KEXINIT_badKexAlg(self):
  1497. """
  1498. Test that the client raises a ConchError if it receives a
  1499. KEXINIT message but doesn't have a key exchange algorithm that we
  1500. understand.
  1501. """
  1502. self.proto.supportedKeyExchanges = [b'diffie-hellman-group24-sha1']
  1503. data = self.transport.value().replace(b'group14', b'group24')
  1504. self.assertRaises(ConchError, self.proto.dataReceived, data)
  1505. def test_KEXDH_REPLY(self):
  1506. """
  1507. Test that the KEXDH_REPLY message verifies the server.
  1508. """
  1509. self.test_KEXINIT_group14()
  1510. sharedSecret = common._MPpow(self.proto.g, self.proto.x,
  1511. self.proto.p)
  1512. h = sha1()
  1513. h.update(common.NS(self.proto.ourVersionString) * 2)
  1514. h.update(common.NS(self.proto.ourKexInitPayload) * 2)
  1515. h.update(common.NS(self.blob))
  1516. h.update(self.proto.e)
  1517. h.update(b'\x00\x00\x00\x01\x02') # f
  1518. h.update(sharedSecret)
  1519. exchangeHash = h.digest()
  1520. def _cbTestKEXDH_REPLY(value):
  1521. self.assertIsNone(value)
  1522. self.assertTrue(self.calledVerifyHostKey)
  1523. self.assertEqual(self.proto.sessionID, exchangeHash)
  1524. signature = self.privObj.sign(exchangeHash)
  1525. d = self.proto.ssh_KEX_DH_GEX_GROUP(
  1526. (common.NS(self.blob) + b'\x00\x00\x00\x01\x02' +
  1527. common.NS(signature)))
  1528. d.addCallback(_cbTestKEXDH_REPLY)
  1529. return d
  1530. def test_keySetup(self):
  1531. """
  1532. Test that _keySetup sets up the next encryption keys.
  1533. """
  1534. self.proto.kexAlg = b'diffie-hellman-group14-sha1'
  1535. self.proto.nextEncryptions = MockCipher()
  1536. self.simulateKeyExchange(b'AB', b'CD')
  1537. self.assertEqual(self.proto.sessionID, b'CD')
  1538. self.simulateKeyExchange(b'AB', b'EF')
  1539. self.assertEqual(self.proto.sessionID, b'CD')
  1540. self.assertEqual(self.packets[-1], (transport.MSG_NEWKEYS, b''))
  1541. newKeys = [self.proto._getKey(c, b'AB', b'EF')
  1542. for c in iterbytes(b'ABCDEF')]
  1543. self.assertEqual(self.proto.nextEncryptions.keys,
  1544. (newKeys[0], newKeys[2], newKeys[1], newKeys[3],
  1545. newKeys[4], newKeys[5]))
  1546. def test_NEWKEYS(self):
  1547. """
  1548. Test that NEWKEYS transitions the keys from nextEncryptions to
  1549. currentEncryptions.
  1550. """
  1551. self.test_KEXINITMultipleAlgorithms()
  1552. secure = [False]
  1553. def stubConnectionSecure():
  1554. secure[0] = True
  1555. self.proto.connectionSecure = stubConnectionSecure
  1556. self.proto.nextEncryptions = transport.SSHCiphers(
  1557. b'none', b'none', b'none', b'none')
  1558. self.simulateKeyExchange(b'AB', b'CD')
  1559. self.assertIsNot(self.proto.currentEncryptions,
  1560. self.proto.nextEncryptions)
  1561. self.proto.nextEncryptions = MockCipher()
  1562. self.proto.ssh_NEWKEYS(b'')
  1563. self.assertIsNone(self.proto.outgoingCompression)
  1564. self.assertIsNone(self.proto.incomingCompression)
  1565. self.assertIs(self.proto.currentEncryptions,
  1566. self.proto.nextEncryptions)
  1567. self.assertTrue(secure[0])
  1568. self.proto.outgoingCompressionType = b'zlib'
  1569. self.simulateKeyExchange(b'AB', b'GH')
  1570. self.proto.ssh_NEWKEYS(b'')
  1571. self.assertIsNotNone(self.proto.outgoingCompression)
  1572. self.proto.incomingCompressionType = b'zlib'
  1573. self.simulateKeyExchange(b'AB', b'IJ')
  1574. self.proto.ssh_NEWKEYS(b'')
  1575. self.assertIsNotNone(self.proto.incomingCompression)
  1576. def test_SERVICE_ACCEPT(self):
  1577. """
  1578. Test that the SERVICE_ACCEPT packet starts the requested service.
  1579. """
  1580. self.proto.instance = MockService()
  1581. self.proto.ssh_SERVICE_ACCEPT(b'\x00\x00\x00\x0bMockService')
  1582. self.assertTrue(self.proto.instance.started)
  1583. def test_requestService(self):
  1584. """
  1585. Test that requesting a service sends a SERVICE_REQUEST packet.
  1586. """
  1587. self.proto.requestService(MockService())
  1588. self.assertEqual(self.packets, [(transport.MSG_SERVICE_REQUEST,
  1589. b'\x00\x00\x00\x0bMockService')])
  1590. def test_disconnectKEXDH_REPLYBadSignature(self):
  1591. """
  1592. Test that KEXDH_REPLY disconnects if the signature is bad.
  1593. """
  1594. self.test_KEXDH_REPLY()
  1595. self.proto._continueKEXDH_REPLY(None, self.blob, 3, b"bad signature")
  1596. self.checkDisconnected(transport.DISCONNECT_KEY_EXCHANGE_FAILED)
  1597. def test_disconnectKEX_ECDH_REPLYBadSignature(self):
  1598. """
  1599. Test that KEX_ECDH_REPLY disconnects if the signature is bad.
  1600. """
  1601. kexmsg = (
  1602. b"\xAA" * 16 +
  1603. common.NS(b'ecdh-sha2-nistp256') +
  1604. common.NS(b'ssh-rsa') +
  1605. common.NS(b'aes256-ctr') +
  1606. common.NS(b'aes256-ctr') +
  1607. common.NS(b'hmac-sha1') +
  1608. common.NS(b'hmac-sha1') +
  1609. common.NS(b'none') +
  1610. common.NS(b'none') +
  1611. common.NS(b'') +
  1612. common.NS(b'') +
  1613. b'\x00' + b'\x00\x00\x00\x00')
  1614. self.proto.ssh_KEXINIT(kexmsg)
  1615. self.proto.dataReceived(b"SSH-2.0-OpenSSH\r\n")
  1616. self.proto.ecPriv = ec.generate_private_key(ec.SECP256R1(),
  1617. default_backend())
  1618. self.proto.ecPub = self.proto.ecPriv.public_key()
  1619. # Generate the private key
  1620. thisPriv = ec.generate_private_key(ec.SECP256R1(), default_backend())
  1621. # Get the public key
  1622. thisPub = thisPriv.public_key()
  1623. encPub = thisPub.public_numbers().encode_point()
  1624. self.proto.curve = ec.SECP256R1()
  1625. self.proto.kexAlg = b'ecdh-sha2-nistp256'
  1626. self.proto._ssh_KEX_ECDH_REPLY(
  1627. common.NS(MockFactory().getPublicKeys()[b'ssh-rsa'].blob()) +
  1628. common.NS(encPub) + common.NS(b'bad-signature'))
  1629. self.checkDisconnected(transport.DISCONNECT_KEY_EXCHANGE_FAILED)
  1630. def test_disconnectNEWKEYSData(self):
  1631. """
  1632. Test that NEWKEYS disconnects if it receives data.
  1633. """
  1634. self.proto.ssh_NEWKEYS(b"bad packet")
  1635. self.checkDisconnected()
  1636. def test_disconnectSERVICE_ACCEPT(self):
  1637. """
  1638. Test that SERVICE_ACCEPT disconnects if the accepted protocol is
  1639. differet from the asked-for protocol.
  1640. """
  1641. self.proto.instance = MockService()
  1642. self.proto.ssh_SERVICE_ACCEPT(b'\x00\x00\x00\x03bad')
  1643. self.checkDisconnected()
  1644. def test_noPayloadSERVICE_ACCEPT(self):
  1645. """
  1646. Some commercial SSH servers don't send a payload with the
  1647. SERVICE_ACCEPT message. Conch pretends that it got the correct
  1648. name of the service.
  1649. """
  1650. self.proto.instance = MockService()
  1651. self.proto.ssh_SERVICE_ACCEPT(b'') # no payload
  1652. self.assertTrue(self.proto.instance.started)
  1653. self.assertEqual(len(self.packets), 0) # not disconnected
  1654. class ClientSSHTransportDHGroupExchangeBaseCase(ClientSSHTransportBaseCase):
  1655. """
  1656. Diffie-Hellman group exchange tests for SSHClientTransport.
  1657. """
  1658. def test_KEXINIT_groupexchange(self):
  1659. """
  1660. KEXINIT packet with a group-exchange key exchange results
  1661. in a KEX_DH_GEX_REQUEST message.
  1662. """
  1663. self.proto.supportedKeyExchanges = [self.kexAlgorithm]
  1664. self.proto.dataReceived(self.transport.value())
  1665. # The response will include our advertised group sizes.
  1666. self.assertEqual(self.packets, [(
  1667. transport.MSG_KEX_DH_GEX_REQUEST,
  1668. b'\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x20\x00')])
  1669. def test_KEX_DH_GEX_GROUP(self):
  1670. """
  1671. Test that the KEX_DH_GEX_GROUP message results in a
  1672. KEX_DH_GEX_INIT message with the client's Diffie-Hellman public key.
  1673. """
  1674. self.test_KEXINIT_groupexchange()
  1675. self.proto.ssh_KEX_DH_GEX_GROUP(
  1676. b'\x00\x00\x00\x01\x0f\x00\x00\x00\x01\x02')
  1677. self.assertEqual(self.proto.p, 15)
  1678. self.assertEqual(self.proto.g, 2)
  1679. self.assertEqual(common.MP(self.proto.x)[5:], b'\x99' * 40)
  1680. self.assertEqual(self.proto.e,
  1681. common.MP(pow(2, self.proto.x, 15)))
  1682. self.assertEqual(self.packets[1:], [(transport.MSG_KEX_DH_GEX_INIT,
  1683. self.proto.e)])
  1684. def test_KEX_DH_GEX_REPLY(self):
  1685. """
  1686. Test that the KEX_DH_GEX_REPLY message results in a verified
  1687. server.
  1688. """
  1689. self.test_KEX_DH_GEX_GROUP()
  1690. sharedSecret = common._MPpow(3, self.proto.x, self.proto.p)
  1691. h = self.hashProcessor()
  1692. h.update(common.NS(self.proto.ourVersionString) * 2)
  1693. h.update(common.NS(self.proto.ourKexInitPayload) * 2)
  1694. h.update(common.NS(self.blob))
  1695. # Here is the wire format for advertised min, pref and max DH sizes.
  1696. h.update(b'\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x20\x00')
  1697. h.update(b'\x00\x00\x00\x01\x0f\x00\x00\x00\x01\x02')
  1698. h.update(self.proto.e)
  1699. h.update(b'\x00\x00\x00\x01\x03') # f
  1700. h.update(sharedSecret)
  1701. exchangeHash = h.digest()
  1702. def _cbTestKEX_DH_GEX_REPLY(value):
  1703. self.assertIsNone(value)
  1704. self.assertTrue(self.calledVerifyHostKey)
  1705. self.assertEqual(self.proto.sessionID, exchangeHash)
  1706. signature = self.privObj.sign(exchangeHash)
  1707. d = self.proto.ssh_KEX_DH_GEX_REPLY(
  1708. common.NS(self.blob) +
  1709. b'\x00\x00\x00\x01\x03' +
  1710. common.NS(signature))
  1711. d.addCallback(_cbTestKEX_DH_GEX_REPLY)
  1712. return d
  1713. def test_disconnectGEX_REPLYBadSignature(self):
  1714. """
  1715. Test that KEX_DH_GEX_REPLY disconnects if the signature is bad.
  1716. """
  1717. self.test_KEX_DH_GEX_REPLY()
  1718. self.proto._continueGEX_REPLY(None, self.blob, 3, b"bad signature")
  1719. self.checkDisconnected(transport.DISCONNECT_KEY_EXCHANGE_FAILED)
  1720. def test_disconnectKEX_ECDH_REPLYBadSignature(self):
  1721. """
  1722. Test that KEX_ECDH_REPLY disconnects if the signature is bad.
  1723. """
  1724. kexmsg = (
  1725. b"\xAA" * 16 +
  1726. common.NS(b'ecdh-sha2-nistp256') +
  1727. common.NS(b'ssh-rsa') +
  1728. common.NS(b'aes256-ctr') +
  1729. common.NS(b'aes256-ctr') +
  1730. common.NS(b'hmac-sha1') +
  1731. common.NS(b'hmac-sha1') +
  1732. common.NS(b'none') +
  1733. common.NS(b'none') +
  1734. common.NS(b'') +
  1735. common.NS(b'') +
  1736. b'\x00' + b'\x00\x00\x00\x00')
  1737. self.proto.ssh_KEXINIT(kexmsg)
  1738. self.proto.dataReceived(b"SSH-2.0-OpenSSH\r\n")
  1739. self.proto.ecPriv = ec.generate_private_key(ec.SECP256R1(),
  1740. default_backend())
  1741. self.proto.ecPub = self.proto.ecPriv.public_key()
  1742. # Generate the private key
  1743. thisPriv = ec.generate_private_key(ec.SECP256R1(), default_backend())
  1744. # Get the public key
  1745. thisPub = thisPriv.public_key()
  1746. encPub = thisPub.public_numbers().encode_point()
  1747. self.proto.curve = ec.SECP256R1()
  1748. self.proto.kexAlg = b'ecdh-sha2-nistp256'
  1749. self.proto._ssh_KEX_ECDH_REPLY(
  1750. common.NS(MockFactory().getPublicKeys()[b'ssh-rsa'].blob()) +
  1751. common.NS(encPub) + common.NS(b'bad-signature'))
  1752. self.checkDisconnected(transport.DISCONNECT_KEY_EXCHANGE_FAILED)
  1753. class ClientSSHTransportDHGroupExchangeSHA1Tests(
  1754. ClientSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA1Mixin,
  1755. TransportTestCase):
  1756. """
  1757. diffie-hellman-group-exchange-sha1 tests for SSHClientTransport.
  1758. """
  1759. class ClientSSHTransportDHGroupExchangeSHA256Tests(
  1760. ClientSSHTransportDHGroupExchangeBaseCase, DHGroupExchangeSHA256Mixin,
  1761. TransportTestCase):
  1762. """
  1763. diffie-hellman-group-exchange-sha256 tests for SSHClientTransport.
  1764. """
  1765. class GetMACTests(unittest.TestCase):
  1766. """
  1767. Tests for L{SSHCiphers._getMAC}.
  1768. """
  1769. if dependencySkip:
  1770. skip = dependencySkip
  1771. def setUp(self):
  1772. self.ciphers = transport.SSHCiphers(b'A', b'B', b'C', b'D')
  1773. def getSharedSecret(self):
  1774. """
  1775. Generate a new shared secret to be used with the tests.
  1776. @return: A new secret.
  1777. @rtype: L{bytes}
  1778. """
  1779. return insecureRandom(64)
  1780. def assertGetMAC(self, hmacName, hashProcessor, digestSize, blockPadSize):
  1781. """
  1782. Check that when L{SSHCiphers._getMAC} is called with a supportd HMAC
  1783. algorithm name it returns a tuple of
  1784. (digest object, inner pad, outer pad, digest size) with a C{key}
  1785. attribute set to the value of the key supplied.
  1786. @param hmacName: Identifier of HMAC algorithm.
  1787. @type hmacName: L{bytes}
  1788. @param hashProcessor: Callable for the hash algorithm.
  1789. @type hashProcessor: C{callable}
  1790. @param digestSize: Size of the digest for algorithm.
  1791. @type digestSize: L{int}
  1792. @param blockPadSize: Size of padding applied to the shared secret to
  1793. match the block size.
  1794. @type blockPadSize: L{int}
  1795. """
  1796. secret = self.getSharedSecret()
  1797. params = self.ciphers._getMAC(hmacName, secret)
  1798. key = secret[:digestSize] + b'\x00' * blockPadSize
  1799. innerPad = b''.join(chr(ord(b) ^ 0x36) for b in iterbytes(key))
  1800. outerPad = b''.join(chr(ord(b) ^ 0x5c) for b in iterbytes(key))
  1801. self.assertEqual(
  1802. (hashProcessor, innerPad, outerPad, digestSize), params)
  1803. self.assertEqual(key, params.key)
  1804. def test_hmacsha2512(self):
  1805. """
  1806. When L{SSHCiphers._getMAC} is called with the C{b"hmac-sha2-512"} MAC
  1807. algorithm name it returns a tuple of (sha512 digest object, inner pad,
  1808. outer pad, sha512 digest size) with a C{key} attribute set to the
  1809. value of the key supplied.
  1810. """
  1811. self.assertGetMAC(
  1812. b"hmac-sha2-512", sha512, digestSize=64, blockPadSize=64)
  1813. def test_hmacsha2384(self):
  1814. """
  1815. When L{SSHCiphers._getMAC} is called with the C{b"hmac-sha2-384"} MAC
  1816. algorithm name it returns a tuple of (sha384 digest object, inner pad,
  1817. outer pad, sha384 digest size) with a C{key} attribute set to the
  1818. value of the key supplied.
  1819. """
  1820. self.assertGetMAC(
  1821. b"hmac-sha2-384", sha384, digestSize=48, blockPadSize=80)
  1822. def test_hmacsha2256(self):
  1823. """
  1824. When L{SSHCiphers._getMAC} is called with the C{b"hmac-sha2-256"} MAC
  1825. algorithm name it returns a tuple of (sha256 digest object, inner pad,
  1826. outer pad, sha256 digest size) with a C{key} attribute set to the
  1827. value of the key supplied.
  1828. """
  1829. self.assertGetMAC(
  1830. b"hmac-sha2-256", sha256, digestSize=32, blockPadSize=32)
  1831. def test_hmacsha1(self):
  1832. """
  1833. When L{SSHCiphers._getMAC} is called with the C{b"hmac-sha1"} MAC
  1834. algorithm name it returns a tuple of (sha1 digest object, inner pad,
  1835. outer pad, sha1 digest size) with a C{key} attribute set to the value
  1836. of the key supplied.
  1837. """
  1838. self.assertGetMAC(b"hmac-sha1", sha1, digestSize=20, blockPadSize=44)
  1839. def test_hmacmd5(self):
  1840. """
  1841. When L{SSHCiphers._getMAC} is called with the C{b"hmac-md5"} MAC
  1842. algorithm name it returns a tuple of (md5 digest object, inner pad,
  1843. outer pad, md5 digest size) with a C{key} attribute set to the value of
  1844. the key supplied.
  1845. """
  1846. self.assertGetMAC(b"hmac-md5", md5, digestSize=16, blockPadSize=48)
  1847. def test_none(self):
  1848. """
  1849. When L{SSHCiphers._getMAC} is called with the C{b"none"} MAC algorithm
  1850. name it returns a tuple of (None, "", "", 0).
  1851. """
  1852. key = self.getSharedSecret()
  1853. params = self.ciphers._getMAC(b"none", key)
  1854. self.assertEqual((None, b"", b"", 0), params)
  1855. class SSHCiphersTests(unittest.TestCase):
  1856. """
  1857. Tests for the SSHCiphers helper class.
  1858. """
  1859. if dependencySkip:
  1860. skip = dependencySkip
  1861. def test_init(self):
  1862. """
  1863. Test that the initializer sets up the SSHCiphers object.
  1864. """
  1865. ciphers = transport.SSHCiphers(b'A', b'B', b'C', b'D')
  1866. self.assertEqual(ciphers.outCipType, b'A')
  1867. self.assertEqual(ciphers.inCipType, b'B')
  1868. self.assertEqual(ciphers.outMACType, b'C')
  1869. self.assertEqual(ciphers.inMACType, b'D')
  1870. def test_getCipher(self):
  1871. """
  1872. Test that the _getCipher method returns the correct cipher.
  1873. """
  1874. ciphers = transport.SSHCiphers(b'A', b'B', b'C', b'D')
  1875. iv = key = b'\x00' * 16
  1876. for cipName, (algClass, keySize, counter) in ciphers.cipherMap.items():
  1877. cip = ciphers._getCipher(cipName, iv, key)
  1878. if cipName == b'none':
  1879. self.assertIsInstance(cip, transport._DummyCipher)
  1880. else:
  1881. self.assertIsInstance(cip.algorithm, algClass)
  1882. def test_setKeysCiphers(self):
  1883. """
  1884. Test that setKeys sets up the ciphers.
  1885. """
  1886. key = b'\x00' * 64
  1887. for cipName in transport.SSHTransportBase.supportedCiphers:
  1888. modName, keySize, counter = transport.SSHCiphers.cipherMap[cipName]
  1889. encCipher = transport.SSHCiphers(cipName, b'none', b'none',
  1890. b'none')
  1891. decCipher = transport.SSHCiphers(b'none', cipName, b'none',
  1892. b'none')
  1893. cip = encCipher._getCipher(cipName, key, key)
  1894. bs = cip.algorithm.block_size // 8
  1895. encCipher.setKeys(key, key, b'', b'', b'', b'')
  1896. decCipher.setKeys(b'', b'', key, key, b'', b'')
  1897. self.assertEqual(encCipher.encBlockSize, bs)
  1898. self.assertEqual(decCipher.decBlockSize, bs)
  1899. encryptor = cip.encryptor()
  1900. enc = encryptor.update(key[:bs])
  1901. enc2 = encryptor.update(key[:bs])
  1902. self.assertEqual(encCipher.encrypt(key[:bs]), enc)
  1903. self.assertEqual(encCipher.encrypt(key[:bs]), enc2)
  1904. self.assertEqual(decCipher.decrypt(enc), key[:bs])
  1905. self.assertEqual(decCipher.decrypt(enc2), key[:bs])
  1906. def test_setKeysMACs(self):
  1907. """
  1908. Test that setKeys sets up the MACs.
  1909. """
  1910. key = b'\x00' * 64
  1911. for macName, mod in transport.SSHCiphers.macMap.items():
  1912. outMac = transport.SSHCiphers(b'none', b'none', macName, b'none')
  1913. inMac = transport.SSHCiphers(b'none', b'none', b'none', macName)
  1914. outMac.setKeys(b'', b'', b'', b'', key, b'')
  1915. inMac.setKeys(b'', b'', b'', b'', b'', key)
  1916. if mod:
  1917. ds = mod().digest_size
  1918. else:
  1919. ds = 0
  1920. self.assertEqual(inMac.verifyDigestSize, ds)
  1921. if mod:
  1922. mod, i, o, ds = outMac._getMAC(macName, key)
  1923. seqid = 0
  1924. data = key
  1925. packet = b'\x00' * 4 + key
  1926. if mod:
  1927. mac = mod(o + mod(i + packet).digest()).digest()
  1928. else:
  1929. mac = b''
  1930. self.assertEqual(outMac.makeMAC(seqid, data), mac)
  1931. self.assertTrue(inMac.verify(seqid, data, mac))
  1932. def test_makeMAC(self):
  1933. """
  1934. L{SSHCiphers.makeMAC} computes the HMAC of an outgoing SSH message with
  1935. a particular sequence id and content data.
  1936. """
  1937. # Use the test vectors given in the appendix of RFC 2104.
  1938. vectors = [
  1939. (b"\x0b" * 16, b"Hi There",
  1940. b"9294727a3638bb1c13f48ef8158bfc9d"),
  1941. (b"Jefe", b"what do ya want for nothing?",
  1942. b"750c783e6ab0b503eaa86e310a5db738"),
  1943. (b"\xAA" * 16, b"\xDD" * 50,
  1944. b"56be34521d144c88dbb8c733f0e8b3f6"),
  1945. ]
  1946. for key, data, mac in vectors:
  1947. outMAC = transport.SSHCiphers(b'none', b'none', b'hmac-md5',
  1948. b'none')
  1949. outMAC.outMAC = outMAC._getMAC(b"hmac-md5", key)
  1950. (seqid,) = struct.unpack('>L', data[:4])
  1951. shortened = data[4:]
  1952. self.assertEqual(
  1953. mac, binascii.hexlify(outMAC.makeMAC(seqid, shortened)),
  1954. "Failed HMAC test vector; key=%r data=%r" % (key, data))
  1955. class TransportLoopbackTests(unittest.TestCase):
  1956. """
  1957. Test the server transport and client transport against each other,
  1958. """
  1959. if dependencySkip:
  1960. skip = dependencySkip
  1961. def _runClientServer(self, mod):
  1962. """
  1963. Run an async client and server, modifying each using the mod function
  1964. provided. Returns a Deferred called back when both Protocols have
  1965. disconnected.
  1966. @type mod: C{func}
  1967. @rtype: C{defer.Deferred}
  1968. """
  1969. factory = MockFactory()
  1970. server = transport.SSHServerTransport()
  1971. server.factory = factory
  1972. factory.startFactory()
  1973. server.errors = []
  1974. server.receiveError = lambda code, desc: server.errors.append((
  1975. code, desc))
  1976. client = transport.SSHClientTransport()
  1977. client.verifyHostKey = lambda x, y: defer.succeed(None)
  1978. client.errors = []
  1979. client.receiveError = lambda code, desc: client.errors.append((
  1980. code, desc))
  1981. client.connectionSecure = lambda: client.loseConnection()
  1982. server.supportedPublicKeys = list(
  1983. server.factory.getPublicKeys().keys())
  1984. server = mod(server)
  1985. client = mod(client)
  1986. def check(ignored, server, client):
  1987. name = repr([server.supportedCiphers[0],
  1988. server.supportedMACs[0],
  1989. server.supportedKeyExchanges[0],
  1990. server.supportedCompressions[0]])
  1991. self.assertEqual(client.errors, [])
  1992. self.assertEqual(server.errors, [(
  1993. transport.DISCONNECT_CONNECTION_LOST,
  1994. b"user closed connection")])
  1995. if server.supportedCiphers[0] == b'none':
  1996. self.assertFalse(server.isEncrypted(), name)
  1997. self.assertFalse(client.isEncrypted(), name)
  1998. else:
  1999. self.assertTrue(server.isEncrypted(), name)
  2000. self.assertTrue(client.isEncrypted(), name)
  2001. if server.supportedMACs[0] == b'none':
  2002. self.assertFalse(server.isVerified(), name)
  2003. self.assertFalse(client.isVerified(), name)
  2004. else:
  2005. self.assertTrue(server.isVerified(), name)
  2006. self.assertTrue(client.isVerified(), name)
  2007. d = loopback.loopbackAsync(server, client)
  2008. d.addCallback(check, server, client)
  2009. return d
  2010. def test_ciphers(self):
  2011. """
  2012. Test that the client and server play nicely together, in all
  2013. the various combinations of ciphers.
  2014. """
  2015. deferreds = []
  2016. for cipher in transport.SSHTransportBase.supportedCiphers + [b'none']:
  2017. def setCipher(proto):
  2018. proto.supportedCiphers = [cipher]
  2019. return proto
  2020. deferreds.append(self._runClientServer(setCipher))
  2021. return defer.DeferredList(deferreds, fireOnOneErrback=True)
  2022. def test_macs(self):
  2023. """
  2024. Like test_ciphers, but for the various MACs.
  2025. """
  2026. deferreds = []
  2027. for mac in transport.SSHTransportBase.supportedMACs + [b'none']:
  2028. def setMAC(proto):
  2029. proto.supportedMACs = [mac]
  2030. return proto
  2031. deferreds.append(self._runClientServer(setMAC))
  2032. return defer.DeferredList(deferreds, fireOnOneErrback=True)
  2033. def test_keyexchanges(self):
  2034. """
  2035. Like test_ciphers, but for the various key exchanges.
  2036. """
  2037. deferreds = []
  2038. for kexAlgorithm in transport.SSHTransportBase.supportedKeyExchanges:
  2039. def setKeyExchange(proto):
  2040. proto.supportedKeyExchanges = [kexAlgorithm]
  2041. return proto
  2042. deferreds.append(self._runClientServer(setKeyExchange))
  2043. return defer.DeferredList(deferreds, fireOnOneErrback=True)
  2044. def test_compressions(self):
  2045. """
  2046. Like test_ciphers, but for the various compressions.
  2047. """
  2048. deferreds = []
  2049. for compression in transport.SSHTransportBase.supportedCompressions:
  2050. def setCompression(proto):
  2051. proto.supportedCompressions = [compression]
  2052. return proto
  2053. deferreds.append(self._runClientServer(setCompression))
  2054. return defer.DeferredList(deferreds, fireOnOneErrback=True)
  2055. class RandomNumberTests(unittest.TestCase):
  2056. """
  2057. Tests for the random number generator L{_getRandomNumber} and private
  2058. key generator L{_generateX}.
  2059. """
  2060. if dependencySkip:
  2061. skip = dependencySkip
  2062. def test_usesSuppliedRandomFunction(self):
  2063. """
  2064. L{_getRandomNumber} returns an integer constructed directly from the
  2065. bytes returned by the random byte generator passed to it.
  2066. """
  2067. def random(data):
  2068. # The number of bytes requested will be the value of each byte
  2069. # we return.
  2070. return chr(data) * data
  2071. self.assertEqual(
  2072. transport._getRandomNumber(random, 32),
  2073. 4 << 24 | 4 << 16 | 4 << 8 | 4)
  2074. def test_rejectsNonByteMultiples(self):
  2075. """
  2076. L{_getRandomNumber} raises L{ValueError} if the number of bits
  2077. passed to L{_getRandomNumber} is not a multiple of 8.
  2078. """
  2079. self.assertRaises(
  2080. ValueError,
  2081. transport._getRandomNumber, None, 9)
  2082. def test_excludesSmall(self):
  2083. """
  2084. If the random byte generator passed to L{_generateX} produces bytes
  2085. which would result in 0 or 1 being returned, these bytes are
  2086. discarded and another attempt is made to produce a larger value.
  2087. """
  2088. results = [chr(0), chr(1), chr(127)]
  2089. def random(data):
  2090. return results.pop(0) * data
  2091. self.assertEqual(
  2092. transport._generateX(random, 8),
  2093. 127)
  2094. def test_excludesLarge(self):
  2095. """
  2096. If the random byte generator passed to L{_generateX} produces bytes
  2097. which would result in C{(2 ** bits) - 1} being returned, these bytes
  2098. are discarded and another attempt is made to produce a smaller
  2099. value.
  2100. """
  2101. results = [chr(255), chr(64)]
  2102. def random(data):
  2103. return results.pop(0) * data
  2104. self.assertEqual(
  2105. transport._generateX(random, 8),
  2106. 64)