Add a serialization check after parsing
authorW. Trevor King <wking@tremily.us>
Mon, 23 Dec 2013 00:23:47 +0000 (16:23 -0800)
committerW. Trevor King <wking@tremily.us>
Mon, 23 Dec 2013 21:41:06 +0000 (13:41 -0800)
Ensure that we can serialize everything we parse, using the input data
as a check against our output.

gpg-migrate.py

index f3eb3f9fa379948619f5f6e8ac55f0bcb6304421..8d475f76e86f75436dde8c69221adbbed6e82942 100755 (executable)
@@ -411,6 +411,7 @@ class PGPPacket (dict):
             raise NotImplementedError(
                 'cannot parse packet type {!r}'.format(self['type']))
         method(data=packet)
+        self['raw'] = data[:offset]
         return offset
 
     def _parse_header(self, data):
@@ -978,6 +979,27 @@ class PGPPacket (dict):
             plaintext = plaintext[:-padding]
         return plaintext
 
+    def check_roundtrip(self):
+        serialized = self.to_bytes()
+        source = self['raw']
+        if serialized != source:
+            if len(serialized) != len(source):
+                raise ValueError(
+                    ('serialized {} is {} bytes long, '
+                     'but input is {} bytes long').format(
+                         self['type'], len(serialized), len(source)))
+            chunk_size = 8
+            for i in range(0, len(source), 8):
+                in_chunk = source[i: i + chunk_size]
+                out_chunk = serialized[i: i + chunk_size]
+                if in_chunk != out_chunk:
+                    raise ValueError(
+                        ('serialized {} differs from input packet: '
+                         'at byte {}, {} != {}').format(
+                            self['type'], i,
+                            ' '.join('{:02x}'.format(byte) for byte in out_chunk),
+                            ' '.join('{:02x}'.format(byte) for byte in in_chunk)))
+
 
 class PGPKey (object):
     """An OpenPGP key with public and private parts.
@@ -1055,6 +1077,8 @@ class PGPKey (object):
             raise ValueError(
                 '{} does not start with a secret-key packet'.format(
                     self.fingerprint))
+        for packet in self.public_packets + self.secret_packets:
+            packet.check_roundtrip()
 
     def _packets_from_bytes(self, data):
         offset = 0