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