diff --git a/.gitignore b/.gitignore index e0b97f824..71b1b96f3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ app/vault/ui_*py #[folders] app/vault/src/atdlib/ bin/ +dojo/ etc/blockstack/* etc/zerotier/* ext/pyqt/ diff --git a/RELEASE.md b/RELEASE.md index 9d0d5fed2..8d779840f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,13 +1,16 @@ # Release Notes +## ΔOS v0.8-alpha.9 + +* restore: `logger` +* add-`graphviz`: `logger` graphics + ## ΔOS v0.8-alpha.8 * add-`atdlib`: `vault_module.c` * add-`atdlib`: `deos_module.c` * add-`app`: `wallet` * clone-repo: `electrum` -> `app/wallet` -* restore: `logger` -* add-`graphviz`: `logger` graphics * create-protocol: release notes * restore: `var/wiki` * restore: `boot/*.lz` diff --git a/src/atdlib/atdlib/__init__.py b/src/atdlib/atdlib/__init__.py index 1e24ba625..377fc6a06 100644 --- a/src/atdlib/atdlib/__init__.py +++ b/src/atdlib/atdlib/__init__.py @@ -5,7 +5,7 @@ __author__ = "Andrew DeSantis " __license__ = "GPLv3" -__version__ = "v0.8-alpha.8" +__version__ = "v0.8-alpha.9" import atdlib.deos as deos import atdlib.vault as vault diff --git a/src/trezor-agent/scripts/gpg-init b/src/trezor-agent/scripts/gpg-init index 3c4cdbab7..84906fb2f 100644 --- a/src/trezor-agent/scripts/gpg-init +++ b/src/trezor-agent/scripts/gpg-init @@ -1,22 +1,25 @@ #!/bin/bash set -eu -gpg2 --version >/dev/null # verify that GnuPG 2 is installed +# verify that GnuPG 2 is installed +gpg2 --version >/dev/null USER_ID="${1}" HOMEDIR=~/.gnupg/trezor -CURVE=${CURVE:="nist256p1"} # or "ed25519" -TIMESTAMP=${TIMESTAMP:=`date +%s`} # key creation timestamp +CURVE=${CURVE:="nist256p1"} # or "ed25519" +TIMESTAMP=${TIMESTAMP:=`date +%s`} # key creation timestamp # Prepare new GPG home directory for TREZOR-based identity -rm -rf "${HOMEDIR}" -mkdir -p "${HOMEDIR}" -chmod 700 "${HOMEDIR}" +rm -rf "${HOMEDIR}" && mkdir -p "${HOMEDIR}" && chmod 700 "${HOMEDIR}" # Generate new GPG identity and import into GPG keyring -trezor-gpg-create -v "${USER_ID}" -t "${TIMESTAMP}" -e "${CURVE}" > "${HOMEDIR}/pubkey.asc" +trezor-gpg-create -v "${USER_ID}" \ + -t "${TIMESTAMP}" \ + -e "${CURVE}" > "${HOMEDIR}/pubkey.asc" gpg2 --homedir "${HOMEDIR}" --import < "${HOMEDIR}/pubkey.asc" -rm -f "${HOMEDIR}/S.gpg-agent" # (otherwise, our agent won't be started automatically) + +# (otherwise, our agent won't be started automatically) +rm -f "${HOMEDIR}/S.gpg-agent" # Make new GPG identity with "ultimate" trust (via its fingerprint) FINGERPRINT=$(gpg2 --homedir "${HOMEDIR}" --list-public-keys --with-fingerprint --with-colons | sed -n -E 's/^fpr:::::::::([0-9A-F]+):$/\1/p' | head -n1) diff --git a/src/trezor-agent/scripts/gpg-shell b/src/trezor-agent/scripts/gpg-shell index 584e27aa6..cd1c87b18 100644 --- a/src/trezor-agent/scripts/gpg-shell +++ b/src/trezor-agent/scripts/gpg-shell @@ -1,7 +1,8 @@ #!/bin/bash set -eu -gpg2 --version >/dev/null # verify that GnuPG 2 is installed +# verify that GnuPG 2 is installed +gpg2 --version >/dev/null export GNUPGHOME=~/.gnupg/trezor diff --git a/src/trezor-agent/trezor_agent/device/__init__.py b/src/trezor-agent/trezor_agent/device/__init__.py index 613c1cf11..f6159dfc5 100644 --- a/src/trezor-agent/trezor_agent/device/__init__.py +++ b/src/trezor-agent/trezor_agent/device/__init__.py @@ -1,4 +1,5 @@ -"""Cryptographic hardware device management.""" +""" Cryptographic hardware device management. +""" import logging @@ -15,9 +16,9 @@ ledger.LedgerNanoS, ] - def detect(): - """Detect the first available device and return it to the user.""" + """ Detect the first available device and return it to the user. + """ for device_type in DEVICE_TYPES: try: with device_type() as d: diff --git a/src/trezor-agent/trezor_agent/device/interface.py b/src/trezor-agent/trezor_agent/device/interface.py index 32bd44ecf..942788f7c 100644 --- a/src/trezor-agent/trezor_agent/device/interface.py +++ b/src/trezor-agent/trezor_agent/device/interface.py @@ -1,4 +1,5 @@ -"""Device abstraction layer.""" +""" Device abstraction layer. +""" import hashlib import io @@ -20,17 +21,17 @@ '$' ])) - def string_to_identity(identity_str): - """Parse string into Identity dictionary.""" + """ Parse string into Identity dictionary. + """ m = _identity_regexp.match(identity_str) result = m.groupdict() log.debug('parsed identity: %s', result) return {k: v for k, v in result.items() if v} - def identity_to_string(identity_dict): - """Dump Identity dictionary into its string representation.""" + """ Dump Identity dictionary into its string representation. + """ result = [] if identity_dict.get('proto'): result.append(identity_dict['proto'] + '://') @@ -44,37 +45,42 @@ def identity_to_string(identity_dict): log.debug('identity parts: %s', result) return ''.join(result) - class Error(Exception): - """Device-related error.""" - + """ Device-related error. + """ class NotFoundError(Error): - """Device could not be found.""" - + """ Device could not be found. + """ class DeviceError(Error): - """"Error during device operation.""" - + """ Error during device operation. + """ class Identity(object): - """Represent SLIP-0013 identity, together with a elliptic curve choice.""" + """ Represent SLIP-0013 identity, together w/ a elliptic curve choice. + """ def __init__(self, identity_str, curve_name): - """Configure for specific identity and elliptic curve usage.""" + """ Configure for specific identity and elliptic curve usage. + """ self.identity_dict = string_to_identity(identity_str) self.curve_name = curve_name def items(self): - """Return a copy of identity_dict items.""" + """ Return a copy of identity_dict items. + """ return self.identity_dict.items() def __str__(self): - """Return identity serialized to string.""" - return '<{}|{}>'.format(identity_to_string(self.identity_dict), self.curve_name) + """ Return identity serialized to string. + """ + return '<{}|{}>'.format(identity_to_string(self.identity_dict), + self.curve_name) def get_bip32_address(self, ecdh=False): - """Compute BIP32 derivation address according to SLIP-0013/0017.""" + """ Compute BIP32 derivation address according to SLIP-0013/0017. + """ index = struct.pack('I', e) for e in path)) - def _convert_public_key(ecdsa_curve_name, result): - """Convert Ledger reply into PublicKey object.""" + """ Convert Ledger reply into PublicKey object. + """ if ecdsa_curve_name == 'nist256p1': if (result[64] & 1) != 0: result = bytearray([0x03]) + result[1:33] @@ -32,12 +33,13 @@ def _convert_public_key(ecdsa_curve_name, result): result = b'\x00' + bytes(keyY) return bytes(result) - class LedgerNanoS(interface.Device): - """Connection to Ledger Nano S device.""" + """ Connection to Ledger Nano S device. + """ def connect(self): - """Enumerate and connect to the first USB HID interface.""" + """ Enumerate and connect to the first USB HID interface. + """ try: return comm.getDongle() except comm.CommException as e: @@ -45,7 +47,8 @@ def connect(self): '{} not connected: "{}"'.format(self, e)) def pubkey(self, identity, ecdh=False): - """Get PublicKey object for specified BIP32 address and elliptic curve.""" + """ Get PublicKey object for specified BIP32 address & elliptic curve. + """ curve_name = identity.get_curve_name(ecdh) path = _expand_path(identity.get_bip32_address(ecdh)) if curve_name == 'nist256p1': @@ -60,7 +63,8 @@ def pubkey(self, identity, ecdh=False): return _convert_public_key(curve_name, result) def sign(self, identity, blob): - """Sign given blob and return the signature (as bytes).""" + """ Sign given blob and return the signature (as bytes). + """ path = _expand_path(identity.get_bip32_address(ecdh=False)) if identity.identity_dict['proto'] == 'ssh': ins = '04' @@ -95,7 +99,8 @@ def sign(self, identity, blob): return bytes(result[:64]) def ecdh(self, identity, pubkey): - """Get shared session key using Elliptic Curve Diffie-Hellman.""" + """ Get shared session key using Elliptic Curve Diffie-Hellman. + """ path = _expand_path(identity.get_bip32_address(ecdh=True)) if identity.curve_name == 'nist256p1': p2 = '01' diff --git a/src/trezor-agent/trezor_agent/device/trezor.py b/src/trezor-agent/trezor_agent/device/trezor.py index 6b280eb2a..439a18f80 100644 --- a/src/trezor-agent/trezor_agent/device/trezor.py +++ b/src/trezor-agent/trezor_agent/device/trezor.py @@ -1,4 +1,5 @@ -"""TREZOR-related code (see http://bitcointrezor.com/).""" +""" TREZOR-related code (see http://bitcointrezor.com/). +""" import binascii import logging @@ -10,10 +11,9 @@ log = logging.getLogger(__name__) - class Trezor(interface.Device): - """Connection to TREZOR device.""" - + """ Connection to TREZOR device. + """ @property def _defs(self): from . import trezor_defs @@ -23,7 +23,8 @@ def _defs(self): passphrase = os.environ.get('TREZOR_PASSPHRASE', '') def connect(self): - """Enumerate and connect to the first USB HID interface.""" + """ Enumerate and connect to the first USB HID interface. + """ def passphrase_handler(_): log.debug('using %s passphrase for %s', 'non-empty' if self.passphrase else 'empty', self) @@ -53,11 +54,13 @@ def passphrase_handler(_): raise interface.NotFoundError('{} not connected'.format(self)) def close(self): - """Close connection.""" + """ Close connection. + """ self.conn.close() def pubkey(self, identity, ecdh=False): - """Return public key.""" + """ Return public key. + """ curve_name = identity.get_curve_name(ecdh=ecdh) log.debug('"%s" getting public key (%s) from %s', identity, curve_name, self) @@ -74,7 +77,8 @@ def _identity_proto(self, identity): return result def sign(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) log.debug('"%s" signing %r (%s) on %s', identity, blob, curve_name, self) @@ -94,7 +98,8 @@ def sign(self, identity, blob): raise interface.DeviceError(msg) def ecdh(self, identity, pubkey): - """Get shared session key using Elliptic Curve Diffie-Hellman.""" + """ Get shared session key using Elliptic Curve Diffie-Hellman. + """ curve_name = identity.get_curve_name(ecdh=True) log.debug('"%s" shared session key (%s) for %r from %s', identity, curve_name, pubkey, self) diff --git a/src/trezor-agent/trezor_agent/device/trezor_defs.py b/src/trezor-agent/trezor_agent/device/trezor_defs.py index 00163ad97..c999d2c2a 100644 --- a/src/trezor-agent/trezor_agent/device/trezor_defs.py +++ b/src/trezor-agent/trezor_agent/device/trezor_defs.py @@ -1,4 +1,5 @@ -"""TREZOR-related definitions.""" +""" TREZOR-related definitions. +""" # pylint: disable=unused-import diff --git a/src/trezor-agent/trezor_agent/protocol.py b/src/trezor-agent/trezor_agent/protocol.py index 987becec1..15cefbcd4 100644 --- a/src/trezor-agent/trezor_agent/protocol.py +++ b/src/trezor-agent/trezor_agent/protocol.py @@ -1,12 +1,13 @@ -""" -SSH-agent protocol implementation library. +""" SSH-agent protocol implementation library. See https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.agent and http://ptspts.blogspot.co.il/2010/06/how-to-use-ssh-agent-programmatically.html for more details. + The server's source code can be found here: https://github.com/openssh/openssh-portable/blob/master/authfd.c """ + import io import logging @@ -14,7 +15,6 @@ log = logging.getLogger(__name__) - # Taken from https://github.com/openssh/openssh-portable/blob/master/authfd.h COMMANDS = dict( SSH_AGENTC_REQUEST_RSA_IDENTITIES=1, @@ -42,45 +42,42 @@ SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED=26, ) - def msg_code(name): - """Convert string name into a integer message code.""" + """ Convert string name into a integer message code. + """ return COMMANDS[name] - def msg_name(code): - """Convert integer message code into a string name.""" + """ Convert integer message code into a string name. + """ ids = {v: k for k, v in COMMANDS.items()} return ids[code] - def failure(): - """Return error code to SSH binary.""" + """ Return error code to SSH binary. + """ error_msg = util.pack('B', msg_code('SSH_AGENT_FAILURE')) return util.frame(error_msg) - def _legacy_pubs(buf): - """SSH v1 public keys are not supported.""" + """ SSH v1 public keys are not supported. + """ assert not buf.read() code = util.pack('B', msg_code('SSH_AGENT_RSA_IDENTITIES_ANSWER')) - num = util.pack('L', 0) # no SSH v1 keys + num = util.pack('L', 0) # no SSH v1 keys return util.frame(code, num) - class Handler(object): - """ssh-agent protocol handler.""" - + """ ssh-agent protocol handler. + """ def __init__(self, keys, signer, debug=False): - """ - Create a protocol handler with specified public keys. - - Use specified signer function to sign SSH authentication requests. + """ Create a protocol handler with specified public keys. + Use specified signer function to sign SSH authentication + requests. """ self.public_keys = keys self.signer = signer self.debug = debug - self.methods = { msg_code('SSH_AGENTC_REQUEST_RSA_IDENTITIES'): _legacy_pubs, msg_code('SSH2_AGENTC_REQUEST_IDENTITIES'): self.list_pubs, @@ -88,7 +85,8 @@ def __init__(self, keys, signer, debug=False): } def handle(self, msg): - """Handle SSH message from the SSH client and return the response.""" + """ Handle SSH message from the SSH client and return the response. + """ debug_msg = ': {!r}'.format(msg) if self.debug else '' log.debug('request: %d bytes%s', len(msg), debug_msg) buf = io.BytesIO(msg) @@ -96,7 +94,6 @@ def handle(self, msg): if code not in self.methods: log.warning('Unsupported command: %s (%d)', msg_name(code), code) return failure() - method = self.methods[code] log.debug('calling %s()', method.__name__) reply = method(buf=buf) @@ -105,7 +102,8 @@ def handle(self, msg): return reply def list_pubs(self, buf): - """SSH v2 public keys are serialized and returned.""" + """ SSH v2 public keys are serialized and returned. + """ assert not buf.read() keys = self.public_keys code = util.pack('B', msg_code('SSH2_AGENT_IDENTITIES_ANSWER')) @@ -117,9 +115,7 @@ def list_pubs(self, buf): return util.frame(code, num, *pubs) def sign_message(self, buf): - """ - SSH v2 public key authentication is performed. - + """ SSH v2 public key authentication is performed. If the required key is not supported, raise KeyError If the signature is invalid, raise ValueError """ @@ -128,7 +124,6 @@ def sign_message(self, buf): blob = util.read_frame(buf) assert util.read_frame(buf) == b'' assert not buf.read() - for k in self.public_keys: if (k['fingerprint']) == (key['fingerprint']): log.debug('using key %r (%s)', k['name'], k['fingerprint']) @@ -136,24 +131,20 @@ def sign_message(self, buf): break else: raise KeyError('key not found') - - label = key['name'].decode('ascii') # label should be a string + label = key['name'].decode('ascii') # label should be a string log.debug('signing %d-byte blob with "%s" key', len(blob), label) try: signature = self.signer(blob=blob, identity=key['identity']) except IOError: return failure() log.debug('signature: %r', signature) - try: sig_bytes = key['verifier'](sig=signature, msg=blob) log.info('signature status: OK') except formats.ecdsa.BadSignatureError: log.exception('signature status: ERROR') raise ValueError('invalid ECDSA signature') - log.debug('signature size: %d bytes', len(sig_bytes)) - data = util.frame(util.frame(key['type']), util.frame(sig_bytes)) code = util.pack('B', msg_code('SSH2_AGENT_SIGN_RESPONSE')) return util.frame(code, data)