URI: 
       tMerge pull request #4895 from benma/bitbox - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 5ae9365f7775fe6f235b6c2931abb1839c08c18a
   DIR parent 059beab700fcc4fe5cb979bae8c975ff6968554d
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue,  4 Dec 2018 11:19:00 +0100
       
       Merge pull request #4895 from benma/bitbox
       
       plugins/digitalbitbox: compatibility with firmware v5.0.0
       Diffstat:
         M electrum/plugins/digitalbitbox/dig… |      36 +++++++++++++++++++++++++------
       
       1 file changed, 29 insertions(+), 7 deletions(-)
       ---
   DIR diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py
       t@@ -4,7 +4,7 @@
        #
        
        try:
       -    from electrum.crypto import sha256d, EncodeAES_base64, DecodeAES_base64
       +    from electrum.crypto import sha256d, EncodeAES_base64, EncodeAES_bytes, DecodeAES_bytes, hmac_oneshot
            from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh,
                                          is_address)
            from electrum.bip32 import serialize_xpub, deserialize_xpub
       t@@ -30,6 +30,8 @@ try:
            import base64
            import os
            import sys
       +    import re
       +    import hmac
            DIGIBOX = True
        except ImportError as e:
            DIGIBOX = False
       t@@ -43,6 +45,14 @@ except ImportError as e:
        def to_hexstr(s):
            return binascii.hexlify(s).decode('ascii')
        
       +
       +def derive_keys(x):
       +    h = sha256d(x)
       +    h = hashlib.sha512(h).digest()
       +    return (h[:32],h[32:])
       +
       +MIN_MAJOR_VERSION = 5
       +
        class DigitalBitbox_Client():
        
            def __init__(self, plugin, hidDevice):
       t@@ -110,7 +120,6 @@ class DigitalBitbox_Client():
                else:
                    raise Exception('no reply')
        
       -
            def dbb_has_password(self):
                reply = self.hid_send_plain(b'{"ping":""}')
                if 'ping' not in reply:
       t@@ -121,7 +130,6 @@ class DigitalBitbox_Client():
        
        
            def stretch_key(self, key):
       -        import hmac
                return to_hexstr(hashlib.pbkdf2_hmac('sha512', key.encode('utf-8'), b'Digital Bitbox', iterations = 20480))
        
        
       t@@ -158,6 +166,12 @@ class DigitalBitbox_Client():
        
        
            def check_device_dialog(self):
       +        match = re.search(r'v([0-9])+\.[0-9]+\.[0-9]+', self.dbb_hid.get_serial_number_string())
       +        if match is None:
       +            raise Exception("error detecting firmware version")
       +        major_version = int(match.group(1))
       +        if major_version < MIN_MAJOR_VERSION:
       +            raise Exception("Please upgrade to the newest firmware using the BitBox Desktop app: https://shiftcrypto.ch/start")
                # Set password if fresh device
                if self.password is None and not self.dbb_has_password():
                    if not self.setupRunning:
       t@@ -393,13 +407,21 @@ class DigitalBitbox_Client():
        
        
            def hid_send_encrypt(self, msg):
       +        sha256_byte_len = 32
                reply = ""
                try:
       -            secret = sha256d(self.password)
       -            msg = EncodeAES_base64(secret, msg)
       -            reply = self.hid_send_plain(msg)
       +            encryption_key, authentication_key = derive_keys(self.password)
       +            msg = EncodeAES_bytes(encryption_key, msg)
       +            hmac_digest = hmac_oneshot(authentication_key, msg, hashlib.sha256)
       +            authenticated_msg = base64.b64encode(msg + hmac_digest)
       +            reply = self.hid_send_plain(authenticated_msg)
                    if 'ciphertext' in reply:
       -                reply = DecodeAES_base64(secret, ''.join(reply["ciphertext"]))
       +                b64_unencoded = bytes(base64.b64decode(''.join(reply["ciphertext"])))
       +                reply_hmac = b64_unencoded[-sha256_byte_len:]
       +                hmac_calculated = hmac_oneshot(authentication_key, b64_unencoded[:-sha256_byte_len], hashlib.sha256)
       +                if not hmac.compare_digest(reply_hmac, hmac_calculated):
       +                    raise Exception("Failed to validate HMAC")
       +                reply = DecodeAES_bytes(encryption_key, b64_unencoded[:-sha256_byte_len])
                        reply = to_string(reply, 'utf8')
                        reply = json.loads(reply)
                    if 'error' in reply: