aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2020-02-09 22:51:38 +0100
committerJ08nY2020-02-09 22:52:26 +0100
commit0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45 (patch)
treee6326f22eb1135d04aee10c3efe7b7345faa6089
parent2b8f3752505c3a03f617534ebfadd7fb70e09ba2 (diff)
downloadpyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.tar.gz
pyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.tar.bz2
pyecsca-0d2311a18fd03c0d17393cf4cc92b05a2b8b4e45.zip
Add targets.
-rw-r--r--pyecsca/sca/scope/chipwhisperer.py1
-rw-r--r--pyecsca/sca/scope/picoscope_alt.py1
-rw-r--r--pyecsca/sca/scope/picoscope_sdk.py1
-rw-r--r--pyecsca/sca/target/ISO7816.py63
-rw-r--r--pyecsca/sca/target/PCSC.py46
-rw-r--r--pyecsca/sca/target/__init__.py26
-rw-r--r--pyecsca/sca/target/base.py14
-rw-r--r--pyecsca/sca/target/chipwhisperer.py33
-rw-r--r--pyecsca/sca/target/serial.py13
-rw-r--r--setup.py1
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
diff --git a/setup.py b/setup.py
index d42b997..8718b17 100644
--- a/setup.py
+++ b/setup.py
@@ -37,6 +37,7 @@ setup(
"picoscope_sdk": ["picosdk"],
"picoscope_alt": ["picoscope"],
"chipwhisperer": ["chipwhisperer"],
+ "smartcard": ["pyscard"],
"dev": ["mypy", "flake8"],
"test": ["nose2", "parameterized", "green", "coverage"]
}