Example #1
0
    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, ""
            if self.sam.counter == 1 and not self.sam.active:
                print "Must use CAN to activate"
                return 0x63c1, ""
            self.PACE_SEC = PACE_SEC(self.sam.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 getResponse(self, p1, p2, data):
        if not (p1 == 0 and p2 == 0):
            raise SwError(SW["ERR_INCORRECTP1P2"])

        return self.lastCommandSW, self.lastCommandOffcut
Example #3
0
    def generate_public_key_pair(self, p1, p2, data):
        """
        The GENERATE PUBLIC-KEY PAIR command either initiates the generation
        and storing of a key pair, i.e., a public key and a private key, in the
        card, or accesses a key pair previously generated in the card.

        :param p1: should be 0x00 (generate new key)
        :param p2: '00' (no information provided) or reference of the key to
                   be generated
        :param data: One or more CRTs associated to the key generation if
                     P1-P2 different from '0000'
        """

        from Crypto.PublicKey import RSA, DSA
        from Crypto.Util.randpool import RandomPool
        rnd = RandomPool()

        cipher = self.ct.algorithm

        c_class = locals().get(cipher, None)
        if c_class is None:
            raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])

        if p1 & 0x01 == 0x00:  # Generate key
            PublicKey = c_class.generate(self.dst.keylength, rnd.get_bytes)
            self.dst.key = PublicKey
        else:
            pass  # Read key

        # Encode keys
        if cipher == "RSA":
            # Public key
            n = inttostring(PublicKey.__getstate__()['n'])
            e = inttostring(PublicKey.__getstate__()['e'])
            pk = ((0x81, len(n), n), (0x82, len(e), e))
            result = bertlv_pack(pk)
            # Private key
            d = PublicKey.__getstate__()['d']
        elif cipher == "DSA":
            # DSAParams
            p = inttostring(PublicKey.__getstate__()['p'])
            q = inttostring(PublicKey.__getstate__()['q'])
            g = inttostring(PublicKey.__getstate__()['g'])
            # Public key
            y = inttostring(PublicKey.__getstate__()['y'])
            pk = ((0x81, len(p), p), (0x82, len(q), q), (0x83, len(g), g),
                  (0x84, len(y), y))
            # Private key
            x = inttostring(PublicKey.__getstate__()['x'])
        # Add more algorithms here
        # elif cipher = "ECDSA":
        else:
            raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])

        result = bertlv_pack([[0x7F49, len(pk), pk]])
        # TODO: Internally store key pair

        if p1 & 0x02 == 0x02:
            # We do not support extended header lists yet
            raise SwError["ERR_NOTSUPPORTED"]
        else:
            return SW["NORMAL"], result
Example #4
0
    def parse_SM_CAPDU(self, CAPDU, authenticate_header):
        """
        This methods parses a data field including Secure Messaging objects.
        SM_header indicates whether or not the header of the message shall be
        authenticated. It returns an unprotected command APDU

        :param CAPDU: The protected CAPDU to be parsed
        :param authenticate_header: Whether or not the header should be
            included in authentication mechanisms
        :returns: Unprotected command APDU
        """

        structure = unpack(CAPDU.data)
        return_data = ["", ]

        cla = None
        ins = None
        p1 = None
        p2 = None
        le = None

        if authenticate_header:
            to_authenticate = inttostring(CAPDU.cla) +\
                inttostring(CAPDU.ins) + inttostring(CAPDU.p1) +\
                inttostring(CAPDU.p2)
            to_authenticate = vsCrypto.append_padding(self.cct.blocklength,
                                                      to_authenticate)
        else:
            to_authenticate = ""

        for tlv in structure:
            tag, length, value = tlv

            if tag % 2 == 1:  # Include object in checksum calculation
                to_authenticate += bertlv_pack([[tag, length, value]])

            # SM data objects for encapsulating plain values
            if tag in (SM_Class["PLAIN_VALUE_NO_TLV"],
                       SM_Class["PLAIN_VALUE_NO_TLV_ODD"]):
                return_data.append(value)  # FIXME: Need TLV coding?
            # Encapsulated SM objects. Parse them
            # FIXME: Need to pack value into a dummy CAPDU
            elif tag in (SM_Class["PLAIN_VALUE_TLV_INCULDING_SM"],
                         SM_Class["PLAIN_VALUE_TLV_INCULDING_SM_ODD"]):
                return_data.append(self.parse_SM_CAPDU(value,
                                                       authenticate_header))
            # Encapsulated plaintext BER-TLV objects
            elif tag in (SM_Class["PLAIN_VALUE_TLV_NO_SM"],
                         SM_Class["PLAIN_VALUE_TLV_NO_SM_ODD"]):
                return_data.append(value)
            elif tag in (SM_Class["Ne"], SM_Class["Ne_ODD"]):
                le = value
            elif tag == SM_Class["PLAIN_COMMAND_HEADER"]:
                if len(value) != 8:
                    raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
                else:
                    cla = value[:2]
                    ins = value[2:4]
                    p1 = value[4:6]
                    p2 = value[6:8]

            # SM data objects for confidentiality
            if tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM"],
                       SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM_ODD"]):
                # The cryptogram includes SM objects.
                # We decrypt them and parse the objects.
                plain = self.decipher(tag, 0x80, value)
                # TODO: Need Le = length
                return_data.append(self.parse_SM_CAPDU(plain,
                                                       authenticate_header))
            elif tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM"],
                         SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM_ODD"]):
                # The cryptogram includes BER-TLV encoded plaintext.
                # We decrypt them and return the objects.
                plain = self.decipher(tag, 0x80, value)
                return_data.append(plain)
            elif tag in (SM_Class["CRYPTOGRAM_PADDING_INDICATOR"],
                         SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"]):
                # The first byte of the data field indicates the padding to use
                """
                Value   Meaning
                '00'    No further indication
                '01'    Padding as specified in 6.2.3.1
                '02'    No padding
                '1X'    One to four secret keys for enciphering information,
                        not keys ('X' is a bitmap with any value from '0' to
                        'F')
                '11'    indicates the first key (e.g., an "even" control word
                        in a pay TV system)
                '12'    indicates the second key (e.g., an "odd" control word
                        in a pay TV system)
                '13'    indicates the first key followed by the second key
                        (e.g., a pair of control words in a pay TV system)
                '2X'    Secret key for enciphering keys, not information
                        ('X' is a reference with any value from '0' to 'F')
                        (e.g., in a pay TV system, either an operational key
                        for enciphering control words, or a management key for
                        enciphering operational keys)
                '3X'    Private key of an asymmetric key pair ('X' is a
                        reference with any value from '0' to 'F')
                '4X'    Password ('X' is a reference with any value from '0' to
                        'F')
            '80' to '8E' Proprietary
                """
                padding_indicator = stringtoint(value[0])
                plain = self.decipher(tag, 0x80, value[1:])
                plain = vsCrypto.strip_padding(self.ct.blocklength, plain,
                                               padding_indicator)
                return_data.append(plain)

            # SM data objects for authentication
            if tag == SM_Class["CHECKSUM"]:
                auth = vsCrypto.append_padding(self.cct.blocklength,
                                               to_authenticate)
                checksum = self.compute_cryptographic_checksum(0x8E, 0x80,
                                                               auth)
                if checksum != value:
                    raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
            elif tag == SM_Class["DIGITAL_SIGNATURE"]:
                auth = to_authenticate  # FIXME: Need padding?
                signature = self.compute_digital_signature(0x9E, 0x9A, auth)
                if signature != value:
                    raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
            elif tag in (SM_Class["HASH_CODE"], SM_Class["HASH_CODE_ODD"]):
                hash = self.hash(p1, p2, to_authenticate)
                if hash != value:
                    raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])

        # Form unprotected CAPDU
        if cla is None:
            cla = CAPDU.cla
        if ins is None:
            ins = CAPDU.ins
        if p1 is None:
            p1 = CAPDU.p1
        if p2 is None:
            p2 = CAPDU.p2
        # FIXME:
        # if expected != "":
        #    raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])

        if isinstance(le, bytes):
            # FIXME: C_APDU only handles le with strings of length 1.
            # Better patch utils.py to support extended length apdus
            le_int = stringtoint(le)
            if le_int == 0 and len(le) > 1:
                le_int = MAX_EXTENDED_LE
            le = le_int

        c = C_APDU(cla=cla, ins=ins, p1=p1, p2=p2, le=le,
                   data="".join(return_data))
        return c
