Add PGPPacket.copy and pull out PGPPacket._serialize_body
authorW. Trevor King <wking@tremily.us>
Mon, 23 Dec 2013 19:55:56 +0000 (11:55 -0800)
committerW. Trevor King <wking@tremily.us>
Mon, 23 Dec 2013 21:41:06 +0000 (13:41 -0800)
Use these with _serialize_signature_packet_target to compute key
fingerprints in _parse_generic_public_key_packet.  I'd previously been
hashing over the whole (sub)key packet, but RFC 4880 says the hash
should just be over the public-key part of the packet, even for secret
keys [1]:

  A V4 fingerprint is the 160-bit SHA-1 hash of the octet 0x99,
  followed by the two-octet packet length, followed by the entire
  Public-Key packet starting with the version field.

I've added the fingerprint_target stuff so I don't hash the
secret-key-specific fields.

[1]: http://tools.ietf.org/search/rfc4880#section-12.2

gpg-migrate.py

index 0a7d116b10278681af92cd9a4950fd8f38c230ab..3a3c7e99ac2ad9ddcbaa699dabfe5f6c89179246 100755 (executable)
@@ -309,6 +309,11 @@ class PGPPacket (dict):
         """
         return [k for k,v in dict.items() if v == value][0]
 
+    def copy(self):
+        packet = PGPPacket(key=self.key)
+        packet.update(self)
+        return packet
+
     def __str__(self):
         method_name = '_str_{}'.format(self._clean_type())
         method = getattr(self, method_name, None)
@@ -566,9 +571,12 @@ class PGPPacket (dict):
                 'algorithm-specific key fields for {}'.format(
                     self['public-key-algorithm']))
         fingerprint = _hashlib.sha1()
-        fingerprint.update(b'\x99')
-        fingerprint.update(_struct.pack('>H', len(data)))
-        fingerprint.update(data)
+        fingerprint_target = self
+        if self['type'] != 'public-key packet':
+            fingerprint_target = self.copy()
+            fingerprint_target['type'] = 'public-key packet'
+        fingerprint.update(
+            self._serialize_signature_packet_target(target=fingerprint_target))
         self['fingerprint'] = fingerprint.hexdigest()
         return offset
 
@@ -801,12 +809,9 @@ class PGPPacket (dict):
         self['user'] = str(data, 'utf-8')
 
     def to_bytes(self):
-        method_name = '_serialize_{}'.format(self._clean_type())
-        method = getattr(self, method_name, None)
-        if not method:
-            raise NotImplementedError(
-                'cannot serialize packet type {!r}'.format(self['type']))
-        body = method()
+        body = self._serialize_body()
+        if body is None:
+            raise ValueError(method)
         self['length'] = len(body)
         return b''.join([
             self._serialize_header(),
@@ -832,6 +837,14 @@ class PGPPacket (dict):
             length_data,
             ])
 
+    def _serialize_body(self):
+        method_name = '_serialize_{}'.format(self._clean_type())
+        method = getattr(self, method_name, None)
+        if not method:
+            raise NotImplementedError(
+                'cannot serialize packet type {!r}'.format(self['type']))
+        return method()
+
     @staticmethod
     def _serialize_multiprecision_integer(integer):
         r"""Serialize RFC 4880's multipricision integers