diff --git a/trezor_agent/gpg/__main__.py b/trezor_agent/gpg/__main__.py index 2db9149..19f861b 100755 --- a/trezor_agent/gpg/__main__.py +++ b/trezor_agent/gpg/__main__.py @@ -16,7 +16,7 @@ def run_create(args): """Generate a new pubkey for a new/existing GPG identity.""" user_id = os.environ['TREZOR_GPG_USER_ID'] f = encode.Factory(user_id=user_id, created=args.time, - curve_name=args.ecdsa_curve) + curve_name=args.ecdsa_curve, ecdh=args.ecdh) with contextlib.closing(f): if args.subkey: @@ -38,6 +38,7 @@ def main(): create = subparsers.add_parser('create') create.add_argument('-s', '--subkey', action='store_true', default=False) + create.add_argument('--ecdh', action='store_true', default=False) create.add_argument('-e', '--ecdsa-curve', default='nist256p1') create.add_argument('-t', '--time', type=int, default=int(time.time())) create.set_defaults(run=run_create) diff --git a/trezor_agent/gpg/encode.py b/trezor_agent/gpg/encode.py index 08b80d0..3344deb 100644 --- a/trezor_agent/gpg/encode.py +++ b/trezor_agent/gpg/encode.py @@ -71,7 +71,7 @@ def _time_format(t): class Factory(object): """Performs GPG signing operations.""" - def __init__(self, user_id, created, curve_name): + def __init__(self, user_id, created, curve_name, ecdh=False): """Construct and loads a public key from the device.""" self.user_id = user_id assert curve_name in formats.SUPPORTED_CURVES @@ -79,7 +79,8 @@ class Factory(object): self.conn = HardwareSigner(user_id, curve_name=curve_name) self.pubkey = proto.PublicKey( curve_name=curve_name, created=created, - verifying_key=self.conn.pubkey()) + verifying_key=self.conn.pubkey(), ecdh=ecdh) + self.ecdh = ecdh log.info('%s created at %s for "%s"', self.pubkey, _time_format(self.pubkey.created), user_id) @@ -142,28 +143,38 @@ class Factory(object): self.user_id, util.hexlify(primary['key_id'])) data_to_sign = primary['_to_hash'] + self.pubkey.data_to_hash() - # Primary Key Binding Signature - hashed_subpackets = [ - proto.subpacket_time(self.pubkey.created)] # signature time - unhashed_subpackets = [ - proto.subpacket(16, self.pubkey.key_id())] # issuer key id - log.info('confirm signing subkey with hardware device') - embedded_sig = proto.make_signature( - signer_func=self.conn.sign, - data_to_sign=data_to_sign, - public_algo=self.pubkey.algo_id, - sig_type=0x19, - hashed_subpackets=hashed_subpackets, - unhashed_subpackets=unhashed_subpackets) + if self.ecdh: + embedded_sig = None + else: + # Primary Key Binding Signature + hashed_subpackets = [ + proto.subpacket_time(self.pubkey.created)] # signature time + unhashed_subpackets = [ + proto.subpacket(16, self.pubkey.key_id())] # issuer key id + log.info('confirm signing subkey with hardware device') + embedded_sig = proto.make_signature( + signer_func=self.conn.sign, + data_to_sign=data_to_sign, + public_algo=self.pubkey.algo_id, + sig_type=0x19, + hashed_subpackets=hashed_subpackets, + unhashed_subpackets=unhashed_subpackets) # Subkey Binding Signature + flags = 2 # key flags (certify & sign) + if self.ecdh: + flags = 4 | 8 + hashed_subpackets = [ proto.subpacket_time(self.pubkey.created), # signature time - proto.subpacket_byte(0x1B, 2)] # key flags (certify & sign) - unhashed_subpackets = [ - proto.subpacket(16, primary['key_id']), # issuer key id - proto.subpacket(32, embedded_sig), - proto.CUSTOM_SUBPACKET] + proto.subpacket_byte(0x1B, flags)] + + unhashed_subpackets = [] + unhashed_subpackets.append(proto.subpacket(16, primary['key_id'])) + if embedded_sig is not None: + unhashed_subpackets.append(proto.subpacket(32, embedded_sig)) + unhashed_subpackets.append(proto.CUSTOM_SUBPACKET) + log.info('confirm signing subkey with gpg-agent') gpg_agent = AgentSigner(self.user_id) signature = proto.make_signature( diff --git a/trezor_agent/gpg/proto.py b/trezor_agent/gpg/proto.py index ab46fc3..cc002fe 100644 --- a/trezor_agent/gpg/proto.py +++ b/trezor_agent/gpg/proto.py @@ -131,6 +131,7 @@ SUPPORTED_CURVES = { } } +ECDH_ALGO_ID = 18 CUSTOM_SUBPACKET = subpacket(100, b'TREZOR-GPG') # marks "our" pubkey @@ -145,12 +146,18 @@ def find_curve_by_algo_id(algo_id): class PublicKey(object): """GPG representation for public key packets.""" - def __init__(self, curve_name, created, verifying_key): + def __init__(self, curve_name, created, verifying_key, ecdh=False): """Contruct using a ECDSA VerifyingKey object.""" self.curve_info = SUPPORTED_CURVES[curve_name] self.created = int(created) # time since Epoch self.verifying_key = verifying_key - self.algo_id = self.curve_info['algo_id'] + if ecdh: + self.algo_id = ECDH_ALGO_ID + self.ecdh_packet = b'\x03\x01\x08\x07' + else: + self.algo_id = self.curve_info['algo_id'] + self.ecdh_packet = b'' + self.keygrip = self.curve_info['keygrip'](verifying_key) hex_key_id = util.hexlify(self.key_id())[-8:] self.desc = 'GPG public key {}/{}'.format(curve_name, hex_key_id) @@ -163,7 +170,7 @@ class PublicKey(object): self.algo_id) # public key algorithm ID oid = util.prefix_len('>B', self.curve_info['oid']) blob = self.curve_info['serialize'](self.verifying_key) - return header + oid + blob + return header + oid + blob + self.ecdh_packet def data_to_hash(self): """Data for digest computation."""