Пример #1
0
class SmartcardOS(object):
    """Base class for a smart card OS"""

    mf = make_property("mf", "master file")
    SAM = make_property("SAM", "secure access module")

    def getATR(self):
        """Returns the ATR of the card as string of characters"""
        return ""

    def powerUp(self):
        """Powers up the card"""
        self.mf.current = self.mf
        pass

    def powerDown(self):
        """Powers down the card"""
        pass

    def reset(self):
        """Performs a warm reset of the card (no power down)"""
        self.mf.current = self.mf
        pass

    def execute(self, msg):
        """Returns response to the given APDU as string of characters
        
        :param msg: the APDU as string of characters
        """
        return ""
Пример #2
0
class nPA_SE(Security_Environment):

    eac_step = make_property("eac_step", "next step to performed for EAC")

    def __init__(self, MF, SAM):
        Security_Environment.__init__(self, MF, SAM)
        self.at = nPA_AT_CRT()
        # This breaks support for 3DES
        self.cct.blocklength = 16
        self.cct.algorithm = "CC"
        self.eac_step = 0
        self.sec = None
        self.eac_ctx = None
        self.cvca = None
        self.car = None
        self.ca_key = None
        self.disable_checks = False

    def _set_SE(self, p2, data):
        sw, resp = Security_Environment._set_SE(self, p2, data)

        if self.at.algorithm == "PACE":
            if self.at.keyref_is_pin():
                if self.sam.counter <= 0:
                    print("Must use PUK to unblock")
                    return 0x63c0, ""
                if self.sam.counter == 1 and not self.sam.active:
                    print("Must use CAN to activate")
                    return 0x63c1, ""
            self.eac_step = 0
        elif self.at.algorithm == "TA":
            if self.eac_step != 4:
                raise SwError(SW["ERR_AUTHBLOCKED"])
        elif self.at.algorithm == "CA":
            if self.eac_step != 5:
                raise SwError(SW["ERR_AUTHBLOCKED"])

        return sw, resp

    def general_authenticate(self, p1, p2, data):
        if (p1, p2) != (0x00, 0x00):
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        if self.eac_step == 0 and self.at.algorithm == "PACE":
            return self.__eac_pace_step1(data)
        elif self.eac_step == 1 and self.at.algorithm == "PACE":
            return self.__eac_pace_step2(data)
        elif self.eac_step == 2 and self.at.algorithm == "PACE":
            return self.__eac_pace_step3(data)
        elif self.eac_step == 3 and self.at.algorithm == "PACE":
            return self.__eac_pace_step4(data)
        elif self.eac_step == 5 and self.at.algorithm == "CA":
            return self.__eac_ca(data)
        elif self.eac_step == 6:
            # TODO implement RI
            # "\x7c\x22\x81\x20\" is some prefix and the rest is our RI
            return SW["NORMAL"], b"\x7c\x22\x81\x20\x48\x1e\x58\xd1\x7c\x12" + \
                                 b"\x9a\x0a\xb4\x63\x7d\x43\xc7\xf7\xeb\x2b" + \
                                 b"\x06\x10\x6f\x26\x90\xe3\x00\xc4\xe7\x03" + \
                                 b"\x54\xa0\x41\xf0\xd3\x90"

        raise SwError(SW["ERR_INCORRECTPARAMETERS"])

    @staticmethod
    def __unpack_general_authenticate(data):
        data_structure = []
        structure = unpack(data)
        for tlv in structure:
            tag, length, value = tlv
            if tag == 0x7c:
                data_structure = value
            else:
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])
        return data_structure

    @staticmethod
    def __pack_general_authenticate(data):
        tlv_data = bertlv_pack(data)
        return bertlv_pack([[0x7c, len(tlv_data), tlv_data]])

    def __eac_pace_step1(self, data):
        tlv_data = nPA_SE.__unpack_general_authenticate(data)
        if tlv_data != []:
            raise SwError(SW["WARN_NOINFO63"])

        if self.at.keyref_is_mrz():
            self.PACE_SEC = PACE_SEC(self.sam.mrz, eac.PACE_MRZ)
        elif self.at.keyref_is_can():
            self.PACE_SEC = PACE_SEC(self.sam.can, eac.PACE_CAN)
        elif self.at.keyref_is_pin():
            if self.sam.counter <= 0:
                print("Must use PUK to unblock")
                return 0x63c0, b""
            if self.sam.counter == 1 and not self.sam.active:
                print("Must use CAN to activate")
                return 0x63c1, b""
            self.PACE_SEC = PACE_SEC(self.sam.eid_pin, eac.PACE_PIN)
            self.sam.counter -= 1
            if self.sam.counter <= 1:
                self.sam.active = False
        elif self.at.keyref_is_puk():
            if self.sam.counter_puk <= 0:
                raise SwError(SW["WARN_NOINFO63"])
            self.PACE_SEC = PACE_SEC(self.sam.puk, eac.PACE_PUK)
            self.sam.counter_puk -= 1
        else:
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])
        self.sec = self.PACE_SEC.sec

        if not self.eac_ctx:
            eac.EAC_init()

            self.EAC_CTX = EAC_CTX()
            self.eac_ctx = self.EAC_CTX.ctx
            eac.CA_disable_passive_authentication(self.eac_ctx)

            ef_card_security = self.mf.select('fid', 0x011d)
            ef_card_security_data = ef_card_security.data
            eac.EAC_CTX_init_ef_cardsecurity(ef_card_security_data,
                                             self.eac_ctx)

            if self.ca_key:
                ca_pubkey = eac.CA_get_pubkey(self.eac_ctx,
                                              ef_card_security_data)
                if 1 != eac.CA_set_key(self.eac_ctx, self.ca_key, ca_pubkey):
                    eac.print_ossl_err()
                    raise SwError(SW["WARN_NOINFO63"])
            else:
                # we don't have a good CA key, so we simply generate an
                # ephemeral one
                comp_pubkey = eac.TA_STEP3_generate_ephemeral_key(self.eac_ctx)
                pubkey = eac.CA_STEP2_get_eph_pubkey(self.eac_ctx)
                if not comp_pubkey or not pubkey:
                    eac.print_ossl_err()
                    raise SwError(SW["WARN_NOINFO63"])

                # save public key in EF.CardSecurity (and invalidate the
                # signature)
                # FIXME this only works for the default EF.CardSecurity.
                # Better use an ASN.1 parser to do this manipulation
                ef_card_security = self.mf.select('fid', 0x011d)
                ef_card_security_data = ef_card_security.data
                ef_card_security_data = \
                    ef_card_security_data[:61+4+239+2+1] + pubkey + \
                    ef_card_security_data[61+4+239+2+1+len(pubkey):]
                ef_card_security.data = ef_card_security_data

        nonce = eac.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec)
        if not nonce:
            eac.print_ossl_err()
            raise SwError(SW["WARN_NOINFO63"])

        resp = nPA_SE.__pack_general_authenticate([[0x80, len(nonce), nonce]])

        self.eac_step += 1

        return 0x9000, resp

    def __eac_pace_step2(self, data):
        tlv_data = nPA_SE.__unpack_general_authenticate(data)

        pubkey = eac.PACE_STEP3A_generate_mapping_data(self.eac_ctx)
        if not pubkey:
            eac.print_ossl_err()
            raise SwError(SW["WARN_NOINFO63"])

        for tag, length, value in tlv_data:
            if tag == 0x81:
                eac.PACE_STEP3A_map_generator(self.eac_ctx, value)
            else:
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        self.eac_step += 1

        return 0x9000, \
            nPA_SE.__pack_general_authenticate([[0x82, len(pubkey), pubkey]])

    def __eac_pace_step3(self, data):
        tlv_data = nPA_SE.__unpack_general_authenticate(data)

        self.my_pace_eph_pubkey = \
            eac.PACE_STEP3B_generate_ephemeral_key(self.eac_ctx)
        if not self.my_pace_eph_pubkey:
            eac.print_ossl_err()
            raise SwError(SW["WARN_NOINFO63"])
        eph_pubkey = self.my_pace_eph_pubkey

        for tag, length, value in tlv_data:
            if tag == 0x83:
                self.pace_opp_pub_key = value
                eac.PACE_STEP3B_compute_shared_secret(self.eac_ctx,
                                                      self.pace_opp_pub_key)
            else:
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        self.eac_step += 1

        return 0x9000, \
            nPA_SE.__pack_general_authenticate([[0x84, len(eph_pubkey),
                                                 eph_pubkey]])

    def __eac_pace_step4(self, data):
        tlv_data = nPA_SE.__unpack_general_authenticate(data)
        eac.PACE_STEP3C_derive_keys(self.eac_ctx)
        my_token = \
            eac.PACE_STEP3D_compute_authentication_token(self.eac_ctx,
                                                         self.pace_opp_pub_key)
        token = b""
        for tag, length, value in tlv_data:
            if tag == 0x85:
                token = value
            else:
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        ver = eac.PACE_STEP3D_verify_authentication_token(self.eac_ctx, token)
        if not my_token or ver != 1:
            eac.print_ossl_err()
            raise SwError(SW["WARN_NOINFO63"])

        print("Established PACE channel")

        if self.at.keyref_is_can():
            if (self.sam.counter == 1):
                self.sam.active = True
                print("PIN resumed")
        elif self.at.keyref_is_pin():
            self.sam.active = True
            self.sam.counter = 3
        elif self.at.keyref_is_puk():
            self.sam.active = True
            self.sam.counter = 3
            print("PIN unblocked")

        self.eac_step += 1
        self.at.algorithm = "TA"

        self.new_encryption_ctx = eac.EAC_ID_PACE

        result = [[0x86, len(my_token), my_token]]
        if self.at.chat:
            if self.cvca:
                self.car = CVC(self.cvca).get_chr()
            result.append([0x87, len(self.car), self.car])
            if (self.disable_checks):
                eac.TA_disable_checks(self.eac_ctx)
            if not eac.EAC_CTX_init_ta(self.eac_ctx, None, self.cvca):
                eac.print_ossl_err()
                raise SwError(SW["WARN_NOINFO63"])

        return 0x9000, nPA_SE.__pack_general_authenticate(result)

    def __eac_ca(self, data):
        tlv_data = nPA_SE.__unpack_general_authenticate(data)

        pubkey = ""
        for tag, length, value in tlv_data:
            if tag == 0x80:
                pubkey = value
            else:
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        if eac.CA_STEP4_compute_shared_secret(self.eac_ctx, pubkey) != 1:
            eac.print_ossl_err()
            raise SwError(SW["ERR_NOINFO69"])

        nonce, token = eac.CA_STEP5_derive_keys(self.eac_ctx, pubkey)
        if not nonce or not token:
            eac.print_ossl_err()
            raise SwError(SW["WARN_NOINFO63"])

        self.eac_step += 1

        print("Generated Nonce and Authentication Token for CA")

        # TODO activate SM
        self.new_encryption_ctx = eac.EAC_ID_CA

        return 0x9000, \
            nPA_SE.__pack_general_authenticate([[0x81, len(nonce), nonce],
                                                [0x82, len(token), token]])

    def verify_certificate(self, p1, p2, data):
        if (p1, p2) != (0x00, 0xbe):
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        cert = bertlv_pack([[0x7f21, len(data), data]])
        if 1 != eac.TA_STEP2_import_certificate(self.eac_ctx, cert):
            eac.print_ossl_err()
            raise SwError(SW["ERR_NOINFO69"])

        print("Imported Certificate")

        return b""

    def external_authenticate(self, p1, p2, data):
        """
        Authenticate the terminal to the card. Check whether Terminal correctly
        encrypted the given challenge or not
        """
        if self.dst.keyref_public_key:  # TODO check if this is the correct CAR
            id_picc = eac.EAC_Comp(self.eac_ctx, eac.EAC_ID_PACE,
                                   self.my_pace_eph_pubkey)

            # FIXME auxiliary_data might be from an older run of PACE
            if hasattr(self.at, "auxiliary_data"):
                auxiliary_data = self.at.auxiliary_data
            else:
                auxiliary_data = None

            if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
                                        auxiliary_data, data):
                eac.print_ossl_err()
                print("Could not verify Terminal's signature")
                raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])

            print("Terminal's signature verified")

            self.eac_step += 1

            return 0x9000, b""

        raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])

    def compute_cryptographic_checksum(self, p1, p2, data):
        checksum = eac.EAC_authenticate(self.eac_ctx, data)
        if not checksum:
            eac.print_ossl_err()
            raise SwError(SW["ERR_NOINFO69"])

        return checksum

    def encipher(self, p1, p2, data):
        padded = vsCrypto.append_padding(self.cct.blocklength, data)
        cipher = eac.EAC_encrypt(self.eac_ctx, padded)
        if not cipher:
            eac.print_ossl_err()
            raise SwError(SW["ERR_NOINFO69"])

        return cipher

    def decipher(self, p1, p2, data):
        plain = eac.EAC_decrypt(self.eac_ctx, data)
        if not plain:
            eac.print_ossl_err()
            raise SwError(SW["ERR_NOINFO69"])

        return plain

    def protect_response(self, sw, result):
        """
        This method protects a response APDU using secure messaging mechanisms

        :returns: the protected data and the SW bytes
        """

        return_data = b""

        if result:
            # Encrypt the data included in the RAPDU
            encrypted = self.encipher(0x82, 0x80, result)
            encrypted = b"\x01" + encrypted
            encrypted_tlv = bertlv_pack([
                (SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"], len(encrypted),
                 encrypted)
            ])
            return_data += encrypted_tlv

        sw_str = inttostring(sw)
        length = len(sw_str)
        tag = SM_Class["PLAIN_PROCESSING_STATUS"]
        tlv_sw = bertlv_pack([(tag, length, sw_str)])
        return_data += tlv_sw

        if self.cct.algorithm is None:
            raise SwError(SW["CONDITIONSNOTSATISFIED"])
        elif self.cct.algorithm == "CC":
            tag = SM_Class["CHECKSUM"]
            padded = vsCrypto.append_padding(self.cct.blocklength, return_data)
            auth = self.compute_cryptographic_checksum(0x8E, 0x80, padded)
            length = len(auth)
            return_data += bertlv_pack([(tag, length, auth)])
        elif self.cct.algorithm == "SIGNATURE":
            tag = SM_Class["DIGITAL_SIGNATURE"]
            hash = self.hash(0x90, 0x80, return_data)
            auth = self.compute_digital_signature(0x9E, 0x9A, hash)
            length = len(auth)
            return_data += bertlv_pack([(tag, length, auth)])

        return sw, return_data

    def compute_digital_signature(self, p1, p2, data):
        # TODO Signing with brainpoolP256r1 or any other key needs some more
        # effort ;-)
        return b'\x0D\xB2\x9B\xB9\x5E\x97\x7D\x42\x73\xCF\xA5\x45\xB7\xED' + \
               b'\x5C\x39\x3F\xCE\xCD\x4A\xDE\xDC\x2B\x85\x23\x9F\x66\x52' + \
               b'\x10\xC2\x67\xDC\xA6\x35\x94\x2D\x24\xED\xEB\xC8\x34\x6C' + \
               b'\x4B\xD1\xA1\x15\xB4\x48\x3A\xA4\x4A\xCE\xFF\xED\x97\x0E' + \
               b'\x07\xF3\x72\xF0\xFB\xA3\x62\x8C'
