aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/sca/target/PCSC.py
blob: a377751b701269b662b0fbc105b6457ae921629f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
"""
This module provides a smartcard target communicating via PC/SC (Personal Computer/Smart Card).
"""
from typing import Union

from public import public
from smartcard.CardConnection import CardConnection
from smartcard.System import readers
from smartcard.pcsc.PCSCCardConnection import PCSCCardConnection
from smartcard.pcsc.PCSCReader import PCSCReader

from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816


@public
class PCSCTarget(ISO7816Target):  # pragma: no cover
    """A smartcard target communicating via PCSC."""

    def __init__(self, reader: Union[str, PCSCReader]):
        if isinstance(reader, str):
            rs = readers()
            for r in rs:
                if r.name == reader:
                    self.reader = r
                    break
            else:
                raise ValueError("Reader '{}' not found.".format(reader))
        else:
            self.reader = reader
        self.connection: PCSCCardConnection = self.reader.createConnection()

    def connect(self):
        self.connection.connect(CardConnection.T0_protocol | CardConnection.T1_protocol)

    @property
    def atr(self) -> bytes:
        return bytes(self.connection.getATR())

    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:
        resp, sw1, sw2 = self.connection.transmit(list(bytes(apdu)))
        return ResponseAPDU(bytes(resp), sw1 << 8 | sw2)

    def disconnect(self):
        self.connection.disconnect()