Handle Digital Bitbox errors
This commit is contained in:
parent
d279a7e21c
commit
7a8e38c78c
@ -15,7 +15,7 @@ import sys
|
||||
import time
|
||||
|
||||
from ..hwwclient import HardwareWalletClient
|
||||
from ..errors import BadArgumentError, NoPasswordError, UnavailableActionError, DeviceFailureError
|
||||
from ..errors import ActionCanceledError, BadArgumentError, DeviceFailureError, DeviceNotReadyError, NoPasswordError, UnavailableActionError, DeviceFailureError
|
||||
from ..serializations import CTransaction, PSBT, hash256, hash160, ser_sig_der, ser_sig_compact, ser_compact_size
|
||||
from ..base58 import get_xpub_fingerprint, decode, to_address, xpub_main_2_test, get_xpub_fingerprint_hex
|
||||
|
||||
@ -31,6 +31,90 @@ HWW_CMD = 0x80 + 0x40 + 0x01
|
||||
DBB_VENDOR_ID = 0x03eb
|
||||
DBB_DEVICE_ID = 0x2402
|
||||
|
||||
# Errors codes from the device
|
||||
bad_args = [
|
||||
102, # The password length must be at least " STRINGIFY(PASSWORD_LEN_MIN) " characters.
|
||||
103, # No input received.
|
||||
104, # Invalid command.
|
||||
105, # Only one command allowed at a time.
|
||||
109, # JSON parse error.
|
||||
204, # Invalid seed.
|
||||
253, # Incorrect serialized pubkey length. A 33-byte hexadecimal value (66 characters) is expected.
|
||||
254, # Incorrect serialized pubkey hash length. A 32-byte hexadecimal value (64 characters) is expected.
|
||||
256, # Failed to pair with second factor, because the previously received hash of the public key does not match the computed hash of the public key.
|
||||
300, # Incorrect pubkey length. A 33-byte hexadecimal value (66 characters) is expected.
|
||||
301, # Incorrect hash length. A 32-byte hexadecimal value (64 characters) is expected.
|
||||
304, # Incorrect TFA pin.
|
||||
411, # Filenames limited to alphanumeric values, hyphens, and underscores.
|
||||
412, # Please provide an encryption key.
|
||||
112, # Device password matches reset password. Disabling reset password.
|
||||
]
|
||||
|
||||
device_failures = [
|
||||
101, # Please set a password.
|
||||
107, # Output buffer overflow.
|
||||
200, # Seed creation requires an SD card for automatic encrypted backup of the seed.
|
||||
250, # Master key not present.
|
||||
251, # Could not generate key.
|
||||
252, # Could not generate ECDH secret.
|
||||
303, # Could not sign.
|
||||
400, # Please insert SD card.
|
||||
401, # Could not mount the SD card.
|
||||
402, # Could not open a file to write - it may already exist.
|
||||
403, # Could not open the directory.
|
||||
405, # Could not write the file.
|
||||
407, # Could not read the file.
|
||||
408, # May not have erased all files (or no file present).
|
||||
410, # Backup file does not match wallet.
|
||||
500, # Chip communication error.
|
||||
501, # Could not read flash.
|
||||
502, # Could not encrypt.
|
||||
110, # Too many failed access attempts. Device reset.
|
||||
111, # Device locked. Erase device to access this command.
|
||||
113, # Due to many login attempts, the next login requires holding the touch button for 3 seconds.
|
||||
900, # attempts remain before the device is reset.
|
||||
901, # Ignored for non-embedded testing.
|
||||
902, # Too many backup files to read. The list is truncated.
|
||||
903, # attempts remain before the device is reset. The next login requires holding the touch button.
|
||||
]
|
||||
|
||||
cancels = [
|
||||
600, # Aborted by user.
|
||||
601, # Touchbutton timed out.
|
||||
]
|
||||
|
||||
ERR_MEM_SETUP = 503 # Device initialization in progress.
|
||||
|
||||
class DBBError(Exception):
|
||||
def __init__(self, error):
|
||||
Exception.__init__(self)
|
||||
self.error = error
|
||||
|
||||
def get_error(self):
|
||||
return self.error['error']['message']
|
||||
|
||||
def get_code(self):
|
||||
return self.error['error']['code']
|
||||
|
||||
def __str__(self):
|
||||
return 'Error: {}, Code: {}'.format(self.error['error']['message'], self.error['error']['code'])
|
||||
|
||||
def digitalbitbox_exception(f):
|
||||
def func(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except DBBError as e:
|
||||
if e.get_code() in bad_args:
|
||||
raise BadArgumentError(e.get_error())
|
||||
elif e.get_code() in device_failures:
|
||||
raise DeviceFailureError(e.get_error())
|
||||
elif e.get_code() in cancels:
|
||||
raise ActionCanceledError(e.get_error())
|
||||
elif e.get_code() == ERR_MEM_SETUP:
|
||||
raise DeviceNotReadyError(e.get_error())
|
||||
|
||||
return func
|
||||
|
||||
def aes_encrypt_with_iv(key, iv, data):
|
||||
aes_cbc = pyaes.AESModeOfOperationCBC(key, iv=iv)
|
||||
aes = pyaes.Encrypter(aes_cbc)
|
||||
@ -221,12 +305,13 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
|
||||
# Must return a dict with the xpub
|
||||
# Retrieves the public key at the specified BIP 32 derivation path
|
||||
@digitalbitbox_exception
|
||||
def get_pubkey_at_path(self, path):
|
||||
if '\'' not in path and 'h' not in path and 'H' not in path:
|
||||
raise BadArgumentError('The digital bitbox requires one part of the derivation path to be derived using hardened keys')
|
||||
reply = send_encrypt('{"xpub":"' + path + '"}', self.password, self.device)
|
||||
if 'error' in reply:
|
||||
return reply
|
||||
raise DBBError(reply)
|
||||
|
||||
if self.is_testnet:
|
||||
return {'xpub':xpub_main_2_test(reply['xpub'])}
|
||||
@ -235,6 +320,7 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
|
||||
# Must return a hex string with the signed transaction
|
||||
# The tx must be in the PSBT format
|
||||
@digitalbitbox_exception
|
||||
def sign_tx(self, tx):
|
||||
|
||||
# Create a transaction with all scriptsigs blanekd out
|
||||
@ -359,12 +445,12 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
reply = send_encrypt(to_send, self.password, self.device)
|
||||
logging.debug(reply)
|
||||
if 'error' in reply:
|
||||
return reply
|
||||
raise DBBError(reply)
|
||||
print("Touch the device for 3 seconds to sign. Touch briefly to cancel", file=sys.stderr)
|
||||
reply = send_encrypt(to_send, self.password, self.device)
|
||||
logging.debug(reply)
|
||||
if 'error' in reply:
|
||||
return reply
|
||||
raise DBBError(reply)
|
||||
|
||||
# Extract sigs
|
||||
sigs = []
|
||||
@ -384,6 +470,7 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
|
||||
# Must return a base64 encoded string with the signed message
|
||||
# The message can be any string
|
||||
@digitalbitbox_exception
|
||||
def sign_message(self, message, keypath):
|
||||
to_hash = b""
|
||||
to_hash += self.message_magic
|
||||
@ -401,12 +488,12 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
reply = send_encrypt(to_send, self.password, self.device)
|
||||
logging.debug(reply)
|
||||
if 'error' in reply:
|
||||
return reply
|
||||
raise DBBError(reply)
|
||||
print("Touch the device for 3 seconds to sign. Touch briefly to cancel", file=sys.stderr)
|
||||
reply = send_encrypt(to_send, self.password, self.device)
|
||||
logging.debug(reply)
|
||||
if 'error' in reply:
|
||||
return reply
|
||||
raise DBBError(reply)
|
||||
|
||||
sig = binascii.unhexlify(reply['sign'][0]['sig'])
|
||||
r = sig[0:32]
|
||||
@ -422,6 +509,7 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
raise UnavailableActionError('The Digital Bitbox does not have a screen to display addresses on')
|
||||
|
||||
# Setup a new device
|
||||
@digitalbitbox_exception
|
||||
def setup_device(self, label='', passphrase=''):
|
||||
# Make sure this is not initialized
|
||||
reply = send_encrypt('{"device" : "info"}', self.password, self.device)
|
||||
@ -446,6 +534,7 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
return {'success': True}
|
||||
|
||||
# Wipe this device
|
||||
@digitalbitbox_exception
|
||||
def wipe_device(self):
|
||||
reply = send_encrypt('{"reset" : "__ERASE__"}', self.password, self.device)
|
||||
if 'error' in reply:
|
||||
@ -457,6 +546,7 @@ class DigitalbitboxClient(HardwareWalletClient):
|
||||
raise UnavailableActionError('The Digital Bitbox does not support restoring via software')
|
||||
|
||||
# Begin backup process
|
||||
@digitalbitbox_exception
|
||||
def backup_device(self, label='', passphrase=''):
|
||||
key = stretch_backup_key(passphrase)
|
||||
backup_filename = format_backup_filename(label)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user