add script file for easier wallet access

This commit is contained in:
jonesmarvin8
2026-04-21 17:42:27 -04:00
parent eb9df0c224
commit d88d297574
4 changed files with 21 additions and 45 deletions
+189
View File
@@ -0,0 +1,189 @@
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'
DEFAULT_PAIRING_PASSWORD = "KeycardDefaultPairing"
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:
# TODO: need to be able to initialize a card.
return False
if self.pairing_index is None:
pairing_index, pairing_key = self.card.pair(password) #Testing
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
"""
# Needs to be more robust to handle card removal and reinsertion
def is_selected_card_available(self) -> bool:
if self.transport.connection is None:
return False
try:
#TODO: fix this up Try a lightweight operation
# Card is present
self.card.send_apdu(cla=0x00, ins=0xA4, p1=0x04, p2=0x00, data=b'')
# return True
except Exception:
return False
# TODO: attempt to prevent a new card from being inserted
return self.card.is_selected
"""
# Wrapped
def disconnect(self) -> bool:
try:
self.card.unpair(self.pairing_index)
self.pairing_index = None
self.pairing_key = None
return True
except Exception as e:
print(f"Error during disconnect: {e}")
return False
# TODO: add path?
# Wrapped
def get_public_signing_key(self):
uncompressed_pub_key = self.card.export_current_key(public_only=True).public_key
# Convert to VerifyingKey object
vk = VerifyingKey.from_string(uncompressed_pub_key, curve=SECP256k1)
return vk.to_string("compressed")[1:]
"""
# TODO: don't think this possible; blocked by firmware
def get_private_signing_key(self):
try:
exported = self.card.export_current_key(public_only=False)
print(f"Exported key: {exported}")
print(f"Public key: {exported.public_key.hex() if exported.public_key else 'None'}")
print(f"Private key: {exported.private_key.hex() if exported.private_key else 'None'}")
print(f"Chain code: {exported.chain_code.hex() if exported.chain_code else 'None'}")
if exported.private_key is None:
raise ValueError("No private key returned - key may not be loaded on card")
return exported.private_key
except Exception as e:
print(f"Error exporting key: {e}")
raise
"""
# TODO: delete this function
def debug_key_export(self):
"""Debug why key export fails with SW=6985"""
# 1. Check if a key exists
try:
status = self.card.status
print(f"Status: {status}")
except Exception as e:
print(f"Cannot get status: {e}")
# 2. Try public key export first
try:
exported = self.card.export_current_key(public_only=True)
print(f"Public key export: {exported.public_key.hex() if exported.public_key else 'None'}")
except Exception as e:
print(f"Public key export failed: {e}")
# 3. Check if key needs to be generated
try:
key_uid = self. card.generate_key()
print(f"Generated key UID: {key_uid.hex()}")
except Exception as e:
print(f"Key generation failed: {e}")
# 4. Try private export again
try:
exported = self.card.export_current_key(public_only=False)
if exported.private_key:
print(f"Private key: {exported.private_key.hex()}")
else:
print("Private key is None - key may not allow export")
except Exception as e:
print(f"Private key export failed: {e}")
#TODO: check well formed?
# Wrapped
def change_path(self, path):
self.card.derive_key(path)
# Message must be 32 bytes
# TODO: rename to current_path
# Wrapped
def sign_message_current_key(self, message = b"TestMessageMustBe32Bytes!\x00\x00\x00\x00\x00\x00\x00"):
# Message must be sent bytes
return self.card.sign(message, constants.SigningAlgorithm.SCHNORR_BIP340)
# Does not update the path
# Wrapped
def sign_message_with_path(self, path, message = b"TestMessageMustBe32Bytes!\x00\x00\x00\x00\x00\x00\x00"):
# must be sent bytes
return self.card.sign_with_path(message, path, False, constants.SigningAlgorithm.SCHNORR_BIP340)
# Wrapped
def remove_account_keys(self):
self.card.remove_key()
# TODO: update to accept a different language?
def load_account_keys(self, mnemonic) :
mnemo = Mnemonic("english")
seed = mnemo.to_seed(mnemonic, passphrase="")
# Load the seed onto the card
result = self.card.load_key(
key_type= constants.LoadKeyType.BIP39_SEED,
lee_seed=seed
)