Browse Source

jade: update Jade api to 1.0.31

This extends the serial api to recognise recently supported hardware.
master
Jamie C. Driver 1 year ago
parent
commit
11f5ba31ce
No known key found for this signature in database
GPG Key ID: FBB9327247889D30
  1. 12
      electrum/plugins/jade/jade.py
  2. 5
      electrum/plugins/jade/jadepy/README.md
  3. 2
      electrum/plugins/jade/jadepy/__init__.py
  4. 118
      electrum/plugins/jade/jadepy/jade.py
  5. 6
      electrum/plugins/jade/jadepy/jade_serial.py

12
electrum/plugins/jade/jade.py

@ -26,14 +26,15 @@ _logger = get_logger(__name__)
#import logging #import logging
#LOGGING = logging.INFO #LOGGING = logging.INFO
#if LOGGING: #if LOGGING:
# logger = logging.getLogger('jade') # logger = logging.getLogger('electrum.plugins.jade.jadepy.jade')
# logger.setLevel(LOGGING) # logger.setLevel(LOGGING)
# device_logger = logging.getLogger('jade-device') # device_logger = logging.getLogger('electrum.plugins.jade.jadepy.jade-device')
# device_logger.setLevel(LOGGING) # device_logger.setLevel(LOGGING)
try: try:
# Do imports # Do imports
from .jadepy.jade import JadeAPI from .jadepy.jade import JadeAPI
from .jadepy.jade_serial import JadeSerialImpl
from serial.tools import list_ports from serial.tools import list_ports
except ImportError as e: except ImportError as e:
_logger.exception('error importing Jade plugin deps') _logger.exception('error importing Jade plugin deps')
@ -353,12 +354,9 @@ class Jade_KeyStore(Hardware_KeyStore):
class JadePlugin(HW_PluginBase): class JadePlugin(HW_PluginBase):
keystore_class = Jade_KeyStore keystore_class = Jade_KeyStore
minimum_library = (0, 0, 1) minimum_library = (0, 0, 1)
DEVICE_IDS = [(0x10c4, 0xea60), # Development Jade device DEVICE_IDS = JadeSerialImpl.JADE_DEVICE_IDS
(0x1a86, 0x55d4), # Retail Blockstream Jade (And some DIY devices)
(0x0403, 0x6001), # DIY FTDI Based Devices (Eg: M5StickC-Plus)
(0x1a86, 0x7523)] # DIY CH340 Based devices (Eg: ESP32-Wrover)
SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh') SUPPORTED_XTYPES = ('standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh')
MIN_SUPPORTED_FW_VERSION = (0, 1, 32) MIN_SUPPORTED_FW_VERSION = (0, 1, 47)
# For testing with qemu simulator (experimental) # For testing with qemu simulator (experimental)
SIMULATOR_PATH = None # 'tcp:127.0.0.1:2222' SIMULATOR_PATH = None # 'tcp:127.0.0.1:2222'

5
electrum/plugins/jade/jadepy/README.md

