3 import subprocess as _subprocess
4 import struct as _struct
7 def _get_stdout(args, stdin=None):
10 stdin_pipe = _subprocess.PIPE
11 p = _subprocess.Popen(args, stdin=stdin_pipe, stdout=_subprocess.PIPE)
12 stdout, stderr = p.communicate(stdin)
15 raise RuntimeError(status)
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
28 def from_bytes(self, data):
30 always_one = packet_tag & 1 << 7
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')
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[
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]
53 def migrate(old_key, new_key):
54 """Add the old key and sub-keys to the new key
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).
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)
70 pprint.pprint(old_key_packet)
71 pprint.pprint(old_key_secret_packet)
74 if __name__ == '__main__':
77 old_key, new_key = _sys.argv[1:3]
78 migrate(old_key=old_key, new_key=new_key)