python comments

This commit is contained in:
jonesmarvin8 2026-05-14 12:57:36 -04:00
parent aa8331df6c
commit 524a06099c
7 changed files with 134 additions and 9 deletions

View File

@ -12,12 +12,12 @@ Installation:
1. Install math applet on your keycard; this process only needs to be done once. In the root of repo:
```
cd python/keycard_applets
cd keycard_wallet/keycard_applets
java -jar gp.jar --key c212e073ff8b4bbfaff4de8ab655221f --load math.cap
```
2. Install `keycard-desktop` from [github](https://github.com/choppu/keycard-desktop)
- Keycard Desktop is used to install the LEE key protocol to a blank keycard.
- Select (Re)Install Applet and upload the key binary (`python/keycard_applets/LEE_keycard.cap`).
- Select (Re)Install Applet and upload the key binary (`keycard_wallet/keycard_applets/LEE_keycard.cap`).
![keycard-desktop.png](keycard-desktop.png)
## Wallet with Keycard
@ -25,13 +25,13 @@ Keycard functionality is available to Wallet CLI by setting up the following Pyt
```bash
# Install appropriate version of `keycard-py`.
git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git python/keycard-py
git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git keycard_wallet/python/keycard-py
# Set up virtual environment.
python3 -m venv venv
source venv/bin/activate
pip install pyscard mnemonic ecdsa pyaes
pip install -e python/keycard-py
pip install -e keycard_wallet/python/keycard-py
```
**Important**: Keycard wallet commands only work within the virtual environment.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,122 @@
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
DEFAULT_PAIRING_PASSWORD = "KeycardDefaultPairing"
class KeycardWallet:
def __init__(self):
self.card = KeyCard()
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
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
def setup_communication(self, pin: str, password = DEFAULT_PAIRING_PASSWORD) -> bool:
self.card.select()
if not self.card.is_initialized:
raise RuntimeError(f"Error setting up communication: uninitialized keycard")
pairing_index, pairing_key = self.card.pair(password)
self.pairing_index = pairing_index
try:
self.card.open_secure_channel(pairing_index, pairing_key)
self.card.verify_pin(pin)
except Exception as e:
try:
self.card.unpair(pairing_index)
except Exception:
pass
raise RuntimeError(f"Error setting up communication: {e}") from e
return True
def load_mnemonic(self, mnemonic: str) -> bool:
try:
# Convert mnemonic to seed
mnemo = Mnemonic("english")
seed = mnemo.to_seed(mnemonic)
# 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:
raise RuntimeError(f"Error loading mnemonic: {e}") from e
def disconnect(self) -> bool:
try:
if not self.card.is_secure_channel_open:
return False
self.card.unpair(self.pairing_index)
return True
except Exception as e:
raise RuntimeError(f"Error during disconnect: {e}") from e
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:
raise RuntimeError(f"Error getting public key: {e}") from e
def sign_message_for_path(self, message: bytes, 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:
raise RuntimeError(f"Error signing message: {e}") from e

View File

@ -12,8 +12,11 @@ pub fn add_python_path(py: Python<'_>) -> PyResult<()> {
.unwrap_or_else(|| current_dir.clone());
let mut paths_to_add: Vec<PathBuf> = vec![
python_base.join("python"),
python_base.join("python").join("keycard-py"),
python_base.join("keycard_wallet").join("python"),
python_base
.join("keycard_wallet")
.join("python")
.join("keycard-py"),
];
// If a virtualenv is active, add its site-packages so that dependencies

View File

@ -236,7 +236,7 @@ impl WalletSubcommand for AccountSubcommand {
let account_id: nssa::AccountId = account_id_str.parse()?;
// Add account id to the display for keycard users.
log::info!("Account Id: {resolved}");
println!("Account Id: {resolved}");
if let Some(label) = wallet_core.storage.labels.get(&account_id_str) {
println!("Label: {label}");

View File

@ -1,10 +1,10 @@
cargo install --path wallet --force
# Install appropriate version of `keycard-py`.
git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git python/keycard-py
git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git keycard_wallet/python/keycard-py
# Set up virtual environment.
python3 -m venv venv
source venv/bin/activate
pip install pyscard mnemonic ecdsa pyaes
pip install -e python/keycard-py
pip install -e keycard_wallet/python/keycard-py