Preserve public/secret distinction in _serialize_signature_packet_target
[gpg-migrate.git] / gpg-migrate.py
1 #!/usr/bin/python
2
3 import getpass as _getpass
4 import hashlib as _hashlib
5 import logging as _logging
6 import math as _math
7 import re as _re
8 import subprocess as _subprocess
9 import struct as _struct
10
11 import Crypto.Cipher.AES as _crypto_cipher_aes
12 import Crypto.Cipher.Blowfish as _crypto_cipher_blowfish
13 import Crypto.Cipher.CAST as _crypto_cipher_cast
14 import Crypto.Cipher.DES3 as _crypto_cipher_des3
15 import Crypto.PublicKey.DSA as _crypto_publickey_dsa
16 import Crypto.PublicKey.ElGamal as _crypto_publickey_elgamal
17 import Crypto.PublicKey.RSA as _crypto_publickey_rsa
18 import Crypto.Random.random as _crypto_random_random
19 import Crypto.Signature.PKCS1_v1_5 as _crypto_signature_pkcs1_v1_5
20 import Crypto.Hash.SHA as _crypto_hash_sha
21
22
23 LOG = _logging.getLogger('gpg-migrate')
24 LOG.addHandler(_logging.StreamHandler())
25 LOG.setLevel(_logging.WARNING)
26
27
28 def _get_stdout(args, stdin=None):
29     stdin_pipe = None
30     if stdin is not None:
31         stdin_pipe = _subprocess.PIPE
32     p = _subprocess.Popen(args, stdin=stdin_pipe, stdout=_subprocess.PIPE)
33     stdout, stderr = p.communicate(stdin)
34     status = p.wait()
35     if status != 0:
36         raise RuntimeError(status)
37     return stdout
38
39
40 def byte_string(data, sep=' '):
41     r"""Convert a byte-string to human readable form
42
43     >>> byte_string(b'\x12\x34\x56')
44     '12 34 56'
45     """
46     return sep.join('{:02x}'.format(byte) for byte in data)
47
48
49 def string_bytes(data, sep=' '):
50     r"""Reverse byte_string()
51
52     >>> string_bytes('12 fa fb')
53     b'\x12\xfa\xfb'
54     """
55     return bytes(
56         int(c1+c2, base=16) for c1,c2 in
57         zip(data[::2 + len(sep)], data[1::2 + len(sep)]))
58
59
60 class PGPPacket (dict):
61     # http://tools.ietf.org/search/rfc4880
62     _old_format_packet_length_type = {  # type: (bytes, struct type)
63         0: (1, 'B'),  # 1-byte unsigned integer
64         1: (2, 'H'),  # 2-byte unsigned integer
65         2: (4, 'I'),  # 4-byte unsigned integer
66         3: (None, None),
67         }
68
69     _packet_types = {
70         0: 'reserved',
71         1: 'public-key encrypted session key packet',
72         2: 'signature packet',
73         3: 'symmetric-key encrypted session key packet',
74         4: 'one-pass signature packet',
75         5: 'secret-key packet',
76         6: 'public-key packet',
77         7: 'secret-subkey packet',
78         8: 'compressed data packet',
79         9: 'symmetrically encrypted data packet',
80         10: 'marker packet',
81         11: 'literal data packet',
82         12: 'trust packet',
83         13: 'user id packet',
84         14: 'public-subkey packet',
85         17: 'user attribute packet',
86         18: 'sym. encrypted and integrity protected data packet',
87         19: 'modification detection code packet',
88         60: 'private',
89         61: 'private',
90         62: 'private',
91         63: 'private',
92         }
93
94     _public_key_algorithms = {
95         1: 'rsa (encrypt or sign)',
96         2: 'rsa encrypt-only',
97         3: 'rsa sign-only',
98         16: 'elgamal (encrypt-only)',
99         17: 'dsa (digital signature algorithm)',
100         18: 'reserved for elliptic curve',
101         19: 'reserved for ecdsa',
102         20: 'reserved (formerly elgamal encrypt or sign)',
103         21: 'reserved for diffie-hellman',
104         100: 'private',
105         101: 'private',
106         102: 'private',
107         103: 'private',
108         104: 'private',
109         105: 'private',
110         106: 'private',
111         107: 'private',
112         108: 'private',
113         109: 'private',
114         110: 'private',
115         }
116
117     _symmetric_key_algorithms = {
118         0: 'plaintext or unencrypted data',
119         1: 'idea',
120         2: 'tripledes',
121         3: 'cast5',
122         4: 'blowfish',
123         5: 'reserved',
124         6: 'reserved',
125         7: 'aes with 128-bit key',
126         8: 'aes with 192-bit key',
127         9: 'aes with 256-bit key',
128         10: 'twofish',
129         100: 'private',
130         101: 'private',
131         102: 'private',
132         103: 'private',
133         104: 'private',
134         105: 'private',
135         106: 'private',
136         107: 'private',
137         108: 'private',
138         109: 'private',
139         110: 'private',
140         }
141
142     _cipher_block_size = {  # in bits
143         'aes with 128-bit key': 128,
144         'aes with 192-bit key': 128,
145         'aes with 256-bit key': 128,
146         'cast5': 64,
147         'twofish': 128,
148         }
149
150     _crypto_module = {
151         # symmetric-key encryption
152         'aes with 128-bit key': _crypto_cipher_aes,
153         'aes with 192-bit key': _crypto_cipher_aes,
154         'aes with 256-bit key': _crypto_cipher_aes,
155         'blowfish': _crypto_cipher_blowfish,
156         'cast5': _crypto_cipher_cast,
157         'tripledes': _crypto_cipher_des3,
158         # public-key encryption
159         'dsa (digital signature algorithm)': _crypto_publickey_dsa,
160         'elgamal (encrypt-only)': _crypto_publickey_elgamal,
161         'rsa (encrypt or sign)': _crypto_publickey_rsa,
162         'rsa encrypt-only': _crypto_publickey_rsa,
163         'rsa sign-only': _crypto_publickey_rsa,
164         }
165
166     _key_size = {  # in bits
167         'aes with 128-bit key': 128,
168         'aes with 192-bit key': 192,
169         'aes with 256-bit key': 256,
170         'cast5': 128,
171         }
172
173     _compression_algorithms = {
174         0: 'uncompressed',
175         1: 'zip',
176         2: 'zlib',
177         3: 'bzip2',
178         100: 'private',
179         101: 'private',
180         102: 'private',
181         103: 'private',
182         104: 'private',
183         105: 'private',
184         106: 'private',
185         107: 'private',
186         108: 'private',
187         109: 'private',
188         110: 'private',
189         }
190
191     _hash_algorithms = {
192         1: 'md5',
193         2: 'sha-1',
194         3: 'ripe-md/160',
195         4: 'reserved',
196         5: 'reserved',
197         6: 'reserved',
198         7: 'reserved',
199         8: 'sha256',
200         9: 'sha384',
201         10: 'sha512',
202         11: 'sha224',
203         100: 'private',
204         101: 'private',
205         102: 'private',
206         103: 'private',
207         104: 'private',
208         105: 'private',
209         106: 'private',
210         107: 'private',
211         108: 'private',
212         109: 'private',
213         110: 'private',
214         }
215
216     _hashlib_name = {  # map OpenPGP-based names to hashlib names
217         'md5': 'md5',
218         'sha-1': 'sha1',
219         'ripe-md/160': 'ripemd160',
220         'sha256': 'sha256',
221         'sha384': 'sha384',
222         'sha512': 'sha512',
223         'sha224': 'sha224',
224         }
225
226     _string_to_key_types = {
227         0: 'simple',
228         1: 'salted',
229         2: 'reserved',
230         3: 'iterated and salted',
231         100: 'private',
232         101: 'private',
233         102: 'private',
234         103: 'private',
235         104: 'private',
236         105: 'private',
237         106: 'private',
238         107: 'private',
239         108: 'private',
240         109: 'private',
241         110: 'private',
242         }
243
244     _string_to_key_expbias = 6
245
246     _signature_types = {
247         0x00: 'binary document',
248         0x01: 'canonical text document',
249         0x02: 'standalone',
250         0x10: 'generic user id and public-key packet',
251         0x11: 'persona user id and public-key packet',
252         0x12: 'casual user id and public-key packet',
253         0x13: 'postitive user id and public-key packet',
254         0x18: 'subkey binding',
255         0x19: 'primary key binding',
256         0x1F: 'direct key',
257         0x20: 'key revocation',
258         0x28: 'subkey revocation',
259         0x30: 'certification revocation',
260         0x40: 'timestamp',
261         0x50: 'third-party confirmation',
262         }
263
264     _signature_subpacket_types = {
265         0: 'reserved',
266         1: 'reserved',
267         2: 'signature creation time',
268         3: 'signature expiration time',
269         4: 'exportable certification',
270         5: 'trust signature',
271         6: 'regular expression',
272         7: 'revocable',
273         8: 'reserved',
274         9: 'key expiration time',
275         10: 'placeholder for backward compatibility',
276         11: 'preferred symmetric algorithms',
277         12: 'revocation key',
278         13: 'reserved',
279         14: 'reserved',
280         15: 'reserved',
281         16: 'issuer',
282         17: 'reserved',
283         18: 'reserved',
284         19: 'reserved',
285         20: 'notation data',
286         21: 'preferred hash algorithms',
287         22: 'preferred compression algorithms',
288         23: 'key server preferences',
289         24: 'preferred key server',
290         25: 'primary user id',
291         26: 'policy uri',
292         27: 'key flags',
293         28: 'signer user id',
294         29: 'reason for revocation',
295         30: 'features',
296         31: 'signature target',
297         32: 'embedded signature',
298         100: 'private',
299         101: 'private',
300         102: 'private',
301         103: 'private',
302         104: 'private',
303         105: 'private',
304         106: 'private',
305         107: 'private',
306         108: 'private',
307         109: 'private',
308         110: 'private',
309         }
310
311     _clean_type_regex = _re.compile('\W+')
312
313     def __init__(self, key=None):
314         super(PGPPacket, self).__init__()
315         self.key = key
316
317     def _clean_type(self, type=None):
318         if type is None:
319             type = self['type']
320         return self._clean_type_regex.sub('_', type)
321
322     @staticmethod
323     def _reverse(dict, value):
324         """Reverse lookups in dictionaries
325
326         >>> PGPPacket._reverse(PGPPacket._packet_types, 'public-key packet')
327         6
328         """
329         return [k for k,v in dict.items() if v == value][0]
330
331     def copy(self):
332         packet = PGPPacket(key=self.key)
333         packet.update(self)
334         return packet
335
336     def __str__(self):
337         method_name = '_str_{}'.format(self._clean_type())
338         method = getattr(self, method_name, None)
339         if not method:
340             return self['type']
341         details = method()
342         return '{}: {}'.format(self['type'], details)
343
344     def _str_public_key_packet(self):
345         return self._str_generic_key_packet()
346
347     def _str_public_subkey_packet(self):
348         return self._str_generic_key_packet()
349
350     def _str_generic_key_packet(self):
351         return self['fingerprint'][-8:].upper()
352
353     def _str_secret_key_packet(self):
354         return self._str_generic_secret_key_packet()
355
356     def _str_secret_subkey_packet(self):
357         return self._str_generic_secret_key_packet()
358
359     def _str_generic_secret_key_packet(self):
360         lines = [self._str_generic_key_packet()]
361         for label, key in [
362                 ('symmetric encryption',
363                  'symmetric-encryption-algorithm'),
364                 ('s2k hash', 'string-to-key-hash-algorithm'),
365                 ('s2k count', 'string-to-key-count'),
366                 ('s2k salt', 'string-to-key-salt'),
367                 ('IV', 'initial-vector'),
368                 ]:
369             if key in self:
370                 value = self[key]
371                 if isinstance(value, bytes):
372                     value = byte_string(data=value)
373                 lines.append('  {}: {}'.format(label, value))
374         return '\n'.join(lines)
375
376     def _str_signature_packet(self):
377         lines = [self['signature-type']]
378         if self['hashed-subpackets']:
379             lines.append('  hashed subpackets:')
380             lines.extend(self._str_signature_subpackets(
381                 self['hashed-subpackets'], prefix='    '))
382         if self['unhashed-subpackets']:
383             lines.append('  unhashed subpackets:')
384             lines.extend(self._str_signature_subpackets(
385                 self['unhashed-subpackets'], prefix='    '))
386         return '\n'.join(lines)
387
388     def _str_signature_subpackets(self, subpackets, prefix):
389         lines = []
390         for subpacket in subpackets:
391             method_name = '_str_{}_signature_subpacket'.format(
392                 self._clean_type(type=subpacket['type']))
393             method = getattr(self, method_name, None)
394             if method:
395                 lines.append('    {}: {}'.format(
396                     subpacket['type'],
397                     method(subpacket=subpacket)))
398             else:
399                 lines.append('    {}'.format(subpacket['type']))
400         return lines
401
402     def _str_signature_creation_time_signature_subpacket(self, subpacket):
403         return str(subpacket['signature-creation-time'])
404
405     def _str_issuer_signature_subpacket(self, subpacket):
406         return subpacket['issuer'][-8:].upper()
407
408     def _str_key_expiration_time_signature_subpacket(self, subpacket):
409         return str(subpacket['key-expiration-time'])
410
411     def _str_preferred_symmetric_algorithms_signature_subpacket(
412             self, subpacket):
413         return ', '.join(
414             algo for algo in subpacket['preferred-symmetric-algorithms'])
415
416     def _str_preferred_hash_algorithms_signature_subpacket(
417             self, subpacket):
418         return ', '.join(
419             algo for algo in subpacket['preferred-hash-algorithms'])
420
421     def _str_preferred_compression_algorithms_signature_subpacket(
422             self, subpacket):
423         return ', '.join(
424             algo for algo in subpacket['preferred-compression-algorithms'])
425
426     def _str_key_server_preferences_signature_subpacket(self, subpacket):
427         return ', '.join(
428             x for x in sorted(subpacket['key-server-preferences']))
429
430     def _str_primary_user_id_signature_subpacket(self, subpacket):
431         return str(subpacket['primary-user-id'])
432
433     def _str_key_flags_signature_subpacket(self, subpacket):
434         return ', '.join(x for x in sorted(subpacket['key-flags']))
435
436     def _str_features_signature_subpacket(self, subpacket):
437         return ', '.join(x for x in sorted(subpacket['features']))
438
439     def _str_embedded_signature_signature_subpacket(self, subpacket):
440         return subpacket['embedded']['signature-type']
441
442     def _str_user_id_packet(self):
443         return self['user']
444
445     def from_bytes(self, data):
446         offset = self._parse_header(data=data)
447         packet = data[offset:offset + self['length']]
448         if len(packet) < self['length']:
449             raise ValueError('packet too short ({} < {})'.format(
450                 len(packet), self['length']))
451         offset += self['length']
452         method_name = '_parse_{}'.format(self._clean_type())
453         method = getattr(self, method_name, None)
454         if not method:
455             raise NotImplementedError(
456                 'cannot parse packet type {!r}'.format(self['type']))
457         method(data=packet)
458         self['raw'] = data[:offset]
459         return offset
460
461     def _parse_header(self, data):
462         packet_tag = data[0]
463         offset = 1
464         always_one = packet_tag & 1 << 7
465         if not always_one:
466             raise ValueError('most significant packet tag bit not set')
467         self['new-format'] = packet_tag & 1 << 6
468         if self['new-format']:
469             type_code = packet_tag & 0b111111
470             raise NotImplementedError('new-format packet length')
471         else:
472             type_code = packet_tag >> 2 & 0b1111
473             self['length-type'] = packet_tag & 0b11
474             length_bytes, length_type = self._old_format_packet_length_type[
475                 self['length-type']]
476             if not length_bytes:
477                 raise NotImplementedError(
478                     'old-format packet of indeterminate length')
479             length_format = '>{}'.format(length_type)
480             length_data = data[offset: offset + length_bytes]
481             offset += length_bytes
482             self['length'] = _struct.unpack(length_format, length_data)[0]
483         self['type'] = self._packet_types[type_code]
484         return offset
485
486     @staticmethod
487     def _parse_multiprecision_integer(data):
488         r"""Parse RFC 4880's multiprecision integers
489
490         >>> PGPPacket._parse_multiprecision_integer(b'\x00\x01\x01')
491         (3, 1)
492         >>> PGPPacket._parse_multiprecision_integer(b'\x00\x09\x01\xff')
493         (4, 511)
494         """
495         bits = _struct.unpack('>H', data[:2])[0]
496         offset = 2
497         length = (bits + 7) // 8
498         value = 0
499         for i in range(length):
500             value += data[offset + i] * 1 << (8 * (length - i - 1))
501         offset += length
502         return (offset, value)
503
504     @classmethod
505     def _decode_string_to_key_count(cls, data):
506         r"""Decode RFC 4880's string-to-key count
507
508         >>> PGPPacket._decode_string_to_key_count(b'\x97'[0])
509         753664
510         """
511         return (16 + (data & 15)) << ((data >> 4) + cls._string_to_key_expbias)
512
513     def _parse_string_to_key_specifier(self, data):
514         self['string-to-key-type'] = self._string_to_key_types[data[0]]
515         offset = 1
516         if self['string-to-key-type'] == 'simple':
517             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
518                 data[offset]]
519             offset += 1
520         elif self['string-to-key-type'] == 'salted':
521             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
522                 data[offset]]
523             offset += 1
524             self['string-to-key-salt'] = data[offset: offset + 8]
525             offset += 8
526         elif self['string-to-key-type'] == 'iterated and salted':
527             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
528                 data[offset]]
529             offset += 1
530             self['string-to-key-salt'] = data[offset: offset + 8]
531             offset += 8
532             self['string-to-key-count'] = self._decode_string_to_key_count(
533                 data=data[offset])
534             offset += 1
535         else:
536             raise NotImplementedError(
537                 'string-to-key type {}'.format(self['string-to-key-type']))
538         return offset
539
540     def _parse_public_key_packet(self, data):
541         self._parse_generic_public_key_packet(data=data)
542
543     def _parse_public_subkey_packet(self, data):
544         self._parse_generic_public_key_packet(data=data)
545
546     def _parse_generic_public_key_packet(self, data):
547         self['key-version'] = data[0]
548         offset = 1
549         if self['key-version'] != 4:
550             raise NotImplementedError(
551                 'public (sub)key packet version {}'.format(
552                     self['key-version']))
553         length = 5
554         self['creation-time'], algorithm = _struct.unpack(
555             '>IB', data[offset: offset + length])
556         offset += length
557         self['public-key-algorithm'] = self._public_key_algorithms[algorithm]
558         if self['public-key-algorithm'].startswith('rsa '):
559             o, self['public-modulus'] = self._parse_multiprecision_integer(
560                 data[offset:])
561             offset += o
562             o, self['public-exponent'] = self._parse_multiprecision_integer(
563                 data[offset:])
564             offset += o
565         elif self['public-key-algorithm'].startswith('dsa '):
566             o, self['prime'] = self._parse_multiprecision_integer(
567                 data[offset:])
568             offset += o
569             o, self['group-order'] = self._parse_multiprecision_integer(
570                 data[offset:])
571             offset += o
572             o, self['group-generator'] = self._parse_multiprecision_integer(
573                 data[offset:])
574             offset += o
575             o, self['public-key'] = self._parse_multiprecision_integer(
576                 data[offset:])
577             offset += o
578         elif self['public-key-algorithm'].startswith('elgamal '):
579             o, self['prime'] = self._parse_multiprecision_integer(
580                 data[offset:])
581             offset += o
582             o, self['group-generator'] = self._parse_multiprecision_integer(
583                 data[offset:])
584             offset += o
585             o, self['public-key'] = self._parse_multiprecision_integer(
586                 data[offset:])
587             offset += o
588         else:
589             raise NotImplementedError(
590                 'algorithm-specific key fields for {}'.format(
591                     self['public-key-algorithm']))
592         fingerprint = _hashlib.sha1()
593         fingerprint.update(
594             self._serialize_signature_packet_target(target=self))
595         self['fingerprint'] = fingerprint.hexdigest()
596         return offset
597
598     def _parse_secret_key_packet(self, data):
599         self._parse_generic_secret_key_packet(data=data)
600
601     def _parse_secret_subkey_packet(self, data):
602         self._parse_generic_secret_key_packet(data=data)
603
604     def _parse_generic_secret_key_packet(self, data):
605         offset = self._parse_generic_public_key_packet(data=data)
606         string_to_key_usage = data[offset]
607         offset += 1
608         if string_to_key_usage in [255, 254]:
609             self['symmetric-encryption-algorithm'] = (
610                 self._symmetric_key_algorithms[data[offset]])
611             offset += 1
612             offset += self._parse_string_to_key_specifier(data=data[offset:])
613         else:
614             self['symmetric-encryption-algorithm'] = (
615                 self._symmetric_key_algorithms[string_to_key_usage])
616         if string_to_key_usage:
617             block_size_bits = self._cipher_block_size.get(
618                 self['symmetric-encryption-algorithm'], None)
619             if block_size_bits % 8:
620                 raise NotImplementedError(
621                     ('{}-bit block size for {} is not an integer number of bytes'
622                      ).format(
623                          block_size_bits, self['symmetric-encryption-algorithm']))
624             block_size = block_size_bits // 8
625             if not block_size:
626                 raise NotImplementedError(
627                     'unknown block size for {}'.format(
628                         self['symmetric-encryption-algorithm']))
629             self['initial-vector'] = data[offset: offset + block_size]
630             offset += block_size
631             ciphertext = data[offset:]
632             offset += len(ciphertext)
633             decrypted_data = self.decrypt_symmetric_encryption(data=ciphertext)
634         else:
635             decrypted_data = data[offset:key_end]
636         if string_to_key_usage in [0, 255]:
637             key_end = -2
638         elif string_to_key_usage == 254:
639             key_end = -20
640         else:
641             key_end = 0
642         secret_key = decrypted_data[:key_end]
643         secret_offset = 0
644         if key_end:
645             secret_key_checksum = decrypted_data[key_end:]
646             if key_end == -2:
647                 calculated_checksum = sum(secret_key) % 65536
648             else:
649                 checksum_hash = _hashlib.sha1()
650                 checksum_hash.update(secret_key)
651                 calculated_checksum = checksum_hash.digest()
652             if secret_key_checksum != calculated_checksum:
653                 raise ValueError(
654                     'corrupt secret key (checksum {} != expected {})'.format(
655                         secret_key_checksum, calculated_checksum))
656         if self['public-key-algorithm'].startswith('rsa '):
657             o, self['secret-exponent'] = self._parse_multiprecision_integer(
658                 secret_key[secret_offset:])
659             secret_offset += o
660             o, self['secret-prime-p'] = self._parse_multiprecision_integer(
661                 secret_key[secret_offset:])
662             secret_offset += o
663             o, self['secret-prime-q'] = self._parse_multiprecision_integer(
664                 secret_key[secret_offset:])
665             secret_offset += o
666             o, self['secret-inverse-of-p-mod-q'] = (
667                 self._parse_multiprecision_integer(
668                     secret_key[secret_offset:]))
669             secret_offset += o
670         elif self['public-key-algorithm'].startswith('dsa '):
671             o, self['secret-exponent'] = self._parse_multiprecision_integer(
672                 secret_key[secret_offset:])
673             secret_offset += o
674         elif self['public-key-algorithm'].startswith('elgamal '):
675             o, self['secret-exponent'] = self._parse_multiprecision_integer(
676                 secret_key[secret_offset:])
677             secret_offset += o
678         else:
679             raise NotImplementedError(
680                 'algorithm-specific key fields for {}'.format(
681                     self['public-key-algorithm']))
682         if secret_offset != len(secret_key):
683             raise ValueError(
684                 ('parsed {} out of {} bytes of algorithm-specific key fields '
685                  'for {}').format(
686                      secret_offset, len(secret_key),
687                      self['public-key-algorithm']))
688
689     def _parse_signature_subpackets(self, data):
690         offset = 0
691         while offset < len(data):
692             o, subpacket = self._parse_signature_subpacket(data=data[offset:])
693             offset += o
694             yield subpacket
695
696     def _parse_signature_subpacket(self, data):
697         subpacket = {}
698         first = data[0]
699         offset = 1
700         if first < 192:
701             length = first
702         elif first >= 192 and first < 255:
703             second = data[offset]
704             offset += 1
705             length = ((first - 192) << 8) + second + 192
706         else:
707             length = _struct.unpack(
708                 '>I', data[offset: offset + 4])[0]
709             offset += 4
710         subpacket['type'] = self._signature_subpacket_types[data[offset]]
711         offset += 1
712         subpacket_data = data[offset: offset + length - 1]
713         offset += len(subpacket_data)
714         method_name = '_parse_{}_signature_subpacket'.format(
715             self._clean_type(type=subpacket['type']))
716         method = getattr(self, method_name, None)
717         if not method:
718             raise NotImplementedError(
719                 'cannot parse signature subpacket type {!r}'.format(
720                     subpacket['type']))
721         method(data=subpacket_data, subpacket=subpacket)
722         return (offset, subpacket)
723
724     def _parse_signature_packet(self, data):
725         self['signature-version'] = data[0]
726         offset = 1
727         if self['signature-version'] != 4:
728             raise NotImplementedError(
729                 'signature packet version {}'.format(
730                     self['signature-version']))
731         self['signature-type'] = self._signature_types[data[offset]]
732         offset += 1
733         self['public-key-algorithm'] = self._public_key_algorithms[
734             data[offset]]
735         offset += 1
736         self['hash-algorithm'] = self._hash_algorithms[data[offset]]
737         offset += 1
738         hashed_count = _struct.unpack('>H', data[offset: offset + 2])[0]
739         offset += 2
740         self['hashed-subpackets'] = list(self._parse_signature_subpackets(
741             data[offset: offset + hashed_count]))
742         offset += hashed_count
743         unhashed_count = _struct.unpack('>H', data[offset: offset + 2])[0]
744         offset += 2
745         self['unhashed-subpackets'] = list(self._parse_signature_subpackets(
746             data=data[offset: offset + unhashed_count]))
747         offset += unhashed_count
748         self['signed-hash-word'] = data[offset: offset + 2]
749         offset += 2
750         self['signature'] = []
751         while offset < len(data):
752             o, mpi = self._parse_multiprecision_integer(data=data[offset:])
753             offset += o
754             self['signature'].append(mpi)
755         if self.key.secret_packets:
756             packets = self.key.secret_packets
757         else:
758             packets = self.key.public_packets
759         if self['signature-type'] == 'standalone':
760             self['target'] = None
761         elif self['signature-type'].endswith(' user id and public-key packet'):
762             self['target'] = [
763                 packets[0],
764                 [p for p in packets if p['type'] == 'user id packet'][-1],
765                 ]
766         elif self['signature-type'].endswith('key binding'):
767             self['target'] = [
768                 packets[0],
769                 [p for p in packets if p['type'] == 'public-subkey packet'][-1],
770                 ]
771         else:
772             raise NotImplementedError(
773                 'target for {}'.format(self['signature-type']))
774         self.verify()
775
776     def _parse_signature_creation_time_signature_subpacket(
777             self, data, subpacket):
778         subpacket['signature-creation-time'] = _struct.unpack('>I', data)[0]
779
780     def _parse_issuer_signature_subpacket(self, data, subpacket):
781         subpacket['issuer'] = byte_string(data=data, sep='')
782
783     def _parse_key_expiration_time_signature_subpacket(
784             self, data, subpacket):
785         subpacket['key-expiration-time'] = _struct.unpack('>I', data)[0]
786
787     def _parse_preferred_symmetric_algorithms_signature_subpacket(
788             self, data, subpacket):
789         subpacket['preferred-symmetric-algorithms'] = [
790             self._symmetric_key_algorithms[d] for d in data]
791
792     def _parse_preferred_hash_algorithms_signature_subpacket(
793             self, data, subpacket):
794         subpacket['preferred-hash-algorithms'] = [
795             self._hash_algorithms[d] for d in data]
796
797     def _parse_preferred_compression_algorithms_signature_subpacket(
798             self, data, subpacket):
799         subpacket['preferred-compression-algorithms'] = [
800             self._compression_algorithms[d] for d in data]
801
802     def _parse_key_server_preferences_signature_subpacket(
803             self, data, subpacket):
804         subpacket['key-server-preferences'] = set()
805         if data[0] & 0x80:
806             subpacket['key-server-preferences'].add('no-modify')
807
808     def _parse_primary_user_id_signature_subpacket(self, data, subpacket):
809         subpacket['primary-user-id'] = bool(data[0])
810
811     def _parse_key_flags_signature_subpacket(self, data, subpacket):
812         subpacket['key-flags'] = set()
813         if data[0] & 0x1:
814             subpacket['key-flags'].add('can certify')
815         if data[0] & 0x2:
816             subpacket['key-flags'].add('can sign')
817         if data[0] & 0x4:
818             subpacket['key-flags'].add('can encrypt communications')
819         if data[0] & 0x8:
820             subpacket['key-flags'].add('can encrypt storage')
821         if data[0] & 0x10:
822             subpacket['key-flags'].add('private split')
823         if data[0] & 0x20:
824             subpacket['key-flags'].add('can authenticate')
825         if data[0] & 0x80:
826             subpacket['key-flags'].add('private shared')
827
828     def _parse_features_signature_subpacket(self, data, subpacket):
829         subpacket['features'] = set()
830         if data[0] & 0x1:
831             subpacket['features'].add('modification detection')
832
833     def _parse_embedded_signature_signature_subpacket(self, data, subpacket):
834         subpacket['embedded'] = PGPPacket(key=self.key)
835         subpacket['embedded']['type'] = 'signature packet'
836         subpacket['embedded']['embedded'] = True
837         subpacket['embedded']._parse_signature_packet(data=data)
838         subpacket['embedded']['raw'] = data
839
840     def _parse_user_id_packet(self, data):
841         self['user'] = str(data, 'utf-8')
842
843     def to_bytes(self):
844         body = self._serialize_body()
845         if body is None:
846             raise ValueError(method)
847         self['length'] = len(body)
848         return b''.join([
849             self._serialize_header(),
850             body,
851             ])
852
853     def _serialize_header(self):
854         always_one = 1
855         new_format = 0
856         type_code = self._reverse(self._packet_types, self['type'])
857         packet_tag = (
858             always_one * (1 << 7) |
859             new_format * (1 << 6) |
860             type_code * (1 << 2) |
861             self['length-type']
862             )
863         length_bytes, length_type = self._old_format_packet_length_type[
864             self['length-type']]
865         length_format = '>{}'.format(length_type)
866         length_data = _struct.pack(length_format, self['length'])
867         return b''.join([
868             bytes([packet_tag]),
869             length_data,
870             ])
871
872     def _serialize_body(self):
873         method_name = '_serialize_{}'.format(self._clean_type())
874         method = getattr(self, method_name, None)
875         if not method:
876             raise NotImplementedError(
877                 'cannot serialize packet type {!r}'.format(self['type']))
878         return method()
879
880     @staticmethod
881     def _serialize_multiprecision_integer(integer):
882         r"""Serialize RFC 4880's multipricision integers
883
884         >>> PGPPacket._serialize_multiprecision_integer(1)
885         b'\x00\x01\x01'
886         >>> PGPPacket._serialize_multiprecision_integer(511)
887         b'\x00\t\x01\xff'
888         """
889         bit_length = int(_math.log(integer, 2)) + 1
890         chunks = [
891             _struct.pack('>H', bit_length),
892             ]
893         while integer > 0:
894             chunks.insert(1, bytes([integer & 0xff]))
895             integer = integer >> 8
896         return b''.join(chunks)
897
898     @classmethod
899     def _encode_string_to_key_count(cls, count):
900         r"""Encode RFC 4880's string-to-key count
901
902         >>> PGPPacket._encode_string_to_key_count(753664)
903         b'\x97'
904         """
905         coded_count = 0
906         count = count >> cls._string_to_key_expbias
907         while not count & 1:
908             count = count >> 1
909             coded_count += 1 << 4
910         coded_count += count & 15
911         return bytes([coded_count])
912
913     def _serialize_string_to_key_specifier(self):
914         string_to_key_type = bytes([
915             self._reverse(
916                 self._string_to_key_types, self['string-to-key-type']),
917             ])
918         chunks = [string_to_key_type]
919         if self['string-to-key-type'] == 'simple':
920             chunks.append(bytes([self._reverse(
921                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
922         elif self['string-to-key-type'] == 'salted':
923             chunks.append(bytes([self._reverse(
924                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
925             chunks.append(self['string-to-key-salt'])
926         elif self['string-to-key-type'] == 'iterated and salted':
927             chunks.append(bytes([self._reverse(
928                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
929             chunks.append(self['string-to-key-salt'])
930             chunks.append(self._encode_string_to_key_count(
931                 count=self['string-to-key-count']))
932         else:
933             raise NotImplementedError(
934                 'string-to-key type {}'.format(self['string-to-key-type']))
935         return offset
936         return b''.join(chunks)
937
938     def _serialize_public_key_packet(self):
939         return self._serialize_generic_public_key_packet()
940
941     def _serialize_public_subkey_packet(self):
942         return self._serialize_generic_public_key_packet()
943
944     def _serialize_generic_public_key_packet(self):
945         key_version = bytes([self['key-version']])
946         chunks = [key_version]
947         if self['key-version'] != 4:
948             raise NotImplementedError(
949                 'public (sub)key packet version {}'.format(
950                     self['key-version']))
951         chunks.append(_struct.pack('>I', self['creation-time']))
952         chunks.append(bytes([self._reverse(
953             self._public_key_algorithms, self['public-key-algorithm'])]))
954         if self['public-key-algorithm'].startswith('rsa '):
955             chunks.append(self._serialize_multiprecision_integer(
956                 self['public-modulus']))
957             chunks.append(self._serialize_multiprecision_integer(
958                 self['public-exponent']))
959         elif self['public-key-algorithm'].startswith('dsa '):
960             chunks.append(self._serialize_multiprecision_integer(
961                 self['prime']))
962             chunks.append(self._serialize_multiprecision_integer(
963                 self['group-order']))
964             chunks.append(self._serialize_multiprecision_integer(
965                 self['group-generator']))
966             chunks.append(self._serialize_multiprecision_integer(
967                 self['public-key']))
968         elif self['public-key-algorithm'].startswith('elgamal '):
969             chunks.append(self._serialize_multiprecision_integer(
970                 self['prime']))
971             chunks.append(self._serialize_multiprecision_integer(
972                 self['group-generator']))
973             chunks.append(self._serialize_multiprecision_integer(
974                 self['public-key']))
975         else:
976             raise NotImplementedError(
977                 'algorithm-specific key fields for {}'.format(
978                     self['public-key-algorithm']))
979         return b''.join(chunks)
980
981     def _serialize_signature_subpackets(self, subpackets):
982         return b''.join(
983             self._serialize_signature_subpacket(subpacket=subpacket)
984             for subpacket in subpackets)
985
986     def _serialize_signature_subpacket(self, subpacket):
987         method_name = '_serialize_{}_signature_subpacket'.format(
988             self._clean_type(type=subpacket['type']))
989         method = getattr(self, method_name, None)
990         if not method:
991             raise NotImplementedError(
992                 'cannot serialize signature subpacket type {!r}'.format(
993                     subpacket['type']))
994         body = method(subpacket=subpacket)
995         length = len(body) + 1
996         chunks = []
997         if length < 192:
998             chunks.append(bytes([length]))
999         else:
1000             first = ((length - 192) >> 8) + 192
1001             if first < 255:
1002                 chunks.extend([
1003                     first,
1004                     (length - 192) % 256,
1005                     ])
1006             else:
1007                 chunks.append(_struct.pack('>I', length))
1008         chunks.append(bytes([self._reverse(
1009             self._signature_subpacket_types, subpacket['type'])]))
1010         chunks.append(body)
1011         return b''.join(chunks)
1012
1013     def _serialize_signature_packet_target(self, target):
1014         if target is None:
1015             return b''
1016         elif isinstance(target, bytes):
1017             return target
1018         elif isinstance(target, PGPPacket):
1019             if target['type'].endswith('-subkey packet'):
1020                 target = target.copy()
1021                 target['type'] = target['type'].replace(
1022                     '-subkey packet', '-key packet')
1023             serialized = target._serialize_body()
1024             if target['type'] in [
1025                     'public-key packet',
1026                     'public-subkey packet'
1027                     'secret-key packet',
1028                     'secret-subkey packet'
1029                     ]:
1030                 serialized = b''.join([
1031                     b'\x99',
1032                     _struct.pack('>H', len(serialized)),
1033                     serialized,
1034                     ])
1035             elif target['type'] == 'user id packet':
1036                 serialized = b''.join([
1037                     b'\xb4',
1038                     _struct.pack('>I', len(serialized)),
1039                     serialized,
1040                     ])
1041             elif target['type'] == 'user attribute packet':
1042                 serialized = b''.join([
1043                     b'\xd1',
1044                     _struct.pack('>I', len(serialized)),
1045                     serialized,
1046                     ])
1047             return serialized
1048         elif isinstance(target, list):
1049             return b''.join(
1050                 self._serialize_signature_packet_target(target=x)
1051                 for x in target)
1052
1053     def _serialize_hashed_signature_packet(self):
1054         if self['signature-version'] != 4:
1055             raise NotImplementedError(
1056                 'signature packet version {}'.format(
1057                     self['signature-version']))
1058         chunks = [bytes([self['signature-version']])]
1059         chunks.append(bytes([self._reverse(
1060             self._signature_types, self['signature-type'])]))
1061         chunks.append(bytes([self._reverse(
1062             self._public_key_algorithms, self['public-key-algorithm'])]))
1063         chunks.append(bytes([self._reverse(
1064             self._hash_algorithms, self['hash-algorithm'])]))
1065         hashed_subpackets = self._serialize_signature_subpackets(
1066             self['hashed-subpackets'])
1067         chunks.append(_struct.pack('>H', len(hashed_subpackets)))
1068         chunks.append(hashed_subpackets)
1069         return b''.join(chunks)
1070
1071     def _signature_packet_signed_data(self, hashed_signature_data):
1072         target = self._serialize_signature_packet_target(target=self['target'])
1073         return b''.join([
1074             target,
1075             hashed_signature_data,
1076             bytes([self['signature-version']]),
1077             b'\xff',
1078             _struct.pack('>I', len(hashed_signature_data)),
1079             ])
1080
1081     def _serialize_signature_packet(self):
1082         hashed_signature_data = self._serialize_hashed_signature_packet()
1083         chunks = [hashed_signature_data]
1084         unhashed_subpackets = self._serialize_signature_subpackets(
1085             self['unhashed-subpackets'])
1086         chunks.append(_struct.pack('>H', len(unhashed_subpackets)))
1087         chunks.append(unhashed_subpackets)
1088         signed_data = self._signature_packet_signed_data(
1089             hashed_signature_data=hashed_signature_data)
1090         digest, signature = self.key.sign(
1091             data=signed_data, hash_algorithm=self['hash-algorithm'],
1092             signature_algorithm=self['public-key-algorithm'])
1093         chunks.append(digest[:2])
1094         chunks.extend(
1095             self._serialize_multiprecision_integer(integer=integer)
1096             for integer in signature)
1097         return b''.join(chunks)
1098
1099     def _serialize_signature_creation_time_signature_subpacket(
1100             self, subpacket):
1101         return _struct.pack('>I', subpacket['signature-creation-time'])
1102
1103     def _serialize_issuer_signature_subpacket(self, subpacket):
1104         return string_bytes(data=subpacket['issuer'], sep='')
1105
1106     def _serialize_key_expiration_time_signature_subpacket(self, subpacket):
1107         return _struct.pack('>I', subpacket['key-expiration-time'])
1108
1109     def _serialize_preferred_symmetric_algorithms_signature_subpacket(
1110             self, subpacket):
1111         return bytes(
1112             self._reverse(self._symmetric_key_algorithms, a)
1113             for a in subpacket['preferred-symmetric-algorithms'])
1114
1115     def _serialize_preferred_hash_algorithms_signature_subpacket(
1116             self, subpacket):
1117         return bytes(
1118             self._reverse(self._hash_algorithms, a)
1119             for a in subpacket['preferred-hash-algorithms'])
1120
1121     def _serialize_preferred_compression_algorithms_signature_subpacket(
1122             self, subpacket):
1123         return bytes(
1124             self._reverse(self._compression_algorithms, a)
1125             for a in subpacket['preferred-compression-algorithms'])
1126
1127     def _serialize_key_server_preferences_signature_subpacket(self, subpacket):
1128         return bytes([
1129             0x80 * ('no-modify' in subpacket['key-server-preferences']) |
1130             0,
1131             ])
1132
1133     def _serialize_primary_user_id_signature_subpacket(self, subpacket):
1134         return bytes([0x1 * subpacket['primary-user-id']])
1135
1136     def _serialize_key_flags_signature_subpacket(self, subpacket):
1137         return bytes([
1138             0x1 * ('can certify' in subpacket['key-flags']) |
1139             0x2 * ('can sign' in subpacket['key-flags']) |
1140             0x4 * ('can encrypt communications' in subpacket['key-flags']) |
1141             0x8 * ('can encrypt storage' in subpacket['key-flags']) |
1142             0x10 * ('private split' in subpacket['key-flags']) |
1143             0x20 * ('can authenticate' in subpacket['key-flags']) |
1144             0x80 * ('private shated' in subpacket['key-flags']) |
1145             0,
1146             ])
1147
1148     def _serialize_features_signature_subpacket(self, subpacket):
1149         return bytes([
1150             0x1 * ('modification detection' in subpacket['features']) |
1151             0,
1152             ])
1153
1154     def _serialize_embedded_signature_signature_subpacket(self, subpacket):
1155         return subpacket['embedded'].to_bytes()
1156
1157     def _serialize_user_id_packet(self):
1158         return self['user'].encode('utf-8')
1159
1160     def _string_to_key(self, string, key_size):
1161         if key_size % 8:
1162             raise ValueError(
1163                 '{}-bit key is not an integer number of bytes'.format(
1164                     key_size))
1165         key_size_bytes = key_size // 8
1166         hash_name = self._hashlib_name[
1167             self['string-to-key-hash-algorithm']]
1168         string_hash = _hashlib.new(hash_name)
1169         hashes = _math.ceil(key_size_bytes / string_hash.digest_size)
1170         key = b''
1171         if self['string-to-key-type'] == 'simple':
1172             update_bytes = string
1173         elif self['string-to-key-type'] in [
1174                 'salted',
1175                 'iterated and salted',
1176                 ]:
1177             update_bytes = self['string-to-key-salt'] + string
1178             if self['string-to-key-type'] == 'iterated and salted':
1179                 count = self['string-to-key-count']
1180                 if count < len(update_bytes):
1181                     count = len(update_bytes)
1182         else:
1183             raise NotImplementedError(
1184                 'key calculation for string-to-key type {}'.format(
1185                     self['string-to-key-type']))
1186         for padding in range(hashes):
1187             string_hash = _hashlib.new(hash_name)
1188             string_hash.update(padding * b'\x00')
1189             if self['string-to-key-type'] in [
1190                     'simple',
1191                     'salted',
1192                     ]:
1193                 string_hash.update(update_bytes)
1194             elif self['string-to-key-type'] == 'iterated and salted':
1195                 remaining = count
1196                 while remaining > 0:
1197                     string_hash.update(update_bytes[:remaining])
1198                     remaining -= len(update_bytes)
1199             key += string_hash.digest()
1200         key = key[:key_size_bytes]
1201         return key
1202
1203     def decrypt_symmetric_encryption(self, data):
1204         """Decrypt OpenPGP's Cipher Feedback mode"""
1205         algorithm = self['symmetric-encryption-algorithm']
1206         module = self._crypto_module[algorithm]
1207         key_size = self._key_size[algorithm]
1208         segment_size_bits = self._cipher_block_size[algorithm]
1209         if segment_size_bits % 8:
1210             raise NotImplementedError(
1211                 ('{}-bit segment size for {} is not an integer number of bytes'
1212                  ).format(segment_size_bits, algorithm))
1213         segment_size_bytes = segment_size_bits // 8
1214         padding = segment_size_bytes - len(data) % segment_size_bytes
1215         if padding:
1216             data += b'\x00' * padding
1217         if self.key and self.key._cache_passphrase and self.key._passphrase:
1218             passphrase = self.key._passphrase
1219         else:
1220             passphrase = _getpass.getpass(
1221                 'passphrase for {}: '.format(self['fingerprint'][-8:]))
1222             passphrase = passphrase.encode('ascii')
1223             if self.key and self.key._cache_passphrase:
1224                 self.key._passphrase = passphrase
1225         key = self._string_to_key(string=passphrase, key_size=key_size)
1226         cipher = module.new(
1227             key=key,
1228             mode=module.MODE_CFB,
1229             IV=self['initial-vector'],
1230             segment_size=segment_size_bits)
1231         plaintext = cipher.decrypt(data)
1232         if padding:
1233             plaintext = plaintext[:-padding]
1234         return plaintext
1235
1236     def check_roundtrip(self):
1237         serialized = self.to_bytes()
1238         source = self['raw']
1239         if serialized != source:
1240             if len(serialized) != len(source):
1241                 raise ValueError(
1242                     ('serialized {} is {} bytes long, '
1243                      'but input is {} bytes long').format(
1244                          self['type'], len(serialized), len(source)))
1245             chunk_size = 8
1246             for i in range(0, len(source), 8):
1247                 in_chunk = source[i: i + chunk_size]
1248                 out_chunk = serialized[i: i + chunk_size]
1249                 if in_chunk != out_chunk:
1250                     raise ValueError(
1251                         ('serialized {} differs from input packet: '
1252                          'at byte {}, {} != {}').format(
1253                             self['type'], i, byte_string(data=out_chunk),
1254                             byte_string(data=in_chunk)))
1255
1256     def verify(self):
1257         if self['type'] != 'signature packet':
1258             raise NotImplmentedError('verify {}'.format(self['type']))
1259         hashed_signature_data = self._serialize_hashed_signature_packet()
1260         signed_data = self._signature_packet_signed_data(
1261             hashed_signature_data=hashed_signature_data)
1262         key_packet = None
1263         subpackets = self['hashed-subpackets'] + self['unhashed-subpackets']
1264         issuer_subpackets = [p for p in subpackets if p['type'] == 'issuer']
1265         if issuer_subpackets:
1266             issuer = issuer_subpackets[0]
1267             packets = (self.key.public_packets or []) + (
1268                 self.key.secret_packets or [])
1269             keys = [k for k in packets
1270                     if k.get('fingerprint', '').endswith(issuer['issuer'])]
1271             if keys:
1272                 key_packet = keys[-1]
1273             else:
1274                 LOG.info('no packet found for issuer {}'.format(
1275                     issuer['issuer'][-8:].upper()))
1276                 return
1277         LOG.debug('verify {} with {}'.format(
1278             self['signature-type'],
1279             key_packet['fingerprint'][-8:].upper()))
1280         verified = self.key.verify(
1281             data=signed_data, signature=self['signature'],
1282             hash_algorithm=self['hash-algorithm'],
1283             signature_algorithm=self['public-key-algorithm'],
1284             key_packet=key_packet, digest_check=self['signed-hash-word'])
1285         if not verified:
1286             raise ValueError('verification failed for {}'.format(self))
1287         else:
1288             LOG.debug('verified {}'.format(self['signature-type']))
1289
1290
1291 class PGPKey (object):
1292     """An OpenPGP key with public and private parts.
1293
1294     From RFC 4880 [1]:
1295
1296       OpenPGP users may transfer public keys.  The essential elements
1297       of a transferable public key are as follows:
1298
1299       - One Public-Key packet
1300       - Zero or more revocation signatures
1301       - One or more User ID packets
1302       - After each User ID packet, zero or more Signature packets
1303         (certifications)
1304       - Zero or more User Attribute packets
1305       - After each User Attribute packet, zero or more Signature
1306         packets (certifications)
1307       - Zero or more Subkey packets
1308       - After each Subkey packet, one Signature packet, plus
1309         optionally a revocation
1310
1311     Secret keys have a similar packet stream [2]:
1312
1313       OpenPGP users may transfer secret keys.  The format of a
1314       transferable secret key is the same as a transferable public key
1315       except that secret-key and secret-subkey packets are used
1316       instead of the public key and public-subkey packets.
1317       Implementations SHOULD include self-signatures on any user IDs
1318       and subkeys, as this allows for a complete public key to be
1319       automatically extracted from the transferable secret key.
1320       Implementations MAY choose to omit the self-signatures,
1321       especially if a transferable public key accompanies the
1322       transferable secret key.
1323
1324     [1]: http://tools.ietf.org/search/rfc4880#section-11.1
1325     [2]: http://tools.ietf.org/search/rfc4880#section-11.2
1326     """
1327     def __init__(self, fingerprint, cache_passphrase=False):
1328         self.fingerprint = fingerprint
1329         self._cache_passphrase = cache_passphrase
1330         self._passphrase = None
1331         self.public_packets = None
1332         self.secret_packets = None
1333
1334     def __str__(self):
1335         lines = ['key: {}'.format(self.fingerprint)]
1336         if self.public_packets:
1337             lines.append('  public:')
1338             for packet in self.public_packets:
1339                 lines.extend(self._str_packet(packet=packet, prefix='    '))
1340         if self.secret_packets:
1341             lines.append('  secret:')
1342             for packet in self.secret_packets:
1343                 lines.extend(self._str_packet(packet=packet, prefix='    '))
1344         return '\n'.join(lines)
1345
1346     def _str_packet(self, packet, prefix):
1347         lines = str(packet).split('\n')
1348         return [prefix + line for line in lines]
1349
1350     def import_from_gpg(self):
1351         key_export = _get_stdout(
1352             ['gpg', '--export', self.fingerprint])
1353         self.public_packets = []
1354         self._packets_from_bytes(list=self.public_packets, data=key_export)
1355         if self.public_packets[0]['type'] != 'public-key packet':
1356             raise ValueError(
1357                 '{} does not start with a public-key packet'.format(
1358                     self.fingerprint))
1359         key_secret_export = _get_stdout(
1360             ['gpg', '--export-secret-keys', self.fingerprint])
1361         self.secret_packets = []
1362         self._packets_from_bytes(list=self.secret_packets, data=key_secret_export)
1363         if self.secret_packets[0]['type'] != 'secret-key packet':
1364             raise ValueError(
1365                 '{} does not start with a secret-key packet'.format(
1366                     self.fingerprint))
1367         for packet in self.public_packets + self.secret_packets:
1368             packet.check_roundtrip()
1369
1370     def _packets_from_bytes(self, list, data):
1371         offset = 0
1372         while offset < len(data):
1373             packet = PGPPacket(key=self)
1374             offset += packet.from_bytes(data=data[offset:])
1375             list.append(packet)
1376
1377     def export_to_gpg(self):
1378         raise NotImplemetedError('export to gpg')
1379
1380     def import_from_key(self, key):
1381         """Migrate the (sub)keys into this key"""
1382         pass
1383
1384     def _get_signer(self, signature_algorithm=None, key_packet=None,
1385                     secret=False):
1386         if key_packet is None:
1387             if secret:
1388                 key_packet = self.secret_packets[0]
1389             else:
1390                 key_packet = self.public_packets[0]
1391         elif secret:
1392             if 'secret' not in key_packet['type']:
1393                 raise ValueError(
1394                     '{} is not a secret key'.format(key_packet['type']))
1395         if signature_algorithm is None:
1396             signature_algorithm = key_packet['public-key-algorithm']
1397         if signature_algorithm != key_packet['public-key-algorithm']:
1398             raise ValueError(
1399                 'cannot act on a {} signature with a {} key'.format(
1400                     signature_algorithm, key_packet['public-key-algorithm']))
1401         module = key_packet._crypto_module[signature_algorithm]
1402         if signature_algorithm.startswith('rsa '):
1403             key = module.construct((
1404                 key_packet['public-modulus'],   # n
1405                 key_packet['public-exponent'],  # e
1406                 ))
1407             if secret:
1408                 LOG.debug('secret')
1409                 key.d = key_packet['secret-exponent']
1410                 key.p = key_packet['secret-prime-p']
1411                 key.q = key_packet['secret-prime-q']
1412                 key.u = key_packet['secret-inverse-of-p-mod-q']
1413             signer = _crypto_signature_pkcs1_v1_5.new(key)
1414         elif signature_algorithm.startswith('dsa '):
1415             signer = module.construct((
1416                 key_packet['public-key'],       # y
1417                 key_packet['group-generator'],  # g
1418                 key_packet['prime'],            # p
1419                 key_packet['group-order'],      # q
1420                 ))
1421             if secret:
1422                 signer.x = key_packet['secret-exponent']
1423         else:
1424             raise NotImplementedError(
1425                 'construct {}'.format(signature_algorithm))
1426         return (key_packet, signer)
1427
1428     def _hash(self, data, hash_algorithm, key_packet):
1429         hash_name = key_packet._hashlib_name[hash_algorithm]
1430         data_hash = _hashlib.new(hash_name)
1431         data_hash.update(data)
1432         return data_hash
1433
1434     def verify(self, data, signature, hash_algorithm, signature_algorithm=None,
1435                key_packet=None, digest_check=None):
1436         key_packet, signer = self._get_signer(
1437             signature_algorithm=signature_algorithm, key_packet=key_packet)
1438         data_hash = self._hash(
1439             data=data, hash_algorithm=hash_algorithm, key_packet=key_packet)
1440         digest = data_hash.digest()
1441         hexdigest = data_hash.hexdigest()
1442         if digest_check and not digest.startswith(digest_check):
1443             raise ValueError(
1444                 'corrupted hash: {} does not start with {}'.format(
1445                     byte_string(digest),
1446                     byte_string(digest_check)))
1447         if signature_algorithm.startswith('rsa '):
1448             sig_hex = '{:x}'.format(signature[0])
1449             signature = string_bytes(data=sig_hex, sep='')
1450         elif signature_algorithm.startswith('dsa '):
1451             data_hash = digest
1452         LOG.debug('verify signature {} on {} with {}'.format(
1453             signature, hexdigest, signer))
1454         return signer.verify(data_hash, signature)
1455
1456
1457 def migrate(old_key, new_key, cache_passphrase=False):
1458     """Add the old key and sub-keys to the new key
1459
1460     For example, to upgrade your master key, while preserving old
1461     signatures you'd made.  You will lose signature *on* your old key
1462     though, since sub-keys can't be signed (I don't think).
1463     """
1464     old_key = PGPKey(fingerprint=old_key, cache_passphrase=cache_passphrase)
1465     old_key.import_from_gpg()
1466     new_key = PGPKey(fingerprint=new_key, cache_passphrase=cache_passphrase)
1467     new_key.import_from_gpg()
1468     new_key.import_from_key(key=old_key)
1469
1470     print(old_key)
1471     print(new_key)
1472
1473
1474 if __name__ == '__main__':
1475     import sys as _sys
1476
1477     old_key, new_key = _sys.argv[1:3]
1478     migrate(old_key=old_key, new_key=new_key, cache_passphrase=True)