Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / conch / test / test_keys.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Tests for L{twisted.conch.ssh.keys}.
6 """
7
8 try:
9     import Crypto.Cipher.DES3
10 except ImportError:
11     # we'll have to skip these tests without PyCypto and pyasn1
12     Crypto = None
13
14 try:
15     import pyasn1
16 except ImportError:
17     pyasn1 = None
18
19 if Crypto and pyasn1:
20     from twisted.conch.ssh import keys, common, sexpy
21
22 import os, base64
23 from twisted.conch.test import keydata
24 from twisted.python import randbytes
25 from twisted.python.hashlib import sha1
26 from twisted.trial import unittest
27
28
29 class HelpersTestCase(unittest.TestCase):
30
31     if Crypto is None:
32         skip = "cannot run w/o PyCrypto"
33     if pyasn1 is None:
34         skip = "Cannot run without PyASN1"
35
36     def setUp(self):
37         self._secureRandom = randbytes.secureRandom
38         randbytes.secureRandom = lambda x: '\x55' * x
39
40     def tearDown(self):
41         randbytes.secureRandom = self._secureRandom
42         self._secureRandom = None
43
44     def test_pkcs1(self):
45         """
46         Test Public Key Cryptographic Standard #1 functions.
47         """
48         data = 'ABC'
49         messageSize = 6
50         self.assertEqual(keys.pkcs1Pad(data, messageSize),
51                 '\x01\xff\x00ABC')
52         hash = sha1().digest()
53         messageSize = 40
54         self.assertEqual(keys.pkcs1Digest('', messageSize),
55                 '\x01\xff\xff\xff\x00' + keys.ID_SHA1 + hash)
56
57     def _signRSA(self, data):
58         key = keys.Key.fromString(keydata.privateRSA_openssh)
59         sig = key.sign(data)
60         return key.keyObject, sig
61
62     def _signDSA(self, data):
63         key = keys.Key.fromString(keydata.privateDSA_openssh)
64         sig = key.sign(data)
65         return key.keyObject, sig
66
67     def test_signRSA(self):
68         """
69         Test that RSA keys return appropriate signatures.
70         """
71         data = 'data'
72         key, sig = self._signRSA(data)
73         sigData = keys.pkcs1Digest(data, keys.lenSig(key))
74         v = key.sign(sigData, '')[0]
75         self.assertEqual(sig, common.NS('ssh-rsa') + common.MP(v))
76         return key, sig
77
78     def test_signDSA(self):
79         """
80         Test that DSA keys return appropriate signatures.
81         """
82         data = 'data'
83         key, sig = self._signDSA(data)
84         sigData = sha1(data).digest()
85         v = key.sign(sigData, '\x55' * 19)
86         self.assertEqual(sig, common.NS('ssh-dss') + common.NS(
87             Crypto.Util.number.long_to_bytes(v[0], 20) +
88             Crypto.Util.number.long_to_bytes(v[1], 20)))
89         return key, sig
90
91
92     def test_objectType(self):
93         """
94         Test that objectType, returns the correct type for objects.
95         """
96         self.assertEqual(keys.objectType(keys.Key.fromString(
97             keydata.privateRSA_openssh).keyObject), 'ssh-rsa')
98         self.assertEqual(keys.objectType(keys.Key.fromString(
99             keydata.privateDSA_openssh).keyObject), 'ssh-dss')
100         self.assertRaises(keys.BadKeyError, keys.objectType, None)
101
102
103 class KeyTestCase(unittest.TestCase):
104
105     if Crypto is None:
106         skip = "cannot run w/o PyCrypto"
107     if pyasn1 is None:
108         skip = "Cannot run without PyASN1"
109
110     def setUp(self):
111         self.rsaObj = Crypto.PublicKey.RSA.construct((1L, 2L, 3L, 4L, 5L))
112         self.dsaObj = Crypto.PublicKey.DSA.construct((1L, 2L, 3L, 4L, 5L))
113         self.rsaSignature = ('\x00\x00\x00\x07ssh-rsa\x00'
114             '\x00\x00`N\xac\xb4@qK\xa0(\xc3\xf2h \xd3\xdd\xee6Np\x9d_'
115             '\xb0>\xe3\x0c(L\x9d{\txUd|!\xf6m\x9c\xd3\x93\x842\x7fU'
116             '\x05\xf4\xf7\xfaD\xda\xce\x81\x8ea\x7f=Y\xed*\xb7\xba\x81'
117             '\xf2\xad\xda\xeb(\x97\x03S\x08\x81\xc7\xb1\xb7\xe6\xe3'
118             '\xcd*\xd4\xbd\xc0wt\xf7y\xcd\xf0\xb7\x7f\xfb\x1e>\xf9r'
119             '\x8c\xba')
120         self.dsaSignature = ('\x00\x00\x00\x07ssh-dss\x00\x00'
121             '\x00(\x18z)H\x8a\x1b\xc6\r\xbbq\xa2\xd7f\x7f$\xa7\xbf'
122             '\xe8\x87\x8c\x88\xef\xd9k\x1a\x98\xdd{=\xdec\x18\t\xe3'
123             '\x87\xa9\xc72h\x95')
124         self.oldSecureRandom = randbytes.secureRandom
125         randbytes.secureRandom = lambda x: '\xff' * x
126         self.keyFile = self.mktemp()
127         file(self.keyFile, 'wb').write(keydata.privateRSA_lsh)
128
129     def tearDown(self):
130         randbytes.secureRandom = self.oldSecureRandom
131         del self.oldSecureRandom
132         os.unlink(self.keyFile)
133
134     def test__guessStringType(self):
135         """
136         Test that the _guessStringType method guesses string types
137         correctly.
138         """
139         self.assertEqual(keys.Key._guessStringType(keydata.publicRSA_openssh),
140                 'public_openssh')
141         self.assertEqual(keys.Key._guessStringType(keydata.publicDSA_openssh),
142                 'public_openssh')
143         self.assertEqual(keys.Key._guessStringType(
144             keydata.privateRSA_openssh), 'private_openssh')
145         self.assertEqual(keys.Key._guessStringType(
146             keydata.privateDSA_openssh), 'private_openssh')
147         self.assertEqual(keys.Key._guessStringType(keydata.publicRSA_lsh),
148                 'public_lsh')
149         self.assertEqual(keys.Key._guessStringType(keydata.publicDSA_lsh),
150                 'public_lsh')
151         self.assertEqual(keys.Key._guessStringType(keydata.privateRSA_lsh),
152                 'private_lsh')
153         self.assertEqual(keys.Key._guessStringType(keydata.privateDSA_lsh),
154                 'private_lsh')
155         self.assertEqual(keys.Key._guessStringType(
156             keydata.privateRSA_agentv3), 'agentv3')
157         self.assertEqual(keys.Key._guessStringType(
158             keydata.privateDSA_agentv3), 'agentv3')
159         self.assertEqual(keys.Key._guessStringType(
160             '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01\x01'),
161             'blob')
162         self.assertEqual(keys.Key._guessStringType(
163             '\x00\x00\x00\x07ssh-dss\x00\x00\x00\x01\x01'),
164             'blob')
165         self.assertEqual(keys.Key._guessStringType('not a key'),
166                 None)
167
168     def _testPublicPrivateFromString(self, public, private, type, data):
169         self._testPublicFromString(public, type, data)
170         self._testPrivateFromString(private, type, data)
171
172     def _testPublicFromString(self, public, type, data):
173         publicKey = keys.Key.fromString(public)
174         self.assertTrue(publicKey.isPublic())
175         self.assertEqual(publicKey.type(), type)
176         for k, v in publicKey.data().items():
177             self.assertEqual(data[k], v)
178
179     def _testPrivateFromString(self, private, type, data):
180         privateKey = keys.Key.fromString(private)
181         self.assertFalse(privateKey.isPublic())
182         self.assertEqual(privateKey.type(), type)
183         for k, v in data.items():
184             self.assertEqual(privateKey.data()[k], v)
185
186     def test_fromOpenSSH(self):
187         """
188         Test that keys are correctly generated from OpenSSH strings.
189         """
190         self._testPublicPrivateFromString(keydata.publicRSA_openssh,
191                 keydata.privateRSA_openssh, 'RSA', keydata.RSAData)
192         self.assertEqual(keys.Key.fromString(
193             keydata.privateRSA_openssh_encrypted,
194             passphrase='encrypted'),
195             keys.Key.fromString(keydata.privateRSA_openssh))
196         self.assertEqual(keys.Key.fromString(
197             keydata.privateRSA_openssh_alternate),
198             keys.Key.fromString(keydata.privateRSA_openssh))
199         self._testPublicPrivateFromString(keydata.publicDSA_openssh,
200                 keydata.privateDSA_openssh, 'DSA', keydata.DSAData)
201
202     def test_fromOpenSSH_with_whitespace(self):
203         """
204         If key strings have trailing whitespace, it should be ignored.
205         """
206         # from bug #3391, since our test key data doesn't have
207         # an issue with appended newlines
208         privateDSAData = """-----BEGIN DSA PRIVATE KEY-----
209 MIIBuwIBAAKBgQDylESNuc61jq2yatCzZbenlr9llG+p9LhIpOLUbXhhHcwC6hrh
210 EZIdCKqTO0USLrGoP5uS9UHAUoeN62Z0KXXWTwOWGEQn/syyPzNJtnBorHpNUT9D
211 Qzwl1yUa53NNgEctpo4NoEFOx8PuU6iFLyvgHCjNn2MsuGuzkZm7sI9ZpQIVAJiR
212 9dPc08KLdpJyRxz8T74b4FQRAoGAGBc4Z5Y6R/HZi7AYM/iNOM8su6hrk8ypkBwR
213 a3Dbhzk97fuV3SF1SDrcQu4zF7c4CtH609N5nfZs2SUjLLGPWln83Ysb8qhh55Em
214 AcHXuROrHS/sDsnqu8FQp86MaudrqMExCOYyVPE7jaBWW+/JWFbKCxmgOCSdViUJ
215 esJpBFsCgYEA7+jtVvSt9yrwsS/YU1QGP5wRAiDYB+T5cK4HytzAqJKRdC5qS4zf
216 C7R0eKcDHHLMYO39aPnCwXjscisnInEhYGNblTDyPyiyNxAOXuC8x7luTmwzMbNJ
217 /ow0IqSj0VF72VJN9uSoPpFd4lLT0zN8v42RWja0M8ohWNf+YNJluPgCFE0PT4Vm
218 SUrCyZXsNh6VXwjs3gKQ
219 -----END DSA PRIVATE KEY-----"""
220         self.assertEqual(keys.Key.fromString(privateDSAData),
221                          keys.Key.fromString(privateDSAData + '\n'))
222
223     def test_fromLSH(self):
224         """
225         Test that keys are correctly generated from LSH strings.
226         """
227         self._testPublicPrivateFromString(keydata.publicRSA_lsh,
228                 keydata.privateRSA_lsh, 'RSA', keydata.RSAData)
229         self._testPublicPrivateFromString(keydata.publicDSA_lsh,
230                 keydata.privateDSA_lsh, 'DSA', keydata.DSAData)
231         sexp = sexpy.pack([['public-key', ['bad-key', ['p', '2']]]])
232         self.assertRaises(keys.BadKeyError, keys.Key.fromString,
233                 data='{'+base64.encodestring(sexp)+'}')
234         sexp = sexpy.pack([['private-key', ['bad-key', ['p', '2']]]])
235         self.assertRaises(keys.BadKeyError, keys.Key.fromString,
236                 sexp)
237
238     def test_fromAgentv3(self):
239         """
240         Test that keys are correctly generated from Agent v3 strings.
241         """
242         self._testPrivateFromString(keydata.privateRSA_agentv3, 'RSA',
243                 keydata.RSAData)
244         self._testPrivateFromString(keydata.privateDSA_agentv3, 'DSA',
245                 keydata.DSAData)
246         self.assertRaises(keys.BadKeyError, keys.Key.fromString,
247                 '\x00\x00\x00\x07ssh-foo'+'\x00\x00\x00\x01\x01'*5)
248
249     def test_fromStringErrors(self):
250         """
251         keys.Key.fromString should raise BadKeyError when the key is invalid.
252         """
253         self.assertRaises(keys.BadKeyError, keys.Key.fromString, '')
254         # no key data with a bad key type
255         self.assertRaises(keys.BadKeyError, keys.Key.fromString, '',
256                 'bad_type')
257         # trying to decrypt a key which doesn't support encryption
258         self.assertRaises(keys.BadKeyError, keys.Key.fromString,
259                 keydata.publicRSA_lsh, passphrase = 'unencrypted')
260         # trying to decrypt an unencrypted key
261         self.assertRaises(keys.EncryptedKeyError, keys.Key.fromString,
262                 keys.Key(self.rsaObj).toString('openssh', 'encrypted'))
263         # key with no key data
264         self.assertRaises(keys.BadKeyError, keys.Key.fromString,
265                 '-----BEGIN RSA KEY-----\nwA==\n')
266
267     def test_fromFile(self):
268         """
269         Test that fromFile works correctly.
270         """
271         self.assertEqual(keys.Key.fromFile(self.keyFile),
272                 keys.Key.fromString(keydata.privateRSA_lsh))
273         self.assertRaises(keys.BadKeyError, keys.Key.fromFile,
274                 self.keyFile, 'bad_type')
275         self.assertRaises(keys.BadKeyError, keys.Key.fromFile,
276                 self.keyFile, passphrase='unencrypted')
277
278     def test_init(self):
279         """
280         Test that the PublicKey object is initialized correctly.
281         """
282         obj = Crypto.PublicKey.RSA.construct((1L, 2L))
283         key = keys.Key(obj)
284         self.assertEqual(key.keyObject, obj)
285
286     def test_equal(self):
287         """
288         Test that Key objects are compared correctly.
289         """
290         rsa1 = keys.Key(self.rsaObj)
291         rsa2 = keys.Key(self.rsaObj)
292         rsa3 = keys.Key(Crypto.PublicKey.RSA.construct((1L, 2L)))
293         dsa = keys.Key(self.dsaObj)
294         self.assertTrue(rsa1 == rsa2)
295         self.assertFalse(rsa1 == rsa3)
296         self.assertFalse(rsa1 == dsa)
297         self.assertFalse(rsa1 == object)
298         self.assertFalse(rsa1 == None)
299
300     def test_notEqual(self):
301         """
302         Test that Key objects are not-compared correctly.
303         """
304         rsa1 = keys.Key(self.rsaObj)
305         rsa2 = keys.Key(self.rsaObj)
306         rsa3 = keys.Key(Crypto.PublicKey.RSA.construct((1L, 2L)))
307         dsa = keys.Key(self.dsaObj)
308         self.assertFalse(rsa1 != rsa2)
309         self.assertTrue(rsa1 != rsa3)
310         self.assertTrue(rsa1 != dsa)
311         self.assertTrue(rsa1 != object)
312         self.assertTrue(rsa1 != None)
313
314     def test_type(self):
315         """
316         Test that the type method returns the correct type for an object.
317         """
318         self.assertEqual(keys.Key(self.rsaObj).type(), 'RSA')
319         self.assertEqual(keys.Key(self.rsaObj).sshType(), 'ssh-rsa')
320         self.assertEqual(keys.Key(self.dsaObj).type(), 'DSA')
321         self.assertEqual(keys.Key(self.dsaObj).sshType(), 'ssh-dss')
322         self.assertRaises(RuntimeError, keys.Key(None).type)
323         self.assertRaises(RuntimeError, keys.Key(None).sshType)
324         self.assertRaises(RuntimeError, keys.Key(self).type)
325         self.assertRaises(RuntimeError, keys.Key(self).sshType)
326
327     def test_fromBlob(self):
328         """
329         Test that a public key is correctly generated from a public key blob.
330         """
331         rsaBlob = common.NS('ssh-rsa') + common.MP(2) + common.MP(3)
332         rsaKey = keys.Key.fromString(rsaBlob)
333         dsaBlob = (common.NS('ssh-dss') + common.MP(2) + common.MP(3) +
334                 common.MP(4) + common.MP(5))
335         dsaKey = keys.Key.fromString(dsaBlob)
336         badBlob = common.NS('ssh-bad')
337         self.assertTrue(rsaKey.isPublic())
338         self.assertEqual(rsaKey.data(), {'e':2L, 'n':3L})
339         self.assertTrue(dsaKey.isPublic())
340         self.assertEqual(dsaKey.data(), {'p':2L, 'q':3L, 'g':4L, 'y':5L})
341         self.assertRaises(keys.BadKeyError,
342                 keys.Key.fromString, badBlob)
343
344
345     def test_fromPrivateBlob(self):
346         """
347         Test that a private key is correctly generated from a private key blob.
348         """
349         rsaBlob = (common.NS('ssh-rsa') + common.MP(2) + common.MP(3) +
350                    common.MP(4) + common.MP(5) + common.MP(6) + common.MP(7))
351         rsaKey = keys.Key._fromString_PRIVATE_BLOB(rsaBlob)
352         dsaBlob = (common.NS('ssh-dss') + common.MP(2) + common.MP(3) +
353                    common.MP(4) + common.MP(5) + common.MP(6))
354         dsaKey = keys.Key._fromString_PRIVATE_BLOB(dsaBlob)
355         badBlob = common.NS('ssh-bad')
356         self.assertFalse(rsaKey.isPublic())
357         self.assertEqual(
358             rsaKey.data(), {'n':2L, 'e':3L, 'd':4L, 'u':5L, 'p':6L, 'q':7L})
359         self.assertFalse(dsaKey.isPublic())
360         self.assertEqual(dsaKey.data(), {'p':2L, 'q':3L, 'g':4L, 'y':5L, 'x':6L})
361         self.assertRaises(
362             keys.BadKeyError, keys.Key._fromString_PRIVATE_BLOB, badBlob)
363
364
365     def test_blob(self):
366         """
367         Test that the Key object generates blobs correctly.
368         """
369         self.assertEqual(keys.Key(self.rsaObj).blob(),
370                 '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01\x02'
371                 '\x00\x00\x00\x01\x01')
372         self.assertEqual(keys.Key(self.dsaObj).blob(),
373                 '\x00\x00\x00\x07ssh-dss\x00\x00\x00\x01\x03'
374                 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x02'
375                 '\x00\x00\x00\x01\x01')
376
377         badKey = keys.Key(None)
378         self.assertRaises(RuntimeError, badKey.blob)
379
380
381     def test_privateBlob(self):
382         """
383         L{Key.privateBlob} returns the SSH protocol-level format of the private
384         key and raises L{RuntimeError} if the underlying key object is invalid.
385         """
386         self.assertEqual(keys.Key(self.rsaObj).privateBlob(),
387                 '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01\x01'
388                 '\x00\x00\x00\x01\x02\x00\x00\x00\x01\x03\x00'
389                 '\x00\x00\x01\x04\x00\x00\x00\x01\x04\x00\x00'
390                 '\x00\x01\x05')
391         self.assertEqual(keys.Key(self.dsaObj).privateBlob(),
392                 '\x00\x00\x00\x07ssh-dss\x00\x00\x00\x01\x03'
393                 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x02\x00'
394                 '\x00\x00\x01\x01\x00\x00\x00\x01\x05')
395
396         badKey = keys.Key(None)
397         self.assertRaises(RuntimeError, badKey.privateBlob)
398
399
400     def test_toOpenSSH(self):
401         """
402         Test that the Key object generates OpenSSH keys correctly.
403         """
404         key = keys.Key.fromString(keydata.privateRSA_lsh)
405         self.assertEqual(key.toString('openssh'), keydata.privateRSA_openssh)
406         self.assertEqual(key.toString('openssh', 'encrypted'),
407                 keydata.privateRSA_openssh_encrypted)
408         self.assertEqual(key.public().toString('openssh'),
409                 keydata.publicRSA_openssh[:-8]) # no comment
410         self.assertEqual(key.public().toString('openssh', 'comment'),
411                 keydata.publicRSA_openssh)
412         key = keys.Key.fromString(keydata.privateDSA_lsh)
413         self.assertEqual(key.toString('openssh'), keydata.privateDSA_openssh)
414         self.assertEqual(key.public().toString('openssh', 'comment'),
415                 keydata.publicDSA_openssh)
416         self.assertEqual(key.public().toString('openssh'),
417                 keydata.publicDSA_openssh[:-8]) # no comment
418
419     def test_toLSH(self):
420         """
421         Test that the Key object generates LSH keys correctly.
422         """
423         key = keys.Key.fromString(keydata.privateRSA_openssh)
424         self.assertEqual(key.toString('lsh'), keydata.privateRSA_lsh)
425         self.assertEqual(key.public().toString('lsh'),
426                 keydata.publicRSA_lsh)
427         key = keys.Key.fromString(keydata.privateDSA_openssh)
428         self.assertEqual(key.toString('lsh'), keydata.privateDSA_lsh)
429         self.assertEqual(key.public().toString('lsh'),
430                 keydata.publicDSA_lsh)
431
432     def test_toAgentv3(self):
433         """
434         Test that the Key object generates Agent v3 keys correctly.
435         """
436         key = keys.Key.fromString(keydata.privateRSA_openssh)
437         self.assertEqual(key.toString('agentv3'), keydata.privateRSA_agentv3)
438         key = keys.Key.fromString(keydata.privateDSA_openssh)
439         self.assertEqual(key.toString('agentv3'), keydata.privateDSA_agentv3)
440
441     def test_toStringErrors(self):
442         """
443         Test that toString raises errors appropriately.
444         """
445         self.assertRaises(keys.BadKeyError, keys.Key(self.rsaObj).toString,
446                 'bad_type')
447
448     def test_sign(self):
449         """
450         Test that the Key object generates correct signatures.
451         """
452         key = keys.Key.fromString(keydata.privateRSA_openssh)
453         self.assertEqual(key.sign(''), self.rsaSignature)
454         key = keys.Key.fromString(keydata.privateDSA_openssh)
455         self.assertEqual(key.sign(''), self.dsaSignature)
456
457
458     def test_verify(self):
459         """
460         Test that the Key object correctly verifies signatures.
461         """
462         key = keys.Key.fromString(keydata.publicRSA_openssh)
463         self.assertTrue(key.verify(self.rsaSignature, ''))
464         self.assertFalse(key.verify(self.rsaSignature, 'a'))
465         self.assertFalse(key.verify(self.dsaSignature, ''))
466         key = keys.Key.fromString(keydata.publicDSA_openssh)
467         self.assertTrue(key.verify(self.dsaSignature, ''))
468         self.assertFalse(key.verify(self.dsaSignature, 'a'))
469         self.assertFalse(key.verify(self.rsaSignature, ''))
470
471     def test_repr(self):
472         """
473         Test the pretty representation of Key.
474         """
475         self.assertEqual(repr(keys.Key(self.rsaObj)),
476 """<RSA Private Key (0 bits)
477 attr e:
478 \t02
479 attr d:
480 \t03
481 attr n:
482 \t01
483 attr q:
484 \t05
485 attr p:
486 \t04
487 attr u:
488 \t04>""")