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