root / trunk / twisted / conch / ssh / transport.py

Revision 25457, 49.8 kB (checked in by exarkun, 8 months ago)

Merge hashlib-2763-3

Author: wsanchez, exarkun
Reviewer: exarkun, mwhudson
Fixes: #2763

Replace uses of md5 and sha modules in Twisted with use of a new twisted.python.hashlib
module which transparently uses the new hashlib standard library module if it is available
or falls back to md5 and sha if not.

Line 
1 # -*- test-case-name: twisted.conch.test.test_transport -*-
2 #
3 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 """
7 The lowest level SSH protocol.  This handles the key negotiation, the
8 encryption and the compression.  The transport layer is described in
9 RFC 4253.
10
11 Maintainer: Paul Swartz
12 """
13
14 # base library imports
15 import struct
16 import zlib
17 import array
18
19 # external library imports
20 from Crypto import Util
21 from Crypto.Cipher import XOR
22
23 # twisted imports
24 from twisted.internet import protocol, defer
25 from twisted.conch import error
26 from twisted.python import log, randbytes
27 from twisted.python.hashlib import md5, sha1
28
29 # sibling imports
30 from twisted.conch.ssh import keys
31 from twisted.conch.ssh.common import NS, getNS, MP, getMP, _MPpow, ffs
32
33
34
35 class SSHTransportBase(protocol.Protocol):
36     """
37     Protocol supporting basic SSH functionality: sending/receiving packets
38     and message dispatch.  To connect to or run a server, you must use
39     SSHClientTransport or SSHServerTransport.
40
41     @ivar protocolVersion: A string representing the version of the SSH
42         protocol we support.  Currently defaults to '2.0'.
43
44     @ivar version: A string representing the version of the server or client.
45         Currently defaults to 'Twisted'.
46
47     @ivar comment: An optional string giving more information about the
48         server or client.
49
50     @ivar supportedCiphers: A list of strings representing the encryption
51         algorithms supported, in order from most-preferred to least.
52
53     @ivar supportedMACs: A list of strings representing the message
54         authentication codes (hashes) supported, in order from most-preferred
55         to least.  Both this and supportedCiphers can include 'none' to use
56         no encryption or authentication, but that must be done manually,
57
58     @ivar supportedKeyExchanges: A list of strings representing the
59         key exchanges supported, in order from most-preferred to least.
60
61     @ivar supportedPublicKeys:  A list of strings representing the
62         public key types supported, in order from most-preferred to least.
63
64     @ivar supportedCompressions: A list of strings representing compression
65         types supported, from most-preferred to least.
66
67     @ivar supportedLanguages: A list of strings representing languages
68         supported, from most-preferred to least.
69
70     @ivar isClient: A boolean indicating whether this is a client or server.
71
72     @ivar gotVersion: A boolean indicating whether we have receieved the
73         version string from the other side.
74
75     @ivar buf: Data we've received but hasn't been parsed into a packet.
76
77     @ivar outgoingPacketSequence: the sequence number of the next packet we
78         will send.
79
80     @ivar incomingPacketSequence: the sequence number of the next packet we
81         are expecting from the other side.
82
83     @ivar outgoingCompression: an object supporting the .compress(str) and
84         .flush() methods, or None if there is no outgoing compression.  Used to
85         compress outgoing data.
86
87     @ivar outgoingCompressionType: A string representing the outgoing
88         compression type.
89
90     @ivar incomingCompression: an object supporting the .decompress(str)
91         method, or None if there is no incoming compression.  Used to
92         decompress incoming data.
93
94     @ivar incomingCompressionType: A string representing the incoming
95         compression type.
96
97     @ivar ourVersionString: the version string that we sent to the other side.
98         Used in the key exchange.
99
100     @ivar otherVersionString: the version string sent by the other side.  Used
101         in the key exchange.
102
103     @ivar ourKexInitPayload: the MSG_KEXINIT payload we sent.  Used in the key
104         exchange.
105
106     @ivar otherKexInitPayload: the MSG_KEXINIT payload we received.  Used in
107         the key exchange
108
109     @ivar sessionID: a string that is unique to this SSH session.  Created as
110         part of the key exchange, sessionID is used to generate the various
111         encryption and authentication keys.
112
113     @ivar service: an SSHService instance, or None.  If it's set to an object,
114         it's the currently running service.
115
116     @ivar kexAlg: the agreed-upon key exchange algorithm.
117
118     @ivar keyAlg: the agreed-upon public key type for the key exchange.
119
120     @ivar currentEncryptions: an SSHCiphers instance.  It represents the
121         current encryption and authentication options for the transport.
122
123     @ivar nextEncryptions: an SSHCiphers instance.  Held here until the
124         MSG_NEWKEYS messages are exchanged, when nextEncryptions is
125         transitioned to currentEncryptions.
126
127     @ivar first: the first bytes of the next packet.  In order to avoid
128         decrypting data twice, the first bytes are decrypted and stored until
129         the whole packet is available.
130
131     """
132
133
134     protocolVersion = '2.0'
135     version = 'Twisted'
136     comment = ''
137     ourVersionString = ('SSH-' + protocolVersion + '-' + version + ' '
138             + comment).strip()
139     supportedCiphers = ['aes256-ctr', 'aes256-cbc', 'aes192-ctr', 'aes192-cbc',
140                         'aes128-ctr', 'aes128-cbc', 'cast128-ctr',
141                         'cast128-cbc', 'blowfish-ctr', 'blowfish-cbc',
142                         '3des-ctr', '3des-cbc'] # ,'none']
143     supportedMACs = ['hmac-sha1', 'hmac-md5'] # , 'none']
144     # both of the above support 'none', but for security are disabled by
145     # default.  to enable them, subclass this class and add it, or do:
146     #   SSHTransportBase.supportedCiphers.append('none')
147     supportedKeyExchanges = ['diffie-hellman-group-exchange-sha1',
148                              'diffie-hellman-group1-sha1']
149     supportedPublicKeys = ['ssh-rsa', 'ssh-dss']
150     supportedCompressions = ['none', 'zlib']
151     supportedLanguages = ()
152     isClient = False
153     gotVersion = False
154     buf = ''
155     outgoingPacketSequence = 0
156     incomingPacketSequence = 0
157     outgoingCompression = None
158     incomingCompression = None
159     sessionID = None
160     service = None
161
162
163     def connectionLost(self, reason):
164         if self.service:
165             self.service.serviceStopped()
166         if hasattr(self, 'avatar'):
167             self.logoutFunction()
168         log.msg('connection lost')
169
170
171     def connectionMade(self):
172         """
173         Called when the connection is made to the other side.  We sent our
174         version and the MSG_KEXINIT packet.
175         """
176         self.transport.write('%s\r\n' % (self.ourVersionString,))
177         self.currentEncryptions = SSHCiphers('none', 'none', 'none', 'none')
178         self.currentEncryptions.setKeys('', '', '', '', '', '')
179         self.sendKexInit()
180
181
182     def sendKexInit(self):
183         self.ourKexInitPayload = (chr(MSG_KEXINIT) +
184                randbytes.secureRandom(16) +
185                NS(','.join(self.supportedKeyExchanges)) +
186                NS(','.join(self.supportedPublicKeys)) +
187                NS(','.join(self.supportedCiphers)) +
188                NS(','.join(self.supportedCiphers)) +
189                NS(','.join(self.supportedMACs)) +
190                NS(','.join(self.supportedMACs)) +
191                NS(','.join(self.supportedCompressions)) +
192                NS(','.join(self.supportedCompressions)) +
193                NS(','.join(self.supportedLanguages)) +
194                NS(','.join(self.supportedLanguages)) +
195                '\000' + '\000\000\000\000')
196         self.sendPacket(MSG_KEXINIT, self.ourKexInitPayload[1:])
197
198
199     def sendPacket(self, messageType, payload):
200         """
201         Sends a packet.  If it's been set up, compress the data, encrypt it,
202         and authenticate it before sending.
203
204         @param messageType: The type of the packet; generally one of the
205                             MSG_* values.
206         @type messageType: C{int}
207         @param payload: The payload for the message.
208         @type payload: C{str}
209         """
210         payload = chr(messageType) + payload
211         if self.outgoingCompression:
212             payload = (self.outgoingCompression.compress(payload)
213                        + self.outgoingCompression.flush(2))
214         bs = self.currentEncryptions.encBlockSize
215         # 4 for the packet length and 1 for the padding length
216         totalSize = 5 + len(payload)
217         lenPad = bs - (totalSize % bs)
218         if lenPad < 4:
219             lenPad = lenPad + bs
220         packet = (struct.pack('!LB',
221                               totalSize + lenPad - 4, lenPad) +
222                   payload + randbytes.secureRandom(lenPad))
223         encPacket = (
224             self.currentEncryptions.encrypt(packet) +
225             self.currentEncryptions.makeMAC(
226                 self.outgoingPacketSequence, packet))
227         self.transport.write(encPacket)
228         self.outgoingPacketSequence += 1
229
230
231     def getPacket(self):
232         """
233         Try to return a decrypted, authenticated, and decompressed packet
234         out of the buffer.  If there is not enough data, return None.
235
236         @rtype: C{str}/C{None}
237         """
238         bs = self.currentEncryptions.decBlockSize
239         ms = self.currentEncryptions.verifyDigestSize
240         if len(self.buf) < bs: return # not enough data
241         if not hasattr(self, 'first'):
242             first = self.currentEncryptions.decrypt(self.buf[:bs])
243         else:
244             first = self.first
245             del self.first
246         packetLen, paddingLen = struct.unpack('!LB', first[:5])
247         if packetLen > 1048576: # 1024 ** 2
248             self.sendDisconnect(DISCONNECT_PROTOCOL_ERROR,
249                                 'bad packet length %s' % packetLen)
250             return
251         if len(self.buf) < packetLen + 4 + ms:
252             self.first = first
253             return # not enough packet
254         if(packetLen + 4) % bs != 0:
255             self.sendDisconnect(
256                 DISCONNECT_PROTOCOL_ERROR,
257                 'bad packet mod (%i%%%i == %i)' % (packetLen + 4, bs,
258                                                    (packetLen + 4) % bs))
259             return
260         encData, self.buf = self.buf[:4 + packetLen], self.buf[4 + packetLen:]
261         packet = first + self.currentEncryptions.decrypt(encData[bs:])
262         if len(packet) != 4 + packetLen:
263             self.sendDisconnect(DISCONNECT_PROTOCOL_ERROR,
264                                 'bad decryption')
265             return
266         if ms:
267             macData, self.buf = self.buf[:ms], self.buf[ms:]
268             if not self.currentEncryptions.verify(self.incomingPacketSequence,
269                                                   packet, macData):
270                 self.sendDisconnect(DISCONNECT_MAC_ERROR, 'bad MAC')
271                 return
272         payload = packet[5:-paddingLen]
273         if self.incomingCompression:
274             try:
275                 payload = self.incomingCompression.decompress(payload)
276             except: # bare except, because who knows what kind of errors
277                     # decompression can raise
278                 log.err()
279                 self.sendDisconnect(DISCONNECT_COMPRESSION_ERROR,
280                                     'compression error')
281                 return
282         self.incomingPacketSequence += 1
283         return payload
284
285
286     def dataReceived(self, data):
287         """
288         First, check for the version string (SSH-2.0-*).  After that has been
289         received, this method adds data to the buffer, and pulls out any
290         packets.
291
292         @type data: C{str}
293         """
294         self.buf = self.buf + data
295         if not self.gotVersion:
296             if self.buf.find('\n', self.buf.find('SSH-')) == -1:
297                 return
298             lines = self.buf.split('\n')
299             for p in lines:
300                 if p.startswith('SSH-'):
301                     self.gotVersion = True
302                     self.otherVersionString = p.strip()
303                     if p.split('-')[1] not in ('1.99', '2.0'): # bad version
304                         self.sendDisconnect(
305                             DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
306                             'bad version ' + p.split('-')[1])
307                         return
308                     i = lines.index(p)
309                     self.buf = '\n'.join(lines[i + 1:])
310         packet = self.getPacket()
311         while packet:
312             messageNum = ord(packet[0])
313             self.dispatchMessage(messageNum, packet[1:])
314             packet = self.getPacket()
315
316
317     def dispatchMessage(self, messageNum, payload):
318         """
319         Send a received message to the appropriate method.
320
321         @type messageNum: C{int}
322         @type payload: c{str}
323         """
324         if messageNum < 50 and messageNum in messages:
325             messageType = messages[messageNum][4:]
326             f = getattr(self, 'ssh_%s' % messageType, None)
327             if f is not None:
328                 f(payload)
329             else:
330                 log.msg("couldn't handle %s" % messageType)
331                 log.msg(repr(payload))
332                 self.sendUnimplemented()
333         elif self.service:
334             log.callWithLogger(self.service, self.service.packetReceived,
335                                messageNum, payload)
336         else:
337             log.msg("couldn't handle %s" % messageNum)
338             log.msg(repr(payload))
339             self.sendUnimplemented()
340
341
342     def ssh_KEXINIT(self, packet):
343         """
344         Called when we receive a MSG_KEXINIT message.  Payload::
345             bytes[16] cookie
346             string keyExchangeAlgorithms
347             string keyAlgorithms
348             string incomingEncryptions
349             string outgoingEncryptions
350             string incomingAuthentications
351             string outgoingAuthentications
352             string incomingCompressions
353             string outgoingCompressions
354             string incomingLanguages
355             string outgoingLanguages
356             bool firstPacketFollows
357             unit32 0 (reserved)
358
359         Starts setting up the key exchange, keys, encryptions, and
360         authentications.  Extended by ssh_KEXINIT in SSHServerTransport and
361         SSHClientTransport.
362         """
363         self.otherKexInitPayload = chr(MSG_KEXINIT) + packet
364         #cookie = packet[: 16] # taking this is useless
365         k = getNS(packet[16:], 10)
366         strings, rest = k[:-1], k[-1]
367         (kexAlgs, keyAlgs, encCS, encSC, macCS, macSC, compCS, compSC, langCS,
368          langSC) = [s.split(',') for s in strings]
369         # these are the server directions
370         outs = [encSC, macSC, compSC]
371         ins = [encCS, macSC, compCS]
372         if self.isClient:
373             outs, ins = ins, outs # switch directions
374         server = (self.supportedKeyExchanges, self.supportedPublicKeys,
375                 self.supportedCiphers, self.supportedCiphers,
376                 self.supportedMACs, self.supportedMACs,
377                 self.supportedCompressions, self.supportedCompressions)
378         client = (kexAlgs, keyAlgs, outs[0], ins[0], outs[1], ins[1],
379                 outs[2], ins[2])
380         if self.isClient:
381             server, client = client, server
382         self.kexAlg = ffs(client[0], server[0])
383         self.keyAlg = ffs(client[1], server[1])
384         self.nextEncryptions = SSHCiphers(
385             ffs(client[2], server[2]),
386             ffs(client[3], server[3]),
387             ffs(client[4], server[4]),
388             ffs(client[5], server[5]))
389         self.outgoingCompressionType = ffs(client[6], server[6])
390         self.incomingCompressionType = ffs(client[7], server[7])
391         if None in (self.kexAlg, self.keyAlg, self.outgoingCompressionType,
392                     self.incomingCompressionType):
393             self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED,
394                                 "couldn't match all kex parts")
395             return
396         if None in self.nextEncryptions.__dict__.values():
397             self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED,
398                                 "couldn't match all kex parts")
399             return
400         log.msg('kex alg, key alg: %s %s' % (self.kexAlg, self.keyAlg))
401         log.msg('outgoing: %s %s %s' % (self.nextEncryptions.outCipType,
402                                         self.nextEncryptions.outMACType,
403                                         self.outgoingCompressionType))
404         log.msg('incoming: %s %s %s' % (self.nextEncryptions.inCipType,
405                                         self.nextEncryptions.inMACType,
406                                         self.incomingCompressionType))
407         return kexAlgs, keyAlgs, rest # for SSHServerTransport to use
408
409
410     def ssh_DISCONNECT(self, packet):
411         """
412         Called when we receive a MSG_DISCONNECT message.  Payload::
413             long code
414             string description
415
416         This means that the other side has disconnected.  Pass the message up
417         and disconnect ourselves.
418         """
419         reasonCode = struct.unpack('>L', packet[: 4])[0]
420         description, foo = getNS(packet[4:])
421         self.receiveError(reasonCode, description)
422         self.transport.loseConnection()
423
424
425     def ssh_IGNORE(self, packet):
426         """
427         Called when we receieve a MSG_IGNORE message.  No payload.
428         This means nothing; we simply return.
429         """
430
431
432     def ssh_UNIMPLEMENTED(self, packet):
433         """
434         Called when we receieve a MSG_UNIMPLEMENTED message.  Payload::
435             long packet
436
437         This means that the other side did not implement one of our packets.
438         """
439         seqnum, = struct.unpack('>L', packet)
440         self.receiveUnimplemented(seqnum)
441
442
443     def ssh_DEBUG(self, packet):
444         """
445         Called when we receieve a MSG_DEBUG message.  Payload::
446             bool alwaysDisplay
447             string message
448             string language
449
450         This means the other side has passed along some debugging info.
451         """
452         alwaysDisplay = bool(packet[0])
453         message, lang, foo = getNS(packet[1:], 2)
454         self.receiveDebug(alwaysDisplay, message, lang)
455
456
457     def setService(self, service):
458         """
459         Set our service to service and start it running.  If we were
460         running a service previously, stop it first.
461
462         @type service: C{SSHService}
463         """
464         log.msg('starting service %s' % service.name)
465         if self.service:
466             self.service.serviceStopped()
467         self.service = service
468         service.transport = self
469         self.service.serviceStarted()
470
471
472     def sendDebug(self, message, alwaysDisplay=False, language=''):
473         """
474         Send a debug message to the other side.
475
476         @param message: the message to send.
477         @type message: C{str}
478         @param alwaysDisplay: if True, tell the other side to always
479                               display this message.
480         @type alwaysDisplay: C{bool}
481         @param language: optionally, the language the message is in.
482         @type language: C{str}
483         """
484         self.sendPacket(MSG_DEBUG, chr(alwaysDisplay) + NS(message) +
485                         NS(language))
486
487
488     def sendIgnore(self, message):
489         """
490         Send a message that will be ignored by the other side.  This is
491         useful to fool attacks based on guessing packet sizes in the
492         encrypted stream.
493
494         @param message: data to send with the message
495         @type message: C{str}
496         """
497         self.sendPacket(MSG_IGNORE, NS(message))
498
499
500     def sendUnimplemented(self):
501         """
502         Send a message to the other side that the last packet was not
503         understood.
504         """
505         seqnum = self.incomingPacketSequence
506         self.sendPacket(MSG_UNIMPLEMENTED, struct.pack('!L', seqnum))
507
508
509     def sendDisconnect(self, reason, desc):
510         """
511         Send a disconnect message to the other side and then disconnect.
512
513         @param reason: the reason for the disconnect.  Should be one of the
514                        DISCONNECT_* values.
515         @type reason: C{int}
516         @param desc: a descrption of the reason for the disconnection.
517         @type desc: C{str}
518         """
519         self.sendPacket(
520             MSG_DISCONNECT, struct.pack('>L', reason) + NS(desc) + NS(''))
521         log.msg('Disconnecting with error, code %s\nreason: %s' % (reason,
522                                                                    desc))
523         self.transport.loseConnection()
524
525
526     def _getKey(self, c, sharedSecret, exchangeHash):
527         """
528         Get one of the keys for authentication/encryption.
529
530         @type c: C{str}
531         @type sharedSecret: C{str}
532         @type exchangeHash: C{str}
533         """
534         k1 = sha1(sharedSecret + exchangeHash + c + self.sessionID)
535         k1 = k1.digest()
536         k2 = sha1(sharedSecret + exchangeHash + k1).digest()
537         return k1 + k2
538
539
540     def _keySetup(self, sharedSecret, exchangeHash):
541         """
542         Set up the keys for the connection and sends MSG_NEWKEYS when
543         finished,
544
545         @param sharedSecret: a secret string agreed upon using a Diffie-
546                              Hellman exchange, so it is only shared between
547                              the server and the client.
548         @type sharedSecret: C{str}
549         @param exchangeHash: A hash of various data known by both sides.
550         @type exchangeHash: C{str}
551         """
552         if not self.sessionID:
553             self.sessionID = exchangeHash
554         initIVCS = self._getKey('A', sharedSecret, exchangeHash)
555         initIVSC = self._getKey('B', sharedSecret, exchangeHash)
556         encKeyCS = self._getKey('C', sharedSecret, exchangeHash)
557         encKeySC = self._getKey('D', sharedSecret, exchangeHash)
558         integKeyCS = self._getKey('E', sharedSecret, exchangeHash)
559         integKeySC = self._getKey('F', sharedSecret, exchangeHash)
560         outs = [initIVSC, encKeySC, integKeySC]
561         ins = [initIVCS, encKeyCS, integKeyCS]
562         if self.isClient: # reverse for the client
563             log.msg('REVERSE')
564             outs, ins = ins, outs
565         self.nextEncryptions.setKeys(outs[0], outs[1], ins[0], ins[1],
566                                      outs[2], ins[2])
567         self.sendPacket(MSG_NEWKEYS, '')
568
569
570     def isEncrypted(self, direction="out"):
571         """
572         Return True if the connection is encrypted in the given direction.
573         Direction must be one of ["out", "in", "both"].
574         """
575         if direction == "out":
576             return self.currentEncryptions.outCipType != 'none'
577         elif direction == "in":
578             return self.currentEncryptions.inCipType != 'none'
579         elif direction == "both":
580             return self.isEncrypted("in") and self.isEncrypted("out")
581         else:
582             raise TypeError('direction must be "out", "in", or "both"')
583
584
585     def isVerified(self, direction="out"):
586         """
587         Return True if the connecction is verified/authenticated in the
588         given direction.  Direction must be one of ["out", "in", "both"].
589         """
590         if direction == "out":
591             return self.currentEncryptions.outMACType != 'none'
592         elif direction == "in":
593             return self.currentEncryptions.inMACType != 'none'
594         elif direction == "both":
595             return self.isVerified("in")and self.isVerified("out")
596         else:
597             raise TypeError('direction must be "out", "in", or "both"')
598
599
600     def loseConnection(self):
601         """
602         Lose the connection to the other side, sending a
603         DISCONNECT_CONNECTION_LOST message.
604         """
605         self.sendDisconnect(DISCONNECT_CONNECTION_LOST,
606                             "user closed connection")
607
608
609     # client methods
610     def receiveError(self, reasonCode, description):
611         """
612         Called when we receive a disconnect error message from the other
613         side.
614
615         @param reasonCode: the reason for the disconnect, one of the
616                            DISCONNECT_ values.
617         @type reasonCode: C{int}
618         @param description: a human-readable description of the
619                             disconnection.
620         @type description: C{str}
621         """
622         log.msg('Got remote error, code %s\nreason: %s' % (reasonCode,
623                                                            description))
624
625
626     def receiveUnimplemented(self, seqnum):
627         """
628         Called when we receive an unimplemented packet message from the other
629         side.
630
631         @param seqnum: the sequence number that was not understood.
632         @type seqnum: C{int}
633         """
634         log.msg('other side unimplemented packet #%s' % seqnum)
635
636
637     def receiveDebug(self, alwaysDisplay, message, lang):
638         """
639         Called when we receive a debug message from the other side.
640
641         @param alwaysDisplay: if True, this message should always be
642                               displayed.
643         @type alwaysDisplay: C{bool}
644         @param message: the debug message
645         @type message: C{str}
646         @param lang: optionally the language the message is in.
647         @type lang: C{str}
648         """
649         if alwaysDisplay:
650             log.msg('Remote Debug Message: %s' % message)
651
652
653
654 class SSHServerTransport(SSHTransportBase):
655     """
656     SSHServerTransport implements the server side of the SSH protocol.
657
658     @ivar isClient: since we are never the client, this is always False.
659
660     @ivar ignoreNextPacket: if True, ignore the next key exchange packet.  This
661         is set when the client sends a guessed key exchange packet but with
662         an incorrect guess.
663
664     @ivar dhGexRequest: the KEX_DH_GEX_REQUEST(_OLD) that the client sent.
665         The key generation needs this to be stored.
666
667     @ivar g: the Diffie-Hellman group generator.
668
669     @ivar p: the Diffie-Hellman group prime.
670     """
671     isClient = False
672     ignoreNextPacket = 0
673
674
675     def ssh_KEXINIT(self, packet):
676         """
677         Called when we receive a MSG_KEXINIT message.  For a description
678         of the packet, see SSHTransportBase.ssh_KEXINIT().  Additionally,
679         this method checks if a guessed key exchange packet was sent.  If
680         it was sent, and it guessed incorrectly, the next key exchange
681         packet MUST be ignored.
682         """
683         retval = SSHTransportBase.ssh_KEXINIT(self, packet)
684         if not retval: # disconnected
685             return
686         else:
687             kexAlgs, keyAlgs, rest = retval
688         if ord(rest[0]): # first_kex_packet_follows
689             if (kexAlgs[0] != self.supportedKeyExchanges[0] or
690                 keyAlgs[0] != self.supportedPublicKeys[0]):
691                 self.ignoreNextPacket = True # guess was wrong
692
693
694     def ssh_KEX_DH_GEX_REQUEST_OLD(self, packet):
695         """
696         This represents two different key exchange methods that share the
697         same integer value.
698
699         KEXDH_INIT (for diffie-hellman-group1-sha1 exchanges) payload::
700
701                 integer e (the client's Diffie-Hellman public key)
702
703             We send the KEXDH_REPLY with our host key and signature.
704
705         KEX_DH_GEX_REQUEST_OLD (for diffie-hellman-group-exchange-sha1)
706         payload::
707
708                 integer ideal (ideal size for the Diffie-Hellman prime)
709
710             We send the KEX_DH_GEX_GROUP message with the group that is
711             closest in size to ideal.
712
713         If we were told to ignore the next key exchange packet by
714         ssh_KEXINIT, drop it on the floor and return.
715         """
716         if self.ignoreNextPacket:
717             self.ignoreNextPacket = 0
718             return
719         if self.kexAlg == 'diffie-hellman-group1-sha1':
720             # this is really KEXDH_INIT
721             clientDHpublicKey, foo = getMP(packet)
722             y = Util.number.getRandomNumber(512, randbytes.secureRandom)
723             serverDHpublicKey = _MPpow(DH_GENERATOR, y, DH_PRIME)
724             sharedSecret = _MPpow(clientDHpublicKey, y, DH_PRIME)
725             h = sha1()
726             h.update(NS(self.otherVersionString))
727             h.update(NS(self.ourVersionString))
728             h.update(NS(self.otherKexInitPayload))
729             h.update(NS(self.ourKexInitPayload))
730             h.update(NS(self.factory.publicKeys[self.keyAlg].blob()))
731             h.update(MP(clientDHpublicKey))
732             h.update(serverDHpublicKey)
733             h.update(sharedSecret)
734             exchangeHash = h.digest()
735             self.sendPacket(
736                 MSG_KEXDH_REPLY,
737                 NS(self.factory.publicKeys[self.keyAlg].blob()) +
738                 serverDHpublicKey +
739                 NS(self.factory.privateKeys[self.keyAlg].sign(exchangeHash)))
740             self._keySetup(sharedSecret, exchangeHash)
741         elif self.kexAlg == 'diffie-hellman-group-exchange-sha1':
742             self.dhGexRequest = packet
743             ideal = struct.unpack('>L', packet)[0]
744             self.g, self.p = self.factory.getDHPrime(ideal)
745             self.sendPacket(MSG_KEX_DH_GEX_GROUP, MP(self.p) + MP(self.g))
746         else:
747             raise error.ConchError('bad kexalg: %s' % self.kexAlg)
748
749
750     def ssh_KEX_DH_GEX_REQUEST(self, packet):
751         """
752         Called when we receive a MSG_KEX_DH_GEX_REQUEST message.  Payload::
753             integer minimum
754             integer ideal
755             integer maximum
756
757         The client is asking for a Diffie-Hellman group between minimum and
758         maximum size, and close to ideal if possible.  We reply with a
759         MSG_KEX_DH_GEX_GROUP message.
760
761         If we were told to ignore the next key exchange packekt by
762         ssh_KEXINIT, drop it on the floor and return.
763         """
764         if self.ignoreNextPacket:
765             self.ignoreNextPacket = 0
766             return
767         self.dhGexRequest = packet
768         min, ideal, max = struct.unpack('>3L', packet)
769         self.g, self.p = self.factory.getDHPrime(ideal)
770         self.sendPacket(MSG_KEX_DH_GEX_GROUP, MP(self.p) + MP(self.g))
771
772
773     def ssh_KEX_DH_GEX_INIT(self, packet):
774         """
775         Called when we get a MSG_KEX_DH_GEX_INIT message.  Payload::
776             integer e (client DH public key)
777
778         We send the MSG_KEX_DH_GEX_REPLY message with our host key and
779         signature.
780         """
781         clientDHpublicKey, foo = getMP(packet)
782         # TODO: we should also look at the value they send to us and reject
783         # insecure values of f (if g==2 and f has a single '1' bit while the
784         # rest are '0's, then they must have used a small y also).
785
786         # TODO: This could be computed when self.p is set up
787         #  or do as openssh does and scan f for a single '1' bit instead
788
789         pSize = Util.number.size(self.p)
790         y = Util.number.getRandomNumber(pSize, randbytes.secureRandom)
791
792         serverDHpublicKey = _MPpow(self.g, y, self.p)
793         sharedSecret = _MPpow(clientDHpublicKey, y, self.p)
794         h = sha1()
795         h.update(NS(self.otherVersionString))
796         h.update(NS(self.ourVersionString))
797         h.update(NS(self.otherKexInitPayload))
798         h.update(NS(self.ourKexInitPayload))
799         h.update(NS(self.factory.publicKeys[self.keyAlg].blob()))
800         h.update(self.dhGexRequest)
801         h.update(MP(self.p))
802         h.update(MP(self.g))
803         h.update(MP(clientDHpublicKey))
804         h.update(serverDHpublicKey)
805         h.update(sharedSecret)
806         exchangeHash = h.digest()
807         self.sendPacket(
808             MSG_KEX_DH_GEX_REPLY,
809             NS(self.factory.publicKeys[self.keyAlg].blob()) +
810             serverDHpublicKey +
811             NS(self.factory.privateKeys[self.keyAlg].sign(exchangeHash)))
812         self._keySetup(sharedSecret, exchangeHash)
813
814
815     def ssh_NEWKEYS(self, packet):
816         """
817         Called when we get a MSG_NEWKEYS message.  No payload.
818         When we get this, the keys have been set on both sides, and we
819         start using them to encrypt and authenticate the connection.
820         """
821         log.msg('NEW KEYS')
822         if packet != '':
823             self.sendDisconnect(DISCONNECT_PROTOCOL_ERROR,
824                                 "NEWKEYS takes no data")
825             return
826         self.currentEncryptions = self.nextEncryptions
827         if self.outgoingCompressionType == 'zlib':
828             self.outgoingCompression = zlib.compressobj(6)
829         if self.incomingCompressionType == 'zlib':
830             self.incomingCompression = zlib.decompressobj()
831
832
833     def ssh_SERVICE_REQUEST(self, packet):
834         """
835         Called when we get a MSG_SERVICE_REQUEST message.  Payload::
836             string serviceName
837
838         The client has requested a service.  If we can start the service,
839         start it; otherwise, disconnect with
840         DISCONNECT_SERVICE_NOT_AVAILABLE.
841         """
842         service, rest = getNS(packet)
843         cls = self.factory.getService(self, service)
844         if not cls:
845             self.sendDisconnect(DISCONNECT_SERVICE_NOT_AVAILABLE,
846                                 "don't have service %s" % service)
847             return
848         else:
849             self.sendPacket(MSG_SERVICE_ACCEPT, NS(service))
850             self.setService(cls())
851
852
853
854 class SSHClientTransport(SSHTransportBase):
855     """
856     SSHClientTransport implements the client side of the SSH protocol.
857
858     @ivar isClient: since we are always the client, this is always True.
859
860     @ivar _gotNewKeys: if we receive a MSG_NEWKEYS message before we are
861         ready to transition to the new keys, this is set to True so we
862         can transition when the keys are ready locally.
863
864     @ivar x: our Diffie-Hellman private key.
865
866     @ivar e: our Diffie-Hellman public key.
867
868     @ivar g: the Diffie-Hellman group generator.
869
870     @ivar p: the Diffie-Hellman group prime
871
872     @ivar instance: the SSHService object we are requesting.
873     """
874     isClient = True
875
876
877     def connectionMade(self):
878         """
879         Called when the connection is started with the server.  Just sets
880         up a private instance variable.
881         """
882         SSHTransportBase.connectionMade(self)
883         self._gotNewKeys = 0
884
885
886     def ssh_KEXINIT(self, packet):
887         """
888         Called when we receive a MSG_KEXINIT message.  For a description
889         of the packet, see SSHTransportBase.ssh_KEXINIT().  Additionally,
890         this method sends the first key exchange packet.  If the agreed-upon
891         exchange is diffie-hellman-group1-sha1, generate a public key
892         and send it in a MSG_KEXDH_INIT message.  If the exchange is
893         diffie-hellman-group-exchange-sha1, ask for a 2048 bit group with a
894         MSG_KEX_DH_GEX_REQUEST_OLD message.
895         """
896         if SSHTransportBase.ssh_KEXINIT(self, packet) is None:
897             return # we disconnected
898         if self.kexAlg == 'diffie-hellman-group1-sha1':
899             self.x = Util.number.getRandomNumber(512, randbytes.secureRandom)
900             self.e = _MPpow(DH_GENERATOR, self.x, DH_PRIME)
901             self.sendPacket(MSG_KEXDH_INIT, self.e)
902         elif self.kexAlg == 'diffie-hellman-group-exchange-sha1':
903             self.sendPacket(MSG_KEX_DH_GEX_REQUEST_OLD, '\x00\x00\x08\x00')
904         else:
905             raise error.ConchError("somehow, the kexAlg has been set "
906                                    "to something we don't support")
907
908
909     def ssh_KEX_DH_GEX_GROUP(self, packet):
910         """
911         This handles two different message which share an integer value.
912         If the key exchange is diffie-hellman-group1-sha1, this is
913         MSG_KEXDH_REPLY.  Payload::
914             string serverHostKey
915             integer f (server Diffie-Hellman public key)
916             string signature
917
918         We verify the host key by calling verifyHostKey, then continue in
919         _continueKEXDH_REPLY.
920
921         If the key exchange is diffie-hellman-group-exchange-sha1, this is
922         MSG_KEX_DH_GEX_GROUP.  Payload::
923             string g (group generator)
924             string p (group prime)
925
926         We generate a Diffie-Hellman public key and send it in a
927         MSG_KEX_DH_GEX_INIT message.
928         """
929         if self.kexAlg == 'diffie-hellman-group1-sha1':
930             # actually MSG_KEXDH_REPLY
931             pubKey, packet = getNS(packet)
932             f, packet = getMP(packet)
933             signature, packet = getNS(packet)
934             fingerprint = ':'.join([ch.encode('hex') for ch in
935                                     md5(pubKey).digest()])
936             d = self.verifyHostKey(pubKey, fingerprint)
937             d.addCallback(self._continueKEXDH_REPLY, pubKey, f, signature)
938             d.addErrback(
939                 lambda unused: self.sendDisconnect(
940                     DISCONNECT_HOST_KEY_NOT_VERIFIABLE, 'bad host key'))
941             return d
942         else:
943             self.p, rest = getMP(packet)
944             self.g, rest = getMP(rest)
945             self.x = Util.number.getRandomNumber(320, randbytes.secureRandom)
946             self.e = _MPpow(self.g, self.x, self.p)
947             self.sendPacket(MSG_KEX_DH_GEX_INIT, self.e)
948
949
950     def _continueKEXDH_REPLY(self, ignored, pubKey, f, signature):
951         """
952         The host key has been verified, so we generate the keys.
953
954         @param pubKey: the public key blob for the server's public key.
955         @type pubKey: C{str}
956         @param f: the server's Diffie-Hellman public key.
957         @type f: C{long}
958         @param signature: the server's signature, verifying that it has the
959             correct private key.
960         @type signature: C{str}
961         """
962         serverKey = keys.Key.fromString(pubKey)
963         sharedSecret = _MPpow(f, self.x, DH_PRIME)
964         h = sha1()
965         h.update(NS(self.ourVersionString))
966         h.update(NS(self.otherVersionString))
967         h.update(NS(self.ourKexInitPayload))
968         h.update(NS(self.otherKexInitPayload))
969         h.update(NS(pubKey))
970         h.update(self.e)
971         h.update(MP(f))
972         h.update(sharedSecret)
973         exchangeHash = h.digest()
974         if not serverKey.verify(signature, exchangeHash):
975             self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED,
976                                 'bad signature')
977             return
978         self._keySetup(sharedSecret, exchangeHash)
979
980
981     def ssh_KEX_DH_GEX_REPLY(self, packet):
982         """
983         Called when we receieve a MSG_KEX_DH_GEX_REPLY message.  Payload::
984             string server host key
985             integer f (server DH public key)
986
987         We verify the host key by calling verifyHostKey, then continue in
988         _continueGEX_REPLY.
989         """
990         pubKey, packet = getNS(packet)
991         f, packet = getMP(packet)
992         signature, packet = getNS(packet)
993         fingerprint = ':'.join(map(lambda c: '%02x'%ord(c),
994             md5(pubKey).digest()))
995         d = self.verifyHostKey(pubKey, fingerprint)
996         d.addCallback(self._continueGEX_REPLY, pubKey, f, signature)
997         d.addErrback(
998             lambda unused: self.sendDisconnect(
999                 DISCONNECT_HOST_KEY_NOT_VERIFIABLE, 'bad host key'))
1000         return d
1001
1002
1003     def _continueGEX_REPLY(self, ignored, pubKey, f, signature):
1004         """
1005         The host key has been verified, so we generate the keys.
1006
1007         @param pubKey: the public key blob for the server's public key.
1008         @type pubKey: C{str}
1009         @param f: the server's Diffie-Hellman public key.
1010         @type f: C{long}
1011         @param signature: the server's signature, verifying that it has the
1012             correct private key.
1013         @type signature: C{str}
1014         """
1015         serverKey = keys.Key.fromString(pubKey)
1016         sharedSecret = _MPpow(f, self.x, self.p)
1017         h = sha1()
1018         h.update(NS(self.ourVersionString))
1019         h.update(NS(self.otherVersionString))
1020         h.update(NS(self.ourKexInitPayload))
1021         h.update(NS(self.otherKexInitPayload))
1022         h.update(NS(pubKey))
1023         h.update('\x00\x00\x08\x00')
1024         h.update(MP(self.p))
1025         h.update(MP(self.g))
1026         h.update(self.e)
1027         h.update(MP(f))
1028         h.update(sharedSecret)
1029         exchangeHash = h.digest()
1030         if not serverKey.verify(signature, exchangeHash):
1031             self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED,
1032                                 'bad signature')
1033             return
1034         self._keySetup(sharedSecret, exchangeHash)
1035
1036
1037     def _keySetup(self, sharedSecret, exchangeHash):
1038         """
1039         See SSHTransportBase._keySetup().
1040         """
1041         SSHTransportBase._keySetup(self, sharedSecret, exchangeHash)
1042         if self._gotNewKeys:
1043             self.ssh_NEWKEYS('')
1044
1045
1046     def ssh_NEWKEYS(self, packet):
1047         """
1048         Called when we receieve a MSG_NEWKEYS message.  No payload.
1049         If we've finished setting up our own keys, start using them.
1050         Otherwise, remeber that we've receieved this message.
1051         """
1052         if packet != '':
1053             self.sendDisconnect(DISCONNECT_PROTOCOL_ERROR,
1054                                 "NEWKEYS takes no data")
1055             return
1056         if not self.nextEncryptions.encBlockSize:
1057             self._gotNewKeys = 1
1058             return
1059         log.msg('NEW KEYS')
1060         self.currentEncryptions = self.nextEncryptions
1061         if self.outgoingCompressionType == 'zlib':
1062             self.outgoingCompression = zlib.compressobj(6)
1063         if self.incomingCompressionType == 'zlib':
1064             self.incomingCompression = zlib.decompressobj()
1065         self.connectionSecure()
1066
1067
1068     def ssh_SERVICE_ACCEPT(self, packet):
1069         """
1070         Called when we receieve a MSG_SERVICE_ACCEPT message.  Payload::
1071             string service name
1072
1073         Start the service we requested.
1074         """
1075         name = getNS(packet)[0]
1076         if name != self.instance.name:
1077             self.sendDisconnect(
1078                 DISCONNECT_PROTOCOL_ERROR,
1079                 "received accept for service we did not request")
1080         self.setService(self.instance)
1081
1082
1083     def requestService(self, instance):
1084         """
1085         Request that a service be run over this transport.
1086
1087         @type instance: subclass of L{twisted.conch.ssh.service.SSHService}
1088         """
1089         self.sendPacket(MSG_SERVICE_REQUEST, NS(instance.name))
1090         self.instance = instance
1091
1092
1093     # client methods
1094     def verifyHostKey(self, hostKey, fingerprint):
1095         """
1096         Returns a Deferred that gets a callback if it is a valid key, or
1097         an errback if not.
1098
1099         @type hostKey:      C{str}
1100         @type fingerprint:  C{str}
1101         @rtype:             L{twisted.internet.defer.Deferred}
1102         """
1103         # return if it's good
1104         return defer.fail(NotImplementedError())
1105
1106
1107     def connectionSecure(self):
1108         """
1109         Called when the encryption has been set up.  Generally,
1110         requestService() is called to run another service over the transport.
1111         """
1112         raise NotImplementedError()
1113
1114
1115
1116 class _DummyCipher:
1117     """
1118     A cipher for the none encryption method.
1119
1120     @ivar block_size: the block size of the encryption.  In the case of the
1121     none cipher, this is 8 bytes.
1122     """
1123     block_size = 8
1124
1125
1126     def encrypt(self, x):
1127         return x
1128
1129
1130     decrypt = encrypt
1131
1132
1133 class SSHCiphers:
1134     """
1135     SSHCiphers represents all the encryption operations that need to occur
1136     to encrypt and authenticate the SSH connection.
1137
1138     @cvar cipherMap: A dictionary mapping SSH encryption names to 3-tuples of
1139                      (<Crypto.Cipher.* name>, <block size>, <counter mode>)
1140     @cvar macMap: A dictionary mapping SSH MAC names to hash modules.
1141
1142     @ivar outCipType: the string type of the outgoing cipher.
1143     @ivar inCipType: the string type of the incoming cipher.
1144     @ivar outMACType: the string type of the incoming MAC.
1145     @ivar inMACType: the string type of the incoming MAC.
1146     @ivar encBlockSize: the block size of the outgoing cipher.
1147     @ivar decBlockSize: the block size of the incoming cipher.
1148     @ivar verifyDigestSize: the size of the incoming MAC.
1149     @ivar outMAC: a tuple of (<hash module>, <inner key>, <outer key>,
1150         <digest size>) representing the outgoing MAC.
1151     @ivar inMAc: see outMAC, but for the incoming MAC.
1152     """
1153
1154
1155     cipherMap = {
1156         '3des-cbc':('DES3', 24, 0),
1157         'blowfish-cbc':('Blowfish', 16,0 ),
1158         'aes256-cbc':('AES', 32, 0),
1159         'aes192-cbc':('AES', 24, 0),
1160         'aes128-cbc':('AES', 16, 0),
1161         'cast128-cbc':('CAST', 16, 0),
1162         'aes128-ctr':('AES', 16, 1),
1163         'aes192-ctr':('AES', 24, 1),
1164         'aes256-ctr':('AES', 32, 1),
1165         '3des-ctr':('DES3', 24, 1),
1166         'blowfish-ctr':('Blowfish', 16, 1),
1167         'cast128-ctr':('CAST', 16, 1),
1168         'none':(None, 0, 0),
1169     }
1170     macMap = {
1171         'hmac-sha1': sha1,
1172         'hmac-md5': md5,
1173         'none': None
1174      }
1175
1176
1177     def __init__(self, outCip, inCip, outMac, inMac):
1178         self.outCipType = outCip
1179         self.inCipType = inCip
1180         self.outMACType = outMac
1181         self.inMACType = inMac
1182         self.encBlockSize = 0
1183         self.decBlockSize = 0
1184         self.verifyDigestSize = 0
1185         self.outMAC = (None, '', '', 0)
1186         self.inMAC = (None, '', '', 0)
1187
1188
1189     def setKeys(self, outIV, outKey, inIV, inKey, outInteg, inInteg):
1190         """
1191         Set up the ciphers and hashes using the given keys,
1192
1193         @param outIV: the outgoing initialization vector
1194         @param outKey: the outgoing encryption key
1195         @param inIV: the incoming initialization vector
1196         @param inKey: the incoming encryption key
1197         @param outInteg: the outgoing integrity key
1198         @param inInteg: the incoming integrity key.
1199         """
1200         o = self._getCipher(self.outCipType, outIV, outKey)
1201         self.encrypt = o.encrypt
1202         self.encBlockSize = o.block_size
1203         o = self._getCipher(self.inCipType, inIV, inKey)
1204         self.decrypt = o.decrypt
1205         self.decBlockSize = o.block_size
1206         self.outMAC = self._getMAC(self.outMACType, outInteg)
1207         self.inMAC = self._getMAC(self.inMACType, inInteg)
1208         if self.inMAC:
1209             self.verifyDigestSize = self.inMAC[3]
1210
1211
1212     def _getCipher(self, cip, iv, key):
1213         """
1214         Creates an initialized cipher object.
1215
1216         @param cip: the name of the cipher: maps into Crypto.Cipher.*
1217         @param iv: the initialzation vector
1218         @param key: the encryption key
1219         """
1220         modName, keySize, counterMode = self.cipherMap[cip]
1221         if not modName: # no cipher
1222             return _DummyCipher()
1223         mod = __import__('Crypto.Cipher.%s'%modName, {}, {}, 'x')
1224         if counterMode:
1225             return mod.new(key[:keySize], mod.MODE_CTR, iv[:mod.block_size],
1226                            counter=_Counter(iv, mod.block_size))
1227         else:
1228             return mod.new(key[:keySize], mod.MODE_CBC, iv[:mod.block_size])
1229
1230
1231     def _getMAC(self, mac, key):
1232         """
1233         Gets a 4-tuple representing the message authentication code.
1234         (<hash module>, <inner hash value>, <outer hash value>,
1235         <digest size>)
1236
1237         @param mac: a key mapping into macMap
1238         @type mac: C{str}
1239         @param key: the MAC key.
1240         @type key: C{str}
1241         """
1242         mod = self.macMap[mac]
1243         if not mod:
1244             return (None, '', '', 0)
1245         ds = mod().digest_size
1246         key = key[:ds] + '\x00' * (64 - ds)
1247         i = XOR.new('\x36').encrypt(key)
1248         o = XOR.new('\x5c').encrypt(key)
1249         return mod, i, o, ds
1250
1251
1252     def encrypt(self, blocks):
1253         """
1254         Encrypt blocks.  Overridden by the encrypt method of a
1255         Crypto.Cipher.* object in setKeys().
1256
1257         @type blocks: C{str}
1258         """
1259         raise NotImplementedError()
1260
1261
1262     def decrypt(self, blocks):
1263         """
1264         Decrypt blocks.  See encrypt().
1265
1266         @type blocks: C{str}
1267         """
1268         raise NotImplementedError()
1269
1270
1271     def makeMAC(self, seqid, data):
1272         """
1273         Create a message authentication code (MAC) for the given packet using
1274         the outgoing MAC values.
1275
1276         @param seqid: the sequence ID of the outgoing packet
1277         @type seqid: C{int}
1278         @param data: the data to create a MAC for
1279         @type data: C{str}
1280         @rtype: C{str}
1281         """
1282         if not self.outMAC[0]:
1283             return ''
1284         data = struct.pack('>L', seqid) + data
1285         mod, i, o, ds = self.outMAC
1286         inner = mod(i + data)
1287         outer = mod(o + inner.digest())
1288         return outer.digest()
1289
1290
1291     def verify(self, seqid, data, mac):
1292         """
1293         Verify an incoming MAC using the incoming MAC values.  Return True
1294         if the MAC is valid.
1295
1296         @param seqid: the sequence ID of the incoming packet
1297         @type seqid: C{int}
1298         @param data: the packet data to verify
1299         @type data: C{str}
1300         @param mac: the MAC sent with the packet
1301         @type mac: C{str}
1302         @rtype: C{bool}
1303         """
1304         if not self.inMAC[0]:
1305             return mac == ''
1306         data = struct.pack('>L', seqid) + data
1307         mod, i, o, ds = self.inMAC
1308         inner = mod(i + data)
1309         outer = mod(o + inner.digest())
1310         return mac == outer.digest()
1311
1312
1313
1314 class _Counter:
1315     """
1316     Stateful counter which returns results packed in a byte string
1317     """
1318
1319
1320     def __init__(self, initialVector, blockSize):
1321         """
1322         @type initialVector: C{str}
1323         @param initialVector: A byte string representing the initial counter
1324                               value.
1325         @type blockSize: C{int}
1326         @param blockSize: The length of the output buffer, as well as the
1327         number of bytes at the beginning of C{initialVector} to consider.
1328         """
1329         initialVector = initialVector[:blockSize]
1330         self.count = getMP('\xff\xff\xff\xff' + initialVector)[0]
1331         self.blockSize = blockSize
1332         self.count = Util.number.long_to_bytes(self.count - 1)
1333         self.count = '\x00' * (self.blockSize - len(self.count)) + self.count
1334         self.count = array.array('c', self.count)
1335         self.len = len(self.count) - 1
1336
1337
1338     def __call__(self):
1339         """
1340         Increment the counter and return the new value.
1341         """
1342         i = self.len
1343         while i > -1:
1344             self.count[i] = n = chr((ord(self.count[i]) + 1) % 256)
1345             if n == '\x00':
1346                 i -= 1
1347             else:
1348                 return self.count.tostring()
1349
1350         self.count = array.array('c', '\x00' * self.blockSize)
1351         return self.count.tostring()
1352
1353
1354
1355 # Diffie-Hellman primes from Oakley Group 2 [RFC 2409]
1356 DH_PRIME = long('17976931348623159077083915679378745319786029604875601170644'
1357 '442368419718021615851936894783379586492554150218056548598050364644054819923'
1358 '910005079287700335581663922955313623907650873575991482257486257500742530207'
1359 '744771258955095793777842444242661733472762929938766870920560605027081084290'
1360 '7692932019128194467627007L')
1361 DH_GENERATOR = 2L
1362
1363
1364
1365 MSG_DISCONNECT = 1
1366 MSG_IGNORE = 2
1367 MSG_UNIMPLEMENTED = 3
1368 MSG_DEBUG = 4
1369 MSG_SERVICE_REQUEST = 5
1370 MSG_SERVICE_ACCEPT = 6
1371 MSG_KEXINIT = 20
1372 MSG_NEWKEYS = 21
1373 MSG_KEXDH_INIT = 30
1374 MSG_KEXDH_REPLY = 31
1375 MSG_KEX_DH_GEX_REQUEST_OLD = 30
1376 MSG_KEX_DH_GEX_REQUEST = 34
1377 MSG_KEX_DH_GEX_GROUP = 31
1378 MSG_KEX_DH_GEX_INIT = 32
1379 MSG_KEX_DH_GEX_REPLY = 33
1380
1381
1382
1383 DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1
1384 DISCONNECT_PROTOCOL_ERROR = 2
1385 DISCONNECT_KEY_EXCHANGE_FAILED = 3
1386 DISCONNECT_RESERVED = 4
1387 DISCONNECT_MAC_ERROR = 5
1388 DISCONNECT_COMPRESSION_ERROR = 6
1389 DISCONNECT_SERVICE_NOT_AVAILABLE = 7
1390 DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8
1391 DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9
1392 DISCONNECT_CONNECTION_LOST = 10
1393 DISCONNECT_BY_APPLICATION = 11
1394 DISCONNECT_TOO_MANY_CONNECTIONS = 12
1395 DISCONNECT_AUTH_CANCELLED_BY_USER = 13
1396 DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14
1397 DISCONNECT_ILLEGAL_USER_NAME = 15
1398
1399
1400
1401 messages = {}
1402 for name, value in globals().items():
1403     if name.startswith('MSG_'):
1404         messages[value] = name
Note: See TracBrowser for help on using the browser.