Пример #3
0
class Iso7816OS(SmartcardOS):

    mf = make_property("mf", "master file")
    SAM = make_property("SAM", "secure access module")

    def __init__(self, mf, sam, ins2handler=None, extended_length=False):
        self.mf = mf
        self.SAM = sam

        if not ins2handler:
            self.ins2handler = {
                0x0c: self.mf.eraseRecord,
                0x0e: self.mf.eraseBinaryPlain,
                0x0f: self.mf.eraseBinaryEncapsulated,
                0x2a: self.SAM.perform_security_operation,
                0x20: self.SAM.verify,
                0x22: self.SAM.manage_security_environment,
                0x24: self.SAM.change_reference_data,
                0x46: self.SAM.generate_public_key_pair,
                0x82: self.SAM.external_authenticate,
                0x84: self.SAM.get_challenge,
                0x88: self.SAM.internal_authenticate,
                0xa0: self.mf.searchBinaryPlain,
                0xa1: self.mf.searchBinaryEncapsulated,
                0xa4: self.mf.selectFile,
                0xb0: self.mf.readBinaryPlain,
                0xb1: self.mf.readBinaryEncapsulated,
                0xb2: self.mf.readRecordPlain,
                0xb3: self.mf.readRecordEncapsulated,
                0xc0: self.getResponse,
                0xca: self.mf.getDataPlain,
                0xcb: self.mf.getDataEncapsulated,
                0xd0: self.mf.writeBinaryPlain,
                0xd1: self.mf.writeBinaryEncapsulated,
                0xd2: self.mf.writeRecord,
                0xd6: self.mf.updateBinaryPlain,
                0xd7: self.mf.updateBinaryEncapsulated,
                0xda: self.mf.putDataPlain,
                0xdb: self.mf.putDataEncapsulated,
                0xdc: self.mf.updateRecordPlain,
                0xdd: self.mf.updateRecordEncapsulated,
                0xe0: self.mf.createFile,
                0xe2: self.mf.appendRecord,
                0xe4: self.mf.deleteFile,
            }
        else:
            self.ins2handler = ins2handler

        if extended_length:
            self.maxle = MAX_EXTENDED_LE
        else:
            self.maxle = MAX_SHORT_LE

        self.lastCommandOffcut = b""
        self.lastCommandSW = SW["NORMAL"]
        el = extended_length  # only needed to keep following line short
        tsft = Iso7816OS.makeThirdSoftwareFunctionTable(extendedLe=el)
        card_capabilities = self.mf.firstSFT + self.mf.secondSFT + tsft
        self.atr = Iso7816OS.makeATR(
            T=1,
            directConvention=True,
            TA1=0x13,
            histChars=inttostring(0x80) +
            inttostring(0x70 + len(card_capabilities)) + card_capabilities)

    def getATR(self):
        return self.atr

    @staticmethod
    def makeATR(**args):
        """Calculate Answer to Reset (ATR) and returns the bitstring.

            - directConvention (bool): Whether to use direct convention or
                                       inverse convention.
            - TAi, TBi, TCi (optional): Value between 0 and 0xff. Interface
                            Characters (for meaning see ISO 7816-3). Note that
                            if no transmission protocol is given, it is
                            automatically selected with T=max{j-1|TAj in args
                            OR TBj in args OR TCj in args}.
            - T (optional): Value between 0 and 15. Transmission Protocol.
                            Note that if T is set, TAi/TBi/TCi for i>T are
                            omitted.
            - histChars (optional): Bitstring with 0 <= len(histChars) <= 15.
                                    Historical Characters T1 to T15 (for
                                    meaning see ISO 7816-4).

        T0, TDi and TCK are automatically calculated.
        """
        # first byte TS
        if args["directConvention"]:
            atr = b"\x3b"
        else:
            atr = b"\x3f"

        if "T" in args:
            T = args["T"]
        else:
            T = 0

        # find maximum i of TAi/TBi/TCi in args
        maxTD = 0
        i = 15
        while i > 0:
            if ("TA" + str(i) in args or "TB" + str(i) in args
                    or "TC" + str(i) in args):
                maxTD = i - 1
                break
            i -= 1

        if maxTD == 0 and T > 0:
            maxTD = 2

        # insert TDi into args (TD0 is actually T0)
        for i in range(0, maxTD + 1):
            if i == 0 and "histChars" in args:
                args["TD0"] = len(args["histChars"])
            else:
                args["TD" + str(i)] = T

            if i < maxTD:
                args["TD" + str(i)] |= 1 << 7

            if "TA" + str(i + 1) in args:
                args["TD" + str(i)] |= 1 << 4
            if "TB" + str(i + 1) in args:
                args["TD" + str(i)] |= 1 << 5
            if "TC" + str(i + 1) in args:
                args["TD" + str(i)] |= 1 << 6

        # initialize checksum
        TCK = 0

        # add TDi, TAi, TBi and TCi to ATR (TD0 is actually T0)
        for i in range(0, maxTD + 1):
            atr = atr + b"%c" % args["TD" + str(i)]
            TCK ^= args["TD" + str(i)]
            for j in ["A", "B", "C"]:
                if "T" + j + str(i + 1) in args:
                    atr += b"%c" % args["T" + j + str(i + 1)]
                    # calculate checksum for all bytes from T0 to the end
                    TCK ^= args["T" + j + str(i + 1)]

        # add historical characters
        if "histChars" in args:
            atr += args["histChars"]
            for i in range(0, len(args["histChars"])):
                byte = args["histChars"][i]
                if isinstance(byte, str):
                    TCK ^= ord(byte)
                else:
                    TCK ^= byte

        # checksum is omitted for T=0
        if T > 0:
            atr += b"%c" % TCK

        return atr

    @staticmethod
    def makeThirdSoftwareFunctionTable(commandChainging=False,
                                       extendedLe=False,
                                       assignLogicalChannel=0,
                                       maximumChannels=0):
        """
        Returns a byte according to the third software function table from the
        historical bytes of the card capabilities.
        """
        tsft = 0
        if commandChainging:
            tsft |= 1 << 7
        if extendedLe:
            tsft |= 1 << 6
        if assignLogicalChannel:
            if not (0 <= assignLogicalChannel and assignLogicalChannel <= 3):
                raise ValueError
            tsft |= assignLogicalChannel << 3
        if maximumChannels:
            if not (0 <= maximumChannels and maximumChannels <= 7):
                raise ValueError
            tsft |= maximumChannels
        return inttostring(tsft)

    def formatResult(self, seekable, le, data, sw, sm):
        if not seekable:
            self.lastCommandOffcut = data[le:]
            l = len(self.lastCommandOffcut)
            if l == 0:
                self.lastCommandSW = SW["NORMAL"]
            else:
                self.lastCommandSW = sw
                sw = SW["NORMAL_REST"] + min(0xff, l)
        else:
            if le > len(data):
                sw = SW["WARN_EOFBEFORENEREAD"]

        if le is not None:
            result = data[:le]
        else:
            result = data[:0]
        if sm:
            sw, result = self.SAM.protect_result(sw, result)

        return R_APDU(result, inttostring(sw)).render()

    @staticmethod
    def seekable(ins):
        if ins in [
                0xb0, 0xb1, 0xd0, 0xd1, 0xd6, 0xd7, 0xa0, 0xa1, 0xb2, 0xb3,
                0xdc, 0xdd
        ]:
            return True
        else:
            return False

    def getResponse(self, p1, p2, data):
        if not (p1 == 0 and p2 == 0):
            raise SwError(SW["ERR_INCORRECTP1P2"])

        return self.lastCommandSW, self.lastCommandOffcut

    def execute(self, msg):
        def notImplemented(*argz, **args):
            """
            If an application tries to use a function which is not implemented
            by the currently emulated smartcard we raise an exception which
            should result in an appropriate response APDU being passed to the
            application.
            """
            raise SwError(SW["ERR_INSNOTSUPPORTED"])

        logging.info("Command APDU (%d bytes):\n  %s", len(msg),
                     hexdump(msg, indent=2))

        try:
            c = C_APDU(msg)
            logging.debug("%s", str(c))
        except ValueError as e:
            logging.warning(str(e))
            return self.formatResult(False, 0, b"",
                                     SW["ERR_INCORRECTPARAMETERS"], False)

        # Handle Class Byte
        # {{{
        class_byte = c.cla
        SM_STATUS = None
        logical_channel = 0
        command_chaining = 0
        header_authentication = 0

        # Ugly Hack for OpenSC-explorer
        if (class_byte == 0xb0):
            logging.debug("Open SC APDU")
            SM_STATUS = "No SM"

        # If Bit 8,7,6 == 0 then first industry values are used
        if (class_byte & 0xE0 == 0x00):
            # Bit 1 and 2 specify the logical channel
            logical_channel = class_byte & 0x03
            # Bit 3 and 4 specify secure messaging
            secure_messaging = class_byte >> 2
            secure_messaging &= 0x03
            if (secure_messaging == 0x00):
                SM_STATUS = "No SM"
            elif (secure_messaging == 0x01):
                SM_STATUS = "Proprietary SM"  # Not supported ?
            elif (secure_messaging == 0x02):
                SM_STATUS = "Standard SM"
            elif (secure_messaging == 0x03):
                SM_STATUS = "Standard SM"
                header_authentication = 1
        # If Bit 8,7 == 01 then further industry values are used
        elif (class_byte & 0x0C == 0x0C):
            # Bit 1 to 4 specify logical channel. 4 is added, value range is
            # from four to nineteen
            logical_channel = class_byte & 0x0f
            logical_channel += 4
            # Bit 6 indicates secure messaging
            secure_messaging = class_byte >> 6
            if (secure_messaging == 0x00):
                SM_STATUS = "No SM"
            elif (secure_messaging == 0x01):
                SM_STATUS = "Standard SM"
            else:
                # Bit 8 is set to 1, which is not specified by ISO 7816-4
                SM_STATUS = "Proprietary SM"
        # In both cases Bit 5 specifies command chaining
        command_chaining = class_byte >> 5
        command_chaining &= 0x01
        # }}}

        sm = False
        try:
            if SM_STATUS == "Standard SM" or SM_STATUS == "Proprietary SM":
                c = self.SAM.parse_SM_CAPDU(c, header_authentication)
                logging.info("Decrypted APDU:\n%s", str(c))
                sm = True
            sw, result = self.ins2handler.get(c.ins,
                                              notImplemented)(c.p1, c.p2,
                                                              c.data)
            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)

        return answer

    def powerUp(self):
        self.mf.current = self.mf

    def reset(self):
        self.mf.current = self.mf