From 9875c9927e245ae60b4819973fcd9b87feebac94 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Tue, 26 Apr 2016 15:30:18 +0300 Subject: [PATCH] gpg: demo for subkeys decoding --- trezor_agent/gpg/debug_subkeys.py | 46 +++++++++++++++++++++++++++++++ trezor_agent/gpg/decode.py | 17 +++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 trezor_agent/gpg/debug_subkeys.py diff --git a/trezor_agent/gpg/debug_subkeys.py b/trezor_agent/gpg/debug_subkeys.py new file mode 100644 index 0000000..28a0570 --- /dev/null +++ b/trezor_agent/gpg/debug_subkeys.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +"""Check GPG v2 signature for a given public key.""" +import argparse +import base64 +import io +import logging +import pprint + +from . import decode +from .. import util + +log = logging.getLogger(__name__) + + +def main(): + """Main function.""" + p = argparse.ArgumentParser() + p.add_argument('pubkey') + p.add_argument('-v', '--verbose', action='store_true', default=False) + args = p.parse_args() + logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO, + format='%(asctime)s %(levelname)-10s %(message)s') + stream = open(args.pubkey, 'rb') + parser = decode.Parser(util.Reader(stream)) + pubkey, userid, sig1, subkey, sig2 = parser + + digest = decode.digest_packets([pubkey, userid, sig1]) + assert sig1['hash_prefix'] == digest[:2] + decode.verify_digest( + pubkey=pubkey, digest=digest, + signature=sig1['sig'], label='GPG public key (self sig)') + + digest = decode.digest_packets([pubkey, subkey, sig2]) + assert sig2['hash_prefix'] == digest[:2] + decode.verify_digest( + pubkey=pubkey, digest=digest, + signature=sig2['sig'], label='GPG subkey (1st sig)') + + sig3, = sig2['embedded'] + digest = decode.digest_packets([pubkey, subkey, sig3]) + decode.verify_digest( + pubkey=subkey, digest=digest, + signature=sig3['sig'], label='GPG subkey (2nd sig)') + +if __name__ == '__main__': + main() diff --git a/trezor_agent/gpg/decode.py b/trezor_agent/gpg/decode.py index 8204feb..e1f6592 100644 --- a/trezor_agent/gpg/decode.py +++ b/trezor_agent/gpg/decode.py @@ -103,6 +103,15 @@ class Parser(object): p['_to_hash'] = p['content'] return p + def _embedded_signatures(self, subpackets): + for packet in subpackets: + data = bytearray(packet) + if data[0] == 32: + # https://tools.ietf.org/html/rfc4880#section-5.2.3.26 + stream = io.BytesIO(data[1:]) + yield self.signature(util.Reader(stream)) + + def signature(self, stream): """See https://tools.ietf.org/html/rfc4880#section-5.2 for details.""" p = {'type': 'signature'} @@ -121,6 +130,10 @@ class Parser(object): p['_to_hash'] = to_hash.getvalue() + tail_to_hash p['unhashed_subpackets'] = parse_subpackets(stream) + embedded = list(self._embedded_signatures(p['unhashed_subpackets'])) + if embedded: + p['embedded'] = embedded + p['hash_prefix'] = stream.readfmt('2s') p['sig'] = (parse_mpi(stream), parse_mpi(stream)) assert not stream.read() @@ -174,7 +187,9 @@ class Parser(object): mpi = parse_mpi(stream) log.debug('mpi: %x (%d bits)', mpi, mpi.bit_length()) p['verifier'] = parser(mpi) - p['leftover'] = stream.read() # TBD: what is this? + leftover = stream.read() # TBD: what is this? + if leftover: + log.warning('unexpected subkey leftover: %r', leftover) # https://tools.ietf.org/html/rfc4880#section-12.2 packet_data = packet.getvalue()