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