Beispiel #1
0
    def external_authenticate(self, p1, p2, resp_data):
        """Performs the basic access control protocol as defined in
        the ICAO MRTD standard"""
        rnd_icc = self.last_challenge

        # Receive Mutual Authenticate APDU from terminal
        # Decrypt data and check MAC
        Eifd = resp_data[:-8]
        padded_Eifd = vsCrypto.append_padding(self.current_SE.cct.blocklength,
                                              Eifd)
        Mifd = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eifd)
        # Check the MAC
        if not Mifd == resp_data[-8:]:
            raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
        # Decrypt the data
        plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8])
        if plain[8:16] != rnd_icc:
            raise SwError(SW["WARN_NOINFO63"])
        # Extract keying material from IFD, generate ICC keying material
        Kifd = plain[16:]
        rnd_ifd = plain[:8]
        Kicc = urandom(16)
        # Generate Answer
        data = plain[8:16] + plain[:8] + Kicc
        Eicc = vsCrypto.encrypt("DES3-CBC", self.KEnc, data)
        padded_Eicc = vsCrypto.append_padding(self.current_SE.cct.blocklength,
                                              Eicc)
        Micc = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eicc)
        # Derive the final keys and set the current SE
        KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a ^ b)
        self.current_SE.ct.key = self.derive_key(KSseed, 1)
        self.current_SE.cct.key = self.derive_key(KSseed, 2)
        self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
        return SW["NORMAL"], Eicc + Micc
Beispiel #2
0
    def __replace_tag(self, tag, data):
        """
        Adjust the config string using a given tag, value combination. If the
        config string already contains a tag, value pair for the given tag,
        replace it. Otherwise append tag, length and value to the config string.
        """
        position = 0
        while position < len(self.__config_string) and \
                self.__config_string[position] != tag:
            length = stringtoint(self.__config_string[position + 1])
            position += length + 3

        if position < len(self.__config_string):  #Replace Tag
            length = stringtoint(self.__config_string[position + 1])
            self.__config_string = self.__config_string[:position] +\
                                   chr(tag) + inttostring(len(data)) + data +\
                                   self.__config_string[position+2+length:]
        else:  #Add new tag
            self.__config_string += chr(tag) + inttostring(len(data)) + data
Beispiel #3
0
    def __replace_tag(self, tag, data):
        """
        Adjust the config string using a given tag, value combination. If the
        config string already contains a tag, value pair for the given tag,
        replace it. Otherwise append tag, length and value to the config string.
        """
        position = 0
        while position < len(self.__config_string) and \
                self.__config_string[position] != tag:    
            length = stringtoint(self.__config_string[position+1])
            position += length + 3

        if position < len(self.__config_string): #Replace Tag
            length = stringtoint(self.__config_string[position+1])
            self.__config_string = self.__config_string[:position] +\
                                   chr(tag) + inttostring(len(data)) + data +\
                                   self.__config_string[position+2+length:]
        else: #Add new tag
            self.__config_string += chr(tag) + inttostring(len(data)) + data
Beispiel #4
0
    def create(p1, p2, data):

        if data[0:2] != "\xff\xff":
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        args = {
                "parent": None,
                "filedescriptor": 0,
                "fid": stringtoint(data[4:6]),
                }
        if data[6] == "\x01":
            args["data"] = bytes(0)*stringtoint(data[2:4])
            args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
            new_file = TransparentStructureEF(**args)
        elif data[6] == "\x02":
            if len(data) > 16:
                args["maxrecordsize"] = stringtoint(data[16])
            elif p2:
                # if given a number of records
                args["maxrecordsize"] = (stringtoint(data[2:4]) / p2)
            args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_"
                                         "NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x03":
            args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_"
                                         "NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x04":
            args["filedescriptor"] = FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x38":
            if data[12] != "\x03":
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])
            new_file = DF(**args)
        else:
            logging.error("unknown type: 0x%x" % ord(data[6]))
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        return [new_file]
Beispiel #5
0
    def create(p1, p2, data):

        if data[0:2] != "\xff\xff":
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        args = {
            "parent": None,
            "filedescriptor": 0,
            "fid": stringtoint(data[4:6]),
        }
        if data[6] == "\x01":
            args["data"] = chr(0) * stringtoint(data[2:4])
            args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
            new_file = TransparentStructureEF(**args)
        elif data[6] == "\x02":
            if len(data) > 16:
                args["maxrecordsize"] = stringtoint(data[16])
            elif p2:
                # if given a number of records
                args["maxrecordsize"] = (stringtoint(data[2:4]) / p2)
            args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_"
                                         "NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x03":
            args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_"
                                         "NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x04":
            args["filedescriptor"] = FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"]
            new_file = RecordStructureEF(**args)
        elif data[6] == "\x38":
            if data[12] != "\x03":
                raise SwError(SW["ERR_INCORRECTPARAMETERS"])
            new_file = DF(**args)
        else:
            logging.error("unknown type: 0x%x" % ord(data[6]))
            raise SwError(SW["ERR_INCORRECTPARAMETERS"])

        return [new_file]