@ -2,10 +2,9 @@
This is a slightly modified version of the official [Jade](https://github.com/Blockstream/Jade) python library. This is a slightly modified version of the official [Jade](https://github.com/Blockstream/Jade) python library.
This modified version was made from tag [1.0.29](https://github.com/Blockstream/Jade/releases/tag/1.0.29). This modified version was made from tag [1.0.31](https://github.com/Blockstream/Jade/releases/tag/1.0.31).
Intention is to fold these modifications back into Jade repo, for future api release.
## Changes ## Changes
- Removed BLE module, reducing transitive dependencies - Removed BLE module, reducing transitive dependencies
- _http_request() function removed, so cannot be used as unintentional fallback - _http_request() function removed, so cannot be used as unintentional fallback

2
electrum/plugins/jade/jadepy/__init__.py

@ -1,4 +1,4 @@
from .jade import JadeAPI from .jade import JadeAPI
from .jade_error import JadeError from .jade_error import JadeError
__version__ = "0.2.0" __version__ = "1.0.31"

118
electrum/plugins/jade/jadepy/jade.py

@ -7,6 +7,7 @@ import collections
import collections.abc import collections.abc
import traceback import traceback
import random import random
import socket
import sys import sys
# JadeError # JadeError
@ -24,7 +25,7 @@ device_logger = logging.getLogger(f'{__name__}-device')
# It relies on the BLE dependencies being available # It relies on the BLE dependencies being available
try: try:
from .jade_ble import JadeBleImpl from .jade_ble import JadeBleImpl
except ImportError as e: except (ImportError, FileNotFoundError) as e:
logger.warning(e) logger.warning(e)
logger.warning('BLE scanning/connectivity will not be available') logger.warning('BLE scanning/connectivity will not be available')
@ -123,6 +124,32 @@ def _hexlify(data):
# logger.info(e) # logger.info(e)
# logger.info('Default _http_requests() function will not be available') # logger.info('Default _http_requests() function will not be available')
def generate_dump():
while True:
try:
with socket.create_connection(("localhost", 4444)) as s:
output = b""
while b"Open On-Chip Debugger" not in output:
data = s.recv(1024)
if not data:
continue
output += data
s.sendall(b"esp gcov dump\n")
output = b""
while b"Targets disconnected." not in output:
data = s.recv(1024)
if not data:
continue
output += data
s.sendall(b"resume\n")
time.sleep(1)
return
except ConnectionRefusedError:
pass
class JadeAPI: class JadeAPI:
""" """
High-Level Jade Client API High-Level Jade Client API
@ -431,7 +458,8 @@ class JadeAPI:
""" """
return self._jadeRpc('logout') return self._jadeRpc('logout')
def ota_update(self, fwcmp, fwlen, chunksize, fwhash=None, patchlen=None, cb=None): def ota_update(self, fwcmp, fwlen, chunksize, fwhash=None, patchlen=None, cb=None,
gcov_dump=False):
""" """
RPC call to attempt to update the unit's firmware. RPC call to attempt to update the unit's firmware.
@ -507,6 +535,9 @@ class JadeAPI:
if (cb): if (cb):
cb(written, cmplen) cb(written, cmplen)
if gcov_dump:
self.run_remote_gcov_dump()
# All binary data uploaded # All binary data uploaded
return self._jadeRpc('ota_complete') return self._jadeRpc('ota_complete')
@ -523,6 +554,22 @@ class JadeAPI:
""" """
return self._jadeRpc('debug_selfcheck', long_timeout=True) return self._jadeRpc('debug_selfcheck', long_timeout=True)
def run_remote_gcov_dump(self):
"""
RPC call to run in-built gcov-dump.
NOTE: Only available in a DEBUG build of the firmware.
Returns
-------
bool
Always True.
"""
result = self._jadeRpc('debug_gcov_dump', long_timeout=True)
time.sleep(0.5)
generate_dump()
time.sleep(2)
return result
def capture_image_data(self, check_qr=False): def capture_image_data(self, check_qr=False):
""" """
RPC call to capture raw image data from the camera. RPC call to capture raw image data from the camera.
@ -951,6 +998,42 @@ class JadeAPI:
params = {'multisig_file': multisig_file} params = {'multisig_file': multisig_file}
return self._jadeRpc('register_multisig', params) return self._jadeRpc('register_multisig', params)
def get_registered_descriptors(self):
"""
RPC call to fetch brief summaries of any descriptor wallets registered to this signer.
Returns
-------
dict
Brief description of registered descriptor, keyed by registration name.
Each entry contains keys:
descriptor_len - int, length of descriptor output script
num_datavalues - int, total number of substitution placeholders passed with script
master_blinding_key - 32-bytes, any liquid master blinding key for this wallet
"""
return self._jadeRpc('get_registered_descriptors')
def get_registered_descriptor(self, descriptor_name):
"""
RPC call to fetch details of a named descriptor wallet registered to this signer.
Parameters
----------
descriptor_name : string
Name of descriptor registration record to return.
Returns
-------
dict
Description of registered descriptor wallet identified by registration name.
Contains keys:
descriptor_name - str, name of descritpor registration
descriptor - str, descriptor output script, may contain substitution placeholders
datavalues - dict containing placeholders for substitution into script
"""
params = {'descriptor_name': descriptor_name}
return self._jadeRpc('get_registered_descriptor', params)
def register_descriptor(self, network, descriptor_name, descriptor_script, datavalues=None): def register_descriptor(self, network, descriptor_name, descriptor_script, datavalues=None):
""" """
RPC call to register a new descriptor wallet, which must contain the hw signer. RPC call to register a new descriptor wallet, which must contain the hw signer.
@ -959,7 +1042,7 @@ class JadeAPI:
Parameters Parameters
---------- ----------
network : string network : string
Network to which the multisig should apply - eg. 'mainnet', 'liquid', 'testnet', etc. Network to which the descriptor should apply - eg. 'mainnet', 'liquid', 'testnet', etc.
descriptor_name : string descriptor_name : string
Name to use to identify this descriptor wallet registration record. Name to use to identify this descriptor wallet registration record.
@ -1221,6 +1304,35 @@ class JadeAPI:
params = {'identity': identity, 'curve': curve, 'index': index, 'challenge': challenge} params = {'identity': identity, 'curve': curve, 'index': index, 'challenge': challenge}
return self._jadeRpc('sign_identity', params) return self._jadeRpc('sign_identity', params)
def sign_attestation(self, challenge):
"""
RPC call to sign passed challenge with embedded hw RSA-4096 key, such that the caller
can check the authenticity of the hardware unit. eg. whether it is a genuine
Blockstream production Jade unit.
Caller must have the public key of the external verifying authority they wish to validate
against (eg. Blockstream's Jade verification public key).
NOTE: only supported by ESP32S3-based hardware units.
Parameters
----------
challenge : bytes
Challenge bytes to sign
Returns
-------
dict
Contains keys:
signature - 512-bytes, hardware RSA signature of the SHA256 hash of the passed
challenge bytes.
pubkey_pem - str, PEM export of RSA pubkey of the hardware unit, to verify the returned
RSA signature.
ext_signature - bytes, RSA signature of the verifying authority over the returned
pubkey_pem data.
(Caller can verify this signature with the public key of the verifying authority.)
"""
params = {'challenge': challenge}
return self._jadeRpc('sign_attestation', params)
def get_master_blinding_key(self, only_if_silent=False): def get_master_blinding_key(self, only_if_silent=False):
""" """
RPC call to fetch the master (SLIP-077) blinding key for the hw signer. RPC call to fetch the master (SLIP-077) blinding key for the hw signer.

6
electrum/plugins/jade/jadepy/jade_serial.py

@ -2,6 +2,7 @@ import serial
import logging import logging
from serial.tools import list_ports from serial.tools import list_ports
from .jade_error import JadeError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -53,7 +54,10 @@ class JadeSerialImpl:
assert self.ser is not None assert self.ser is not None
if not self.ser.is_open: if not self.ser.is_open:
self.ser.open() try:
self.ser.open()
except serial.serialutil.SerialException:
raise JadeError(1, "Unable to open port", self.device)
# Ensure RTS and DTR are not set (as this can cause the hw to reboot) # Ensure RTS and DTR are not set (as this can cause the hw to reboot)
self.ser.setRTS(False) self.ser.setRTS(False)

Loading…
Cancel
Save