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