[Twisted-Python] Re: Possible bug on twisted.conch.ssh.keys?

Adriano Marques py.adriano at gmail.com
Mon Jul 28 09:04:49 MDT 2008


Folks,

While I was trying to find why the verify method was returning False,
I discovered that inside the _verify method, at RSA.py, there was the
following code:

<RSA_code>
def _verify(self, M, sig):
    m2=self._encrypt(sig[0])
    if m2[0]==M:
        return 1
    else: return 0
</RSA_code>

Using the live debug of wingide, I checked the values, and here is the result:

<debug>
>>> print m2[0] == M
False
>>> print m2[0]
5486124068793688683255936251187209270074392635932332070112001988456197381759672947165175699536362793613284725337872111744958183862744647903224103718245670299614498700710006264535590197791934024641512541262359795191593456970905950167587431434979106624455856690274067852326335446193014962274187407641892263
>>> print M
5486124068793688683255936251187209270074392635932332070112001988456197381759672947165175699536362793613284725337872111744958183862744647903224103718245670299614498700710006264535590197791934024641512541262359795191593456970905950167587431434979106624455857062955565129940502062033292313613058888050118445
>>> print type(m2[0]), type(M)
<type 'long'> <type 'long'>
</debug>

So, m2[0] is really different from M, but why if I'm using the same
keys from the same place?

Hope this helps to clear the issue.


Kind Regards,