Example #5
0
    def verify(self, p1, p2, data):
        if p1 == 0x80 and p2 == 0x00:
            if self.current_SE.eac_step == 6:
                # data should only contain exactly OID
                [(tag, _, value)] = structure = unpack(data)
                if tag == 6:
                    mapped_algo = ALGO_MAPPING[value]
                    eid = self.mf.select(
                        'dfname', '\xe8\x07\x04\x00\x7f\x00'
                        '\x07\x03\x02')
                    if mapped_algo == "DateOfExpiry":
                        [(_, _, [(_, _, mine)])] = \
                            unpack(eid.select('fid', 0x0103).data)
                        logging.info("DateOfExpiry: " + str(mine) +
                                     "; reference: " +
                                     str(self.current_SE.at.DateOfExpiry))
                        if self.current_SE.at.DateOfExpiry < mine:
                            print("Date of expiry verified")
                            return SW["NORMAL"], ""
                        else:
                            print("Date of expiry not verified (expired)")
                            return SW["WARN_NOINFO63"], ""
                    elif mapped_algo == "DateOfBirth":
                        [(_, _, [(_, _, mine)])] = \
                            unpack(eid.select('fid', 0x0108).data)
                        # case1: YYYYMMDD -> good
                        # case2: YYYYMM   -> mapped to last day of given month,
                        #                    i.e. YYYYMM31 ;-)
                        # case3: YYYY     -> mapped to YYYY-12-31
                        if len(str(mine)) == 6:
                            mine = int(str(mine) + "31")
                        elif len(str(mine)) == 4:
                            mine = int(str(mine) + "1231")
                        logging.info("DateOfBirth: " + str(mine) +
                                     "; reference: " +
                                     str(self.current_SE.at.DateOfExpiry))
                        if self.current_SE.at.DateOfBirth < mine:
                            print("Date of birth verified (old enough)")
                            return SW["NORMAL"], ""
                        else:
                            print("Date of birth not verified (too young)")
                            return SW["WARN_NOINFO63"], ""
                    elif mapped_algo == "CommunityID":
                        [(_, _, [(_, _, mine)])] = \
                            unpack(eid.select('fid', 0x0112).data)
                        mine = binascii.hexlify(mine)
                        logging.info("CommunityID: " + str(mine) +
                                     "; reference: " +
                                     str(self.current_SE.at.CommunityID))
                        if mine.startswith(self.current_SE.at.CommunityID):
                            print("Community ID verified (living there)")
                            return SW["NORMAL"], ""
                        else:
                            print(
                                "Community ID not verified (not living"
                                "there)")
                            return SW["WARN_NOINFO63"], ""
                    else:
                        return SwError(SW["ERR_DATANOTFOUND"])
                else:
                    return SwError(SW["ERR_DATANOTFOUND"])

        else:
            return SAM.verify(self, p1, p2, data)
Example #6
0
 def notImplemented(*argz, **args):
     raise SwError(SW["ERR_INSNOTSUPPORTED"])