From 7fe00e503c25a2ceb3532a8d39693a8cdfc992b9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 22 Dec 2013 19:28:59 -0800 Subject: [PATCH] Extract signature packet targets We need to know what we're signing. From RFC 4880 [1]: The concatenation of the data being signed and the signature data from the version number through the hashed subpacket data (inclusive) is hashed. The resulting hash value is what is signed. It's not always clear what the target bytes should be. RFC 4880 is clear for some types, and ambiguous for others. For example, it's clear that a standalone signature has no target [2]: 0x02: Standalone signature. This signature is a signature of only its own subpacket contents. It is calculated identically to a signature over a zero-length binary document. It is less clear how to serialize the targets for a user-id-and-public-key signature [2]: 0x10: Generic certification of a User ID and Public-Key packet. The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID. There is a bit more guidance later on, showing that the key packet should indeed come before the user id packet [3]: When a signature is made over a key, the hash data starts with the octet 0x99, followed by a two-octet length of the key, and then body of the key packet. (Note that this is an old-style packet header for a key packet with two-octet length.) A subkey binding signature (type 0x18) or primary key binding signature (type 0x19) then hashes the subkey using the same format as the main key (also using 0x99 as the first octet). Key revocation signatures (types 0x20 and 0x28) hash only the key being revoked. A certification signature (type 0x10 through 0x13) hashes the User ID being bound to the key into the hash context after the above data. A V3 certification hashes the contents of the User ID or attribute packet packet, without any header. A V4 certification hashes the constant 0xB4 for User ID certifications or the constant 0xD1 for User Attribute certifications, followed by a four-octet number giving the length of the User ID or User Attribute data, and then the User ID or User Attribute data. ... V4 signatures also hash in a final trailer of six octets: the version of the Signature packet, i.e., 0x04; 0xFF; and a four-octet, big-endian number that is the length of the hashed data from the Signature packet (note that this number does not include these final six octets). After all this has been hashed in a single hash context, the resulting hash field is used in the signature algorithm and placed at the end of the Signature packet. [1]: http://tools.ietf.org/search/rfc4880#section-5.2.3 [2]: http://tools.ietf.org/search/rfc4880#section-5.2.1 [3]: http://tools.ietf.org/search/rfc4880#section-5.2.4 --- gpg-migrate.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gpg-migrate.py b/gpg-migrate.py index 95e34c0..f5a757c 100755 --- a/gpg-migrate.py +++ b/gpg-migrate.py @@ -725,6 +725,16 @@ class PGPPacket (dict): self['signed-hash-word'] = data[offset: offset + 2] offset += 2 self['signature'] = data[offset:] + if self['signature-type'] == 'standalone': + self['target'] = None + elif self['signature-type'].endswith(' user id and public-key packet'): + self['target'] = [ + [p for p in self.key.public_packets if p['type'] == 'public-key packet'][-1], + [p for p in self.key.public_packets if p['type'] == 'user id packet'][-1], + ] + else: + raise NotImplementedError( + 'target for {}'.format(self['signature-type'])) def _parse_signature_creation_time_signature_subpacket( self, data, subpacket): -- 2.26.2