mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-14 03:59:30 +00:00
137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
from smartcard.System import readers
|
|
from keycard.exceptions import APDUError, TransportError
|
|
from ecdsa import VerifyingKey, SECP256k1
|
|
|
|
from keycard.keycard import KeyCard
|
|
|
|
from mnemonic import Mnemonic
|
|
from keycard import constants
|
|
|
|
import keycard
|
|
|
|
PIN = '123456'
|
|
PUK = '123456123456'
|
|
DEFAULT_PAIRING_PASSWORD = "KeycardDefaultPairing"
|
|
DEFAULT_MNEMONIC = "fashion degree mountain wool question damp current pond grow dolphin chronic then"
|
|
DEFAULT_PASSPHRASE = ""
|
|
|
|
class KeycardWallet:
|
|
def __init__(self):
|
|
self.card = KeyCard()
|
|
self.pairing_index = None
|
|
self.pairing_key = None
|
|
|
|
def _is_smart_card_reader_detected(self) -> bool:
|
|
try:
|
|
return len(readers()) > 0
|
|
except Exception:
|
|
return False
|
|
|
|
def _is_keycard_detected(self) -> bool:
|
|
try:
|
|
KeyCard().select()
|
|
return True
|
|
except (TransportError, APDUError, Exception):
|
|
# No readers, no card, or card doesn't respond.
|
|
return False
|
|
|
|
# Wrapped
|
|
def is_unpaired_keycard_available(self) -> bool:
|
|
if not self._is_smart_card_reader_detected():
|
|
return False
|
|
elif not self._is_keycard_detected():
|
|
return False
|
|
return True
|
|
|
|
# Wrapped
|
|
def setup_communication(self, pin = PIN, password = DEFAULT_PAIRING_PASSWORD) -> bool:
|
|
try:
|
|
self.card.select()
|
|
|
|
if not self.card.is_initialized:
|
|
return False
|
|
|
|
if self.pairing_index is None:
|
|
pairing_index, pairing_key = self.card.pair(password)
|
|
self.pairing_index = pairing_index
|
|
self.pairing_key = pairing_key
|
|
|
|
|
|
self.card.open_secure_channel(pairing_index, pairing_key)
|
|
self.card.verify_pin(pin)
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
return False
|
|
|
|
def load_mnemonic(self, mnemonic = DEFAULT_MNEMONIC, passphrase = DEFAULT_PASSPHRASE) -> bool:
|
|
try:
|
|
# Convert mnemonic to seed
|
|
mnemo = Mnemonic("english")
|
|
seed = mnemo.to_seed(mnemonic, passphrase)
|
|
|
|
# Load the LEE seed onto the card
|
|
result = self.card.load_key(
|
|
key_type = constants.LoadKeyType.LEE_SEED,
|
|
lee_seed = seed
|
|
)
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error during disconnect: {e}")
|
|
return False
|
|
|
|
def disconnect(self) -> bool:
|
|
try:
|
|
if not self.card.is_secure_channel_open:
|
|
return None
|
|
|
|
self.card.unpair(self.pairing_index)
|
|
self.pairing_index = None
|
|
self.pairing_key = None
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error during unpair: {e}")
|
|
return False
|
|
|
|
def get_public_key_for_path(self, path: str = "m/44'/60'/0'/0/0") -> bytes | None:
|
|
try:
|
|
if not self.card.is_secure_channel_open or not self.card.is_pin_verified:
|
|
return None
|
|
|
|
public_key = self.card.export_key(
|
|
derivation_option = constants.DerivationOption.DERIVE,
|
|
public_only = True,
|
|
keypath = path
|
|
)
|
|
|
|
public_key = public_key.public_key
|
|
public_key = VerifyingKey.from_string(public_key[1:], curve=SECP256k1)
|
|
public_key = public_key.to_string("compressed")[1:]
|
|
|
|
return public_key
|
|
|
|
except Exception as e:
|
|
print(f"Error getting public key: {e}")
|
|
return None
|
|
|
|
|
|
def sign_message_for_path(self, message: bytes = b"DefaultMessageTestDefaultMessage", path: str = "m/44'/60'/0'/0/0") -> bytes | None:
|
|
try:
|
|
if not self.card.is_secure_channel_open or not self.card.is_pin_verified:
|
|
return None
|
|
|
|
signature = self.card.sign_with_path(
|
|
digest = message,
|
|
path = path,
|
|
algorithm = constants.SigningAlgorithm.SCHNORR_BIP340,
|
|
make_current = False
|
|
)
|
|
|
|
return signature.signature
|
|
|
|
except Exception as e:
|
|
print(f"Error signing message: {e}")
|
|
return None |