Adjust PGPKey._packets_from_bytes to append on the fly
[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 __str__(self):
313         method_name = '_str_{}'.format(self._clean_type())
314         method = getattr(self, method_name, None)
315         if not method:
316             return self['type']
317         details = method()
318         return '{}: {}'.format(self['type'], details)
319
320     def _str_public_key_packet(self):
321         return self._str_generic_key_packet()
322
323     def _str_public_subkey_packet(self):
324         return self._str_generic_key_packet()
325
326     def _str_generic_key_packet(self):
327         return self['fingerprint'][-8:].upper()
328
329     def _str_secret_key_packet(self):
330         return self._str_generic_secret_key_packet()
331
332     def _str_secret_subkey_packet(self):
333         return self._str_generic_secret_key_packet()
334
335     def _str_generic_secret_key_packet(self):
336         lines = [self._str_generic_key_packet()]
337         for label, key in [
338                 ('symmetric encryption',
339                  'symmetric-encryption-algorithm'),
340                 ('s2k hash', 'string-to-key-hash-algorithm'),
341                 ('s2k count', 'string-to-key-count'),
342                 ('s2k salt', 'string-to-key-salt'),
343                 ('IV', 'initial-vector'),
344                 ]:
345             if key in self:
346                 value = self[key]
347                 if isinstance(value, bytes):
348                     value = byte_string(data=value)
349                 lines.append('  {}: {}'.format(label, value))
350         return '\n'.join(lines)
351
352     def _str_signature_packet(self):
353         lines = [self['signature-type']]
354         if self['hashed-subpackets']:
355             lines.append('  hashed subpackets:')
356             lines.extend(self._str_signature_subpackets(
357                 self['hashed-subpackets'], prefix='    '))
358         if self['unhashed-subpackets']:
359             lines.append('  unhashed subpackets:')
360             lines.extend(self._str_signature_subpackets(
361                 self['unhashed-subpackets'], prefix='    '))
362         return '\n'.join(lines)
363
364     def _str_signature_subpackets(self, subpackets, prefix):
365         lines = []
366         for subpacket in subpackets:
367             method_name = '_str_{}_signature_subpacket'.format(
368                 self._clean_type(type=subpacket['type']))
369             method = getattr(self, method_name, None)
370             if method:
371                 lines.append('    {}: {}'.format(
372                     subpacket['type'],
373                     method(subpacket=subpacket)))
374             else:
375                 lines.append('    {}'.format(subpacket['type']))
376         return lines
377
378     def _str_signature_creation_time_signature_subpacket(self, subpacket):
379         return str(subpacket['signature-creation-time'])
380
381     def _str_issuer_signature_subpacket(self, subpacket):
382         return subpacket['issuer'][-8:].upper()
383
384     def _str_key_expiration_time_signature_subpacket(self, subpacket):
385         return str(subpacket['key-expiration-time'])
386
387     def _str_preferred_symmetric_algorithms_signature_subpacket(
388             self, subpacket):
389         return ', '.join(
390             algo for algo in subpacket['preferred-symmetric-algorithms'])
391
392     def _str_preferred_hash_algorithms_signature_subpacket(
393             self, subpacket):
394         return ', '.join(
395             algo for algo in subpacket['preferred-hash-algorithms'])
396
397     def _str_preferred_compression_algorithms_signature_subpacket(
398             self, subpacket):
399         return ', '.join(
400             algo for algo in subpacket['preferred-compression-algorithms'])
401
402     def _str_key_server_preferences_signature_subpacket(self, subpacket):
403         return ', '.join(
404             x for x in sorted(subpacket['key-server-preferences']))
405
406     def _str_primary_user_id_signature_subpacket(self, subpacket):
407         return str(subpacket['primary-user-id'])
408
409     def _str_key_flags_signature_subpacket(self, subpacket):
410         return ', '.join(x for x in sorted(subpacket['key-flags']))
411
412     def _str_features_signature_subpacket(self, subpacket):
413         return ', '.join(x for x in sorted(subpacket['features']))
414
415     def _str_embedded_signature_signature_subpacket(self, subpacket):
416         return subpacket['embedded']['signature-type']
417
418     def _str_user_id_packet(self):
419         return self['user']
420
421     def from_bytes(self, data):
422         offset = self._parse_header(data=data)
423         packet = data[offset:offset + self['length']]
424         if len(packet) < self['length']:
425             raise ValueError('packet too short ({} < {})'.format(
426                 len(packet), self['length']))
427         offset += self['length']
428         method_name = '_parse_{}'.format(self._clean_type())
429         method = getattr(self, method_name, None)
430         if not method:
431             raise NotImplementedError(
432                 'cannot parse packet type {!r}'.format(self['type']))
433         method(data=packet)
434         self['raw'] = data[:offset]
435         return offset
436
437     def _parse_header(self, data):
438         packet_tag = data[0]
439         offset = 1
440         always_one = packet_tag & 1 << 7
441         if not always_one:
442             raise ValueError('most significant packet tag bit not set')
443         self['new-format'] = packet_tag & 1 << 6
444         if self['new-format']:
445             type_code = packet_tag & 0b111111
446             raise NotImplementedError('new-format packet length')
447         else:
448             type_code = packet_tag >> 2 & 0b1111
449             self['length-type'] = packet_tag & 0b11
450             length_bytes, length_type = self._old_format_packet_length_type[
451                 self['length-type']]
452             if not length_bytes:
453                 raise NotImplementedError(
454                     'old-format packet of indeterminate length')
455             length_format = '>{}'.format(length_type)
456             length_data = data[offset: offset + length_bytes]
457             offset += length_bytes
458             self['length'] = _struct.unpack(length_format, length_data)[0]
459         self['type'] = self._packet_types[type_code]
460         return offset
461
462     @staticmethod
463     def _parse_multiprecision_integer(data):
464         r"""Parse RFC 4880's multiprecision integers
465
466         >>> PGPPacket._parse_multiprecision_integer(b'\x00\x01\x01')
467         (3, 1)
468         >>> PGPPacket._parse_multiprecision_integer(b'\x00\x09\x01\xff')
469         (4, 511)
470         """
471         bits = _struct.unpack('>H', data[:2])[0]
472         offset = 2
473         length = (bits + 7) // 8
474         value = 0
475         for i in range(length):
476             value += data[offset + i] * 1 << (8 * (length - i - 1))
477         offset += length
478         return (offset, value)
479
480     @classmethod
481     def _decode_string_to_key_count(cls, data):
482         r"""Decode RFC 4880's string-to-key count
483
484         >>> PGPPacket._decode_string_to_key_count(b'\x97'[0])
485         753664
486         """
487         return (16 + (data & 15)) << ((data >> 4) + cls._string_to_key_expbias)
488
489     def _parse_string_to_key_specifier(self, data):
490         self['string-to-key-type'] = self._string_to_key_types[data[0]]
491         offset = 1
492         if self['string-to-key-type'] == 'simple':
493             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
494                 data[offset]]
495             offset += 1
496         elif self['string-to-key-type'] == 'salted':
497             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
498                 data[offset]]
499             offset += 1
500             self['string-to-key-salt'] = data[offset: offset + 8]
501             offset += 8
502         elif self['string-to-key-type'] == 'iterated and salted':
503             self['string-to-key-hash-algorithm'] = self._hash_algorithms[
504                 data[offset]]
505             offset += 1
506             self['string-to-key-salt'] = data[offset: offset + 8]
507             offset += 8
508             self['string-to-key-count'] = self._decode_string_to_key_count(
509                 data=data[offset])
510             offset += 1
511         else:
512             raise NotImplementedError(
513                 'string-to-key type {}'.format(self['string-to-key-type']))
514         return offset
515
516     def _parse_public_key_packet(self, data):
517         self._parse_generic_public_key_packet(data=data)
518
519     def _parse_public_subkey_packet(self, data):
520         self._parse_generic_public_key_packet(data=data)
521
522     def _parse_generic_public_key_packet(self, data):
523         self['key-version'] = data[0]
524         offset = 1
525         if self['key-version'] != 4:
526             raise NotImplementedError(
527                 'public (sub)key packet version {}'.format(
528                     self['key-version']))
529         length = 5
530         self['creation-time'], algorithm = _struct.unpack(
531             '>IB', data[offset: offset + length])
532         offset += length
533         self['public-key-algorithm'] = self._public_key_algorithms[algorithm]
534         if self['public-key-algorithm'].startswith('rsa '):
535             o, self['public-modulus'] = self._parse_multiprecision_integer(
536                 data[offset:])
537             offset += o
538             o, self['public-exponent'] = self._parse_multiprecision_integer(
539                 data[offset:])
540             offset += o
541         elif self['public-key-algorithm'].startswith('dsa '):
542             o, self['prime'] = self._parse_multiprecision_integer(
543                 data[offset:])
544             offset += o
545             o, self['group-order'] = self._parse_multiprecision_integer(
546                 data[offset:])
547             offset += o
548             o, self['group-generator'] = self._parse_multiprecision_integer(
549                 data[offset:])
550             offset += o
551             o, self['public-key'] = self._parse_multiprecision_integer(
552                 data[offset:])
553             offset += o
554         elif self['public-key-algorithm'].startswith('elgamal '):
555             o, self['prime'] = self._parse_multiprecision_integer(
556                 data[offset:])
557             offset += o
558             o, self['group-generator'] = self._parse_multiprecision_integer(
559                 data[offset:])
560             offset += o
561             o, self['public-key'] = self._parse_multiprecision_integer(
562                 data[offset:])
563             offset += o
564         else:
565             raise NotImplementedError(
566                 'algorithm-specific key fields for {}'.format(
567                     self['public-key-algorithm']))
568         fingerprint = _hashlib.sha1()
569         fingerprint.update(b'\x99')
570         fingerprint.update(_struct.pack('>H', len(data)))
571         fingerprint.update(data)
572         self['fingerprint'] = fingerprint.hexdigest()
573         return offset
574
575     def _parse_secret_key_packet(self, data):
576         self._parse_generic_secret_key_packet(data=data)
577
578     def _parse_secret_subkey_packet(self, data):
579         self._parse_generic_secret_key_packet(data=data)
580
581     def _parse_generic_secret_key_packet(self, data):
582         offset = self._parse_generic_public_key_packet(data=data)
583         string_to_key_usage = data[offset]
584         offset += 1
585         if string_to_key_usage in [255, 254]:
586             self['symmetric-encryption-algorithm'] = (
587                 self._symmetric_key_algorithms[data[offset]])
588             offset += 1
589             offset += self._parse_string_to_key_specifier(data=data[offset:])
590         else:
591             self['symmetric-encryption-algorithm'] = (
592                 self._symmetric_key_algorithms[string_to_key_usage])
593         if string_to_key_usage:
594             block_size_bits = self._cipher_block_size.get(
595                 self['symmetric-encryption-algorithm'], None)
596             if block_size_bits % 8:
597                 raise NotImplementedError(
598                     ('{}-bit block size for {} is not an integer number of bytes'
599                      ).format(
600                          block_size_bits, self['symmetric-encryption-algorithm']))
601             block_size = block_size_bits // 8
602             if not block_size:
603                 raise NotImplementedError(
604                     'unknown block size for {}'.format(
605                         self['symmetric-encryption-algorithm']))
606             self['initial-vector'] = data[offset: offset + block_size]
607             offset += block_size
608             ciphertext = data[offset:]
609             offset += len(ciphertext)
610             decrypted_data = self.decrypt_symmetric_encryption(data=ciphertext)
611         else:
612             decrypted_data = data[offset:key_end]
613         if string_to_key_usage in [0, 255]:
614             key_end = -2
615         elif string_to_key_usage == 254:
616             key_end = -20
617         else:
618             key_end = 0
619         secret_key = decrypted_data[:key_end]
620         secret_offset = 0
621         if key_end:
622             secret_key_checksum = decrypted_data[key_end:]
623             if key_end == -2:
624                 calculated_checksum = sum(secret_key) % 65536
625             else:
626                 checksum_hash = _hashlib.sha1()
627                 checksum_hash.update(secret_key)
628                 calculated_checksum = checksum_hash.digest()
629             if secret_key_checksum != calculated_checksum:
630                 raise ValueError(
631                     'corrupt secret key (checksum {} != expected {})'.format(
632                         secret_key_checksum, calculated_checksum))
633         if self['public-key-algorithm'].startswith('rsa '):
634             o, self['secret-exponent'] = self._parse_multiprecision_integer(
635                 secret_key[secret_offset:])
636             secret_offset += o
637             o, self['secret-prime-p'] = self._parse_multiprecision_integer(
638                 secret_key[secret_offset:])
639             secret_offset += o
640             o, self['secret-prime-q'] = self._parse_multiprecision_integer(
641                 secret_key[secret_offset:])
642             secret_offset += o
643             o, self['secret-inverse-of-p-mod-q'] = (
644                 self._parse_multiprecision_integer(
645                     secret_key[secret_offset:]))
646             secret_offset += o
647         elif self['public-key-algorithm'].startswith('dsa '):
648             o, self['secret-exponent'] = self._parse_multiprecision_integer(
649                 secret_key[secret_offset:])
650             secret_offset += o
651         elif self['public-key-algorithm'].startswith('elgamal '):
652             o, self['secret-exponent'] = self._parse_multiprecision_integer(
653                 secret_key[secret_offset:])
654             secret_offset += o
655         else:
656             raise NotImplementedError(
657                 'algorithm-specific key fields for {}'.format(
658                     self['public-key-algorithm']))
659         if secret_offset != len(secret_key):
660             raise ValueError(
661                 ('parsed {} out of {} bytes of algorithm-specific key fields '
662                  'for {}').format(
663                      secret_offset, len(secret_key),
664                      self['public-key-algorithm']))
665
666     def _parse_signature_subpackets(self, data):
667         offset = 0
668         while offset < len(data):
669             o, subpacket = self._parse_signature_subpacket(data=data[offset:])
670             offset += o
671             yield subpacket
672
673     def _parse_signature_subpacket(self, data):
674         subpacket = {}
675         first = data[0]
676         offset = 1
677         if first < 192:
678             length = first
679         elif first >= 192 and first < 255:
680             second = data[offset]
681             offset += 1
682             length = ((first - 192) << 8) + second + 192
683         else:
684             length = _struct.unpack(
685                 '>I', data[offset: offset + 4])[0]
686             offset += 4
687         subpacket['type'] = self._signature_subpacket_types[data[offset]]
688         offset += 1
689         subpacket_data = data[offset: offset + length - 1]
690         offset += len(subpacket_data)
691         method_name = '_parse_{}_signature_subpacket'.format(
692             self._clean_type(type=subpacket['type']))
693         method = getattr(self, method_name, None)
694         if not method:
695             raise NotImplementedError(
696                 'cannot parse signature subpacket type {!r}'.format(
697                     subpacket['type']))
698         method(data=subpacket_data, subpacket=subpacket)
699         return (offset, subpacket)
700
701     def _parse_signature_packet(self, data):
702         self['signature-version'] = data[0]
703         offset = 1
704         if self['signature-version'] != 4:
705             raise NotImplementedError(
706                 'signature packet version {}'.format(
707                     self['signature-version']))
708         self['signature-type'] = self._signature_types[data[offset]]
709         offset += 1
710         self['public-key-algorithm'] = self._public_key_algorithms[
711             data[offset]]
712         offset += 1
713         self['hash-algorithm'] = self._hash_algorithms[data[offset]]
714         offset += 1
715         hashed_count = _struct.unpack('>H', data[offset: offset + 2])[0]
716         offset += 2
717         self['hashed-subpackets'] = list(self._parse_signature_subpackets(
718             data[offset: offset + hashed_count]))
719         offset += hashed_count
720         unhashed_count = _struct.unpack('>H', data[offset: offset + 2])[0]
721         offset += 2
722         self['unhashed-subpackets'] = list(self._parse_signature_subpackets(
723             data=data[offset: offset + unhashed_count]))
724         offset += unhashed_count
725         self['signed-hash-word'] = data[offset: offset + 2]
726         offset += 2
727         self['signature'] = data[offset:]
728         if self['signature-type'] == 'standalone':
729             self['target'] = None
730         elif self['signature-type'].endswith(' user id and public-key packet'):
731             self['target'] = [
732                 [p for p in self.key.public_packets if p['type'] == 'public-key packet'][-1],
733                 [p for p in self.key.public_packets if p['type'] == 'user id packet'][-1],
734                 ]
735         else:
736             raise NotImplementedError(
737                 'target for {}'.format(self['signature-type']))
738
739     def _parse_signature_creation_time_signature_subpacket(
740             self, data, subpacket):
741         subpacket['signature-creation-time'] = _struct.unpack('>I', data)[0]
742
743     def _parse_issuer_signature_subpacket(self, data, subpacket):
744         subpacket['issuer'] = byte_string(data=data, sep='')
745
746     def _parse_key_expiration_time_signature_subpacket(
747             self, data, subpacket):
748         subpacket['key-expiration-time'] = _struct.unpack('>I', data)[0]
749
750     def _parse_preferred_symmetric_algorithms_signature_subpacket(
751             self, data, subpacket):
752         subpacket['preferred-symmetric-algorithms'] = [
753             self._symmetric_key_algorithms[d] for d in data]
754
755     def _parse_preferred_hash_algorithms_signature_subpacket(
756             self, data, subpacket):
757         subpacket['preferred-hash-algorithms'] = [
758             self._hash_algorithms[d] for d in data]
759
760     def _parse_preferred_compression_algorithms_signature_subpacket(
761             self, data, subpacket):
762         subpacket['preferred-compression-algorithms'] = [
763             self._compression_algorithms[d] for d in data]
764
765     def _parse_key_server_preferences_signature_subpacket(
766             self, data, subpacket):
767         subpacket['key-server-preferences'] = set()
768         if data[0] & 0x80:
769             subpacket['key-server-preferences'].add('no-modify')
770
771     def _parse_primary_user_id_signature_subpacket(self, data, subpacket):
772         subpacket['primary-user-id'] = bool(data[0])
773
774     def _parse_key_flags_signature_subpacket(self, data, subpacket):
775         subpacket['key-flags'] = set()
776         if data[0] & 0x1:
777             subpacket['key-flags'].add('can certify')
778         if data[0] & 0x2:
779             subpacket['key-flags'].add('can sign')
780         if data[0] & 0x4:
781             subpacket['key-flags'].add('can encrypt communications')
782         if data[0] & 0x8:
783             subpacket['key-flags'].add('can encrypt storage')
784         if data[0] & 0x10:
785             subpacket['key-flags'].add('private split')
786         if data[0] & 0x20:
787             subpacket['key-flags'].add('can authenticate')
788         if data[0] & 0x80:
789             subpacket['key-flags'].add('private shared')
790
791     def _parse_features_signature_subpacket(self, data, subpacket):
792         subpacket['features'] = set()
793         if data[0] & 0x1:
794             subpacket['features'].add('modification detection')
795
796     def _parse_embedded_signature_signature_subpacket(self, data, subpacket):
797         subpacket['embedded'] = PGPPacket(key=self.key)
798         subpacket['embedded']._parse_signature_packet(data=data)
799
800     def _parse_user_id_packet(self, data):
801         self['user'] = str(data, 'utf-8')
802
803     def to_bytes(self):
804         method_name = '_serialize_{}'.format(self._clean_type())
805         method = getattr(self, method_name, None)
806         if not method:
807             raise NotImplementedError(
808                 'cannot serialize packet type {!r}'.format(self['type']))
809         body = method()
810         self['length'] = len(body)
811         return b''.join([
812             self._serialize_header(),
813             body,
814             ])
815
816     def _serialize_header(self):
817         always_one = 1
818         new_format = 0
819         type_code = self._reverse(self._packet_types, self['type'])
820         packet_tag = (
821             always_one * (1 << 7) |
822             new_format * (1 << 6) |
823             type_code * (1 << 2) |
824             self['length-type']
825             )
826         length_bytes, length_type = self._old_format_packet_length_type[
827             self['length-type']]
828         length_format = '>{}'.format(length_type)
829         length_data = _struct.pack(length_format, self['length'])
830         return b''.join([
831             bytes([packet_tag]),
832             length_data,
833             ])
834
835     @staticmethod
836     def _serialize_multiprecision_integer(integer):
837         r"""Serialize RFC 4880's multipricision integers
838
839         >>> PGPPacket._serialize_multiprecision_integer(1)
840         b'\x00\x01\x01'
841         >>> PGPPacket._serialize_multiprecision_integer(511)
842         b'\x00\t\x01\xff'
843         """
844         bit_length = int(_math.log(integer, 2)) + 1
845         chunks = [
846             _struct.pack('>H', bit_length),
847             ]
848         while integer > 0:
849             chunks.insert(1, bytes([integer & 0xff]))
850             integer = integer >> 8
851         return b''.join(chunks)
852
853     @classmethod
854     def _encode_string_to_key_count(cls, count):
855         r"""Encode RFC 4880's string-to-key count
856
857         >>> PGPPacket._encode_string_to_key_count(753664)
858         b'\x97'
859         """
860         coded_count = 0
861         count = count >> cls._string_to_key_expbias
862         while not count & 1:
863             count = count >> 1
864             coded_count += 1 << 4
865         coded_count += count & 15
866         return bytes([coded_count])
867
868     def _serialize_string_to_key_specifier(self):
869         string_to_key_type = bytes([
870             self._reverse(
871                 self._string_to_key_types, self['string-to-key-type']),
872             ])
873         chunks = [string_to_key_type]
874         if self['string-to-key-type'] == 'simple':
875             chunks.append(bytes([self._reverse(
876                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
877         elif self['string-to-key-type'] == 'salted':
878             chunks.append(bytes([self._reverse(
879                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
880             chunks.append(self['string-to-key-salt'])
881         elif self['string-to-key-type'] == 'iterated and salted':
882             chunks.append(bytes([self._reverse(
883                 self._hash_algorithms, self['string-to-key-hash-algorithm'])]))
884             chunks.append(self['string-to-key-salt'])
885             chunks.append(self._encode_string_to_key_count(
886                 count=self['string-to-key-count']))
887         else:
888             raise NotImplementedError(
889                 'string-to-key type {}'.format(self['string-to-key-type']))
890         return offset
891         return b''.join(chunks)
892
893     def _serialize_public_key_packet(self):
894         return self._serialize_generic_public_key_packet()
895
896     def _serialize_public_subkey_packet(self):
897         return self._serialize_generic_public_key_packet()
898
899     def _serialize_generic_public_key_packet(self):
900         key_version = bytes([self['key-version']])
901         chunks = [key_version]
902         if self['key-version'] != 4:
903             raise NotImplementedError(
904                 'public (sub)key packet version {}'.format(
905                     self['key-version']))
906         chunks.append(_struct.pack('>I', self['creation-time']))
907         chunks.append(bytes([self._reverse(
908             self._public_key_algorithms, self['public-key-algorithm'])]))
909         if self['public-key-algorithm'].startswith('rsa '):
910             chunks.append(self._serialize_multiprecision_integer(
911                 self['public-modulus']))
912             chunks.append(self._serialize_multiprecision_integer(
913                 self['public-exponent']))
914         elif self['public-key-algorithm'].startswith('dsa '):
915             chunks.append(self._serialize_multiprecision_integer(
916                 self['prime']))
917             chunks.append(self._serialize_multiprecision_integer(
918                 self['group-order']))
919             chunks.append(self._serialize_multiprecision_integer(
920                 self['group-generator']))
921             chunks.append(self._serialize_multiprecision_integer(
922                 self['public-key']))
923         elif self['public-key-algorithm'].startswith('elgamal '):
924             chunks.append(self._serialize_multiprecision_integer(
925                 self['prime']))
926             chunks.append(self._serialize_multiprecision_integer(
927                 self['group-generator']))
928             chunks.append(self._serialize_multiprecision_integer(
929                 self['public-key']))
930         else:
931             raise NotImplementedError(
932                 'algorithm-specific key fields for {}'.format(
933                     self['public-key-algorithm']))
934         return b''.join(chunks)
935
936     def _serialize_user_id_packet(self):
937         return self['user'].encode('utf-8')
938
939     def _string_to_key(self, string, key_size):
940         if key_size % 8:
941             raise ValueError(
942                 '{}-bit key is not an integer number of bytes'.format(
943                     key_size))
944         key_size_bytes = key_size // 8
945         hash_name = self._hashlib_name[
946             self['string-to-key-hash-algorithm']]
947         string_hash = _hashlib.new(hash_name)
948         hashes = _math.ceil(key_size_bytes / string_hash.digest_size)
949         key = b''
950         if self['string-to-key-type'] == 'simple':
951             update_bytes = string
952         elif self['string-to-key-type'] in [
953                 'salted',
954                 'iterated and salted',
955                 ]:
956             update_bytes = self['string-to-key-salt'] + string
957             if self['string-to-key-type'] == 'iterated and salted':
958                 count = self['string-to-key-count']
959                 if count < len(update_bytes):
960                     count = len(update_bytes)
961         else:
962             raise NotImplementedError(
963                 'key calculation for string-to-key type {}'.format(
964                     self['string-to-key-type']))
965         for padding in range(hashes):
966             string_hash = _hashlib.new(hash_name)
967             string_hash.update(padding * b'\x00')
968             if self['string-to-key-type'] in [
969                     'simple',
970                     'salted',
971                     ]:
972                 string_hash.update(update_bytes)
973             elif self['string-to-key-type'] == 'iterated and salted':
974                 remaining = count
975                 while remaining > 0:
976                     string_hash.update(update_bytes[:remaining])
977                     remaining -= len(update_bytes)
978             key += string_hash.digest()
979         key = key[:key_size_bytes]
980         return key
981
982     def decrypt_symmetric_encryption(self, data):
983         """Decrypt OpenPGP's Cipher Feedback mode"""
984         algorithm = self['symmetric-encryption-algorithm']
985         module = self._crypto_module[algorithm]
986         key_size = self._key_size[algorithm]
987         segment_size_bits = self._cipher_block_size[algorithm]
988         if segment_size_bits % 8:
989             raise NotImplementedError(
990                 ('{}-bit segment size for {} is not an integer number of bytes'
991                  ).format(segment_size_bits, algorithm))
992         segment_size_bytes = segment_size_bits // 8
993         padding = segment_size_bytes - len(data) % segment_size_bytes
994         if padding:
995             data += b'\x00' * padding
996         if self.key and self.key._cache_passphrase and self.key._passphrase:
997             passphrase = self.key._passphrase
998         else:
999             passphrase = _getpass.getpass(
1000                 'passphrase for {}: '.format(self['fingerprint'][-8:]))
1001             passphrase = passphrase.encode('ascii')
1002             if self.key and self.key._cache_passphrase:
1003                 self.key._passphrase = passphrase
1004         key = self._string_to_key(string=passphrase, key_size=key_size)
1005         cipher = module.new(
1006             key=key,
1007             mode=module.MODE_CFB,
1008             IV=self['initial-vector'],
1009             segment_size=segment_size_bits)
1010         plaintext = cipher.decrypt(data)
1011         if padding:
1012             plaintext = plaintext[:-padding]
1013         return plaintext
1014
1015     def check_roundtrip(self):
1016         serialized = self.to_bytes()
1017         source = self['raw']
1018         if serialized != source:
1019             if len(serialized) != len(source):
1020                 raise ValueError(
1021                     ('serialized {} is {} bytes long, '
1022                      'but input is {} bytes long').format(
1023                          self['type'], len(serialized), len(source)))
1024             chunk_size = 8
1025             for i in range(0, len(source), 8):
1026                 in_chunk = source[i: i + chunk_size]
1027                 out_chunk = serialized[i: i + chunk_size]
1028                 if in_chunk != out_chunk:
1029                     raise ValueError(
1030                         ('serialized {} differs from input packet: '
1031                          'at byte {}, {} != {}').format(
1032                             self['type'], i, byte_string(data=out_chunk),
1033                             byte_string(data=in_chunk)))
1034
1035
1036 class PGPKey (object):
1037     """An OpenPGP key with public and private parts.
1038
1039     From RFC 4880 [1]:
1040
1041       OpenPGP users may transfer public keys.  The essential elements
1042       of a transferable public key are as follows:
1043
1044       - One Public-Key packet
1045       - Zero or more revocation signatures
1046       - One or more User ID packets
1047       - After each User ID packet, zero or more Signature packets
1048         (certifications)
1049       - Zero or more User Attribute packets
1050       - After each User Attribute packet, zero or more Signature
1051         packets (certifications)
1052       - Zero or more Subkey packets
1053       - After each Subkey packet, one Signature packet, plus
1054         optionally a revocation
1055
1056     Secret keys have a similar packet stream [2]:
1057
1058       OpenPGP users may transfer secret keys.  The format of a
1059       transferable secret key is the same as a transferable public key
1060       except that secret-key and secret-subkey packets are used
1061       instead of the public key and public-subkey packets.
1062       Implementations SHOULD include self-signatures on any user IDs
1063       and subkeys, as this allows for a complete public key to be
1064       automatically extracted from the transferable secret key.
1065       Implementations MAY choose to omit the self-signatures,
1066       especially if a transferable public key accompanies the
1067       transferable secret key.
1068
1069     [1]: http://tools.ietf.org/search/rfc4880#section-11.1
1070     [2]: http://tools.ietf.org/search/rfc4880#section-11.2
1071     """
1072     def __init__(self, fingerprint, cache_passphrase=False):
1073         self.fingerprint = fingerprint
1074         self._cache_passphrase = cache_passphrase
1075         self._passphrase = None
1076         self.public_packets = None
1077         self.secret_packets = None
1078
1079     def __str__(self):
1080         lines = ['key: {}'.format(self.fingerprint)]
1081         if self.public_packets:
1082             lines.append('  public:')
1083             for packet in self.public_packets:
1084                 lines.extend(self._str_packet(packet=packet, prefix='    '))
1085         if self.secret_packets:
1086             lines.append('  secret:')
1087             for packet in self.secret_packets:
1088                 lines.extend(self._str_packet(packet=packet, prefix='    '))
1089         return '\n'.join(lines)
1090
1091     def _str_packet(self, packet, prefix):
1092         lines = str(packet).split('\n')
1093         return [prefix + line for line in lines]
1094
1095     def import_from_gpg(self):
1096         key_export = _get_stdout(
1097             ['gpg', '--export', self.fingerprint])
1098         self.public_packets = []
1099         self._packets_from_bytes(list=self.public_packets, data=key_export)
1100         if self.public_packets[0]['type'] != 'public-key packet':
1101             raise ValueError(
1102                 '{} does not start with a public-key packet'.format(
1103                     self.fingerprint))
1104         key_secret_export = _get_stdout(
1105             ['gpg', '--export-secret-keys', self.fingerprint])
1106         self.secret_packets = []
1107         self._packets_from_bytes(list=self.secret_packets, data=key_secret_export)
1108         if self.secret_packets[0]['type'] != 'secret-key packet':
1109             raise ValueError(
1110                 '{} does not start with a secret-key packet'.format(
1111                     self.fingerprint))
1112         for packet in self.public_packets + self.secret_packets:
1113             packet.check_roundtrip()
1114
1115     def _packets_from_bytes(self, list, data):
1116         offset = 0
1117         while offset < len(data):
1118             packet = PGPPacket(key=self)
1119             offset += packet.from_bytes(data=data[offset:])
1120             list.append(packet)
1121
1122     def export_to_gpg(self):
1123         raise NotImplemetedError('export to gpg')
1124
1125     def import_from_key(self, key):
1126         """Migrate the (sub)keys into this key"""
1127         pass
1128
1129
1130 def migrate(old_key, new_key, cache_passphrase=False):
1131     """Add the old key and sub-keys to the new key
1132
1133     For example, to upgrade your master key, while preserving old
1134     signatures you'd made.  You will lose signature *on* your old key
1135     though, since sub-keys can't be signed (I don't think).
1136     """
1137     old_key = PGPKey(fingerprint=old_key, cache_passphrase=cache_passphrase)
1138     old_key.import_from_gpg()
1139     new_key = PGPKey(fingerprint=new_key, cache_passphrase=cache_passphrase)
1140     new_key.import_from_gpg()
1141     new_key.import_from_key(key=old_key)
1142
1143     print(old_key)
1144     print(new_key)
1145
1146
1147 if __name__ == '__main__':
1148     import sys as _sys
1149
1150     old_key, new_key = _sys.argv[1:3]
1151     migrate(old_key=old_key, new_key=new_key, cache_passphrase=True)