Beispiel #6
0
    def _get_referenced_key(self, p1, p2):
        """
        This method returns the key specified by the p2 parameter. The key may
        be stored on the cards filesystem.

        :param p1: Specifies the algorithm to use. Needed to know the keylength.
        :param p2: Specifies a reference to the key to be used for encryption

            == == == == == == == == =============================================
            b8 b7 b6 b5 b4 b3 b2 b1 Meaning
            == == == == == == == == =============================================
            0  0  0  0  0  0  0  0  No information is given
            0  -  -  -  -  -  -  -  Global reference data(e.g. MF specific key)
            1  -  -  -  -  -  -  -  Specific reference data(e.g. DF specific key)
            -  -  -  x  x  x  x  x  Number of the secret
            == == == == == == == == =============================================

            Any other value RFU
        """

        key = None
        qualifier = p2 & 0x1F
        algo = get_referenced_cipher(p1)
        keylength = vsCrypto.get_cipher_keylen(algo)

        if (p2 == 0x00):  #No information given, use the global card key
            key = self.cardSecret
        #We treat global and specific reference data alike
        #elif ((p2 >> 7) == 0x01 or (p2 >> 7) == 0x00):
        else:
            #Interpret qualifier as an short fid (try to read the key from FS)
            if self.mf == None:
                raise SwError(SW["ERR_REFNOTUSABLE"])
            df = self.mf.currentDF()
            fid = df.select("fid", stringtoint(qualifier))
            key = fid.readbinary(keylength)

        if key != None:
            return key
        else:
            raise SwError(SW["ERR_REFNOTUSABLE"])
Beispiel #7
0
    def _get_referenced_key(self, p1, p2):
        """
        This method returns the key specified by the p2 parameter. The key may
        be stored on the cards filesystem.

        :param p1: Specifies the algorithm to use. Needed to know the keylength.
        :param p2: Specifies a reference to the key to be used for encryption

            == == == == == == == == =============================================
            b8 b7 b6 b5 b4 b3 b2 b1 Meaning
            == == == == == == == == =============================================
            0  0  0  0  0  0  0  0  No information is given
            0  -  -  -  -  -  -  -  Global reference data(e.g. MF specific key)
            1  -  -  -  -  -  -  -  Specific reference data(e.g. DF specific key)
            -  -  -  x  x  x  x  x  Number of the secret
            == == == == == == == == =============================================

            Any other value RFU
        """
        
        key = None
        qualifier = p2 & 0x1F
        algo = get_referenced_cipher(p1)        
        keylength = vsCrypto.get_cipher_keylen(algo)

        if (p2 == 0x00): #No information given, use the global card key
            key = self.cardSecret
        #We treat global and specific reference data alike
        #elif ((p2 >> 7) == 0x01 or (p2 >> 7) == 0x00):
        else:		
            #Interpret qualifier as an short fid (try to read the key from FS)
            if self.mf == None:
                raise SwError(SW["ERR_REFNOTUSABLE"])
            df = self.mf.currentDF()
            fid = df.select("fid", stringtoint(qualifier))
            key = fid.readbinary(keylength)

        if key != None:
            return key
        else: 
            raise SwError(SW["ERR_REFNOTUSABLE"])
Beispiel #8
0
def decodeOffsetDataObjects(tlv_data):
    offsets = []
    for (tag, length, newvalue) in tlv_find_tag(tlv_data, TAG["OFFSET_DATA"]):
        offsets.append(stringtoint(newvalue))
    return offsets
Beispiel #9
0
def decodeOffsetDataObjects(tlv_data): 
    offsets = []
    for (tag, length, newvalue) in tlv_find_tag(tlv_data,
            TAG["OFFSET_DATA"]):
        offsets.append(stringtoint(newvalue))
    return offsets
Beispiel #10
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 == None:
            cla = CAPDU.cla
        if ins == None:
            ins = CAPDU.ins
        if p1 == None:
            p1 = CAPDU.p1
        if p2 == None:
            p2 = CAPDU.p2
        # FIXME
        #if expected != "":
        #raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])

        if isinstance(le, str):
            # 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
Beispiel #11
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 == None:
            cla = CAPDU.cla
        if ins == None:
            ins = CAPDU.ins
        if p1 == None:
            p1 = CAPDU.p1
        if p2 == None:
            p2 = CAPDU.p2
        if le == None:
            le = CAPDU.le
        # FIXME
        #if expected != "":
            #raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])

        if isinstance(le, str):
            # 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