PageRenderTime 264ms CodeModel.GetById 25ms app.highlight 203ms RepoModel.GetById 1ms app.codeStats 1ms

/desktop/core/ext-py/Twisted/twisted/conch/ssh/transport.py

https://github.com/jcrobak/hue
Python | 1362 lines | 1195 code | 38 blank | 129 comment | 16 complexity | e1f291c6967d42a312eec423600194d3 MD5 | raw file
   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"""
   7The lowest level SSH protocol.  This handles the key negotiation, the
   8encryption and the compression.  The transport layer is described in
   9RFC 4253.
  10
  11Maintainer: Paul Swartz
  12"""
  13
  14# base library imports
  15import struct
  16import md5
  17import sha
  18import zlib
  19import array
  20
  21# external library imports
  22from Crypto import Util
  23from Crypto.Cipher import XOR
  24
  25# twisted imports
  26from twisted.internet import protocol, defer
  27from twisted.conch import error
  28from twisted.python import log, randbytes
  29
  30# sibling imports
  31from twisted.conch.ssh import keys
  32from twisted.conch.ssh.common import NS, getNS, MP, getMP, _MPpow, ffs
  33
  34
  35
  36class 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
 655class 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
 855class 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
1117class _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
1134class 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
1318class _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]
1360DH_PRIME = long('17976931348623159077083915679378745319786029604875601170644'
1361'442368419718021615851936894783379586492554150218056548598050364644054819923'
1362'9100050792877003355816639