diff options
| author | J08nY | 2020-02-09 22:51:38 +0100 |
|---|---|---|
| committer | J08nY | 2020-02-09 22:52:26 +0100 |
| commit | 0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45 (patch) | |
| tree | e6326f22eb1135d04aee10c3efe7b7345faa6089 | |
| parent | 2b8f3752505c3a03f617534ebfadd7fb70e09ba2 (diff) | |
| download | pyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.tar.gz pyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.tar.zst pyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.zip | |
| -rw-r--r-- | pyecsca/sca/scope/chipwhisperer.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/scope/picoscope_alt.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/scope/picoscope_sdk.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/target/ISO7816.py | 63 | ||||
| -rw-r--r-- | pyecsca/sca/target/PCSC.py | 46 | ||||
| -rw-r--r-- | pyecsca/sca/target/__init__.py | 26 | ||||
| -rw-r--r-- | pyecsca/sca/target/base.py | 14 | ||||
| -rw-r--r-- | pyecsca/sca/target/chipwhisperer.py | 33 | ||||
| -rw-r--r-- | pyecsca/sca/target/serial.py | 13 | ||||
| -rw-r--r-- | setup.py | 1 |
10 files changed, 199 insertions, 0 deletions
diff --git a/pyecsca/sca/scope/chipwhisperer.py b/pyecsca/sca/scope/chipwhisperer.py index 8f5590d..344ed56 100644 --- a/pyecsca/sca/scope/chipwhisperer.py +++ b/pyecsca/sca/scope/chipwhisperer.py @@ -12,6 +12,7 @@ class ChipWhispererScope(Scope): # pragma: no cover """A ChipWhisperer based scope.""" def __init__(self, scope: OpenADC): + super().__init__() self.scope = scope self.triggers: Set[str] = set() diff --git a/pyecsca/sca/scope/picoscope_alt.py b/pyecsca/sca/scope/picoscope_alt.py index 5f8ad85..acf5807 100644 --- a/pyecsca/sca/scope/picoscope_alt.py +++ b/pyecsca/sca/scope/picoscope_alt.py @@ -13,6 +13,7 @@ from .base import Scope class PicoScopeAlt(Scope): # pragma: no cover def __init__(self, ps: Union[PS4000, PS6000]): + super().__init__() self.ps = ps def open(self) -> None: diff --git a/pyecsca/sca/scope/picoscope_sdk.py b/pyecsca/sca/scope/picoscope_sdk.py index e552188..5016203 100644 --- a/pyecsca/sca/scope/picoscope_sdk.py +++ b/pyecsca/sca/scope/picoscope_sdk.py @@ -47,6 +47,7 @@ class PicoScopeSdk(Scope): # pragma: no cover } def __init__(self): + super().__init__() self.handle: ctypes.c_int16 = ctypes.c_int16() self.frequency: Optional[int] = None self.samples: Optional[int] = None diff --git a/pyecsca/sca/target/ISO7816.py b/pyecsca/sca/target/ISO7816.py new file mode 100644 index 0000000..afa75c5 --- /dev/null +++ b/pyecsca/sca/target/ISO7816.py @@ -0,0 +1,63 @@ +from dataclasses import dataclass +from typing import Optional + +from public import public + +from .base import Target + + +@public +@dataclass +class CommandAPDU(object): # pragma: no cover + """A command APDU that can be sent to an ISO7816-4 target.""" + cls: int + ins: int + p1: int + p2: int + data: Optional[bytes] + + def __bytes__(self): + if self.data is None or len(self.data) == 0: + return bytes([self.cls, self.ins, self.p1, self.p2]) + elif len(self.data) <= 255: + return bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)]) + self.data + else: + data_len = len(self.data) + return bytes([self.cls, self.ins, self.p1, self.p2, 0, data_len >> 8, + data_len & 0xff]) + self.data + + +@public +@dataclass +class ResponseAPDU(object): + """A response APDU that can be received from an ISO7816-4 target.""" + data: Optional[bytes] + sw: int + + +@public +class ISO7816Target(Target): + """An ISO7816-4 target.""" + + @property + def atr(self) -> bytes: + """The ATR (Answer To Reset) of the target.""" + raise NotImplementedError + + def select(self, aid: bytes) -> bool: + """ + Select an applet with `aid`. + + :param aid: The AID of the applet to select. + :return: Whether the selection was successful. + """ + raise NotImplementedError + + def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU: + """ + Send an APDU to the selected applet. + + :param apdu: The APDU to send. + :return: The response. + """ + raise NotImplementedError diff --git a/pyecsca/sca/target/PCSC.py b/pyecsca/sca/target/PCSC.py new file mode 100644 index 0000000..78f92f2 --- /dev/null +++ b/pyecsca/sca/target/PCSC.py @@ -0,0 +1,46 @@ +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 + + +@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 == 0x9000 + + def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU: + resp, sw1, sw2 = self.connection.transmit(bytes(apdu)) + return ResponseAPDU(bytes(resp), sw1 << 8 | sw2) + + def disconnect(self): + self.connection.disconnect() diff --git a/pyecsca/sca/target/__init__.py b/pyecsca/sca/target/__init__.py index 750f885..e9d41d6 100644 --- a/pyecsca/sca/target/__init__.py +++ b/pyecsca/sca/target/__init__.py @@ -1 +1,27 @@ """Package for communicating with targets of measurement.""" + +from .ISO7816 import * +from .base import * + +has_chipwhisperer = False +has_pyscard = False + +try: + import chipwhisperer + + has_chipwhisperer = True +except ImportError: + pass + +try: + import pyscard + + has_pyscard = True +except ImportError: + pass + +if has_pyscard: + from .PCSC import * + +if has_chipwhisperer: + from .chipwhisperer import * diff --git a/pyecsca/sca/target/base.py b/pyecsca/sca/target/base.py new file mode 100644 index 0000000..ed51c02 --- /dev/null +++ b/pyecsca/sca/target/base.py @@ -0,0 +1,14 @@ +from public import public + + +@public +class Target(object): + """A target.""" + + def connect(self): + """Connect to the target device.""" + raise NotImplementedError + + def disconnect(self): + """Disconnect from the target device.""" + raise NotImplementedError diff --git a/pyecsca/sca/target/chipwhisperer.py b/pyecsca/sca/target/chipwhisperer.py new file mode 100644 index 0000000..1b03713 --- /dev/null +++ b/pyecsca/sca/target/chipwhisperer.py @@ -0,0 +1,33 @@ +from typing import Union + +from chipwhisperer.capture.scopes import OpenADC +from chipwhisperer.capture.targets.simpleserial_readers.cw import SimpleSerial_ChipWhisperer +from chipwhisperer.capture.targets.simpleserial_readers.cwlite import SimpleSerial_ChipWhispererLite +from chipwhisperer.capture.targets.simpleserial_readers.sys_serial import SimpleSerial_serial +from public import public + +from .serial import SerialTarget + + +@public +class SimpleSerialTarget(SerialTarget): # pragma: no cover + + def __init__(self, ser: Union[ + SimpleSerial_ChipWhisperer, SimpleSerial_ChipWhispererLite, SimpleSerial_serial], + scope: OpenADC): + super().__init__() + self.ser = ser + self.scope = scope + + def connect(self): + self.ser.con(self.scope) + + def write(self, data: bytes): + self.ser.write(data) + self.ser.flush() + + def read(self, timeout: int) -> bytes: + return self.ser.read(0, timeout) + + def disconnect(self): + self.ser.dis() diff --git a/pyecsca/sca/target/serial.py b/pyecsca/sca/target/serial.py new file mode 100644 index 0000000..ab61d9e --- /dev/null +++ b/pyecsca/sca/target/serial.py @@ -0,0 +1,13 @@ +from public import public + +from .base import Target + + +@public +class SerialTarget(Target): + + def write(self, data: bytes): + raise NotImplementedError + + def read(self, timeout: int) -> bytes: + raise NotImplementedError @@ -37,6 +37,7 @@ setup( "picoscope_sdk": ["picosdk"], "picoscope_alt": ["picoscope"], "chipwhisperer": ["chipwhisperer"], + "smartcard": ["pyscard"], "dev": ["mypy", "flake8"], "test": ["nose2", "parameterized", "green", "coverage"] } |
