"""APDU constructors for PersonalizationApplet. Mirrors the INS table in applet/INSTALL.md. See AliroAids.PROVISIONING for the AID and PersonalizationApplet.java for the INS handlers. """ CLA_PROPRIETARY = 0x80 INS_SET_CRED_PRIV = 0x20 INS_SET_CRED_PUBK = 0x21 INS_SET_READER_PUBK = 0x22 INS_WRITE_ACCESS_DOC = 0x23 INS_FINALIZE_ACCESS_DOC = 0x24 INS_SET_CRED_ISSUER_PUBK = 0x25 INS_COMMIT = 0x2C # Provisioning AID — must match AliroAids.PROVISIONING in the applet. # (CSA RID + ACCE 55 99 01 — DT-internal, namespaced under the CSA RID so # all three applets fit in one CAP.) PROVISIONING_AID = bytes.fromhex("A000000909ACCE559901") CRED_PRIV_LEN = 32 CRED_PUBK_LEN = 64 READER_PUBK_LEN = 64 CRED_ISSUER_PUBK_LEN = 64 def select_provisioning_apdu() -> bytes: """ISO 7816-4 SELECT by name: 00 A4 04 00 Lc .""" return bytes([0x00, 0xA4, 0x04, 0x00, len(PROVISIONING_AID)]) + PROVISIONING_AID def set_credential_priv_apdu(priv: bytes) -> bytes: if len(priv) != CRED_PRIV_LEN: raise ValueError(f"credential_PrivK must be {CRED_PRIV_LEN}B, got {len(priv)}") return _short_apdu(INS_SET_CRED_PRIV, 0, 0, priv) def set_credential_pubk_apdu(pubk_xy: bytes) -> bytes: if len(pubk_xy) != CRED_PUBK_LEN: raise ValueError(f"credential_PubK must be {CRED_PUBK_LEN}B (x||y), got {len(pubk_xy)}") return _short_apdu(INS_SET_CRED_PUBK, 0, 0, pubk_xy) def set_reader_pubk_apdu(pubk_xy: bytes) -> bytes: if len(pubk_xy) != READER_PUBK_LEN: raise ValueError(f"reader_PubK must be {READER_PUBK_LEN}B (x||y), got {len(pubk_xy)}") return _short_apdu(INS_SET_READER_PUBK, 0, 0, pubk_xy) def set_credential_issuer_pubk_apdu(pubk_xy: bytes) -> bytes: """Push the Credential Issuer public key (P-256 x||y, no 0x04 prefix). The applet stages it for IssuerAuth COSE_Sign1 verify at FINALIZE_AD.""" if len(pubk_xy) != CRED_ISSUER_PUBK_LEN: raise ValueError( f"credential_issuer_PubK must be {CRED_ISSUER_PUBK_LEN}B (x||y), got {len(pubk_xy)}" ) return _short_apdu(INS_SET_CRED_ISSUER_PUBK, 0, 0, pubk_xy) def commit_apdu() -> bytes: return bytes([CLA_PROPRIETARY, INS_COMMIT, 0x00, 0x00]) def _short_apdu(ins: int, p1: int, p2: int, data: bytes) -> bytes: """Short-form APDU: CLA INS P1 P2 Lc data.""" if not data: return bytes([CLA_PROPRIETARY, ins, p1, p2]) if len(data) > 255: raise ValueError(f"data too long for short-form APDU: {len(data)}B") return bytes([CLA_PROPRIETARY, ins, p1, p2, len(data)]) + data