aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/sca/target
diff options
context:
space:
mode:
authorJ08nY2023-02-17 13:40:33 +0100
committerJ08nY2023-02-17 13:40:33 +0100
commit9efa088d462899c94afd06fbad25003c403a6cee (patch)
treebd279114ad01e4549fc9841f190090b63e03c3ce /pyecsca/sca/target
parentfb6f0a428dbbd1e53fce582b5e6c7af3b7316485 (diff)
downloadpyecsca-9efa088d462899c94afd06fbad25003c403a6cee.tar.gz
pyecsca-9efa088d462899c94afd06fbad25003c403a6cee.tar.zst
pyecsca-9efa088d462899c94afd06fbad25003c403a6cee.zip
Diffstat (limited to 'pyecsca/sca/target')
-rw-r--r--pyecsca/sca/target/ISO7816.py49
-rw-r--r--pyecsca/sca/target/PCSC.py16
-rw-r--r--pyecsca/sca/target/ectester.py171
-rw-r--r--pyecsca/sca/target/leia.py41
4 files changed, 176 insertions, 101 deletions
diff --git a/pyecsca/sca/target/ISO7816.py b/pyecsca/sca/target/ISO7816.py
index 4441f94..d0e2118 100644
--- a/pyecsca/sca/target/ISO7816.py
+++ b/pyecsca/sca/target/ISO7816.py
@@ -1,6 +1,7 @@
"""Provides classes for working with ISO7816-4 APDUs and an abstract base class for an ISO7816-4 based target."""
from abc import abstractmethod, ABC
from dataclasses import dataclass
+from enum import IntEnum
from typing import Optional
from public import public
@@ -9,6 +10,18 @@ from .base import Target
@public
+class CardConnectionException(Exception):
+ pass
+
+
+@public
+class CardProtocol(IntEnum):
+ """Card protocol to use/negotiate."""
+ T0 = 0
+ T1 = 1
+
+
+@public
@dataclass
class CommandAPDU: # pragma: no cover
"""Command APDU that can be sent to an ISO7816-4 target."""
@@ -45,35 +58,35 @@ class CommandAPDU: # pragma: no cover
if len(self.data) <= 255:
# Case 3s
return (
- bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
- + self.data
+ bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ + self.data
)
else:
# Case 3e
return (
- bytes([self.cls, self.ins, self.p1, self.p2, 0])
- + len(self.data).to_bytes(2, "big")
- + self.data
+ bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ + len(self.data).to_bytes(2, "big")
+ + self.data
)
else:
if len(self.data) <= 255 and self.ne <= 256:
# Case 4s
return (
- bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
- + self.data
- + bytes([self.ne if self.ne != 256 else 0])
+ bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ + self.data
+ + bytes([self.ne if self.ne != 256 else 0])
)
else:
# Case 4e
return (
- bytes([self.cls, self.ins, self.p1, self.p2, 0])
- + len(self.data).to_bytes(2, "big")
- + self.data
- + (
- self.ne.to_bytes(2, "big")
- if self.ne != 65536
- else bytes([0, 0])
- )
+ bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ + len(self.data).to_bytes(2, "big")
+ + self.data
+ + (
+ self.ne.to_bytes(2, "big")
+ if self.ne != 65536
+ else bytes([0, 0])
+ )
)
@@ -90,6 +103,10 @@ class ResponseAPDU:
class ISO7816Target(Target, ABC):
"""ISO7816-4 target."""
+ @abstractmethod
+ def connect(self, protocol: Optional[CardProtocol] = None):
+ raise NotImplementedError
+
@property
@abstractmethod
def atr(self) -> bytes:
diff --git a/pyecsca/sca/target/PCSC.py b/pyecsca/sca/target/PCSC.py
index f36843f..ace59cc 100644
--- a/pyecsca/sca/target/PCSC.py
+++ b/pyecsca/sca/target/PCSC.py
@@ -1,5 +1,5 @@
"""Provides a smartcard target communicating via PC/SC (Personal Computer/Smart Card)."""
-from typing import Union
+from typing import Union, Optional
from public import public
from smartcard.CardConnection import CardConnection
@@ -7,7 +7,7 @@ from smartcard.System import readers
from smartcard.pcsc.PCSCCardConnection import PCSCCardConnection
from smartcard.pcsc.PCSCReader import PCSCReader
-from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816
+from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816, CardProtocol, CardConnectionException
@public
@@ -27,8 +27,16 @@ class PCSCTarget(ISO7816Target): # pragma: no cover
self.reader = reader
self.connection: PCSCCardConnection = self.reader.createConnection()
- def connect(self):
- self.connection.connect(CardConnection.T0_protocol | CardConnection.T1_protocol)
+ def connect(self, protocol: Optional[CardProtocol] = None):
+ proto = CardConnection.T0_protocol | CardConnection.T1_protocol
+ if protocol == CardProtocol.T0:
+ proto = CardConnection.T0_protocol
+ elif protocol == CardProtocol.T1:
+ proto = CardConnection.T1_protocol
+ try:
+ self.connection.connect(proto)
+ except: # noqa
+ raise CardConnectionException()
@property
def atr(self) -> bytes:
diff --git a/pyecsca/sca/target/ectester.py b/pyecsca/sca/target/ectester.py
index a4a13c2..74c4a95 100644
--- a/pyecsca/sca/target/ectester.py
+++ b/pyecsca/sca/target/ectester.py
@@ -8,10 +8,9 @@ from operator import or_
from typing import Optional, Mapping, List, Union
from public import public
-from smartcard.CardConnection import CardConnection
-from smartcard.Exceptions import CardConnectionException
-from .ISO7816 import CommandAPDU, ResponseAPDU, ISO7816
+from .ISO7816 import CommandAPDU, ResponseAPDU, ISO7816, ISO7816Target, CardProtocol, CardConnectionException
+from .leia import LEIATarget
from .PCSC import PCSCTarget
from ...ec.model import ShortWeierstrassModel
from ...ec.params import DomainParameters
@@ -247,7 +246,7 @@ class Response(ABC): # pragma: no cover
offset = 0
for i in range(num_sw):
if len(resp.data) >= offset + 2:
- self.sws[i] = int.from_bytes(resp.data[offset : offset + 2], "big")
+ self.sws[i] = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
if self.sws[i] != ISO7816.SW_NO_ERROR:
self.success = False
@@ -264,13 +263,13 @@ class Response(ABC): # pragma: no cover
self.success = False
self.error = True
break
- param_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ param_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
if len(resp.data) < offset + param_len:
self.success = False
self.error = True
break
- self.params[i] = resp.data[offset : offset + param_len]
+ self.params[i] = resp.data[offset: offset + param_len]
offset += param_len
def __repr__(self):
@@ -342,11 +341,11 @@ class ExportResponse(Response): # pragma: no cover
parameters: ParameterEnum
def __init__(
- self,
- resp: ResponseAPDU,
- keypair: KeypairEnum,
- key: KeyEnum,
- params: ParameterEnum,
+ self,
+ resp: ResponseAPDU,
+ keypair: KeypairEnum,
+ key: KeyEnum,
+ params: ParameterEnum,
):
self.keypair = keypair
self.key = key
@@ -472,31 +471,31 @@ class InfoResponse(Response): # pragma: no cover
super().__init__(resp, 1, 0)
offset = 2
- version_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ version_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
- self.version = resp.data[offset : offset + version_len].decode()
+ self.version = resp.data[offset: offset + version_len].decode()
offset += version_len
self.base = AppletBaseEnum(
- int.from_bytes(resp.data[offset : offset + 2], "big")
+ int.from_bytes(resp.data[offset: offset + 2], "big")
)
offset += 2
- system_version = int.from_bytes(resp.data[offset : offset + 2], "big")
+ system_version = int.from_bytes(resp.data[offset: offset + 2], "big")
system_major = system_version >> 8
system_minor = system_version & 0xFF
minor_size = 1 if system_minor == 0 else ceil(log(system_minor, 10))
self.system_version = system_major + system_minor / (minor_size * 10)
offset += 2
self.object_deletion_supported = (
- int.from_bytes(resp.data[offset : offset + 2], "big") == 1
+ int.from_bytes(resp.data[offset: offset + 2], "big") == 1
)
offset += 2
- self.buf_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ self.buf_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
- self.ram1_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ self.ram1_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
- self.ram2_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ self.ram2_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
- self.apdu_len = int.from_bytes(resp.data[offset : offset + 2], "big")
+ self.apdu_len = int.from_bytes(resp.data[offset: offset + 2], "big")
offset += 2
def __repr__(self):
@@ -508,7 +507,7 @@ class InfoResponse(Response): # pragma: no cover
@public
-class ECTesterTarget(PCSCTarget): # pragma: no cover
+class ECTesterTarget(ISO7816Target, ABC): # pragma: no cover
"""Smartcard target which communicates with the `ECTester <https://github.com/crocs-muni/ECTester>`_ sapplet on smartcards of the JavaCard platform using PCSC."""
CLA_ECTESTER = 0xB0
@@ -519,15 +518,15 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
chunking: bool
- def connect(self):
+ def connect(self, protocol: Optional[CardProtocol] = None):
self.chunking = False
try:
- self.connection.connect(CardConnection.T1_protocol)
+ super().connect(CardProtocol.T1)
except CardConnectionException:
- self.connection.connect(CardConnection.T0_protocol)
+ super().connect(CardProtocol.T0)
self.chunking = True
- def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU:
+ def send(self, apdu: CommandAPDU) -> ResponseAPDU:
if self.chunking:
data = bytes(apdu)
num_chunks = (len(data) + 254) // 255
@@ -536,23 +535,23 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
chunk_length = 255
if chunk_start + chunk_length > len(data):
chunk_length = len(data) - chunk_start
- chunk = data[chunk_start : chunk_start + chunk_length]
+ chunk = data[chunk_start: chunk_start + chunk_length]
chunk_apdu = CommandAPDU(
self.CLA_ECTESTER, InstructionEnum.INS_BUFFER, 0, 0, chunk
)
- resp = super().send_apdu(chunk_apdu)
+ resp = self.send_apdu(chunk_apdu)
if resp.sw != 0x9000:
raise ChunkingException()
apdu = CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_PERFORM, 0, 0)
- resp = super().send_apdu(apdu)
+ resp = self.send_apdu(apdu)
if resp.sw & 0xFF00 == ISO7816.SW_BYTES_REMAINING_00:
- resp = super().send_apdu(
+ resp = self.send_apdu(
CommandAPDU(0x00, 0xC0, 0x00, 0x00, None, resp.sw & 0xFF)
)
return resp
def select_applet(
- self, latest_version: bytes = AID_CURRENT_VERSION, count_back: int = 10
+ self, latest_version: bytes = AID_CURRENT_VERSION, count_back: int = 10
) -> bool:
"""
Select the *ECTester* applet, with a specified version or older.
@@ -590,7 +589,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
@staticmethod
def encode_parameters(
- params: ParameterEnum, obj: Union[DomainParameters, Point, int]
+ params: ParameterEnum, obj: Union[DomainParameters, Point, int]
) -> Mapping[ParameterEnum, bytes]:
"""Encode values from `obj` into the byte parameters that the **ECTester** applet expects."""
@@ -603,7 +602,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
result = {}
if isinstance(obj, DomainParameters) and isinstance(
- obj.curve.model, ShortWeierstrassModel
+ obj.curve.model, ShortWeierstrassModel
):
for param in params & ParameterEnum.DOMAIN_FP:
if param == ParameterEnum.G:
@@ -623,7 +622,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
result[param] = convert_point(obj)
elif isinstance(obj, int):
for param in params & (
- (ParameterEnum.DOMAIN_FP ^ ParameterEnum.G) | ParameterEnum.S
+ (ParameterEnum.DOMAIN_FP ^ ParameterEnum.G) | ParameterEnum.S
):
result[param] = convert_int(obj)
else:
@@ -637,7 +636,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param ka_type: Which KeyAgreement type to allocate.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ALLOCATE_KA,
@@ -655,7 +654,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param sig_type: Which Signature type to allocate.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ALLOCATE_SIG,
@@ -667,11 +666,11 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return AllocateSigResponse(resp)
def allocate(
- self,
- keypair: KeypairEnum,
- builder: KeyBuildEnum,
- key_length: int,
- key_class: KeyClassEnum,
+ self,
+ keypair: KeypairEnum,
+ builder: KeyBuildEnum,
+ key_length: int,
+ key_class: KeyClassEnum,
) -> AllocateResponse:
"""
Send the Allocate KeyPair command.
@@ -682,7 +681,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param key_class: Type of the allocated keypair.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ALLOCATE,
@@ -700,17 +699,17 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param keypair: Which keypair to clear.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEAR, keypair, 0, None)
)
return ClearResponse(resp, keypair)
def set(
- self,
- keypair: KeypairEnum,
- curve: CurveEnum,
- params: ParameterEnum,
- values: Optional[Mapping[ParameterEnum, bytes]] = None,
+ self,
+ keypair: KeypairEnum,
+ curve: CurveEnum,
+ params: ParameterEnum,
+ values: Optional[Mapping[ParameterEnum, bytes]] = None,
) -> SetResponse:
"""
Send the Set command.
@@ -732,7 +731,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
if e == ParameterEnum.S:
break
e <<= 1
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve, payload
)
@@ -740,7 +739,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
elif values is not None:
raise ValueError("Values should be specified only if curve is external.")
else:
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_SET,
@@ -752,11 +751,11 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return SetResponse(resp, keypair)
def transform(
- self,
- keypair: KeypairEnum,
- key: KeyEnum,
- params: ParameterEnum,
- transformation: TransformationEnum,
+ self,
+ keypair: KeypairEnum,
+ key: KeyEnum,
+ params: ParameterEnum,
+ transformation: TransformationEnum,
) -> TransformResponse:
"""
Send the Transform command.
@@ -767,7 +766,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param transformation: What transformation to apply.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_TRANSFORM,
@@ -785,7 +784,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param keypair: Which keypair to generate.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER, InstructionEnum.INS_GENERATE, keypair, 0, None
)
@@ -793,7 +792,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return GenerateResponse(resp, keypair)
def export(
- self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum
+ self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum
) -> ExportResponse:
"""
Send the Export command.
@@ -803,7 +802,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param params: Which parameters to export.
:return: The response, containing the exported parameters.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_EXPORT,
@@ -815,12 +814,12 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return ExportResponse(resp, keypair, key, params)
def ecdh(
- self,
- pubkey: KeypairEnum,
- privkey: KeypairEnum,
- export: bool,
- transformation: TransformationEnum,
- ka_type: KeyAgreementEnum,
+ self,
+ pubkey: KeypairEnum,
+ privkey: KeypairEnum,
+ export: bool,
+ transformation: TransformationEnum,
+ ka_type: KeyAgreementEnum,
) -> ECDHResponse:
"""
Send the ECDH command.
@@ -832,7 +831,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param ka_type: The key-agreement type to use.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ECDH,
@@ -846,12 +845,12 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return ECDHResponse(resp, export)
def ecdh_direct(
- self,
- privkey: KeypairEnum,
- export: bool,
- transformation: TransformationEnum,
- ka_type: KeyAgreementEnum,
- pubkey: bytes,
+ self,
+ privkey: KeypairEnum,
+ export: bool,
+ transformation: TransformationEnum,
+ ka_type: KeyAgreementEnum,
+ pubkey: bytes,
) -> ECDHResponse:
"""
Send the ECDH direct command.
@@ -863,7 +862,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param pubkey: The raw bytes that will be used as a pubkey in the key-agreement.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ECDH_DIRECT,
@@ -878,7 +877,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return ECDHResponse(resp, export)
def ecdsa(
- self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
+ self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
) -> ECDSAResponse:
"""
Send the ECDSA command.
@@ -889,7 +888,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param data: The data to sign and verify.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ECDSA,
@@ -901,7 +900,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return ECDSAResponse(resp, export)
def ecdsa_sign(
- self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
+ self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
) -> ECDSAResponse:
"""
Send the ECDSA sign command.
@@ -912,7 +911,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param data: The data to sign.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ECDSA_SIGN,
@@ -924,7 +923,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return ECDSAResponse(resp, export)
def ecdsa_verify(
- self, keypair: KeypairEnum, sig_type: SignatureEnum, sig: bytes, data: bytes
+ self, keypair: KeypairEnum, sig_type: SignatureEnum, sig: bytes, data: bytes
) -> ECDSAResponse:
"""
Send the ECDSA verify command.
@@ -935,7 +934,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param data: The data.
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_ECDSA_VERIFY,
@@ -952,7 +951,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEANUP, 0, 0, None)
)
return CleanupResponse(resp)
@@ -963,7 +962,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GET_INFO, 0, 0, None)
)
return InfoResponse(resp)
@@ -974,7 +973,7 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
- resp = self.send_apdu(
+ resp = self.send(
CommandAPDU(
self.CLA_ECTESTER,
InstructionEnum.INS_SET_DRY_RUN_MODE,
@@ -984,3 +983,13 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
)
)
return RunModeResponse(resp)
+
+
+@public
+class ECTesterTargetPCSC(ECTesterTarget, PCSCTarget):
+ pass
+
+
+@public
+class ECTesterTargetLEIA(ECTesterTarget, LEIATarget):
+ pass
diff --git a/pyecsca/sca/target/leia.py b/pyecsca/sca/target/leia.py
new file mode 100644
index 0000000..00f784c
--- /dev/null
+++ b/pyecsca/sca/target/leia.py
@@ -0,0 +1,41 @@
+"""Provides a smartcard target communicating via the LEIA board in solo mode."""
+from typing import Optional
+
+from smartleia import LEIA, create_APDU_from_bytes, T
+
+from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816, CardProtocol, CardConnectionException
+
+
+class LEIATarget(ISO7816Target):
+ """Smartcard target communicating via LEIA in solo mode."""
+
+ def __init__(self, leia: LEIA):
+ self.leia = leia
+
+ @property
+ def atr(self) -> bytes:
+ return self.leia.get_ATR().normalized()
+
+ def select(self, aid: bytes) -> bool:
+ apdu = CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid)
+ resp = self.send_apdu(apdu)
+ return resp.sw == ISO7816.SW_NO_ERROR
+
+ def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU:
+ leia_apdu = create_APDU_from_bytes(bytes(apdu))
+ resp = self.leia.send_APDU(leia_apdu)
+ return ResponseAPDU(resp.data, resp.sw1 << 8 | resp.sw2)
+
+ def connect(self, protocol: Optional[CardProtocol] = None):
+ proto = T.AUTO
+ if protocol == CardProtocol.T0:
+ proto = T.T0
+ elif protocol == CardProtocol.T1:
+ proto = T.T1
+ try:
+ self.leia.configure_smartcard(protocol_to_use=proto)
+ except: # noqa
+ raise CardConnectionException()
+
+ def disconnect(self):
+ pass