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