~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/conch/ssh/keys.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.conch.test.test_keys -*-
 
2
# Copyright (c) 2001-2010 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().blob().",
 
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).public().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'