root / trunk / twisted / conch / ssh / keys.py

Revision 27079, 33.4 kB (checked in by therve, 2 days ago)

Merge dsa-key-failure-3391-2

Authors: z3p, therve
Reviewers: glyph, jml
Fixes #3391

Replace Conch custom ASN1 parser by pyasn1. It introduces a new dependency for
Conch, and solve a bunch of problems related to the previously custom parser.

Line 
1 # -*- test-case-name: twisted.conch.test.test_keys -*-
2 # Copyright (c) 2001-2009 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         klass = str(self.keyObject.__class__)
465         if klass.startswith('Crypto.PublicKey'):
466             type = klass.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 def getPublicKeyString(filename=None, line=0, data=''):
733     """
734     Return a public key string suitable for being sent over the wire.
735     Takes a filename or data of a public key.  Currently handles OpenSSH
736     and LSH keys.
737
738     This function has been deprecated since Twisted Conch 0.9.  Use
739     Key.fromString() instead.
740
741     @type filename: C{str}
742     @type line:     C{int}
743     @type data:     C{str}
744     @rtype:         C{str}
745     """
746     warnings.warn("getPublicKeyString is deprecated since Twisted Conch 0.9."
747             "  Use Key.fromString().",
748             DeprecationWarning, stacklevel=2)
749     if filename and data:
750         raise BadKeyError("either filename or data, not both")
751     if filename:
752         lines = open(filename).readlines()
753         data = lines[line]
754     return Key.fromString(data).blob()
755
756 def makePublicKeyString(obj, comment='', kind='openssh'):
757     """
758     Return an public key given a C{Crypto.PublicKey.pubkey.pubkey}
759     object.
760     kind is one of ('openssh', 'lsh')
761
762     This function is deprecated since Twisted Conch 0.9.  Instead use
763     Key(obj).toString().
764
765     @type obj:      C{Crypto.PublicKey.pubkey.pubkey}
766     @type comment:  C{str}
767     @type kind:     C{str}
768     @rtype:         C{str}
769     """
770     warnings.warn("makePublicKeyString is deprecated since Twisted Conch 0.9."
771             "  Use Key(obj).toString().",
772             DeprecationWarning, stacklevel=2)
773     return Key(obj).public().toString(kind, comment)
774
775 def getPublicKeyObject(data):
776     """
777     Return a C{Crypto.PublicKey.pubkey.pubkey} corresponding to the SSHv2
778     public key data.  data is in the over-the-wire public key format.
779
780     This function is deprecated since Twisted Conch 0.9. Instead, use
781     Key.fromString().
782
783     @type data:     C{str}
784     @rtype:         C{Crypto.PublicKey.pubkey.pubkey}
785     """
786     warnings.warn("getPublicKeyObject is deprecated since Twisted Conch 0.9."
787             "  Use Key.fromString().",
788             DeprecationWarning, stacklevel=2)
789     return Key.fromString(data).keyObject
790
791 def getPrivateKeyObject(filename=None, data='', passphrase=''):
792     """
793     Return a C{Crypto.PublicKey.pubkey.pubkey} object corresponding to the
794     private key file/data.  If the private key is encrypted, passphrase B{must}
795     be specified, other wise a L{BadKeyError} will be raised.
796
797     This method is deprecated since Twisted Conch 0.9.  Instead, use
798     the fromString or fromFile classmethods of Key.
799
800     @type filename:     C{str}
801     @type data:         C{str}
802     @type passphrase:   C{str}
803     @rtype: C{Crypto.PublicKey.pubkey.pubkey}
804     @raises BadKeyError: if the key is invalid or a passphrase is not specified
805     """
806     warnings.warn("getPrivateKeyObject is deprecated since Twisted Conch 0.9."
807             "  Use Key.fromString().",
808             DeprecationWarning, stacklevel=2)
809     if filename and data:
810         raise BadKeyError("either filename or data, not both")
811     if filename:
812         return Key.fromFile(filename, passphrase=passphrase).keyObject
813     else:
814         return Key.fromString(data, passphrase=passphrase).keyObject
815
816 def makePrivateKeyString(obj, passphrase=None, kind='openssh'):
817     """
818     Return an OpenSSH-style private key for a
819     C{Crypto.PublicKey.pubkey.pubkey} object.  If passphrase is given, encrypt
820     the private key with it.
821     kind is one of ('openssh', 'lsh', 'agentv3')
822
823     This function is deprecated since Twisted Conch 0.9. Instead use
824     Key(obj).toString().
825
826     @type obj:          C{Crypto.PublicKey.pubkey.pubkey}
827     @type passphrase:   C{str}/C{None}
828     @type kind:         C{str}
829     @rtype:             C{str}
830     """
831     warnings.warn("makePrivateKeyString is deprecated since Twisted Conch 0.9."
832             "  Use Key(obj).toString().",
833             DeprecationWarning, stacklevel=2)
834     return Key(obj).toString(kind, passphrase)
835
836 def makePublicKeyBlob(obj):
837     """
838     Make a public key blob from a C{Crypto.PublicKey.pubkey.pubkey}.
839
840     This function is deprecated since Twisted Conch 0.9.  Use
841     Key().blob() instead.
842     """
843     warnings.warn("makePublicKeyBlob is deprecated since Twisted Conch 0.9."
844             "  Use Key(obj).blob().",
845             DeprecationWarning, stacklevel=2)
846     return Key(obj).blob()
847
848 def objectType(obj):
849     """
850     Return the SSH key type corresponding to a C{Crypto.PublicKey.pubkey.pubkey}
851     object.
852
853     @type obj:  C{Crypto.PublicKey.pubkey.pubkey}
854     @rtype:     C{str}
855     """
856     keyDataMapping = {
857         ('n', 'e', 'd', 'p', 'q'): 'ssh-rsa',
858         ('n', 'e', 'd', 'p', 'q', 'u'): 'ssh-rsa',
859         ('y', 'g', 'p', 'q', 'x'): 'ssh-dss'
860     }
861     try:
862         return keyDataMapping[tuple(obj.keydata)]
863     except (KeyError, AttributeError):
864         raise BadKeyError("invalid key object", obj)
865
866 def pkcs1Pad(data, messageLength):
867     """
868     Pad out data to messageLength according to the PKCS#1 standard.
869     @type data: C{str}
870     @type messageLength: C{int}
871     """
872     lenPad = messageLength - 2 - len(data)
873     return '\x01' + ('\xff' * lenPad) + '\x00' + data
874
875 def pkcs1Digest(data, messageLength):
876     """
877     Create a message digest using the SHA1 hash algorithm according to the
878     PKCS#1 standard.
879     @type data: C{str}
880     @type messageLength: C{str}
881     """
882     digest = sha1(data).digest()
883     return pkcs1Pad(ID_SHA1+digest, messageLength)
884
885 def lenSig(obj):
886     """
887     Return the length of the signature in bytes for a key object.
888
889     @type obj: C{Crypto.PublicKey.pubkey.pubkey}
890     @rtype: C{long}
891     """
892     return obj.size()/8
893
894 def signData(obj, data):
895     """
896     Sign the data with the given C{Crypto.PublicKey.pubkey.pubkey} object.
897
898     This method is deprecated since Twisted Conch 0.9.  Instead use
899     Key().sign().
900
901     @type obj:  C{Crypto.PublicKey.pubkey.pubkey}
902     @type data: C{str}
903     @rtype:     C{str}
904     """
905     warnings.warn("signData is deprecated since Twisted Conch 0.9."
906             "  Use Key(obj).sign(data).",
907             DeprecationWarning, stacklevel=2)
908     return Key(obj).sign(data)
909
910 def verifySignature(obj, sig, data):
911     """
912     Verify that the signature for the data is valid.
913
914     This method is deprecated since Twisted Conch 0.9.  Use
915     Key().verify().
916
917     @type obj:  C{Crypto.PublicKey.pubkey.pubkey}
918     @type sig:  C{str}
919     @type data: C{str}
920     @rtype:     C{bool}
921     """
922     warnings.warn("verifySignature is deprecated since Twisted Conch 0.9."
923             "  Use Key(obj).verify(signature, data).",
924             DeprecationWarning, stacklevel=2)
925     return Key(obj).verify(sig, data)
926
927 def printKey(obj):
928     """
929     Pretty print a C{Crypto.PublicKey.pubkey.pubkey} object.
930
931     This function is deprecated since Twisted Conch 0.9.  Use
932     repr(Key()).
933
934     @type obj: C{Crypto.PublicKey.pubkey.pubkey}
935     """
936     warnings.warn("printKey is deprecated since Twisted Conch 0.9."
937             "  Use repr(Key(obj)).",
938             DeprecationWarning, stacklevel=2)
939     return repr(Key(obj))[1:-1]
940
941 ID_SHA1 = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'
Note: See TracBrowser for help on using the browser.