Support Signify-based signatures
http://www.openbsd.org/papers/bsdcan-signify.html
This commit is contained in:
@@ -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',
|
||||||
]},
|
]},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
110
libagent/signify/__init__.py
Normal file
110
libagent/signify/__init__.py
Normal 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)
|
||||||
Reference in New Issue
Block a user