On Mon, Jul 28, 2008 at 10:29 AM, Adriano Marques <py.adriano at gmail.com> wrote:
> Hello folks,
>
> I'm facing a problem here while trying to implement a ssh server and
> client using twisted. There are some deprecated functions that are
> pointing me to use new ones, which in turn doesn't seem to work fine.
> I'm not sure if it is the case, mainly because the deprecated
> functions are calling the new ones, but while debuging the code I
> found that something there is wrong, and that can potentially be
> hapenning because of the changes recently made to that module. As I'm
> somehow new to twisted I'm not sure about my statements arround the
> problem. Here follows the codes and exceptions I'm getting:
>
> <client_code>
> #!/usr/bin/env python
>
> # This code was based on an example from twisted website
>
> from twisted.conch.ssh import transport, userauth, connection
> from twisted.conch.ssh import common, keys, channel
> from twisted.internet import defer, protocol, reactor
> from twisted.python import log
> import struct, sys, getpass, os
>
> USER = 'ssh'
> HOST = 'localhost'
> PORT = 2222
> PUBKEY = "ssh-rsa
> AAAAB3NzaC1yc2EAAAADAQABAAAAgQD3n4sYqbdnlVyvy2SRUmQ5HJg2NDHyC0m4FIykDygXW2PTcSHU/Jany95D54qAEkUra0KHk94KvyGhNVYXqPmzPLu4onlr0Y/gQzEsIy15jDGjMWVCX/8+PQJxMYObSKs8U1o/h5fDNeKQs4bZbrm+35eT/Kvyerqa6ab5OsIEnQ=="
> PRIVKEY = """-----BEGIN RSA PRIVATE KEY-----
> MIICXAIBAAKBgQD3n4sYqbdnlVyvy2SRUmQ5HJg2NDHyC0m4FIykDygXW2PTcSHU
> /Jany95D54qAEkUra0KHk94KvyGhNVYXqPmzPLu4onlr0Y/gQzEsIy15jDGjMWVC
> X/8+PQJxMYObSKs8U1o/h5fDNeKQs4bZbrm+35eT/Kvyerqa6ab5OsIEnQIDAQAB
> AoGAK2MWASVDkG+4RMkTfu77xpH/DYhJtApMSWe4WMqbELSfoh2xetsjHpV3BVjd
> iKEq43ewuYasIh/pKZDp281sqqXdg9VI9ZW7kB00FhO2wA4emCIVj6CHMl1K9NrU
> 9Spy40garaYnDdud79SnKtxlKQEALOwCpTEcrjGOLBTbirUCQQD/FkP1zhpOSaOy
> B32CiC13W5fFfREzBxzQ3Med1i1OYtFfBEdJqv/Z7APadNN8ySI3r+UAfGatTzJI
> zbiSZ2fHAkEA+IJwWpw6d8R3t7+8UgTrqdsZhLRdidO0cHcIFcnU/QYZg+iw3F+q
> Tuz/mngpfb5214r8zwcwlNzsC4+fyOOYewJBAMPstRw6Rog1FW8rQ6Kbt9hCWItO
> aYR5BRADU6sOk1PuoIPLhHm3xrX6CmejbcEdt5NwHYNHCZI6DxRONmL024cCQBky
> eJvnXVJJfG4IJdsXHqIBUiwPcbCI47HHj+1NoqfpF2s1i8E8ffM0upH5/xL93eTq
> 9ck0DGv7nn9pl6Tx1sMCQCWZbWDnmEdsSoNUQZ8fyQEDoZ/2gUP+R2/WpLikwmSA
> 41n7cUONVthIYOZw0qQPP//PyPtMVxcYT76D5QACEK8=
> -----END RSA PRIVATE KEY-----"""
>
> class ClientTransport(transport.SSHClientTransport):
>    def connectionMade(self):
>        transport.SSHClientTransport.connectionMade(self)
>
>    def verifyHostKey(self, hostKey, fingerprint):
>        return defer.succeed(1)
>
>    def connectionSecure(self):
>        self.requestService(ClientUserAuth(USER, ClientConnection()))
>
> class ClientUserAuth(userauth.SSHUserAuthClient):
>    def getPublicKey(self):
>        # Issue 1
>        return PUBKEY.encode("ascii")
>
>    def getPrivateKey(self):
>        # Issue 2
>        return defer.succeed(keys.getPrivateKeyObject(data=PRIVKEY))
>
>    def getPassword(self):
>        return defer.succeed(getpass.getpass("%s@%s's password: " %
> (USER, HOST)))
>
>    def getGenericAnswers(self, name, instruction, questions):
>        import pdb; pdb.set_trace()
>        log.debug("Get Generic answers: %s, %s, %s" % (name,
>                                                       instruction,
>                                                       questions))
>
>        answers = []
>        for prompt, echo in questions:
>            if echo:
>                answer = raw_input(prompt)
>            else:
>                answer = getpass.getpass(prompt)
>            answers.append(answer)
>        return defer.succeed(answers)
>
>    def tryAuth(self, kind):
>        kind = "publickey"
>        log.debug("tryAuth: %s" % kind)
>        log.debug("Public Key: %s" % self.getPublicKey())
>        return userauth.SSHUserAuthClient.tryAuth(self, kind)
>
>    def signData(self, publicKey, signData):
>        log.debug("Ran signData with %s and %s" % (publicKey, signData))
>        import pdb; pdb.set_trace()
>        signed = userauth.SSHUserAuthClient.signData(self, publicKey, signData)
>
>        return signed
>
>    def auth_publickey(self):
>        log.debug("Trying Auth Method: Public Key")
>        return userauth.SSHUserAuthClient.auth_publickey(self)
>
> class ClientConnection(connection.SSHConnection):
>    def serviceStarted(self):
>        log.debug("Service started")
>        self.openChannel(TrueChannel(2**16, 2**15, self))
>        self.openChannel(FalseChannel(2**16, 2**15, self))
>        self.openChannel(CatChannel(2**16, 2**15, self))
>
> class TrueChannel(channel.SSHChannel):
>    name = 'session'
>
>    def openFailed(self, reason):
>        print 'true failed', reason
>
>    def channelOpen(self, ignoredData):
>        self.conn.sendRequest(self, 'exec', common.NS('true'))
>
>    def request_exit_status(self, data):
>        status = struct.unpack('>L', data)[0]
>        print 'true status was: %s' % status
>        self.loseConnection()
>
> class FalseChannel(channel.SSHChannel):
>    name = 'session'
>
>    def openFailed(self, reason):
>        print 'false failed', reason
>
>    def channelOpen(self, ignoredData):
>        self.conn.sendRequest(self, 'exec', common.NS('false'))
>
>    def request_exit_status(self, data):
>        status = struct.unpack('>L', data)[0]
>        print 'false status was: %s' % status
>        self.loseConnection()
>
> class CatChannel(channel.SSHChannel):
>    name = 'session'
>
>    def openFailed(self, reason):
>        print 'echo failed', reason
>
>    def channelOpen(self, ignoredData):
>        self.data = ''
>        d = self.conn.sendRequest(self, 'exec', common.NS('cat'), wantReply = 1)
>        d.addCallback(self._cbRequest)
>
>    def _cbRequest(self, ignored):
>        self.write('hello conch\n')
>        self.conn.sendEOF(self)
>
>    def dataReceived(self, data):
>        self.data += data
>
>    def closed(self):
>        print 'got data from cat: %s' % repr(self.data)
>        self.loseConnection()
>        reactor.stop()
>
> protocol.ClientCreator(reactor, ClientTransport).connectTCP(HOST, 2222)
> reactor.run()
> </client_code>
>
> The issues:
>
> 1 - I had to encode the public key to ascii prior to using it. I don't
> know why, but the struct was raising an exception telling that one of
> the chars where out of the ascii range. But it doesn't look to have
> any character outside the ascii range, AFAIK. Anyway, it worked when I
> did the conversion to ascii.
>
> 2 - When I use: keys.Key.fromString(data=PRIVKEY) instead of the
> deprecated keys.getPrivateKeyObject(data=PRIVKEY), I get this error
> while trying to authenticate:
>
> <error>
> # This was caught directly from pdb prompt:
>> connectSSH.py(77)signData()
> -> signed = userauth.SSHUserAuthClient.signData(self, publicKey, signData)
> (Pdb) n
>> site-packages/twisted/conch/ssh/userauth.py:374: DeprecationWarning: signData is deprecated since Twisted Conch 0.9.  Use Key(obj).sign(data).
>  return keys.signData(privateKey, signData)
>> connectSSH.py(79)signData()
> -> return signed
> (Pdb) p signed
> <Deferred at 0x12a1760  current result:
> <twisted.python.failure.Failure <type 'exceptions.RuntimeError'>>>
> (Pdb) p signed.result
> <twisted.python.failure.Failure <type 'exceptions.RuntimeError'>>
> (Pdb) p signed.result.getErrorMessage()
> 'unknown type of object: <RSA Private Key (1023 bits)\nattr
> e:\n\t01:00:01\nattr
> d:\n\t2b:63:16:01:25:43:90:6f:b8:44:c9:13:7e:ee:fb:\n\tc6:91:ff:0d:88:49:b4:0a:4c:49:67:b8:58:ca:9b:\n\t10:b4:9f:a2:1d:b1:7a:db:23:1e:95:77:05:58:dd:\n\t88:a1:2a:e3:77:b0:b9:86:ac:22:1f:e9:29:90:e9:\n\tdb:cd:6c:aa:a5:dd:83:d5:48:f5:95:bb:90:1d:34:\n\t16:13:b6:c0:0e:1e:98:22:15:8f:a0:87:32:5d:4a:\n\tf4:da:d4:f5:2a:72:e3:48:1a:ad:a6:27:0d:db:9d:\n\tef:d4:a7:2a:dc:65:29:01:00:2c:ec:02:a5:31:1c:\n\tae:31:8e:2c:14:db:8a:b5\nattr
> n:\n\t00:f7:9f:8b:18:a9:b7:67:95:5c:af:cb:64:91:52:\n\t64:39:1c:98:36:34:31:f2:0b:49:b8:14:8c:a4:0f:\n\t28:17:5b:63:d3:71:21:d4:fc:96:a7:cb:de:43:e7:\n\t8a:80:12:45:2b:6b:42:87:93:de:0a:bf:21:a1:35:\n\t56:17:a8:f9:b3:3c:bb:b8:a2:79:6b:d1:8f:e0:43:\n\t31:2c:23:2d:79:8c:31:a3:31:65:42:5f:ff:3e:3d:\n\t02:71:31:83:9b:48:ab:3c:53:5a:3f:87:97:c3:35:\n\te2:90:b3:86:d9:6e:b9:be:df:97:93:fc:ab:f2:7a:\n\tba:9a:e9:a6:f9:3a:c2:04:9d\nattr
> q:\n\t00:ff:16:43:f5:ce:1a:4e:49:a3:b2:07:7d:82:88:\n\t2d:77:5b:97:c5:7d:11:33:07:1c:d0:dc:c7:9d:d6:\n\t2d:4e:62:d1:5f:04:47:49:aa:ff:d9:ec:03:da:74:\n\td3:7c:c9:22:37:af:e5:00:7c:66:ad:4f:32:48:cd:\n\tb8:92:67:67:c7\nattr
> p:\n\t00:f8:82:70:5a:9c:3a:77:c4:77:b7:bf:bc:52:04:\n\teb:a9:db:19:84:b4:5d:89:d3:b4:70:77:08:15:c9:\n\td4:fd:06:19:83:e8:b0:dc:5f:aa:4e:ec:ff:9a:78:\n\t29:7d:be:76:d7:8a:fc:cf:07:30:94:dc:ec:0b:8f:\n\t9f:c8:e3:98:7b\nattr
> u:\n\t25:99:6d:60:e7:98:47:6c:4a:83:54:41:9f:1f:c9:\n\t01:03:a1:9f:f6:81:43:fe:47:6f:d6:a4:b8:a4:c2:\n\t64:80:e3:59:fb:71:43:8d:56:d8:48:60:e6:70:d2:\n\ta4:0f:3f:ff:cf:c8:fb:4c:57:17:18:4f:be:83:e5:\n\t00:02:10:af>'
> </error>
>
> This is the function that raised that exception:
> http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.1.0/twisted/conch/ssh/keys.py#L387
> It looks like the problem is that the class name doesn't start with
> Crypto.PublicKey. Then, when I use the deprecated method, this is what
> I have:
>
> <not_an_error>
>> connectSSH.py(77)signData()
> -> signed = userauth.SSHUserAuthClient.signData(self, publicKey, signData)
> (Pdb) n
> tests/connectSSH.py:48: DeprecationWarning: getPrivateKeyObject is
> deprecated since Twisted Conch 0.9.  Use Key.fromString().
>  return defer.succeed(keys.getPrivateKeyObject(data=PRIVKEY))
> site-packages/twisted/conch/ssh/userauth.py:374: DeprecationWarning:
> signData is deprecated since Twisted Conch 0.9.  Use
> Key(obj).sign(data).
>  return keys.signData(privateKey, signData)
>> connectSSH.py(79)signData()
> -> return signed
> (Pdb) p signed
> <Deferred at 0x12a1760  current result:
> '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x80\xa9w-\x0c\x9e\xc3\xe8\xd0\xff\xb8\xf0\xbbi*\xecJ\x8e5\xac\x0e(@\x18\x81\x11\xf4\xff\xda\xde\xd6\x9b\xbe\xef\\\xc2\xd0F8Q\x15\xd0A\xa1!$\xe2\xe5\xa0\xf8\x12O\xa0*4\xe1)\xc2:\xbf\x16\x0b\x8a\xd2J\xa8D\x01\xdd\x10\xe3\xeb\x8b*9,\xf3#b\xff\xbf\xc9\xe9\xdc\x81\xa4B\x88$\r\x8b\xef|y\x80\r\x08\x8a*\x08x:\x0c{\xcf\x97\xb7"\xe19\x1f\xa2_wV4\xfa\x19"Vf\xcf\x80\xa7i\x98\xfa\xefM'>
> </not_an_error>
>
> After moving to the deprecated method, everything seems to work ok
> with the client, but then I started to have a similar problem at the
> server side, while verifying client's signature. Here is the relevant
> part of the server code, which is the method requestAvatarId of the
> Credential Checker I implemented:
>
> <server_code>
> # This code is based in an example that I caught from twisted website
> class PublicKeyChecker():
>    implements(checkers.ICredentialsChecker)
>    credentialInterfaces = (credentials.ISSHPrivateKey, )
>
>    def requestAvatarId(self, credentials):
>        # I removed here the code that verified if the user is
> registered in our database, to easy for you guys to understand the
> relevant part of the code. Just consider that the variable user
> contains the found user object.
>
>        user_key = user.key.public_key
>        if not credentials.blob == user_key:
>            raise failure.Failure(error.ConchError("Wrong key."))
>        if not credentials.signature:
>            return failure.Failure(error.ValidPublicKey())
>
>        pub_key = Key.fromString(data=credentials.blob)
>        # Issue 3
>        if verifySignature(pub_key,
>                           credentials.signature,
>                           credentials.sigData):
>            return credentials.username
>        else:
>            return failure.Failure(error.ConchError("Incorrect Signature."))
>    else:
>        return failure.Failure(error.ConchError("Authentication Failed."))
> </server_code>
>
> 3 - Then, while I get to the verifySignature method, I get the following error:
>
> <error>
> RuntimeError: unknown type of object: <RSA Public Key (1023 bits)
> attr e:
>        01:00:01
> attr n:
>        00:f7:9f:8b:18:a9:b7:67:95:5c:af:cb:64:91:52:
>        64:39:1c:98:36:34:31:f2:0b:49:b8:14:8c:a4:0f:
>        28:17:5b:63:d3:71:21:d4:fc:96:a7:cb:de:43:e7:
>        8a:80:12:45:2b:6b:42:87:93:de:0a:bf:21:a1:35:
>        56:17:a8:f9:b3:3c:bb:b8:a2:79:6b:d1:8f:e0:43:
>        31:2c:23:2d:79:8c:31:a3:31:65:42:5f:ff:3e:3d:...
> </error>
>
> Which is the same exception raised by the key.type() function that
> have put us at the same trouble in the client. Then, at the debuger, I
> found that the correct key (which is a class with name starting with
> Crypto.PublicKey) is right inside this "unknown" object, which is a
> RSA Public Key in object.keyObject.keyObject! I ran the WingIDE
> debugger and simply saw that, as you can see in this screenshot:
> http://www.imageno.com/z99azx3v7qnzpic.html
>
> Ok, I also get the deprecated warning while using the verifySignature
> method instead of Key(obj).verify(sig, data), then I decided to test
> it also just to don't make you guys wast your time with me for
> nothing. I called pub_key.verify(credentials.signature,
> credentials.sigData) instead of that verifySignature call, and it
> returned False instead leading to a failure in authentication.
>
> Both apps (client and server) are running inside the same machine,
> consulting the same database, the same keys and user, so there it is
> not reasonable to be failing if my code is correct. Maybe I missed
> something. Can you guys help me?
>
> Sorry about the very long email, but I tried to be as much verbose as
> I could in order to help you guys help me.
>
>
> Kind Regards,
>
> --
> Adriano Monteiro Marques
>
> http://adriano-marques.blogspot.com
> http://umit.sourceforge.net
> py.adriano at gmail.com
>
> "Don't stay in bed, unless you can make money in bed." - George Burns
>



-- 
Adriano Monteiro Marques

http://adriano-marques.blogspot.com
http://umit.sourceforge.net
py.adriano at gmail.com

"Don't stay in bed, unless you can make money in bed." - George Burns




More information about the Twisted-Python mailing list