55 lines
2.1 KiB
Python
55 lines
2.1 KiB
Python
"""Device abstraction layer for GPG operations."""
|
|
|
|
from .. import factory, formats, util
|
|
|
|
|
|
class HardwareSigner(object):
|
|
"""Sign messages and get public keys from a hardware device."""
|
|
|
|
def __init__(self, user_id, curve_name):
|
|
"""Connect to the device and retrieve required public key."""
|
|
self.client_wrapper = factory.load()
|
|
self.identity = self.client_wrapper.identity_type()
|
|
self.identity.proto = 'gpg'
|
|
self.identity.host = user_id
|
|
self.curve_name = curve_name
|
|
|
|
def pubkey(self, ecdh=False):
|
|
"""Return public key as VerifyingKey object."""
|
|
addr = util.get_bip32_address(identity=self.identity, ecdh=ecdh)
|
|
if ecdh:
|
|
curve_name = formats.get_ecdh_curve_name(self.curve_name)
|
|
else:
|
|
curve_name = self.curve_name
|
|
public_node = self.client_wrapper.connection.get_public_node(
|
|
n=addr, ecdsa_curve_name=curve_name)
|
|
|
|
return formats.decompress_pubkey(
|
|
pubkey=public_node.node.public_key,
|
|
curve_name=curve_name)
|
|
|
|
def sign(self, digest):
|
|
"""Sign the digest and return a serialized signature."""
|
|
result = self.client_wrapper.connection.sign_identity(
|
|
identity=self.identity,
|
|
challenge_hidden=digest,
|
|
challenge_visual='',
|
|
ecdsa_curve_name=self.curve_name)
|
|
assert result.signature[:1] == b'\x00'
|
|
sig = result.signature[1:]
|
|
return (util.bytes2num(sig[:32]), util.bytes2num(sig[32:]))
|
|
|
|
def ecdh(self, pubkey):
|
|
"""Derive shared secret using ECDH from remote public key."""
|
|
result = self.client_wrapper.connection.get_ecdh_session_key(
|
|
identity=self.identity,
|
|
peer_public_key=pubkey,
|
|
ecdsa_curve_name=formats.get_ecdh_curve_name(self.curve_name))
|
|
assert len(result.session_key) in {65, 33} # NIST256 or Curve25519
|
|
assert result.session_key[:1] == b'\x04'
|
|
return result.session_key
|
|
|
|
def close(self):
|
|
"""Close the connection to the device."""
|
|
self.client_wrapper.connection.close()
|