Extract the packet length from the old-format length header
[gpg-migrate.git] / gpg-migrate.py
1 #!/usr/bin/python
2
3 import subprocess as _subprocess
4 import struct as _struct
5
6
7 def _get_stdout(args, stdin=None):
8     stdin_pipe = None
9     if stdin is not None:
10         stdin_pipe = _subprocess.PIPE
11     p = _subprocess.Popen(args, stdin=stdin_pipe, stdout=_subprocess.PIPE)
12     stdout, stderr = p.communicate(stdin)
13     status = p.wait()
14     if status != 0:
15         raise RuntimeError(status)
16     return stdout
17
18
19 class PGPPacket (dict):
20     # http://tools.ietf.org/search/rfc4880
21     _old_format_packet_length_type = {  # type: (bytes, struct type)
22         0: (1, 'B'),  # 1-byte unsigned integer
23         1: (2, 'H'),  # 2-byte unsigned integer
24         2: (4, 'I'),  # 4-byte unsigned integer
25         3: (None, None),
26         }
27
28     def from_bytes(self, data):
29         packet_tag = data[0]
30         always_one = packet_tag & 1 << 7
31         if not always_one:
32             raise ValueError('most significant packet tag bit not set')
33         self['new-format'] = packet_tag & 1 << 6
34         if self['new-format']:
35             self['packet-tag'] = packet_tag & 0b111111
36             raise NotImplementedError('new-format packet length')
37         else:
38             self['packet-tag'] = packet_tag >> 2 & 0b1111
39             self['length-type'] = packet_tag & 0b11
40             length_bytes, length_type = self._old_format_packet_length_type[
41                 self['length-type']]
42             if not length_bytes:
43                 raise NotImplementedError(
44                     'old-format packet of indeterminate length')
45             length_format = '>{}'.format(length_type)
46             length_data = data[1: 1 + length_bytes]
47             self['length'] = _struct.unpack(length_format, length_data)[0]
48
49     def to_bytes(self):
50         pass
51
52
53 def migrate(old_key, new_key):
54     """Add the old key and sub-keys to the new key
55
56     For example, to upgrade your master key, while preserving old
57     signatures you'd made.  You will lose signature *on* your old key
58     though, since sub-keys can't be signed (I don't think).
59     """
60     old_key_export = _get_stdout(
61         ['gpg', '--export', old_key])
62     old_key_packet = PGPPacket()
63     old_key_packet.from_bytes(data=old_key_export)
64     old_key_secret_export = _get_stdout(
65         ['gpg', '--export-secret-keys', old_key])
66     old_key_secret_packet = PGPPacket()
67     old_key_secret_packet.from_bytes(data=old_key_secret_export)
68
69     import pprint
70     pprint.pprint(old_key_packet)
71     pprint.pprint(old_key_secret_packet)
72
73
74 if __name__ == '__main__':
75     import sys as _sys
76
77     old_key, new_key = _sys.argv[1:3]
78     migrate(old_key=old_key, new_key=new_key)