Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hwilib/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
'digitalbitbox',
'coldcard',
'bitbox02',
'jade'
'jade',
'cypherock'
]
162 changes: 162 additions & 0 deletions hwilib/devices/cypherock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""
Cypherock X1
************
"""

from typing import (
Any,
Dict,
List,
Optional,
Union
)

from hwilib.descriptor import MultisigDescriptor
from hwilib.errors import DeviceConnectionError, UnavailableActionError
from hwilib.hwwclient import HardwareWalletClient
from hwilib.key import ExtendedKey
from hwilib.psbt import PSBT

from ..common import (
AddressType,
Chain,
)

import hid

CYPHEROCK_VENDOR_ID = 0x3503
CYPHEROCK_PRODUCT_ID = 0x0103

class CypherockClient(HardwareWalletClient):
"""
The `CypherockClient` is a `HardwareWalletClient` for interacting with Cypherock X1 devices.
"""

def __init__(self, path: str, password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN) -> None:
super(CypherockClient, self).__init__(path, password, expert, chain)

try:
self.device = hid.device()
self.device.open_path(path.encode())
except Exception as e:
raise DeviceConnectionError(f"Failed to connect to Cypherock X1: {e}")

def get_master_fingerprint(self) -> bytes:
return bytes.fromhex(self.device.get_serial_number_string())

def get_pubkey_at_path(self, path: str) -> ExtendedKey:
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")

def sign_tx(self, tx: PSBT) -> PSBT:
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")

def sign_message(self, message: Union[str, bytes], keypath: str) -> str:
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")

def display_singlesig_address(
self,
keypath: str,
addr_type: AddressType,
) -> str:
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")

def display_multisig_address(
self,
addr_type: AddressType,
multisig: MultisigDescriptor,
) -> str:
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")

def setup_device(self, label: str = "", passphrase: str = "") -> bool:
"""
Cypherock X1 does not support setup via software.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not support software setup')

def wipe_device(self) -> bool:
"""
Cypherock X1 does not support wiping via software.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not support wiping via software')

def restore_device(self, label: str = "", word_count: int = 24) -> bool:
"""
Cypherock X1 does not support restoring via software.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not support restoring via software')

def backup_device(self, label: str = "", passphrase: str = "") -> bool:
"""
Cypherock X1 does not support backing up via software.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not support creating a backup via software')

def close(self) -> None:
self.device.close()

def prompt_pin(self) -> bool:
"""
Cypherock X1 does not need a PIN sent from the host.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not need a PIN sent from the host')

def send_pin(self, pin: str) -> bool:
"""
Cypherock X1 does not need a PIN sent from the host.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not need a PIN sent from the host')

def toggle_passphrase(self) -> bool:
"""
Cypherock X1 does not support toggling passphrase from the host.

:raises UnavailableActionError: Always, this function is unavailable
"""
raise UnavailableActionError('The Cypherock X1 does not support toggling passphrase from the host')

def can_sign_taproot(self) -> bool:
"""
Cypherock X1 supports Taproot if the Firmware version is greater than or equal to 0.6.3089

:returns: True if Firmware version is greater than or equal to 0.6.3089, False otherwise.
"""
raise NotImplementedError("The CypherockClient class "
"has not yet implemented this method")


def enumerate(password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN, allow_emulators: bool = False) -> List[Dict[str, Any]]:
results = []
devices = []
devices.extend(hid.enumerate(CYPHEROCK_VENDOR_ID, CYPHEROCK_PRODUCT_ID))

for d in devices:
if d.get('path') is not None and d.get('serial_number') is not None:
d_data: Dict[str, Any] = {}
d_data['type'] = 'cypherock'
d_data['model'] = 'cypherock-x1'
d_data['label'] = None
d_data['needs_pin_sent'] = False
d_data['needs_passphrase_sent'] = False
d_data['path'] = d['path'].decode()
d_data['fingerprint'] = bytes.fromhex(d['serial_number']).hex() # Using the serial number as the fingerprint for now

results.append(d_data)

return results
Loading