""" Applet validation profiles for the j3r180 batch provisioning tool. Each profile defines how to validate a specific applet after installation. Profiles are matched to CAP files automatically via their applet AID. To add a new profile: 1. Subclass AppletProfile 2. Implement name and validate() 3. Register it in APPLET_PROFILES keyed by the applet AID (hex string) """ from abc import ABC, abstractmethod from smartcard.Exceptions import CardConnectionException, NoCardException from smartcard.util import toHexString class AppletProfile(ABC): """Base class for applet-specific validation profiles.""" @property @abstractmethod def name(self) -> str: """Human-readable name for logging/display.""" ... @abstractmethod def validate(self, connection) -> dict[str, str]: """ Validate the applet on a live pyscard connection. Must return a dict of named results: {"installed": "yes", "public_key": "04AB..."} -- success {"installed": "no", "error": "SELECT failed"} -- failure Must NOT raise exceptions. """ ... class DefaultProfile(AppletProfile): """Fallback profile for CAPs without a registered profile. SELECT-only check.""" def __init__(self, aid: list[int]): self._aid = aid self._name = f"Applet {bytes(aid).hex().upper()}" @property def name(self) -> str: return self._name def validate(self, connection) -> dict[str, str]: select_apdu = [0x00, 0xA4, 0x04, 0x00, len(self._aid)] + self._aid try: response, sw1, sw2 = connection.transmit(select_apdu) except (CardConnectionException, NoCardException): return {"installed": "no", "error": "Card communication error"} if (sw1, sw2) == (0x90, 0x00): return {"installed": "yes"} return {"installed": "no", "error": f"SELECT failed (SW={sw1:02X}{sw2:02X})"} class TeslaVCSEC(AppletProfile): """Profile for the Tesla VCSEC identity applet (TeslaIdent.cap).""" PACKAGE_AID = [0xF4, 0x65, 0x73, 0x6C, 0x61, 0x4C, 0x6F, 0x67, 0x69, 0x63] GET_PUBLIC_KEY_APDU = [0x80, 0x04, 0x00, 0x00, 0x00] @property def name(self) -> str: return "Tesla VCSEC" def validate(self, connection) -> dict[str, str]: select_apdu = [0x00, 0xA4, 0x04, 0x00, len(self.PACKAGE_AID)] + self.PACKAGE_AID try: response, sw1, sw2 = connection.transmit(select_apdu) except (CardConnectionException, NoCardException): return {"installed": "no", "error": "Card communication error during SELECT"} if (sw1, sw2) != (0x90, 0x00): return {"installed": "no", "error": f"SELECT failed (SW={sw1:02X}{sw2:02X})"} try: response, sw1, sw2 = connection.transmit(self.GET_PUBLIC_KEY_APDU) except (CardConnectionException, NoCardException): return {"installed": "yes", "error": "Card communication error reading public key"} if (sw1, sw2) != (0x90, 0x00): return {"installed": "yes", "error": f"GET PUBLIC KEY failed (SW={sw1:02X}{sw2:02X})"} pubkey_hex = toHexString(response).replace(" ", "") return {"installed": "yes", "public_key": pubkey_hex} # --------------------------------------------------------------------------- # Registry: applet AID (hex string) → profile instance # Keys are the APPLET-level AID (as returned by extract_applet_aids()), # not the package AID. # --------------------------------------------------------------------------- APPLET_PROFILES: dict[str, AppletProfile] = { "F465736C614C6F67696330303201": TeslaVCSEC(), } def get_profile(aid_bytes: list[int]) -> AppletProfile | None: """Look up a registered profile for the given applet AID.""" aid_hex = bytes(aid_bytes).hex().upper() return APPLET_PROFILES.get(aid_hex)