diff options
| author | J08nY | 2020-06-12 00:47:34 +0200 |
|---|---|---|
| committer | J08nY | 2020-06-12 00:47:34 +0200 |
| commit | 23b3638a496637c1810fb5a2bd610b63b1a72521 (patch) | |
| tree | 2da85765ac24028022e8daf1f23c1de9a21bd626 /pyecsca/sca/target | |
| parent | c2b75500aa3dbcb0d2367439e29d33440bf77688 (diff) | |
| download | pyecsca-23b3638a496637c1810fb5a2bd610b63b1a72521.tar.gz pyecsca-23b3638a496637c1810fb5a2bd610b63b1a72521.tar.zst pyecsca-23b3638a496637c1810fb5a2bd610b63b1a72521.zip | |
Diffstat (limited to 'pyecsca/sca/target')
| -rw-r--r-- | pyecsca/sca/target/ISO7816.py | 55 | ||||
| -rw-r--r-- | pyecsca/sca/target/PCSC.py | 6 | ||||
| -rw-r--r-- | pyecsca/sca/target/__init__.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/target/ectester.py | 594 |
4 files changed, 645 insertions, 11 deletions
diff --git a/pyecsca/sca/target/ISO7816.py b/pyecsca/sca/target/ISO7816.py index b5790e6..8d5385a 100644 --- a/pyecsca/sca/target/ISO7816.py +++ b/pyecsca/sca/target/ISO7816.py @@ -15,18 +15,34 @@ class CommandAPDU(object): # pragma: no cover ins: int p1: int p2: int - data: Optional[bytes] + data: Optional[bytes] = None + ne: Optional[int] = None 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 + if self.ne is None or self.ne == 0: + # Case 1 + return bytes([self.cls, self.ins, self.p1, self.p2]) + elif self.ne <= 256: + # Case 2s + return bytes([self.cls, self.ins, self.p1, self.p2, self.ne if self.ne != 256 else 0]) + else: + # Case 2e + return bytes([self.cls, self.ins, self.p1, self.p2]) + (self.ne.to_bytes(2, "big") if self.ne != 65536 else bytes([0, 0])) + elif self.ne is None or self.ne == 0: + if len(self.data) <= 255: + # Case 3s + return 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 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 - + 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]) + 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])) @public @dataclass @@ -65,3 +81,26 @@ class ISO7816Target(Target, ABC): :return: The response. """ ... + +@public +class ISO7816: + SW_FILE_FULL = 0x6A84 + SW_UNKNOWN = 0x6F00 + SW_CLA_NOT_SUPPORTED = 0x6E00 + SW_INS_NOT_SUPPORTED = 0x6D00 + SW_CORRECT_LENGTH_00 = 0x6C00 + SW_WRONG_P1P2 = 0x6B00 + SW_INCORRECT_P1P2 = 0x6A86 + SW_RECORD_NOT_FOUND = 0x6A83 + SW_FILE_NOT_FOUND = 0x6A82 + SW_FUNC_NOT_SUPPORTED = 0x6A81 + SW_WRONG_DATA = 0x6A80 + SW_APPLET_SELECT_FAILED = 0x6999 + SW_COMMAND_NOT_ALLOWED = 0x6986 + SW_CONDITIONS_NOT_SATISFIED = 0x6985 + SW_DATA_INVALID = 0x6984 + SW_FILE_INVALID = 0x6983 + SW_SECURITY_STATUS_NOT_SATISFIED = 0x6982 + SW_WRONG_LENGTH = 0x6700 + SW_BYTES_REMAINING_00 = 0x6100 + SW_NO_ERROR = 0x9000 diff --git a/pyecsca/sca/target/PCSC.py b/pyecsca/sca/target/PCSC.py index 78f92f2..6ffc8d4 100644 --- a/pyecsca/sca/target/PCSC.py +++ b/pyecsca/sca/target/PCSC.py @@ -6,7 +6,7 @@ from smartcard.System import readers from smartcard.pcsc.PCSCCardConnection import PCSCCardConnection from smartcard.pcsc.PCSCReader import PCSCReader -from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU +from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816 @public @@ -36,10 +36,10 @@ class PCSCTarget(ISO7816Target): # pragma: no cover def select(self, aid: bytes) -> bool: apdu = CommandAPDU(0x00, 0xa4, 0x04, 0x00, aid) resp = self.send_apdu(apdu) - return resp.sw == 0x9000 + return resp.sw == ISO7816.SW_NO_ERROR def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU: - resp, sw1, sw2 = self.connection.transmit(bytes(apdu)) + resp, sw1, sw2 = self.connection.transmit(list(bytes(apdu))) return ResponseAPDU(bytes(resp), sw1 << 8 | sw2) def disconnect(self): diff --git a/pyecsca/sca/target/__init__.py b/pyecsca/sca/target/__init__.py index 8ea8822..a30ea25 100644 --- a/pyecsca/sca/target/__init__.py +++ b/pyecsca/sca/target/__init__.py @@ -26,6 +26,7 @@ except ImportError: # pragma: no cover if has_pyscard: from .PCSC import * + from .ectester import ECTesterTarget if has_chipwhisperer: from .chipwhisperer import * diff --git a/pyecsca/sca/target/ectester.py b/pyecsca/sca/target/ectester.py new file mode 100644 index 0000000..31ff2a2 --- /dev/null +++ b/pyecsca/sca/target/ectester.py @@ -0,0 +1,594 @@ +from abc import ABC +from enum import IntEnum, IntFlag +from functools import reduce +from math import ceil, log +from operator import or_ +from typing import Optional, Mapping, List + +from public import public +from smartcard.CardConnection import CardConnection +from smartcard.Exceptions import CardConnectionException + +from .PCSC import PCSCTarget +from .. import CommandAPDU, ResponseAPDU, ISO7816 + + +class ShiftableFlag(IntFlag): + def __lshift__(self, other): + val = int(self) << other + for e in self.__class__: + if val == e.value: + return e + raise ValueError + + def __rshift__(self, other): + val = int(self) >> other + for e in self.__class__: + if val == e.value: + return e + raise ValueError + + +@public +class KeypairEnum(ShiftableFlag): + KEYPAIR_LOCAL = 0x01 + KEYPAIR_REMOTE = 0x02 + KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE + + +@public +class InstructionEnum(IntEnum): + INS_ALLOCATE = 0x5a + INS_CLEAR = 0x5b + INS_SET = 0x5c + INS_TRANSFORM = 0x5d + INS_GENERATE = 0x5e + INS_EXPORT = 0x5f + INS_ECDH = 0x70 + INS_ECDH_DIRECT = 0x71 + INS_ECDSA = 0x72 + INS_ECDSA_SIGN = 0x73 + INS_ECDSA_VERIFY = 0x74 + INS_CLEANUP = 0x75 + INS_ALLOCATE_KA = 0x76 + INS_ALLOCATE_SIG = 0x77 + INS_GET_INFO = 0x78 + INS_SET_DRY_RUN_MODE = 0x79 + INS_BUFFER = 0x7a + INS_PERFORM = 0x7b + + +@public +class KeyBuildEnum(IntEnum): + BUILD_KEYPAIR = 0x01 + BUILD_KEYBUILDER = 0x02 + + +@public +class ExportEnum(IntEnum): + EXPORT_TRUE = 0xff + EXPORT_FALSE = 0x00 + + @classmethod + def from_bool(cls, val: bool): + return cls.EXPORT_TRUE if val else cls.EXPORT_FALSE + + +@public +class RunModeEnum(IntEnum): + MODE_NORMAL = 0xaa + MODE_DRY_RUN = 0xbb + + +@public +class KeyEnum(ShiftableFlag): + PUBLIC = 0x01 + PRIVATE = 0x02 + BOTH = PRIVATE | PUBLIC + + +@public +class AppletBaseEnum(IntEnum): + BASE_221 = 0x0221 + BASE_222 = 0x0222 + + +@public +class KeyClassEnum(IntEnum): + ALG_EC_F2M = 4 + ALG_EC_FP = 5 + + +@public +class KeyAgreementEnum(IntEnum): + ALG_EC_SVDP_DH = 1 + ALG_EC_SVDP_DH_KDF = 1 + ALG_EC_SVDP_DHC = 2 + ALG_EC_SVDP_DHC_KDF = 2 + ALG_EC_SVDP_DH_PLAIN = 3 + ALG_EC_SVDP_DHC_PLAIN = 4 + ALG_EC_PACE_GM = 5 + ALG_EC_SVDP_DH_PLAIN_XY = 6 + + +@public +class SignatureEnum(IntEnum): + ALG_ECDSA_SHA = 17 + ALG_ECDSA_SHA_224 = 37 + ALG_ECDSA_SHA_256 = 33 + ALG_ECDSA_SHA_384 = 34 + ALG_ECDSA_SHA_512 = 38 + + +@public +class TransformationEnum(ShiftableFlag): + NONE = 0x00 + FIXED = 0x01 + FULLRANDOM = 0x02 + ONEBYTERANDOM = 0x04 + ZERO = 0x08 + ONE = 0x10 + MAX = 0x20 + INCREMENT = 0x40 + INFINITY = 0x80 + COMPRESS = 0x0100 + COMPRESS_HYBRID = 0x0200 + MASK_04 = 0x0400 + + +@public +class FormatEnum(IntEnum): + UNCOMPRESSED = 0 + COMPRESSED = 1 + HYBRID = 2 + + +@public +class CurveEnum(IntEnum): + default = 0x00 + external = 0xff + secp112r1 = 0x01 + secp128r1 = 0x02 + secp160r1 = 0x03 + secp192r1 = 0x04 + secp224r1 = 0x05 + secp256r1 = 0x06 + secp384r1 = 0x07 + secp521r1 = 0x08 + sect163r1 = 0x09 + sect233r1 = 0x0a + sect283r1 = 0x0b + sect409r1 = 0x0c + sect571r1 = 0x0d + + +@public +class ParameterEnum(ShiftableFlag): + NONE = 0x00 + FP = 0x01 + F2M = 0x02 + A = 0x04 + B = 0x08 + G = 0x10 + R = 0x20 + K = 0x40 + W = 0x80 + S = 0x0100 + DOMAIN_FP = FP | A | B | G | R | K + DOMAIN_F2M = F2M | A | B | G | R | K + KEYPAIR = W | S + ALL = FP | F2M | A | B | G | R | K | W | S + + +@public +class ChunkingException(Exception): + pass + + +class Response(ABC): + resp: ResponseAPDU + sws: List[int] + params: List[bytes] + success: bool = True + error: bool = False + + def __init__(self, resp: ResponseAPDU, num_sw: int, num_params: int): + self.resp = resp + self.sws = [0 for _ in range(num_sw)] + self.params = [bytes() for _ in range(num_params)] + + 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") + offset += 2 + if self.sws[i] != ISO7816.SW_NO_ERROR: + self.success = False + else: + self.success = False + self.error = True + + if self.resp.sw != ISO7816.SW_NO_ERROR: + self.success = False + self.error = False + + for i in range(num_params): + if len(resp.data) < offset + 2: + self.success = False + self.error = True + break + 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] + offset += param_len + + +class AllocateKaResponse(Response): + + def __init__(self, resp: ResponseAPDU): + super().__init__(resp, 1, 0) + + +class AllocateSigResponse(Response): + + def __init__(self, resp: ResponseAPDU): + super().__init__(resp, 1, 0) + + +class AllocateResponse(Response): + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum): + super().__init__(resp, 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1, 0) + + +class ClearResponse(Response): + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum): + super().__init__(resp, 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1, 0) + + +class SetResponse(Response): + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum): + super().__init__(resp, 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1, 0) + + +class TransformResponse(Response): + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum): + super().__init__(resp, 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1, 0) + + +class GenerateResponse(Response): + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum): + super().__init__(resp, 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1, 0) + + +class ExportResponse(Response): + keypair: KeypairEnum + key: KeyEnum + parameters: ParameterEnum + + def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum, key: KeyEnum, + params: ParameterEnum): + self.keypair = keypair + self.key = key + self.parameters = params + exported = 2 if keypair == KeypairEnum.KEYPAIR_BOTH else 1 + keys = 2 if key == KeyEnum.BOTH else 1 + param_count = 0 + param = ParameterEnum.FP + while True: + if param & params: + param_count += 1 + if param == ParameterEnum.K: + break + param << 1 + other = 0 + other += 1 if key & KeyEnum.PUBLIC and params & ParameterEnum.W else 0 + other += 1 if key & KeyEnum.PRIVATE and params & ParameterEnum.S else 0 + super().__init__(resp, exported, exported * keys * param_count + exported * other) + + def get_index(self, keypair: KeypairEnum, param: ParameterEnum) -> Optional[int]: + pair = KeypairEnum.KEYPAIR_LOCAL + index = 0 + while True: + mask = ParameterEnum.FP + while True: + if pair == keypair and param == mask: + return index + if self.parameters & mask and self.keypair & pair: + if mask == ParameterEnum.W: + if self.key & KeyEnum.PUBLIC: + index += 1 + elif mask == ParameterEnum.S: + if self.key & KeyEnum.PRIVATE: + index += 1 + else: + index += 1 + if mask == ParameterEnum.S: + break + mask <<= 1 + if pair == KeypairEnum.KEYPAIR_REMOTE: + break + pair <<= 1 + return None + + def get_param(self, keypair: KeypairEnum, param: ParameterEnum) -> Optional[bytes]: + index = self.get_index(keypair, param) + if index is not None: + return self.params[index] + return None + + +class ECDHResponse(Response): + + def __init__(self, resp: ResponseAPDU, export: bool): + super().__init__(resp, 1, 1 if export else 0) + + @property + def secret(self): + if len(self.params) == 0: + return self.params[0] + return None + + +class ECDSAResponse(Response): + + def __init__(self, resp: ResponseAPDU, export: bool): + super().__init__(resp, 1, 1 if export else 0) + + @property + def signature(self): + if len(self.params) == 0: + return self.params[0] + return None + + +class CleanupResponse(Response): + + def __init__(self, resp: ResponseAPDU): + super().__init__(resp, 1, 0) + + +class RunModeResponse(Response): + + def __init__(self, resp: ResponseAPDU): + super().__init__(resp, 1, 0) + + +class InfoResponse(Response): + sw: int + version: str + base: AppletBaseEnum + system_version: float + object_deletion_supported: bool + buf_len: int + ram1_len: int + ram2_len: int + apdu_len: int + + def __init__(self, resp: ResponseAPDU): + super().__init__(resp, 1, 0) + + offset = 0 + self.sw = int.from_bytes(resp.data[offset:offset + 2], "big") + offset += 2 + version_len = int.from_bytes(resp.data[offset:offset + 2], "big") + offset += 2 + self.version = resp.data[offset:offset + version_len].decode() + offset += version_len + self.base = AppletBaseEnum(int.from_bytes(resp.data[offset:offset + 2], "big")) + offset += 2 + 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 + offset += 2 + 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") + offset += 2 + 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") + offset += 2 + + +@public +class ECTesterTarget(PCSCTarget): + CLA_ECTESTER = 0xb0 + AID_PREFIX = bytes([0x45, 0x43, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72]) + AID_CURRENT_VERSION = bytes([0x30, 0x33, 0x33]) # Version v0.3.3 + AID_SUFFIX_221 = bytes([0x62]) + AID_SUFFIX_222 = bytes([0x78]) + + chunking: bool + + def connect(self): + self.chunking = False + try: + self.connection.connect(CardConnection.T1_protocol) + except CardConnectionException: + self.connection.connect(CardConnection.T0_protocol) + self.chunking = True + + def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU: + if self.chunking: + data = bytes(apdu) + num_chunks = (len(data) + 254) // 255 + for i in range(num_chunks): + chunk_start = i * 255 + 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_apdu = CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_BUFFER, 0, 0, chunk) + resp = super().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) + if resp.sw & 0xff00 == ISO7816.SW_BYTES_REMAINING_00: + resp = super().send_apdu(CommandAPDU(0x00, 0xc0, 0x00, 0x00, None, resp.sw & 0xff)) + return resp + + def select_applet(self, latest_version: bytes = AID_CURRENT_VERSION): + version_bytes = bytearray(latest_version) + for i in range(10): + aid_222 = self.AID_PREFIX + version_bytes + self.AID_SUFFIX_222 + if self.select(aid_222): + break + else: + aid_221 = self.AID_PREFIX + version_bytes + self.AID_SUFFIX_221 + if self.select(aid_221): + break + # Count down by versions + if version_bytes[2] == 0x30: + if version_bytes[1] == 0x30: + if version_bytes[0] == 0x30: + return False + else: + version_bytes[0] -= 1 + version_bytes[1] = 0x39 + version_bytes[2] = 0x39 + else: + version_bytes[1] -= 1 + version_bytes[2] = 0x39 + else: + version_bytes[2] -= 1 + else: + return False + return True + + def allocate_ka(self, ka_type: KeyAgreementEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE_KA, 0, 0, + bytes([ka_type]))) + return AllocateKaResponse(resp) + + def allocate_sig(self, sig_type: SignatureEnum): + resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE_SIG, 0, 0, + bytes([sig_type]))) + return AllocateSigResponse(resp) + + def allocate(self, keypair: KeypairEnum, builder: KeyBuildEnum, key_length: int, + key_class: KeyClassEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE, keypair, builder, + key_length.to_bytes(2, "big") + bytes([key_class]))) + return AllocateResponse(resp, keypair) + + def clear(self, keypair: KeypairEnum): + resp = self.send_apdu( + 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): + if curve == CurveEnum.external and values is not None: + if params != reduce(or_, values.keys()): + raise ValueError("Params and values need to have the same keys.") + payload = params.to_bytes(2, "big") + e = ParameterEnum.FP + while True: + if e in values: + payload += len(values[e]).to_bytes(2, "big") + values[e] + if e == ParameterEnum.S: + break + e <<= 1 + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve, + payload)) + elif values is not None: + raise ValueError("Values should be specified only if curve is external.") + else: + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve, + params.to_bytes(2, "big"))) + return SetResponse(resp, keypair) + + def transform(self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum, + transformation: TransformationEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_TRANSFORM, keypair, key, + params.to_bytes(2, "big") + transformation.to_bytes(2, "big"))) + return TransformResponse(resp, keypair) + + def generate(self, keypair: KeypairEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GENERATE, keypair, 0, None)) + return GenerateResponse(resp, keypair) + + def export(self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_EXPORT, keypair, key, + params.to_bytes(2, "big"))) + return ExportResponse(resp, keypair, key, params) + + def ecdh(self, pubkey: KeypairEnum, privkey: KeypairEnum, export: bool, + transformation: TransformationEnum, ka_type: KeyAgreementEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDH, pubkey, privkey, + bytes([ExportEnum.from_bool(export)]) + transformation.to_bytes( + 2, "big") + bytes([ka_type]))) + return ECDHResponse(resp, export) + + def ecdh_direct(self, privkey: KeypairEnum, export: bool, transformation: TransformationEnum, + ka_type: KeyAgreementEnum, pubkey: bytes): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDH_DIRECT, privkey, + ExportEnum.from_bool(export), + transformation.to_bytes(2, "big") + bytes([ka_type]) + len( + pubkey).to_bytes(2, "big") + pubkey)) + return ECDHResponse(resp, export) + + def ecdsa(self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes): + resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA, keypair, + ExportEnum.from_bool(export), + bytes([sig_type]) + len(data).to_bytes(2, "big") + data)) + return ECDSAResponse(resp, export) + + def ecdsa_sign(self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, + data: Optional[bytes] = None): + if not data: + data = bytes() + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA_SIGN, keypair, + ExportEnum.from_bool(export), + bytes([sig_type]) + len(data).to_bytes(2, "big") + data)) + return ECDSAResponse(resp, export) + + def ecdsa_verify(self, keypair: KeypairEnum, sig_type: SignatureEnum, sig: bytes, + data: Optional[bytes] = None): + if not data: + data = bytes() + resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA_VERIFY, + keypair, sig_type, + len(data).to_bytes(2, "big") + data + len(sig).to_bytes(2, + "big") + sig)) + return ECDSAResponse(resp, False) + + def cleanup(self): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEANUP, 0, 0, None)) + return CleanupResponse(resp) + + def info(self): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GET_INFO, 0, 0, None)) + return InfoResponse(resp) + + def run_mode(self, run_mode: RunModeEnum): + resp = self.send_apdu( + CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET_DRY_RUN_MODE, run_mode, 0, + None)) + return RunModeResponse(resp) |
