From b57b383a1de12caa20bdce3ce27eeb10b014a459 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Mon, 9 May 2022 22:26:58 +0200 Subject: [PATCH] vicc: move detailed description to APDU class for generic use --- .../src/vpicc/virtualsmartcard/SWutils.py | 33 +++++++++++-------- .../virtualsmartcard/VirtualSmartcard.py | 18 +++++----- .../src/vpicc/virtualsmartcard/utils.py | 31 ++++++++++++++--- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py index d076292..273f5be 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py @@ -67,34 +67,38 @@ SW = { SW_MESSAGES = { 0x9000: 'Normal processing (No further qualification)', - 0x6200: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6200: 'Warning processing (State of NVM is unchanged): ' 'No information given', - 0x6281: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6281: 'Warning processing (State of NVM is unchanged): ' 'Part of returned data may be corrupted', - 0x6282: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6282: 'Warning processing (State of NVM is unchanged): ' 'End of file or record reached before reading Ne bytes', - 0x6283: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6283: 'Warning processing (State of NVM is unchanged): ' 'Selected file deactivated', - 0x6284: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6284: 'Warning processing (State of NVM is unchanged): ' 'File control information not formatted according to 5.3.3', - 0x6285: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6285: 'Warning processing (State of NVM is unchanged): ' 'Selected file in termination state', - 0x6286: 'Warning processing (State of non-volatile memory is unchanged): ' + 0x6286: 'Warning processing (State of NVM is unchanged): ' 'No input data available from a sensor on the card', + 0x6287: 'Warning processing (State of NVM is unchanged): ' + 'At least one of the referenced records is deactivated', - 0x6300: 'Warning processing (State of non-volatile memory has changed): ' + 0x6300: 'Warning processing (State of NVM may have changed): ' 'No information given', - 0x6381: 'Warning processing (State of non-volatile memory has changed): ' + 0x6340: 'Warning processing (State of NVM may have changed): ' + 'Unsuccessful comparison', + 0x6381: 'Warning processing (State of NVM may have changed): ' 'File filled up by the last write', - 0x6400: 'Execution error (State of non-volatile memory is unchanged): ' + 0x6400: 'Execution error (State of NVM is unchanged): ' 'Execution error', - 0x6401: 'Execution error (State of non-volatile memory is unchanged): ' + 0x6401: 'Execution error (State of NVM is unchanged): ' 'Immediate response required by the card', - 0x6500: 'Execution error (State of non-volatile memory has changed): ' + 0x6500: 'Execution error (State of NVM may have changed): ' 'No information given', - 0x6581: 'Execution error (State of non-volatile memory has changed): ' + 0x6581: 'Execution error (State of NVM may have changed): ' 'Memory failure', 0x6700: 'Checking error: Wrong length; no further indication', @@ -167,6 +171,9 @@ for i in range(0, 0xff): SW_MESSAGES[0x6600 + i] = 'Execution error (Security-related issues)' SW_MESSAGES[0x6C00 + i] = 'Checking error (Wrong Le field; ' + str(i) + \ ' available data bytes)' + if i >= 0xC0: + SW_MESSAGES[0x6300 + i] = 'Warning processing (State of NVM may have changed); ' + str(i - 0xC0) + \ + ' further allowed tries)' class SwError(Exception): diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index 227ee93..5e9f28c 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -23,7 +23,6 @@ import logging import socket import struct import sys -import traceback from virtualsmartcard.ConstantDefinitions import MAX_EXTENDED_LE, MAX_SHORT_LE from virtualsmartcard.SWutils import SwError, SW from virtualsmartcard.SmartcardFilesystem import make_property @@ -351,8 +350,6 @@ class Iso7816OS(SmartcardOS): answer = self.formatResult(Iso7816OS.seekable(c.ins), c.effective_Le, result, sw, sm) except SwError as e: - logging.debug(traceback.format_exc().rstrip()) - logging.info(e.message) sw = e.sw result = b"" answer = self.formatResult(False, 0, result, sw, sm) @@ -602,20 +599,25 @@ class VirtualICC(object): logging.warning("Expected %u bytes, but received only %u", size, len(msg)) - logging.info("Command APDU (%d bytes):\n %s\n", len(msg), + logging.info("Command APDU (%d bytes):\n %s", len(msg), hexdump(msg, indent=2)) + proprietary = False try: - logging.debug(str(C_APDU(msg))) + parsed = C_APDU(msg) + logging.debug(str(parsed)) + if parsed.CLA & 0b10000000 == 0b10000000: + proprietary = True except: - pass + proprietary = True answer = self.os.execute(msg) try: - logging.debug(str(R_APDU(answer))) + if not proprietary: + logging.debug(str(R_APDU(answer))) except: pass - logging.info("Response APDU (%d bytes):\n %s\n", len(answer), + logging.info("Response APDU (%d bytes):\n %s", len(answer), hexdump(answer, indent=2)) self.__sendToVPICC(answer) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py index 412c950..3b796f8 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py @@ -20,6 +20,7 @@ import binascii import string import struct from virtualsmartcard.ConstantDefinitions import MAX_SHORT_LE, MAX_EXTENDED_LE +from virtualsmartcard.SWutils import SW_MESSAGES def stringtoint(data): @@ -201,11 +202,33 @@ class APDU(object): def __str__(self): result = "%s(%s)" % (self.__class__.__name__, ", ".join(self._format_fields())) - if len(self.data) > 0: - return result + ":\n " + hexdump(self.data, indent=2) - else: - return result + result = result + "\n " + hexdump(self.data, indent=2) + + if hasattr(self, "SW"): + sw_int = int.from_bytes(self.SW, 'big') + if sw_int in SW_MESSAGES.keys(): + result = result + '\n' + SW_MESSAGES[sw_int] + + if hasattr(self, "CLA"): + debug_CLA = [] + if self.CLA & 0b10000000 == 0b10000000: + debug_CLA.append('proprietary class') + if self.CLA & 0b00010000 == 0b00010000: + debug_CLA.append('command chaining') + if self.CLA & 0b00001100 == 0b00000100: + debug_CLA.append('proprietary secure messaging') + if self.CLA & 0b00001100 == 0b00001000: + debug_CLA.append('secure messaging (command header not processed)') + if self.CLA & 0b00001100 == 0b00001100: + debug_CLA.append('secure messaging (command header authenticated)') + if self.CLA & 0b00000011: + debug_CLA.append('logical channel %d' % self.CLA & 0b00000011) + if debug_CLA: + result = result + '\n' + ', '.join(debug_CLA) + + return result + def __repr__(self): parts = self._format_fields()