Support Signify-based signatures

http://www.openbsd.org/papers/bsdcan-signify.html
This commit is contained in:
Roman Zeyde
2019-07-20 12:52:31 +03:00
committed by Roman Zeyde
parent dbae284487
commit 020572ef5f
5 changed files with 124 additions and 7 deletions

View File

@@ -36,5 +36,6 @@ setup(
'trezor-agent = trezor_agent:ssh_agent', 'trezor-agent = trezor_agent:ssh_agent',
'trezor-gpg = trezor_agent:gpg_tool', 'trezor-gpg = trezor_agent:gpg_tool',
'trezor-gpg-agent = trezor_agent:gpg_agent', 'trezor-gpg-agent = trezor_agent:gpg_agent',
'trezor-signify = trezor_agent:signify_tool',
]}, ]},
) )

View File

@@ -1,7 +1,7 @@
import libagent.gpg from libagent import signify, gpg, ssh
import libagent.ssh
from libagent.device.trezor import Trezor as DeviceType from libagent.device.trezor import Trezor as DeviceType
ssh_agent = lambda: libagent.ssh.main(DeviceType) ssh_agent = lambda: ssh.main(DeviceType)
gpg_tool = lambda: libagent.gpg.main(DeviceType) gpg_tool = lambda: gpg.main(DeviceType)
gpg_agent = lambda: libagent.gpg.run_agent(DeviceType) gpg_agent = lambda: gpg.run_agent(DeviceType)
signify_tool = lambda: signify.main(DeviceType)

View File

@@ -98,6 +98,11 @@ class Trezor(interface.Device):
return result return result
def sign(self, identity, blob): def sign(self, identity, blob):
"""Sign given blob and return the signature (as bytes)."""
sig, _ = self.sign_with_pubkey(identity, blob)
return sig
def sign_with_pubkey(self, identity, blob):
"""Sign given blob and return the signature (as bytes).""" """Sign given blob and return the signature (as bytes)."""
curve_name = identity.get_curve_name(ecdh=False) curve_name = identity.get_curve_name(ecdh=False)
log.debug('"%s" signing %r (%s) on %s', log.debug('"%s" signing %r (%s) on %s',
@@ -112,7 +117,7 @@ class Trezor(interface.Device):
log.debug('result: %s', result) log.debug('result: %s', result)
assert len(result.signature) == 65 assert len(result.signature) == 65
assert result.signature[:1] == b'\x00' assert result.signature[:1] == b'\x00'
return bytes(result.signature[1:]) return bytes(result.signature[1:]), bytes(result.public_key)
except self._defs.TrezorFailure as e: except self._defs.TrezorFailure as e:
msg = '{} error: {}'.format(self, e) msg = '{} error: {}'.format(self, e)
log.debug(msg, exc_info=True) log.debug(msg, exc_info=True)

View File

@@ -0,0 +1,110 @@
"""TREZOR support for Ed25519 signify signatures."""
import argparse
import binascii
import contextlib
import functools
import hashlib
import logging
import os
import re
import struct
import subprocess
import sys
import time
import pkg_resources
import semver
from .. import formats, server, util
from ..device import interface, ui
log = logging.getLogger(__name__)
def _create_identity(user_id):
result = interface.Identity(identity_str='signify://', curve_name='ed25519')
result.identity_dict['host'] = user_id
return result
class Client:
"""Sign messages and get public keys from a hardware device."""
def __init__(self, device):
"""C-tor."""
self.device = device
def pubkey(self, identity):
"""Return public key as VerifyingKey object."""
with self.device:
return bytes(self.device.pubkey(ecdh=False, identity=identity))
def sign_with_pubkey(self, identity, data):
"""Sign the data and return a signature."""
log.info('please confirm Signify signature on %s for "%s"...',
self.device, identity.to_string())
log.debug('signing data: %s', util.hexlify(data))
with self.device:
sig, pubkey = self.device.sign_with_pubkey(blob=data, identity=identity)
assert len(sig) == 64
assert len(pubkey) == 33
assert pubkey[:1] == b"\x00"
return sig, pubkey[1:]
def format_payload(pubkey, data):
"""See http://www.openbsd.org/papers/bsdcan-signify.html for details."""
keynum = hashlib.sha256(pubkey).digest()[:8]
return binascii.b2a_base64(b"Ed" + keynum + data).decode("ascii")
def run_pubkey(device_type, args):
"""Export hardware-based Signify public key."""
util.setup_logging(verbosity=args.verbose)
log.warning('This Signify tool is still in EXPERIMENTAL mode, '
'so please note that the key derivation, API, and features '
'may change without backwards compatibility!')
identity = _create_identity(user_id=args.user_id)
pubkey = Client(device=device_type()).pubkey(identity=identity)
comment = f'untrusted comment: identity {identity.to_string()}\n'
result = comment + format_payload(pubkey=pubkey, data=pubkey)
print(result, end="")
def run_sign(device_type, args):
"""Sign an input blob using Ed25519."""
util.setup_logging(verbosity=args.verbose)
identity = _create_identity(user_id=args.user_id)
data = sys.stdin.buffer.read()
sig, pubkey = Client(device=device_type()).sign_with_pubkey(identity, data)
pubkey_str = format_payload(pubkey=pubkey, data=pubkey)
comment = f'untrusted comment: pubkey {pubkey_str}'
result = comment + format_payload(pubkey=pubkey, data=sig)
print(result, end="")
def main(device_type):
"""Parse command-line arguments."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='Action', dest='action')
subparsers.required = True
p = subparsers.add_parser('pubkey')
p.add_argument('user_id')
p.add_argument('-v', '--verbose', default=0, action='count')
p.set_defaults(func=run_pubkey)
p = subparsers.add_parser('sign')
p.add_argument('user_id')
p.add_argument('-v', '--verbose', default=0, action='count')
p.set_defaults(func=run_sign)
args = parser.parse_args()
device_type.ui = ui.UI(device_type=device_type, config=vars(args))
device_type.ui.cached_passphrase_ack = util.ExpiringCache(seconds=float(60))
return args.func(device_type=device_type, args=args)

View File

@@ -12,7 +12,8 @@ setup(
'libagent', 'libagent',
'libagent.device', 'libagent.device',
'libagent.gpg', 'libagent.gpg',
'libagent.ssh' 'libagent.signify',
'libagent.ssh',
], ],
install_requires=[ install_requires=[
'docutils>=0.14', 'docutils>=0.14',