Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(119)

Side by Side Diff: third_party/twisted_8_1/twisted/conch/ssh/transport.py

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

Powered by Google App Engine
This is Rietveld 408576698