Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / conch / ssh / keys.py
1 # -*- test-case-name: twisted.conch.test.test_keys -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Handling of RSA and DSA keys.
7
8 Maintainer: U{Paul Swartz}
9 """
10
11 # base library imports
12 import base64
13 import warnings
14 import itertools
15
16 # external library imports
17 from Crypto.Cipher import DES3
18 from Crypto.PublicKey import RSA, DSA
19 from Crypto import Util
20 from pyasn1.type import univ
21 from pyasn1.codec.ber import decoder as berDecoder
22 from pyasn1.codec.ber import encoder as berEncoder
23
24 # twisted
25 from twisted.python import randbytes
26 from twisted.python.hashlib import md5, sha1
27
28 # sibling imports
29 from twisted.conch.ssh import common, sexpy
30
31
32 class BadKeyError(Exception):
33     """
34     Raised when a key isn't what we expected from it.
35
36     XXX: we really need to check for bad keys
37     """
38
39 class EncryptedKeyError(Exception):
40     """
41     Raised when an encrypted key is presented to fromString/fromFile without
42     a password.
43     """
44
45 class Key(object):
46     """
47     An object representing a key.  A key can be either a public or
48     private key.  A public key can verify a signature; a private key can
49     create or verify a signature.  To generate a string that can be stored
50     on disk, use the toString method.  If you have a private key, but want
51     the string representation of the public key, use Key.public().toString().
52
53     @ivar keyObject: The C{Crypto.PublicKey.pubkey.pubkey} object that
54                   operations are performed with.
55     """
56
57     def fromFile(Class, filename, type=None, passphrase=None):
58         """
59         Return a Key object corresponding to the data in filename.  type
60         and passphrase function as they do in fromString.
61         """
62         return Class.fromString(file(filename, 'rb').read(), type, passphrase)
63     fromFile = classmethod(fromFile)
64
65     def fromString(Class, data, type=None, passphrase=None):
66         """
67         Return a Key object corresponding to the string data.
68         type is optionally the type of string, matching a _fromString_*
69         method.  Otherwise, the _guessStringType() classmethod will be used
70         to guess a type.  If the key is encrypted, passphrase is used as
71         the decryption key.
72
73         @type data: C{str}
74         @type type: C{None}/C{str}
75         @type passphrase: C{None}/C{str}
76         @rtype: C{Key}
77         """
78         if type is None:
79             type = Class._guessStringType(data)
80         if type is None:
81             raise BadKeyError('cannot guess the type of %r' % data)
82         method = getattr(Class, '_fromString_%s' % type.upper(), None)
83         if method is None:
84             raise BadKeyError('no _fromString method for %s' % type)
85         if method.func_code.co_argcount == 2: # no passphrase
86             if passphrase:
87                 raise BadKeyError('key not encrypted')
88             return method(data)
89         else:
90             return method(data, passphrase)
91     fromString = classmethod(fromString)
92
93     def _fromString_BLOB(Class, blob):
94         """
95         Return a public key object corresponding to this public key blob.
96         The format of a RSA public key blob is::
97             string 'ssh-rsa'
98             integer e
99             integer n
100
101         The format of a DSA public key blob is::
102             string 'ssh-dss'
103             integer p
104             integer q
105             integer g
106             integer y
107
108         @type blob: C{str}
109         @return: a C{Crypto.PublicKey.pubkey.pubkey} object
110         @raises BadKeyError: if the key type (the first string) is unknown.
111         """
112         keyType, rest = common.getNS(blob)
113         if keyType == 'ssh-rsa':
114             e, n, rest = common.getMP(rest, 2)
115             return Class(RSA.construct((n, e)))
116         elif keyType == 'ssh-dss':
117             p, q, g, y, rest = common.getMP(rest, 4)
118             return Class(DSA.construct((y, g, p, q)))
119         else:
120             raise BadKeyError('unknown blob type: %s' % keyType)
121     _fromString_BLOB = classmethod(_fromString_BLOB)
122
123     def _fromString_PRIVATE_BLOB(Class, blob):
124         """
125         Return a private key object corresponding to this private key blob.
126         The blob formats are as follows:
127
128         RSA keys::
129             string 'ssh-rsa'
130             integer n
131             integer e
132             integer d
133             integer u
134             integer p
135             integer q
136
137         DSA keys::
138             string 'ssh-dss'
139             integer p
140             integer q
141             integer g
142             integer y
143             integer x
144
145         @type blob: C{str}
146         @return: a C{Crypto.PublicKey.pubkey.pubkey} object
147         @raises BadKeyError: if the key type (the first string) is unknown.
148         """
149         keyType, rest = common.getNS(blob)
150
151         if keyType == 'ssh-rsa':
152             n, e, d, u, p, q, rest = common.getMP(rest, 6)
153             rsakey = Class(RSA.construct((n, e, d, p, q, u)))
154             return rsakey
155         elif keyType == 'ssh-dss':
156             p, q, g, y, x, rest = common.getMP(rest, 5)
157             dsakey =  Class(DSA.construct((y, g, p, q, x)))
158             return dsakey
159         else:
160             raise BadKeyError('unknown blob type: %s' % keyType)
161     _fromString_PRIVATE_BLOB = classmethod(_fromString_PRIVATE_BLOB)
162
163     def _fromString_PUBLIC_OPENSSH(Class, data):
164         """
165         Return a public key object corresponding to this OpenSSH public key
166         string.  The format of an OpenSSH public key string is::
167             <key type> <base64-encoded public key blob>
168
169         @type data: C{str}
170         @return: A {Crypto.PublicKey.pubkey.pubkey} object
171         @raises BadKeyError: if the blob type is unknown.
172         """
173         blob = base64.decodestring(data.split()[1])
174         return Class._fromString_BLOB(blob)
175     _fromString_PUBLIC_OPENSSH = classmethod(_fromString_PUBLIC_OPENSSH)
176
177     def _fromString_PRIVATE_OPENSSH(Class, data, passphrase):
178         """
179         Return a private key object corresponding to this OpenSSH private key
180         string.  If the key is encrypted, passphrase MUST be provided.
181         Providing a passphrase for an unencrypted key is an error.
182
183         The format of an OpenSSH private key string is::
184             -----BEGIN <key type> PRIVATE KEY-----
185             [Proc-Type: 4,ENCRYPTED
186             DEK-Info: DES-EDE3-CBC,<initialization value>]
187             <base64-encoded ASN.1 structure>
188             ------END <key type> PRIVATE KEY------
189
190         The ASN.1 structure of a RSA key is::
191             (0, n, e, d, p, q)
192
193         The ASN.1 structure of a DSA key is::
194             (0, p, q, g, y, x)
195
196         @type data: C{str}
197         @type passphrase: C{str}
198         @return: a C{Crypto.PublicKey.pubkey.pubkey} object
199         @raises BadKeyError: if
200             * a passphrase is provided for an unencrypted key
201             * a passphrase is not provided for an encrypted key
202             * the ASN.1 encoding is incorrect
203         """
204         lines = [x + '\n' for x in data.split('\n')]
205         kind = lines[0][11:14]
206         if lines[1].startswith('Proc-Type: 4,ENCRYPTED'): # encrypted key
207             ivdata = lines[2].split(',')[1][:-1]
208             iv = ''.join([chr(int(ivdata[i:i + 2], 16)) for i in range(0,
209                 len(ivdata), 2)])
210             if not passphrase:
211                 raise EncryptedKeyError('encrypted key with no passphrase')
212             ba = md5(passphrase + iv).digest()
213             bb = md5(ba + passphrase + iv).digest()
214             decKey = (ba + bb)[:24]
215             b64Data = base64.decodestring(''.join(lines[3:-1]))
216             keyData = DES3.new(decKey, DES3.MODE_CBC, iv).decrypt(b64Data)
217             removeLen = ord(keyData[-1])
218             keyData = keyData[:-removeLen]
219         else:
220             b64Data = ''.join(lines[1:-1])
221             keyData = base64.decodestring(b64Data)
222         try:
223             decodedKey = berDecoder.decode(keyData)[0]
224         except Exception, e:
225             raise BadKeyError, 'something wrong with decode'
226         if kind == 'RSA':
227             if len(decodedKey) == 2: # alternate RSA key
228                 decodedKey = decodedKey[0]
229             if len(decodedKey) < 6:
230                 raise BadKeyError('RSA key failed to decode properly')
231             n, e, d, p, q = [long(value) for value in decodedKey[1:6]]
232             if p > q: # make p smaller than q
233                 p, q = q, p
234             return Class(RSA.construct((n, e, d, p, q)))
235         elif kind == 'DSA':
236             p, q, g, y, x = [long(value) for value in decodedKey[1: 6]]
237             if len(decodedKey) < 6:
238                 raise BadKeyError('DSA key failed to decode properly')
239             return Class(DSA.construct((y, g, p, q, x)))
240     _fromString_PRIVATE_OPENSSH = classmethod(_fromString_PRIVATE_OPENSSH)
241
242     def _fromString_PUBLIC_LSH(Class, data):
243         """
244         Return a public key corresponding to this LSH public key string.
245         The LSH public key string format is::
246             <s-expression: ('public-key', (<key type>, (<name, <value>)+))>
247
248         The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e.
249         The names for a DSA (key type 'dsa') key are: y, g, p, q.
250
251         @type data: C{str}
252         @return: a C{Crypto.PublicKey.pubkey.pubkey} object
253         @raises BadKeyError: if the key type is unknown
254         """
255         sexp = sexpy.parse(base64.decodestring(data[1:-1]))
256         assert sexp[0] == 'public-key'
257         kd = {}
258         for name, data in sexp[1][1:]:
259             kd[name] = common.getMP(common.NS(data))[0]
260         if sexp[1][0] == 'dsa':
261             return Class(DSA.construct((kd['y'], kd['g'], kd['p'], kd['q'])))
262         elif sexp[1][0] == 'rsa-pkcs1-sha1':
263             return Class(RSA.construct((kd['n'], kd['e'])))
264         else:
265             raise BadKeyError('unknown lsh key type %s' % sexp[1][0])
266     _fromString_PUBLIC_LSH = classmethod(_fromString_PUBLIC_LSH)
267
268     def _fromString_PRIVATE_LSH(Class, data):
269         """
270         Return a private key corresponding to this LSH private key string.
271         The LSH private key string format is::
272             <s-expression: ('private-key', (<key type>, (<name>, <value>)+))>
273
274         The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e, d, p, q.
275         The names for a DSA (key type 'dsa') key are: y, g, p, q, x.
276
277         @type data: C{str}
278         @return: a {Crypto.PublicKey.pubkey.pubkey} object
279         @raises BadKeyError: if the key type is unknown
280         """
281         sexp = sexpy.parse(data)
282         assert sexp[0] == 'private-key'
283         kd = {}
284         for name, data in sexp[1][1:]:
285             kd[name] = common.getMP(common.NS(data))[0]
286         if sexp[1][0] == 'dsa':
287             assert len(kd) == 5, len(kd)
288             return Class(DSA.construct((kd['y'], kd['g'], kd['p'],
289                 kd['q'], kd['x'])))
290         elif sexp[1][0] == 'rsa-pkcs1':
291             assert len(kd) == 8, len(kd)
292             if kd['p'] > kd['q']: # make p smaller than q
293                 kd['p'], kd['q'] = kd['q'], kd['p']
294             return Class(RSA.construct((kd['n'], kd['e'], kd['d'],
295                 kd['p'], kd['q'])))
296         else:
297             raise BadKeyError('unknown lsh key type %s' % sexp[1][0])
298     _fromString_PRIVATE_LSH = classmethod(_fromString_PRIVATE_LSH)
299
300     def _fromString_AGENTV3(Class, data):
301         """
302         Return a private key object corresponsing to the Secure Shell Key
303         Agent v3 format.
304
305         The SSH Key Agent v3 format for a RSA key is::
306             string 'ssh-rsa'
307             integer e
308             integer d
309             integer n
310             integer u
311             integer p
312             integer q
313
314         The SSH Key Agent v3 format for a DSA key is::
315             string 'ssh-dss'
316             integer p
317             integer q
318             integer g
319             integer y
320             integer x
321
322         @type data: C{str}
323         @return: a C{Crypto.PublicKey.pubkey.pubkey} object
324         @raises BadKeyError: if the key type (the first string) is unknown
325         """
326         keyType, data = common.getNS(data)
327         if keyType == 'ssh-dss':
328             p, data = common.getMP(data)
329             q, data = common.getMP(data)
330             g, data = common.getMP(data)
331             y, data = common.getMP(data)
332             x, data = common.getMP(data)
333             return Class(DSA.construct((y,g,p,q,x)))
334         elif keyType == 'ssh-rsa':
335             e, data = common.getMP(data)
336             d, data = common.getMP(data)
337             n, data = common.getMP(data)
338             u, data = common.getMP(data)
339             p, data = common.getMP(data)
340             q, data = common.getMP(data)
341             return Class(RSA.construct((n,e,d,p,q,u)))
342         else:
343             raise BadKeyError("unknown key type %s" % keyType)
344     _fromString_AGENTV3 = classmethod(_fromString_AGENTV3)
345
346     def _guessStringType(Class, data):
347         """
348         Guess the type of key in data.  The types map to _fromString_*
349         methods.
350         """
351         if data.startswith('ssh-'):
352             return 'public_openssh'
353         elif data.startswith('-----BEGIN'):
354             return 'private_openssh'
355         elif data.startswith('{'):
356             return 'public_lsh'
357         elif data.startswith('('):
358             return 'private_lsh'
359         elif data.startswith('\x00\x00\x00\x07ssh-'):
360             ignored, rest = common.getNS(data)
361             count = 0
362             while rest:
363                 count += 1
364                 ignored, rest = common.getMP(rest)
365             if count > 4:
366                 return 'agentv3'
367             else:
368                 return 'blob'
369     _guessStringType = classmethod(_guessStringType)
370
371     def __init__(self, keyObject):
372         """
373         Initialize a PublicKey with a C{Crypto.PublicKey.pubkey.pubkey}
374         object.
375
376         @type keyObject: C{Crypto.PublicKey.pubkey.pubkey}
377         """
378         self.keyObject = keyObject
379
380     def __eq__(self, other):
381         """
382         Return True if other represents an object with the same key.
383         """
384         if type(self) == type(other):
385             return self.type() == other.type() and self.data() == other.data()
386         else:
387             return NotImplemented
388
389     def __ne__(self, other):
390         """
391         Return True if other represents anything other than this key.
392         """
393         result = self.__eq__(other)
394         if result == NotImplemented:
395             return result
396         return not result
397
398     def __repr__(self):
399         """
400         Return a pretty representation of this object.
401         """
402         lines = ['<%s %s (%s bits)' % (self.type(),
403             self.isPublic() and 'Public Key' or 'Private Key',
404             self.keyObject.size())]
405         for k, v in self.data().items():
406             lines.append('attr %s:' % k)
407             by = common.MP(v)[4:]
408             while by:
409                 m = by[:15]
410                 by = by[15:]
411                 o = ''
412                 for c in m:
413                     o = o + '%02x:' % ord(c)
414                 if len(m) < 15:
415                     o = o[:-1]
416                 lines.append('\t' + o)
417         lines[-1] = lines[-1] + '>'
418         return '\n'.join(lines)
419
420     def isPublic(self):
421         """
422         Returns True if this Key is a public key.
423         """
424         return not self.keyObject.has_private()
425
426     def public(self):
427         """
428         Returns a version of this key containing only the public key data.
429         If this is a public key, this may or may not be the same object
430         as self.
431         """
432         return Key(self.keyObject.publickey())
433
434
435     def fingerprint(self):
436         """
437         Get the user presentation of the fingerprint of this L{Key}.  As
438         described by U{RFC 4716 section
439         4<http://tools.ietf.org/html/rfc4716#section-4>}::
440
441             The fingerprint of a public key consists of the output of the MD5
442             message-digest algorithm [RFC1321].  The input to the algorithm is
443             the public key data as specified by [RFC4253].  (...)  The output
444             of the (MD5) algorithm is presented to the user as a sequence of 16
445             octets printed as hexadecimal with lowercase letters and separated
446             by colons.
447
448         @since: 8.2
449
450         @return: the user presentation of this L{Key}'s fingerprint, as a
451         string.
452
453         @rtype: L{str}
454         """
455         return ':'.join([x.encode('hex') for x in md5(self.blob()).digest()])
456
457
458     def type(self):
459         """
460         Return the type of the object we wrap.  Currently this can only be
461         'RSA' or 'DSA'.
462         """
463         # the class is Crypto.PublicKey.<type>.<stuff we don't care about>
464         mod = self.keyObject.__class__.__module__
465         if mod.startswith('Crypto.PublicKey'):
466             type = mod.split('.')[2]
467         else:
468             raise RuntimeError('unknown type of object: %r' % self.keyObject)
469         if type in ('RSA', 'DSA'):
470             return type
471         else:
472             raise RuntimeError('unknown type of key: %s' % type)
473
474     def sshType(self):
475         """
476         Return the type of the object we wrap as defined in the ssh protocol.
477         Currently this can only be 'ssh-rsa' or 'ssh-dss'.
478         """
479         return {'RSA':'ssh-rsa', 'DSA':'ssh-dss'}[self.type()]
480
481     def data(self):
482         """
483         Return the values of the public key as a dictionary.
484
485         @rtype: C{dict}
486         """
487         keyData = {}
488         for name in self.keyObject.keydata:
489             value = getattr(self.keyObject, name, None)
490             if value is not None:
491                 keyData[name] = value
492         return keyData
493
494     def blob(self):
495         """
496         Return the public key blob for this key.  The blob is the
497         over-the-wire format for public keys:
498
499         RSA keys::
500             string  'ssh-rsa'
501             integer e
502             integer n
503
504         DSA keys::
505             string  'ssh-dss'
506             integer p
507             integer q
508             integer g
509             integer y
510
511         @rtype: C{str}
512         """
513         type = self.type()
514         data = self.data()
515         if type == 'RSA':
516             return (common.NS('ssh-rsa') + common.MP(data['e']) +
517                     common.MP(data['n']))
518         elif type == 'DSA':
519             return (common.NS('ssh-dss') + common.MP(data['p']) +
520                     common.MP(data['q']) + common.MP(data['g']) +
521                     common.MP(data['y']))
522
523     def privateBlob(self):
524         """
525         Return the private key blob for this key.  The blob is the
526         over-the-wire format for private keys:
527
528         RSA keys::
529             string 'ssh-rsa'
530             integer n
531             integer e
532             integer d
533             integer u
534             integer p
535             integer q
536
537         DSA keys::
538             string 'ssh-dss'
539             integer p
540             integer q
541             integer g
542             integer y
543             integer x
544         """
545         type = self.type()
546         data = self.data()
547         if type == 'RSA':
548             return (common.NS('ssh-rsa') + common.MP(data['n']) +
549                     common.MP(data['e']) + common.MP(data['d']) +
550                     common.MP(data['u']) + common.MP(data['p']) +
551                     common.MP(data['q']))
552         elif type == 'DSA':
553             return (common.NS('ssh-dss') + common.MP(data['p']) +
554                     common.MP(data['q']) + common.MP(data['g']) +
555                     common.MP(data['y']) + common.MP(data['x']))
556
557     def toString(self, type, extra=None):
558         """
559         Create a string representation of this key.  If the key is a private
560         key and you want the represenation of its public key, use
561         C{key.public().toString()}.  type maps to a _toString_* method.
562
563         @param type: The type of string to emit.  Currently supported values
564             are C{'OPENSSH'}, C{'LSH'}, and C{'AGENTV3'}.
565         @type type: L{str}
566
567         @param extra: Any extra data supported by the selected format which
568             is not part of the key itself.  For public OpenSSH keys, this is
569             a comment.  For private OpenSSH keys, this is a passphrase to
570             encrypt with.
571         @type extra: L{str} or L{NoneType}
572
573         @rtype: L{str}
574         """
575         method = getattr(self, '_toString_%s' % type.upper(), None)
576         if method is None:
577             raise BadKeyError('unknown type: %s' % type)
578         if method.func_code.co_argcount == 2:
579             return method(extra)
580         else:
581             return method()
582
583     def _toString_OPENSSH(self, extra):
584         """
585         Return a public or private OpenSSH string.  See
586         _fromString_PUBLIC_OPENSSH and _fromString_PRIVATE_OPENSSH for the
587         string formats.  If extra is present, it represents a comment for a
588         public key, or a passphrase for a private key.
589
590         @type extra: C{str}
591         @rtype: C{str}
592         """
593         data = self.data()
594         if self.isPublic():
595             b64Data = base64.encodestring(self.blob()).replace('\n', '')
596             if not extra:
597                 extra = ''
598             return ('%s %s %s' % (self.sshType(), b64Data, extra)).strip()
599         else:
600             lines = ['-----BEGIN %s PRIVATE KEY-----' % self.type()]
601             if self.type() == 'RSA':
602                 p, q = data['p'], data['q']
603                 objData = (0, data['n'], data['e'], data['d'], q, p,
604                         data['d'] % (q - 1), data['d'] % (p - 1),
605                         data['u'])
606             else:
607                 objData = (0, data['p'], data['q'], data['g'], data['y'],
608                     data['x'])
609             asn1Sequence = univ.Sequence()
610             for index, value in itertools.izip(itertools.count(), objData):
611                 asn1Sequence.setComponentByPosition(index, univ.Integer(value))
612             asn1Data = berEncoder.encode(asn1Sequence)
613             if extra:
614                 iv = randbytes.secureRandom(8)
615                 hexiv = ''.join(['%02X' % ord(x) for x in iv])
616                 lines.append('Proc-Type: 4,ENCRYPTED')
617                 lines.append('DEK-Info: DES-EDE3-CBC,%s\n' % hexiv)
618                 ba = md5(extra + iv).digest()
619                 bb = md5(ba + extra + iv).digest()
620                 encKey = (ba + bb)[:24]
621                 padLen = 8 - (len(asn1Data) % 8)
622                 asn1Data += (chr(padLen) * padLen)
623                 asn1Data = DES3.new(encKey, DES3.MODE_CBC,
624                     iv).encrypt(asn1Data)
625             b64Data = base64.encodestring(asn1Data).replace('\n', '')
626             lines += [b64Data[i:i + 64] for i in range(0, len(b64Data), 64)]
627             lines.append('-----END %s PRIVATE KEY-----' % self.type())
628             return '\n'.join(lines)
629
630     def _toString_LSH(self):
631         """
632         Return a public or private LSH key.  See _fromString_PUBLIC_LSH and
633         _fromString_PRIVATE_LSH for the key formats.
634
635         @rtype: C{str}
636         """
637         data = self.data()
638         if self.isPublic():
639             if self.type() == 'RSA':
640                 keyData = sexpy.pack([['public-key', ['rsa-pkcs1-sha1',
641                                     ['n', common.MP(data['n'])[4:]],
642                                     ['e', common.MP(data['e'])[4:]]]]])
643             elif self.type() == 'DSA':
644                 keyData = sexpy.pack([['public-key', ['dsa',
645                                     ['p', common.MP(data['p'])[4:]],
646                                     ['q', common.MP(data['q'])[4:]],
647                                     ['g', common.MP(data['g'])[4:]],
648                                     ['y', common.MP(data['y'])[4:]]]]])
649             return '{' + base64.encodestring(keyData).replace('\n', '') + '}'
650         else:
651             if self.type() == 'RSA':
652                 p, q = data['p'], data['q']
653                 return sexpy.pack([['private-key', ['rsa-pkcs1',
654                                 ['n', common.MP(data['n'])[4:]],
655                                 ['e', common.MP(data['e'])[4:]],
656                                 ['d', common.MP(data['d'])[4:]],
657                                 ['p', common.MP(q)[4:]],
658                                 ['q', common.MP(p)[4:]],
659                                 ['a', common.MP(data['d'] % (q - 1))[4:]],
660                                 ['b', common.MP(data['d'] % (p - 1))[4:]],
661                                 ['c', common.MP(data['u'])[4:]]]]])
662             elif self.type() == 'DSA':
663                 return sexpy.pack([['private-key', ['dsa',
664                                 ['p', common.MP(data['p'])[4:]],
665                                 ['q', common.MP(data['q'])[4:]],
666                                 ['g', common.MP(data['g'])[4:]],
667                                 ['y', common.MP(data['y'])[4:]],
668                                 ['x', common.MP(data['x'])[4:]]]]])
669
670     def _toString_AGENTV3(self):
671         """
672         Return a private Secure Shell Agent v3 key.  See
673         _fromString_AGENTV3 for the key format.
674
675         @rtype: C{str}
676         """
677         data = self.data()
678         if not self.isPublic():
679             if self.type() == 'RSA':
680                 values = (data['e'], data['d'], data['n'], data['u'],
681                         data['p'], data['q'])
682             elif self.type() == 'DSA':
683                 values = (data['p'], data['q'], data['g'], data['y'],
684                         data['x'])
685             return common.NS(self.sshType()) + ''.join(map(common.MP, values))
686
687
688     def sign(self, data):
689         """
690         Returns a signature with this Key.
691
692         @type data: C{str}
693         @rtype: C{str}
694         """
695         if self.type() == 'RSA':
696             digest = pkcs1Digest(data, self.keyObject.size()/8)
697             signature = self.keyObject.sign(digest, '')[0]
698             ret = common.NS(Util.number.long_to_bytes(signature))
699         elif self.type() == 'DSA':
700             digest = sha1(data).digest()
701             randomBytes = randbytes.secureRandom(19)
702             sig = self.keyObject.sign(digest, randomBytes)
703             # SSH insists that the DSS signature blob be two 160-bit integers
704             # concatenated together. The sig[0], [1] numbers from obj.sign
705             # are just numbers, and could be any length from 0 to 160 bits.
706             # Make sure they are padded out to 160 bits (20 bytes each)
707             ret = common.NS(Util.number.long_to_bytes(sig[0], 20) +
708                              Util.number.long_to_bytes(sig[1], 20))
709         return common.NS(self.sshType()) + ret
710
711     def verify(self, signature, data):
712         """
713         Returns true if the signature for data is valid for this Key.
714
715         @type signature: C{str}
716         @type data: C{str}
717         @rtype: C{bool}
718         """
719         signatureType, signature = common.getNS(signature)
720         if signatureType != self.sshType():
721             return False
722         if self.type() == 'RSA':
723             numbers = common.getMP(signature)
724             digest = pkcs1Digest(data, self.keyObject.size() / 8)
725         elif self.type() == 'DSA':
726             signature = common.getNS(signature)[0]
727             numbers = [Util.number.bytes_to_long(n) for n in signature[:20],
728                     signature[20:]]
729             digest = sha1(data).digest()
730         return self.keyObject.verify(digest, numbers)
731
732
733 def objectType(obj):
734     """
735     Return the SSH key type corresponding to a C{Crypto.PublicKey.pubkey.pubkey}
736     object.
737
738     @type obj:  C{Crypto.PublicKey.pubkey.pubkey}
739     @rtype:     C{str}
740     """
741     keyDataMapping = {
742         ('n', 'e', 'd', 'p', 'q'): 'ssh-rsa',
743         ('n', 'e', 'd', 'p', 'q', 'u'): 'ssh-rsa',
744         ('y', 'g', 'p', 'q', 'x'): 'ssh-dss'
745     }
746     try:
747         return keyDataMapping[tuple(obj.keydata)]
748     except (KeyError, AttributeError):
749         raise BadKeyError("invalid key object", obj)
750
751 def pkcs1Pad(data, messageLength):
752     """
753     Pad out data to messageLength according to the PKCS#1 standard.
754     @type data: C{str}
755     @type messageLength: C{int}
756     """
757     lenPad = messageLength - 2 - len(data)
758     return '\x01' + ('\xff' * lenPad) + '\x00' + data
759
760 def pkcs1Digest(data, messageLength):
761     """
762     Create a message digest using the SHA1 hash algorithm according to the
763     PKCS#1 standard.
764     @type data: C{str}
765     @type messageLength: C{str}
766     """
767     digest = sha1(data).digest()
768     return pkcs1Pad(ID_SHA1+digest, messageLength)
769
770 def lenSig(obj):
771     """
772     Return the length of the signature in bytes for a key object.
773
774     @type obj: C{Crypto.PublicKey.pubkey.pubkey}
775     @rtype: C{long}
776     """
777     return obj.size()/8
778
779
780 ID_SHA1 = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'