From cc3a6cae2d7bbf40634afce7d164d2fc61cecff2 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Sat, 29 Dec 2018 00:10:50 -0500 Subject: [PATCH] Implement setup, wipe, restore. and backup for Digital Bitbox The Digital BitBox can be setup, wiped, and backed up via software. However it cannot be restored via software, so raise an error for that --- hwilib/devices/digitalbitbox.py | 47 +++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/hwilib/devices/digitalbitbox.py b/hwilib/devices/digitalbitbox.py index e488b67..e2c4f22 100644 --- a/hwilib/devices/digitalbitbox.py +++ b/hwilib/devices/digitalbitbox.py @@ -9,8 +9,9 @@ import hashlib import os import binascii import logging +import time -from ..hwwclient import HardwareWalletClient, NoPasswordError +from ..hwwclient import HardwareWalletClient, NoPasswordError, UnavailableActionError 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 @@ -141,6 +142,13 @@ def send_encrypt(msg, password, device): reply = {'error':'Exception caught while sending encrypted message to DigitalBitbox ' + str(e)} return reply +def stretch_backup_key(password): + key = hashlib.pbkdf2_hmac('sha512', password.encode(), b'Digital Bitbox', 20480) + return binascii.hexlify(key).decode() + +def format_backup_filename(name): + return '{}-{}.pdf'.format(name, time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime())) + # This class extends the HardwareWalletClient for Digital Bitbox specific things class DigitalbitboxClient(HardwareWalletClient): @@ -356,19 +364,48 @@ class DigitalbitboxClient(HardwareWalletClient): # Setup a new device def setup_device(self, label='', passphrase=''): - raise NotImplementedError('The DigitalBitbox does not currently implement setup') + # Make sure this is not initialized + reply = send_encrypt('{"device" : "info"}', self.password, self.device) + if 'error' not in reply or ('error' in reply and reply['error']['code'] != 101): + raise DeviceAlreadyInitError('Device is already initialized. Use wipe first and try again') + + # Need a wallet name and backup passphrase + if not label or not passphrase: + raise ValueError('THe label and backup passphrase for a new Digital Bitbox wallet must be specified and cannot be empty') + + # Set password + to_send = {'password': self.password} + reply = send_plain(json.dumps(to_send).encode(), self.device) + + # Now make the wallet + key = stretch_backup_key(passphrase) + backup_filename = format_backup_filename(label) + to_send = {'seed': {'source': 'create', 'key': key, 'filename': backup_filename}} + reply = send_encrypt(json.dumps(to_send).encode(), self.password, self.device) + if 'error' in reply: + return {'success': False, 'error': reply['error']['message']} + return {'success': True} # Wipe this device def wipe_device(self): - raise NotImplementedError('The DigitalBitbox does not currently implement wipe') + reply = send_encrypt('{"reset" : "__ERASE__"}', self.password, self.device) + if 'error' in reply: + return {'success': False, 'error': reply['error']['message']} + return {'success': True} # Restore device from mnemonic or xprv def restore_device(self, label=''): - raise NotImplementedError('The Digital Bitbox does not implement device restoring') + raise UnavailableActionError('The Digital Bitbox does not support restoring via software') # Begin backup process def backup_device(self, label='', passphrase=''): - raise NotImplementedError('The Digital BitBox does not implement this method') + key = stretch_backup_key(passphrase) + backup_filename = format_backup_filename(label) + to_send = {'backup': {'source': 'HWW', 'key': key, 'filename': backup_filename}} + reply = send_encrypt(json.dumps(to_send).encode(), self.password, self.device) + if 'error' in reply: + return {'success': False, 'error': reply['error']['message']} + return {'success': True} # Close the device def close(self):