Add public key parsing to PGPPacket
authorW. Trevor King <wking@tremily.us>
Thu, 19 Dec 2013 05:52:42 +0000 (21:52 -0800)
committerW. Trevor King <wking@tremily.us>
Fri, 20 Dec 2013 18:56:23 +0000 (10:56 -0800)
Use the same parser for public-key and public-subkey packets.  From
RFC 4880 [1]:

  A Public-Subkey packet (tag 14) has exactly the same format as a
  Public-Key packet, but denotes a subkey.  One or more subkeys may be
  associated with a top-level key.

The generic (sub)key parsing is specified in section 5.5.2 [2]:

  OpenPGP implementations MUST create keys with version 4 format.  V3
  keys are deprecated; an implementation MUST NOT generate a V3 key,
  but MAY accept it.

  ...

  A version 4 packet contains:

  - A one-octet version number (4).
  - A four-octet number denoting the time that the key was created.
  - A one-octet number denoting the public-key algorithm of this key.
  - A series of multiprecision integers comprising the key material.

Also check that the --export packets begin with a public-key packet.
From RFC 4880 [3]:

  A Public-Key packet starts a series of packets that forms an OpenPGP
  key (sometimes called an OpenPGP certificate).

[1]: http://tools.ietf.org/search/rfc4880#section-5.5.1.2
[2]: http://tools.ietf.org/search/rfc4880#section-5.5.2
[3]: http://tools.ietf.org/search/rfc4880#section-5.5.1.1

gpg-migrate.py

index f754aef8b6498935d8e1f43c4e672ba19c39a669..066463be4455c49b8246ced5c4f30c009bca0f55 100755 (executable)
@@ -96,6 +96,25 @@ class PGPPacket (dict):
         self['type'] = self._packet_types[type_code]
         return offset
 
+    def _parse_public_key_packet(self, data):
+        self._parse_generic_public_key_packet(data=data)
+
+    def _parse_public_subkey_packet(self, data):
+        self._parse_generic_public_key_packet(data=data)
+
+    def _parse_generic_public_key_packet(self, data):
+        self['key-version'] = data[0]
+        offset = 1
+        if self['key-version'] != 4:
+            raise NotImplementedError(
+                'public (sub)key packet version {}'.format(
+                    self['key-version']))
+        length = 5
+        self['creation_time'], self['public-key-algorithm'] = _struct.unpack(
+            '>IB', data[offset: offset + length])
+        offset += length
+        self['key'] = data[offset:]
+
     def to_bytes(self):
         pass
 
@@ -119,6 +138,10 @@ def migrate(old_key, new_key):
         ['gpg', '--export', old_key])
     old_key_packets = list(
         packets_from_bytes(data=old_key_export))
+    if old_key_packets[0]['type'] != 'public-key packet':
+        raise ValueError(
+            '{} does not start with a public-key packet'.format(
+                old_key))
     old_key_secret_export = _get_stdout(
         ['gpg', '--export-secret-keys', old_key])
     old_key_secret_packets = list(