From 54faade586a6e66623269efa3281430fd7a0de6c Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Tue, 4 Jul 2017 19:46:17 +0900 Subject: [PATCH 1/7] [Bug Fix] Maximum value of Le byte is 255(0xFF). --- pygp/connection/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygp/connection/connection.py b/pygp/connection/connection.py index 4314e12..89d077d 100644 --- a/pygp/connection/connection.py +++ b/pygp/connection/connection.py @@ -186,7 +186,7 @@ def send_apdu(bytelist_capdu): capdu_Le = len(bytelist_rapdu) if capdu_Le == 0x00: - capdu_Le = 256 + capdu_Le = 255 bytelist_capdu[4] = bytelist_rapdu[1] #resend the command log_management_apdu(TO_CARD,bytelist_capdu) From 45f9c0ab153cfa65fdbfc770a39f4eac95579ecb Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Thu, 13 Jul 2017 12:26:38 +0900 Subject: [PATCH 2/7] Revert "[Bug Fix] Maximum value of Le byte is 255(0xFF)." This reverts commit 54faade586a6e66623269efa3281430fd7a0de6c. --- pygp/connection/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygp/connection/connection.py b/pygp/connection/connection.py index 89d077d..4314e12 100644 --- a/pygp/connection/connection.py +++ b/pygp/connection/connection.py @@ -186,7 +186,7 @@ def send_apdu(bytelist_capdu): capdu_Le = len(bytelist_rapdu) if capdu_Le == 0x00: - capdu_Le = 255 + capdu_Le = 256 bytelist_capdu[4] = bytelist_rapdu[1] #resend the command log_management_apdu(TO_CARD,bytelist_capdu) From 9258e5c8e7643e656ceb8ff348b252ca21c62d2c Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Mon, 31 Jul 2017 12:09:28 +0900 Subject: [PATCH 3/7] Bug fix : Can't load applet if the size of CAP is bigger than 64KByte. --- pygp/loadfile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygp/loadfile.py b/pygp/loadfile.py index 496c7aa..4fa001a 100644 --- a/pygp/loadfile.py +++ b/pygp/loadfile.py @@ -70,8 +70,10 @@ def __createHeaderSize__(self): return headerSize + utils.intToHexString(length) elif (length < 256 ): return headerSize + '81' + utils.intToHexString(length) - else: + elif (length < 65536): return headerSize + '82' + utils.intToHexString(length, 2) + else: + return headerSize + '83' + utils.intToHexString(length, 3) def get_raw_code(self): ''' Returns the raw code of the load file as string (ie. All components excluding Descriptor and Debug Component)''' From 4913fa0b2c5e24b19a6fb97c0854292588e89bdc Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Mon, 7 Aug 2017 17:48:15 +0900 Subject: [PATCH 4/7] Set block number to 00 to start from it when block number reach to 0xFF. --- pygp/gp/gp_functions.py | 3500 ++++++++++++++++++++------------------- 1 file changed, 1751 insertions(+), 1749 deletions(-) diff --git a/pygp/gp/gp_functions.py b/pygp/gp/gp_functions.py index b5d3402..e95feca 100644 --- a/pygp/gp/gp_functions.py +++ b/pygp/gp/gp_functions.py @@ -1,1749 +1,1751 @@ -import time -import pygp.crypto as crypto -from pygp.logger import * -from pygp.connection.connection import * -from pygp.error import * -from pygp.constants import * -from pygp.gp.gp_crypto import * -import pygp.loadfile as loadfile -from pygp.tlv import * - - - -# global variable for gp_functions -# information of each channel. -# [0] for channel 0, [1] for channel 1, [2] for channel 2, [3] for channel 3 -# [4] it save current working channel number. This channel number used when sending APDU. -securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] - -last_apdu_response = None -last_apdu_status = None -apdu_timing = False - -payload_mode_activated = False -payload_list = [] - -total_time = 0.0 - -def clear_securityInfo(): - global securityInfo - securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] - -def get_payload_list(): - global payload_list - return payload_list - -def set_payload_mode(activate): - global payload_mode_activated - global payload_list - payload_mode_activated = activate - # clear the list on activation - if (activate == True): - payload_list.clear() - -def last_response(): - global last_apdu_response - return last_apdu_response - -def last_status(): - global last_apdu_status - return last_apdu_status - -def set_apdu_timing(activated): - global apdu_timing - apdu_timing = activated - -def set_start_timing(): - global total_time - total_time = 0.0 - -def get_total_execution_time(): - return total_time - -def __check_security_info__(security_info): - if security_info == None: - error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) - return error_status - else: - error_status = create_no_error_status(0x00) - return error_status - - -def select_channel(logical_channel): - global securityInfo - - log_start("select_channel") - - # Display All Channel Information - logger.log_info("Channel Status Information [0~3]") - logger.log_info("Channel SCP SCPi SecurityLevel AID") - logger.log_info("------- --- ---- ------------- ------------------") - channel_id = 0 - for sChannelInfo in securityInfo[0:4]: - if (securityInfo[4] == channel_id): - strWorking = '*' - else: - strWorking = ' ' - if (('channelStatus' in sChannelInfo.keys()) and (sChannelInfo['channelStatus'] == "ON")): - if 'selectedAID' in sChannelInfo.keys(): - strAID = sChannelInfo['selectedAID'] - else: - strAID = 'NONE' - - if 'secureChannelProtocolImpl' in sChannelInfo.keys(): - logger.log_info(" %s 0%d %d %s %d %s" \ - %(strWorking, channel_id, sChannelInfo['secureChannelProtocol'],\ - sChannelInfo['secureChannelProtocolImpl'], sChannelInfo['securityLevel'], strAID )) - else: - logger.log_info(" %s 0%d n/a n/a n/a %s" %(strWorking, channel_id, strAID)) - else: - logger.log_info(" 0%d Not Available" %(channel_id)) - - channel_id += 1 - - # check the parameter value - if logical_channel == None or logical_channel < 0x00 or logical_channel > 0x03: - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - log_info("\tInvalid logical channel (%-0.2X)" % securityInfo[4]) - return error_status - - # check the status of logical channel - sChannelInfo = securityInfo[logical_channel] - if sChannelInfo['channelStatus'] == "ON": - # set the working channel value - securityInfo[4] = logical_channel - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_info("\tLogical channel has changed to %-0.2X" % securityInfo[4]) - else: - error_status = create_error_status(ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE, runtimeErrorDict[ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE]) - - log_end("select_channel", error_status) - - return error_status - - -def wrap_command(security_info, capdu): - ''' Wrap APDU according to the security info ''' - #TODO: update doc - - log_start("wrap_command") - - # no security level defined, just return - if (security_info == None): - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - return error_status, capdu - - # security_info level defined - if ('securityLevel' in security_info) == False: - error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) - return error_status, '' - - # trivial case, just return - if security_info['securityLevel'] == SECURITY_LEVEL_NO_SECURE_MESSAGING: - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - return error_status, capdu - - # Determine which type of Exchange between the reader - # Convert capdu from string to list of bytes - bytelist_capdu = toByteArray(capdu) - - ISO_case = -1 - Le = None - - if len(bytelist_capdu) == 4: - ISO_case = CASE_1 - - elif len(bytelist_capdu) == 5: - ISO_case = CASE_2S - Le = bytelist_capdu[4] - - else: - if (bytelist_capdu[4] != 0): - if (bytelist_capdu[4] == len(bytelist_capdu) - 5): - ISO_case = CASE_3S - elif (bytelist_capdu[4] == len(bytelist_capdu) - 6): - ISO_case = CASE_4S - Le = bytelist_capdu[-1:] - else: - pass - - - if ISO_case == -1: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, '' - - # Manage ISO case: Get Le if any and prepare APDU - - apdu_to_wrap = [] - - - # C_MAC on modified APDU - if ( security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or - security_info['secureChannelProtocol'] == GP_SCP03): - - - ############## ISO Case 1 & 2 ############## - if ISO_case == CASE_1: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap.append(0x08) - - elif ISO_case == CASE_2S: - Le = bytelist_capdu[-1:] - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = 0x08 - - elif ISO_case == CASE_3S: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 - - elif ISO_case == CASE_4S: - Le = bytelist_capdu[-1:] - apdu_to_wrap = bytelist_capdu[:-1] - # put lc with the length of the mac - apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 - - else: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, None - - #CLA - indicate security level 1 or 3 - apdu_to_wrap[0] = bytelist_capdu[0] | 0x04 - else: - # C_MAC on unmodified APDU - ############## ISO Case 1 & 2 ############## - if ISO_case == CASE_1: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap.append(0x00) - - elif ISO_case == CASE_2S: - Le = bytelist_capdu[:-1] - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = 0x00 - - elif ISO_case == CASE_4S: - Le = bytelist_capdu[:-1] - apdu_to_wrap = bytelist_capdu[:-1] - - - else: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, None - - # ICV encryption - iv = None - if security_info['secureChannelProtocol'] == GP_SCP02: - if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1A or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1B or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55) : - - iv = encipher_iv_SCP02(security_info['lastC_MAC'], security_info['C_MACSessionKey'][:16]) - - elif(security_info['secureChannelProtocol']== GP_SCP03): - iv = crypto.ISO_9797_M1_Padding_left(intToHexString(security_info['icv_counter'],2), 16) - iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - # Get the data field of the APDU - encData = apdu_to_wrap[5:] - # if we have to encrypt: - if (security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC_R_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC): - # retrieve only the data field (ie: remove APDU header) - apdu_data_field = apdu_to_wrap[5:] - - # cipher data - if security_info['secureChannelProtocol'] == GP_SCP02: - encData = encipher_data_SCP02(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , crypto.ICV_NULL_8) - encData = toByteArray(encData) # type mismatch - - log_debug("wrap_command: encrypted data field: %s" %toHexString(encData)) - - elif(security_info['secureChannelProtocol']== GP_SCP03): - encData = encipher_data_SCP03(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , iv) - - encData = toByteArray(encData) # type mismatch - # re put lc with the length of the encipher data - apdu_to_wrap[4] = len(encData) + 8 # enc data - - - # MAC calculation - if security_info['secureChannelProtocol'] == GP_SCP02: - mac = calculate_mac_SCP02(toHexString(apdu_to_wrap), security_info['C_MACSessionKey'], iv) - # re put lc with the length of the encipher data - apdu_to_wrap[4] = len(encData) + 8# enc data - elif(security_info['secureChannelProtocol']== GP_SCP03): - mac = calculate_mac_SCP03(toHexString(apdu_to_wrap[:5]) + toHexString(encData), security_info['C_MACSessionKey'], security_info['lastC_MAC']) - pass - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - log_debug("wrap_command: Data for MAC computation: %s" %toHexString(apdu_to_wrap)) - log_debug("wrap_command: ICV for MAC: %s" %iv) - log_debug("wrap_command: Generated MAC: %s" %mac) - - #update the last iv - security_info['lastC_MAC'] = mac - - # create the wrapped APDU - if security_info['secureChannelProtocol'] == GP_SCP02: - wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac - - elif(security_info['secureChannelProtocol'] == GP_SCP03): - wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac[:16] - # don't forget tp update the counter for ICV_NULL_8 - log_debug("wrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] ,2)) - security_info['icv_counter'] = security_info['icv_counter'] + 1 - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - # we have to put Le bytes !! and manage RMAC level - if Le != None: - wrappedAPDU = wrappedAPDU + toHexString(Le) - - error_status = create_no_error_status(0x00) - - log_end("wrap_command", error_status) - - return error_status, wrappedAPDU - - -def unwrap_command(security_info, rapdu): - ''' unwrap APDU response according to the security info ''' - #TODO: update doc - - log_start("unwrap_command") - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - - # no security level defined, just return - if (security_info == None): - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_end("unwrap_command") - return error_status, rapdu - - # trivial case, just return - if (security_info['securityLevel'] != SECURITY_LEVEL_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_MAC_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_C_MAC_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC) : - - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_end("unwrap_command") - return error_status, rapdu - - # convert to byte array - bytelist_rapdu = toByteArray(rapdu) - if security_info['secureChannelProtocol'] == GP_SCP02: - # only status word so no RMAC - if len(bytelist_rapdu) == 2: - return error_status, rapdu - #TODO - - elif(security_info['secureChannelProtocol'] == GP_SCP03): - # only status word so no RMAC - if len(bytelist_rapdu) == 2: - return error_status, rapdu - else: - - # get the mac of the command (8 bytes before the rapdu status word) - len_reponse_date_without_sw = len(bytelist_rapdu) - 2 - len_response_data_without_mac = len_reponse_date_without_sw - 8 - response_data_without_mac = bytelist_rapdu[0:len_response_data_without_mac] - response_data_mac = bytelist_rapdu[ len_response_data_without_mac: len_reponse_date_without_sw] - response_data_sw = bytelist_rapdu[len_reponse_date_without_sw:] - log_debug("unwrap_command: Response MAC: %s" %toHexString(response_data_mac)) - # calculate the off card MAC - mac = calculate_mac_SCP03(toHexString(response_data_without_mac) + toHexString(response_data_sw), security_info['R_MACSessionKey'], security_info['lastC_MAC']) - log_debug("unwrap_command: Data for MAC computation: %s" %(toHexString(response_data_without_mac) + toHexString(response_data_sw))) - log_debug("unwrap_command: ICV for Generated MAC: %s" %security_info['lastC_MAC']) - log_debug("unwrap_command: Generated MAC: %s" %mac) - if toHexString(response_data_mac) != mac[:16]: - error_status = create_error_status(ERROR_VALIDATION_R_MAC, runtimeErrorDict[ERROR_VALIDATION_R_MAC]) - log_end("unwrap_command") - return error_status, None - else: - # check if we have to decipher the APDU - if security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC: - if(security_info['secureChannelProtocol']== GP_SCP03): - log_debug("unwrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] - 1,2)) - iv = crypto.ISO_9797_M2_Padding_left(intToHexString(security_info['icv_counter']-1 ,2), 16) - iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) - log_debug("unwrap_command: current ICV : %s " %iv) - - # decipher data - decipher_data = decipher_data_SCP03(toHexString(response_data_without_mac), security_info['encryptionSessionKey'], iv) - decipher_data = crypto.Remove_ISO_9797_M2_Padding(decipher_data) - return error_status, decipher_data + toHexString(response_data_sw) - - pass - else: - return error_status, toHexString(bytelist_rapdu) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("unwrap_command") - return error_status, None - - -def send_APDU(capdu, raw_mode = False, exsw = None, exdata = None): - - global securityInfo - global last_apdu_response - global last_apdu_status - global apdu_timing - global payload_mode_activated - global payload_list - global total_time - - log_start("send_APDU") - #TODO: managing security info wrap the command - - if raw_mode == True: - # no wrapping management, just send the apdu - c_wrapped_apdu = capdu - else: - # get securityInfo of the channel - currentChannel = securityInfo[4] - securityInfoOfChannel = securityInfo[currentChannel] - - # wrap command - error_status, c_wrapped_apdu = wrap_command(securityInfoOfChannel, capdu) - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == True: - payload_list.append(remove_space(c_wrapped_apdu)) - error_status = create_no_error_status(0x00) - return error_status, None - else: - #convert capdu from string to list of bytes - bytelist_capdu = toByteArray(c_wrapped_apdu) - - if raw_mode == False: - # manage the selected logical channel - bytelist_capdu[0] |= securityInfo[4] - - start_time = time.perf_counter() - - error_status, rapdu = send_apdu(bytelist_capdu) - - end_time = time.perf_counter() - total_time = total_time + (end_time - start_time) - - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if raw_mode == True: - c_unwrapped_rapdu = rapdu - else: - error_status, c_unwrapped_rapdu = unwrap_command(securityInfoOfChannel, rapdu) - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if apdu_timing == True: - log_info("execution time: %.3f sec." %(end_time - start_time)) - # update global variables - last_apdu_response = c_unwrapped_rapdu[:-4] # response without status - last_apdu_status = c_unwrapped_rapdu[-4:] # only status - - # check if it is an ISO7816 status word error - if exsw == None: - error_status = check_ISO7816_status_word(rapdu) - else: - # convert exsw to list, compare between expected status word and response. - # The format of exsw "9000, 6Cxx, 6xxx" - exsw = exsw.upper() - exsw = exsw.replace(',', ' ') - exsw = exsw.replace('X', 'F') - byte_list_exsw = toByteArray(exsw) - byte_list_sw = toByteArray(last_apdu_status) - found = False - for offset in range(0, len(byte_list_exsw), 2): - if ((byte_list_exsw[offset] & byte_list_sw[-2]) == byte_list_sw[-2]): - if ((byte_list_exsw[offset+1] & byte_list_sw[-1]) == byte_list_sw[-1]): - # the status word is same as our expectation - error_status = create_no_error_status(last_apdu_status) - found = True - if found == False: - error_status = create_error_status(last_apdu_status, "Differ with expected status word") - log_error("expected status word " + exsw) - #log_end("send_APDU", error_status['errorStatus']) - - if (exdata != None) and (error_status['errorStatus'] == 0x00): - exdata = exdata.upper() - exdata = exdata.replace(' ', '') - for offset in range(0, len(exdata), 1): - if exdata[offset] == 'X': - continue - elif exdata[offset] == rapdu[offset].upper(): - continue - else: - error_status = create_error_status(last_apdu_status, "Differ with expected data") - log_error("expected data " + exdata) - break - - return error_status, rapdu - - -def select_issuerSecurityDomain(logical_channel = 0): - - log_start("select_issuerSecurityDomain") - - if(logical_channel < 0 or logical_channel > 3): - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - return error_status, None - - capdu = intToHexString(logical_channel, 1) + " A4 04 00 00" - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: - log_end("select_issuerSecurityDomain", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so the application is selected. now store its aid - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != '6F': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - for response_tlv in response_tlv.list_childs_tlv(): - if response_tlv.getTAG() == '84': - current_selected_aid = response_tlv.getValue() - else: - current_selected_aid = ISD_APPLICATION_AID - - # there is no error. Do initialize securityInfo of selected channel - securityInfo[4] = logical_channel - securityInfo[logical_channel] = {} - securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING - securityInfo[logical_channel]['channelStatus'] = "ON" - securityInfo[logical_channel]['selectedAID'] = current_selected_aid - log_end("select_issuerSecurityDomain", error_status['errorStatus'], error_status['errorMessage']) - - return error_status, rapdu - - -def select_application(str_AID, logical_channel = 0): - - log_start("select_application") - - if(logical_channel < 0 or logical_channel > 3): - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - return error_status, None - - capdu = intToHexString(logical_channel, 1) + " A4 04 00 " + lv (str_AID) - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: - log_end("select_application", error_status['errorStatus']) - return error_status, None - - # there is no error. Do initialize securityInfo of selected channel - securityInfo[4] = logical_channel - securityInfo[logical_channel] = {} - securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING - securityInfo[logical_channel]['channelStatus'] = "ON" - securityInfo[logical_channel]['selectedAID'] = str_AID - log_end("select_application", error_status['errorStatus']) - - return error_status, rapdu - - -def set_status(cardElement, lifeCycleState, aid): - - log_start("set_status") - # supress blank if any - import re - aid = ''.join( re.split( '\W+', aid.upper() ) ) - - capdu = "80 F0 " + cardElement + lifeCycleState + lv (aid) - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("set_status", error_status['errorStatus']) - return error_status - - log_end("set_status", error_status['errorStatus']) - - return error_status - - -def set_crs_status(status_type, status_value, aid): - - log_start("set_crs_status") - # supress blank if any - import re - aid = ''.join( re.split( '\W+', aid.upper() ) ) - - capdu = "80 F0 " + status_type + status_value + "4F" + lv(aid) - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("set_crs_status", error_status['errorStatus']) - return error_status, None - - log_end("set_crs_status", error_status['errorStatus']) - - #TODO: needs to parse the response data based on the GP Amdt.C Table 3-23 - - return error_status, rapdu - - -def delete_application(str_AID, exsw): - - log_start("delete_application") - - capdu = "80 E4 00 00 " + lv ('4F' + lv(str_AID)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("select_application", error_status['errorStatus']) - return error_status - - log_end("select_application", error_status['errorStatus']) - - return error_status - - -def delete_package(str_AID, exsw): - - log_start("delete_package") - - capdu = "80 E4 00 80 " + lv ('4F' + lv(str_AID)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("delete_package", error_status['errorStatus']) - return error_status - - log_end("delete_package", error_status['errorStatus']) - - return error_status - - -def delete_key(KeyIdentifier, keyVersionNumber, exsw): - - log_start("delete_key") - - capdu = "80 E4 00 00 " + lv('D0' + lv(KeyIdentifier)) + lv('D2' + lv(keyVersionNumber)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("delete_key", error_status['errorStatus']) - return error_status - - log_end("delete_key", error_status['errorStatus']) - - return error_status - - -def get_cplc_data(): - - log_start("get_cplc_data") - - capdu = "80 CA 9F 7F 00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_cplc_data", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so display results - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != '9F7F': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - - log_end("get_cplc_data", error_status['errorStatus']) - - return error_status, response_tlv.getValue() - else: - return error_status, None - - -def get_key_information_template(): - - log_start("get_key_information_template") - - capdu = "80 CA 00 E0 00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_key_information_template", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so display results - # key information is list of Tuple : (Key id, Key version number, KeyLength, KeyType) - keyInformation = [] - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != 'E0': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - # manage the list of TLV with tag C0 into this response_tlv - key_info_list = response_tlv.list_childs_tlv() - for key_info in key_info_list: - - if key_info.getTAG() != 'C0': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - else: - index = 0 - KeyIdentifier = key_info.getValue()[index: index + 2] - index = index + 2 - KeyVersionNumber = key_info.getValue()[index: index + 2] - index = index + 2 - #check key type : if coded with 2 bytes the key is in format 2 - KeyTypeIndex = key_info.getValue()[index: index + 2] - - if (KeyTypeIndex == 'FF'): - p_bool_Iskey_format2 = True - index = index + 2 - while (KeyTypeIndex == 'FF'): - KeyType = key_info.getValue()[index: index + 2] # 1 byte - KeyLength= key_info.getValue()[index + 2: index + 6] # 2 bytes - keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyTypeIndex + KeyType)) - KeyTypeIndex = key_info.getValue()[index + 6: index + 8] - index = index + 8 - #get the key usage and key access - keyUsageLength = KeyTypeIndex - if keyUsageLength != '00': - keyUsage =key_info.getValue()[index: index + 2] - index = index + 2 - - keyAccessLength = key_info.getValue()[index: index + 2] - if keyAccessLength != '00': - keyAccess = key_info.getValue()[index + 2: index + 4] - index = index + 4 - else: - index = index + 2 - - else: - while index < (len(key_info.getValue())): - KeyType = key_info.getValue()[index: index + 2] - index = index + 2 - KeyLength= key_info.getValue()[index: index + 2] - index = index + 2 - keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyType)) - - else: - keyInformation = [] - - - log_end("get_key_information_template", error_status['errorStatus']) - - return error_status, keyInformation - - -def store_data(data): - - log_start("store_data") - block_size =239 # 255 bytes minus 8 byte MAC minus 8 byte encryption padding - block_number = 0x00 - - # supress blank if any - import re - data = ''.join( re.split( '\W+', data.upper() ) ) - # convert to byte array - bytelist_data = toByteArray(data) - remaining_bytes = len(bytelist_data) - read_bytes = 0x00 - while remaining_bytes > 0: - # build the APDU - capdu = "80 E2 " - if remaining_bytes <= block_size: - capdu = capdu + '80' + intToHexString(block_number) - capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + remaining_bytes]) ) - read_bytes = read_bytes + remaining_bytes - remaining_bytes = remaining_bytes - remaining_bytes - else: - capdu = capdu + '00' + intToHexString(block_number) - capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + block_size]) ) - read_bytes = read_bytes + block_size - remaining_bytes = remaining_bytes - block_size - - # send the APDU - error_status, rapdu = send_APDU(capdu) - block_number = block_number + 1 - - if error_status['errorStatus'] != 0x00: - log_end("store_data", error_status['errorStatus']) - return error_status - - - if error_status['errorStatus'] != 0x00: - log_end("get_data", error_status['errorStatus']) - return error_status - - log_end("store_data", error_status['errorStatus']) - - return error_status - - -def get_data(identifier): - - log_start("get_data") - - # build the APDU - # supress blank if any - import re - identifier = ''.join( re.split( '\W+', identifier.upper() ) ) - # check the size of the identifier (it is a string so 2 byte correspond to 4 characters ) - if len(identifier) < 0x00 or len(identifier) > 0x04: - # identifier must be 1 or two byte string - error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) - return error_status, None - - capdu = "80 CA " - - if len(identifier) == 0x04: - capdu = capdu + identifier + '00' - else: - #one byte identifier - capdu = capdu + identifier + '00' + '00' - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_data", error_status['errorStatus']) - return error_status, None - - log_end("get_data", error_status['errorStatus']) - - return error_status, rapdu - - -def get_status(card_element): - - log_start("get_status") - - # build the APDU - # supress blank if any - import re - card_element = ''.join( re.split( '\W+', card_element.upper() ) ) - - capdu = "80 F2 " + card_element + "02 02 4F 00" + "00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_status", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # store the response - card_response = last_response() - # check if more data available - while last_status() == '6310': - # send a get status next occurence - capdu = "80 F2 " + card_element + "03 02 4F 00" + "00" - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_status", error_status['errorStatus']) - return error_status, None - - card_response = card_response + last_response() - else: - card_response = None - error_status = create_no_error_status(0x00) - - - log_end("get_status", error_status['errorStatus']) - - return error_status, card_response - - - -def get_crs_status(aid, tag_list): - - log_start("get_crs_status") - - # build data field tag with given aid and tag list - if aid == '': data_field = "4F00" - else: data_field = "4F" + lv(aid) - - if tag_list == '': pass - else: data_field = data_field + "5C" + lv(tag_list) - - capdu = "80 F2 40 00 " + lv(data_field) + "00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_crs_status", error_status['errorStatus']) - return error_status, None - - # store the response - card_response = last_response() - # check if more data available - while last_status() == '6310': - # send a get status next occurence - capdu = "80 F2 40 01 " + lv(data_field) + "00" - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_crs_status", error_status['errorStatus']) - return error_status, None - - card_response = card_response + last_response() - - # we have the card_response TLV. create the get status response dictionnary - response_tlvs = TLVs(toByteArray(card_response)) - app_info_list = [] - app_aid = None - app_lifecycle = None - uniform_resource_locator = None - app_image_template = None - display_message = None - app_update_counter = None - selection_priority = None - app_group_head = None - app_group_members = None - crel_application_aid_list = None - policy_restricted_app = None - app_discretionary_data = None - application_family = None - display_required_indicator = None - assinged_protocol = None - continuous_processing = None - recognition_algorithm = None - - - for response_tlv in response_tlvs.list_childs_tlv(): - if response_tlv.getTAG() == '61': - # manage the list of TLV into this response_tlv - app_info_tlv_list = response_tlv.list_childs_tlv() - for app_info in app_info_tlv_list: - if app_info.getTAG() == '4F': app_aid = app_info.getValue() - elif app_info.getTAG() == '9F70': app_lifecycle = app_info.getValue() - elif app_info.getTAG() == '7F20': - display_control_tlv_list = app_info.list_childs_tlv() - for display_control_info in display_control_tlv_list: - if display_control_info.getTAG() == '5F50': - uniform_resource_locator = display_control_info.getValue() - elif display_control_info.getTAG() == '6D': - app_image_template = display_control_info.getValue() - elif display_control_info.getTAG() == '5F45': - display_message = display_control_info.getValue() - elif app_info.getTAG() == '80': app_update_counter = app_info.getValue() - elif app_info.getTAG() == '81': selection_priority = app_info.getValue() - elif app_info.getTAG() == 'A2': - app_group_head_tlv_list = app_info.list_childs_tlv() - for app_group_head in app_group_head_tlv_list: - if app_group_head.getTAG() == '4F': - app_group_head = app_group_head.getValue() - # below 3 parameters tag 'A3' to 'A5' can be multiple so need to find a better way to handle - elif app_info.getTAG() == 'A3': - app_group_members_tlv_list = app_info.list_childs_tlv() - for app_group_info in app_group_members_tlv_list: - if app_group_info.getTAG() == '4F': - app_group_members = app_group_info.getValue() - elif app_info.getTAG() == 'A4': - crel_app_aid_list_tlv_list = app_info.list_childs_tlv() - for crel_app_aid_info in crel_app_aid_list_tlv_list: - if crel_app_aid_info.getTAG() == '4F': - crel_app_aid_list = crel_app_aid_info.getValue() - elif app_info.getTAG() == 'A5': - policy_restricted_app_tlv_list = app_info.list_childs_tlv() - for policy_restricted_app_info in policy_restricted_app_tlv_list: - if policy_restricted_app_info.getTAG() == '4F': - policy_restricted_app = policy_restricted_app_info.getValue() - elif app_info.getTAG() == 'A6': app_discretionary_data = app_info.getValue() - elif app_info.getTAG() == '87': app_family = app_info.getValue() - elif app_info.getTAG() == '88': display_required_indicator = app_info.getValue() - elif app_info.getTAG() == '8C': assinged_protocol = app_info.getValue() - elif app_info.getTAG() == '8A': continuous_processing = app_info.getValue() - elif app_info.getTAG() == '8B': recognition_algorithm = app_info.getValue() - app_info_list.append({'aid':app_aid, 'lifecycle':app_lifecycle[:2], \ - 'app update counter':app_update_counter, 'selection priority':selection_priority, \ - 'app group head':app_group_head, 'app group members':app_group_members, \ - 'crel app list':crel_app_aid_list, 'policy restricted app':policy_restricted_app, \ - 'app discretionary data':app_discretionary_data, 'app family':app_family, \ - 'display required indicator':display_required_indicator, 'assinged protocol':assinged_protocol, \ - 'continuous processing':continuous_processing, 'recognition algorithm':recognition_algorithm}) - - else: - error_status = create_no_error_status(0x00) - - log_end("get_crs_status", error_status['errorStatus']) - - return error_status, app_info_list - - -def initialize_update(key_set_version , base_key, enc_key , mac_key , dek_key , scp, scp_implementation, sequence_counter = "000000" ): - - global last_apdu_response - global last_apdu_status - global payload_mode_activated - - log_start("initialize_update") - # create a host challenge - hostChallenge = crypto.RANDOM(8) - - log_debug("initialize_update: Host challenge Data: %s " % hostChallenge) - # create the initialize update APDU command (with Le = Max data) - initUpdateAPDU = '80' + '50' + key_set_version + '00 08' + hostChallenge + '00' - error_status, rapdu = send_APDU(initUpdateAPDU) - - if error_status['errorStatus'] != 0x00: - log_end("initialize_update", error_status['errorStatus']) - return error_status, None - - - # Set the security info structure needed for all GP operations - security_info = securityInfo[securityInfo[4]] - - - # checking payload mode - if payload_mode_activated == True: - # update the security information structure - security_info['secureChannelProtocol'] = int(scp, 16) - - if security_info['secureChannelProtocol'] == GP_SCP02: - # manage init update response - # in SCP02 the card challenge is calculated using the session key. So we need to create session key before. - sequenceCounter = toByteArray(sequence_counter) - # just check the sequence counter length. In SCP02 sequence counter is expressedwith 2 bytes - if len(sequenceCounter) != 2: - error_status = create_error_status(ERROR_INCONSISTENT_SEQUENCE_COUNTER, runtimeErrorDict[ERROR_INCONSISTENT_SEQUENCE_COUNTER]) - return error_status, None - - elif security_info['secureChannelProtocol'] == GP_SCP03: - # manage init update response - sequenceCounter = increment(sequence_counter, 0x01) - cardChallenge = calculate_card_challenge_SCP03(sequenceCounter + security_info['selectedAID'], enc_key) - # type coherency - cardChallenge = toByteArray(cardChallenge) - sequenceCounter= toByteArray(sequenceCounter) - log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) - log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - - security_info['secureChannelProtocolImpl'] = scp_implementation - security_info['keySetVersion'] = key_set_version - # we set it to a dummy value - security_info['keyIndex'] = 0xFF - - - - - - else: - - # managing authentication data - bytearray_initUpdateResponse = toByteArray(last_apdu_response) - # check init_update_response length, it must be 28, 29 or 32 bytes - # SCP01/SCP02 = 30 bytes, SCP03 31 or 34 bytes - if len (bytearray_initUpdateResponse) != 28 and len (bytearray_initUpdateResponse) != 29 and len (bytearray_initUpdateResponse) != 32: - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - # managing response of INITIALIZE UPDATE - keyDiversificationData = bytearray_initUpdateResponse[:10] - keyInformationData = bytearray_initUpdateResponse[10:12] - - # check if a scp has been set by the user, if not take the scp into the init update response - if scp == None: - scp = intToHexString(keyInformationData[1]) - - # test if reported SCP is consistent with passed SCP - if int(str(scp), 16) != keyInformationData[1]: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - # update the security information structure - security_info['secureChannelProtocol'] = int(str(scp), 16) - - - # in SCP03 the scp implementation value is returned by the init update response - # in SCP02 this value is not present so this value should be set by the user. - if security_info['secureChannelProtocol'] == GP_SCP02: - if scp_implementation == None: - scp_implementation = intToHexString(0x55) - # error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - # return error_status, None - - if security_info['secureChannelProtocol'] == GP_SCP03: - # key information data on 3 bytes - keyInformationData = bytearray_initUpdateResponse[10:13] - scpi = keyInformationData[2] - if scp_implementation == None: - scp_implementation = intToHexString(scpi) - else: - #test if reported SCP implementation is consistent with passed SCP implementation - if scp_implementation != intToHexString(scpi): - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - return error_status, None - - security_info['secureChannelProtocolImpl'] = scp_implementation - security_info['keySetVersion'] = keyInformationData[0] - # we set it to a dummy value - security_info['keyIndex'] = 0xFF - - if security_info['secureChannelProtocol'] == GP_SCP02: - # manage init update response - sequenceCounter = bytearray_initUpdateResponse[12:14] - cardChallenge= bytearray_initUpdateResponse[14:20] # 6 bytes - cardCryptogram = bytearray_initUpdateResponse[20:28] # 8 bytes - - if security_info['secureChannelProtocol'] == GP_SCP03: - # manage init update response - cardChallenge= bytearray_initUpdateResponse[13:21] # 8 bytes - cardCryptogram = bytearray_initUpdateResponse[21:29] # 8 bytes - sequenceCounter = bytearray_initUpdateResponse[29:32] # 3 bytes - - - log_debug("initialize_update: Key Diversification Data: %s " % toHexString(keyDiversificationData)) - - log_debug("initialize_update: Key Information Data: %s " % toHexString(keyInformationData)) - - log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) - - log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) - - log_debug("initialize_update: Card Cryptogram %s " % toHexString(cardCryptogram)) - - # create session key regarding the scp implementation - - if security_info['secureChannelProtocol'] == GP_SCP02: - ## Secure Channel base key - if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54): - - # calculation of encryption session key using on base key - security_info['encryptionSessionKey'] = create_session_key_SCP02(base_key, KENC_TYPE, toHexString(sequenceCounter)) - # calculation of C-MAC session key - security_info['C_MACSessionKey'] = create_session_key_SCP02(baseKey, KMAC_TYPE, toHexString(sequenceCounter)) - #calculation of R-MAC session key - security_info['R_MACSessionKey'] = create_session_key_SCP02(baseKey, KRMAC_TYPE, toHexString(sequenceCounter)) - #calculation of data encryption session key - security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(baseKey, KDEK_TYPE, toHexString(sequenceCounter)) - - ## 3 Secure Channel Keys */ - elif (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45): - - # calculation of encryption session key using on 3 static key - security_info['encryptionSessionKey'] = create_session_key_SCP02(enc_key, KENC_TYPE, toHexString(sequenceCounter)) - # calculation of C-MAC session key - security_info['C_MACSessionKey'] = create_session_key_SCP02(mac_key, KMAC_TYPE, toHexString(sequenceCounter)) - #calculation of R-MAC session key - security_info['R_MACSessionKey'] = create_session_key_SCP02(dek_key, KRMAC_TYPE, toHexString(sequenceCounter)) - #calculation of data encryption session key - security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(dek_key, KDEK_TYPE, toHexString(sequenceCounter)) - - if payload_mode_activated == True: - cardChallenge = self.calculate_card_challenge_SCP02(security_info['selectedAID'], security_info['C_MACSessionKey']) - # type coherency - cardChallenge = int(cardChallenge, 16) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - return error_status, None - - elif security_info['secureChannelProtocol'] == GP_SCP03: - - # calculation of encryption session key using on 3 static key - session_key_value = create_session_key_SCP03(enc_key, KENC_TYPE, toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['encryptionSessionKey'] = session_key_value - - # calculation of C-MAC session key - session_key_value = create_session_key_SCP03(mac_key, KMAC_TYPE , toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['C_MACSessionKey'] = session_key_value - #calculation of R-MAC session key - session_key_value = create_session_key_SCP03(mac_key, KRMAC_TYPE, toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['R_MACSessionKey'] = session_key_value - # calculation of data encryption session key - # warning: no session key for data encryption in SCP03 - # session_key_value = create_session_key_SCP03(dek_key, KDEK_TYPE , toHexString(cardChallenge), hostChallenge) - # if session_key_value == None: - # error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - # return error_status, None - # else: - security_info['dataEncryptionSessionKey'] = dek_key - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - log_debug("initialize_update: S-ENC Session Key: %s" %security_info['encryptionSessionKey']) - log_debug("initialize_update: S-MAC Session Key: %s" %security_info['C_MACSessionKey']) - log_debug("initialize_update: DEK Session Key: %s" %security_info['dataEncryptionSessionKey']) - log_debug("initialize_update: R-MAC Session Key: %s" %security_info['R_MACSessionKey']) - - if security_info['secureChannelProtocol'] == GP_SCP02: - offcardCryptogram = calculate_card_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) - elif security_info['secureChannelProtocol'] == GP_SCP03: - offcardCryptogram = calculate_card_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - if payload_mode_activated == False: - # compare cryptograms - if toHexString(cardCryptogram) != offcardCryptogram: - error_status = create_error_status(ERROR_CARD_CRYPTOGRAM_VERIFICATION, runtimeErrorDict[ERROR_CARD_CRYPTOGRAM_VERIFICATION]) - return error_status , None - - - host_cryptogram = None - if security_info['secureChannelProtocol'] == GP_SCP02: - host_cryptogram = calculate_host_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) - - elif security_info['secureChannelProtocol'] == GP_SCP03: - host_cryptogram = calculate_host_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - error_status = create_no_error_status(0x00) - log_end("initialize_update", error_status['errorStatus']) - return error_status, host_cryptogram - -def internal_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): - - log_start("internal_authenticate") - - - - # build the APDU - data = "5F49" + lv(ePK_OCE) - data_field = crt_data + data - - internal_authenticate_apdu = '80 88' + key_version_number + key_identifier + lv(data_field) - - error_status, rapdu = send_APDU(internal_authenticate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("internal_authenticate", error_status['errorStatus']) - return error_status, None - - - log_end("internal_authenticate", error_status['errorStatus']) - return error_status, rapdu - - -def mutual_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): - - log_start("mutual_authenticate") - - - # build the APDU - data = "5F49" + lv(ePK_OCE) - data_field = crt_data + data - - mutual_authenticate_apdu = '80 82' + key_version_number + key_identifier + lv(data_field) - - error_status, rapdu = send_APDU(mutual_authenticate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("mutual_authenticate", error_status['errorStatus']) - return error_status, None - - - log_end("mutual_authenticate", error_status['errorStatus']) - return error_status, rapdu - -def external_authenticate(security_level, host_cryptogram): - - log_start("external_authenticate") - - # get security_info dictionary of working channel - security_info = securityInfo[securityInfo[4]] - # create the external authenticate APDU command - externalAuthAPDU = '84' + '82' + intToHexString(security_level) + '00' + '10' + host_cryptogram - - if security_info['secureChannelProtocol'] == GP_SCP03: - mac = calculate_mac_SCP03(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_16) - security_info['lastC_MAC'] = mac - security_info['icv_counter'] = 0x01 - # add the mac to the command - externalAuthAPDU = externalAuthAPDU + mac[:16] - - elif security_info['secureChannelProtocol'] == GP_SCP02: - - mac = calculate_mac_SCP02(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_8) - security_info['lastC_MAC'] = mac - # add the mac to the command - externalAuthAPDU = externalAuthAPDU + mac - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status - - error_status, rapdu = send_APDU(externalAuthAPDU) - - if error_status['errorStatus'] != 0x00: - log_end("external_authenticate", error_status['errorStatus']) - return error_status - - - #update security info - security_info['securityLevel'] = security_level - - log_end("external_authenticate", error_status['errorStatus']) - return error_status - -def get_certificate(key_version_number, key_identifier): - log_start("get_certificate") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("get_certificate", error_status['errorStatus']) - return error_status - - # build the APDU - - - get_certificate_apdu = '80 CA BF 21' + lv ('A6' + lv ( '83' + lv (key_version_number + key_identifier))) - - error_status, rapdu = send_APDU(get_certificate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_certificate", error_status['errorStatus']) - return error_status, None - - - log_end("get_certificate", error_status['errorStatus']) - return error_status, rapdu - - - -def perform_security_operation(key_version_number, key_identifier , crt_data ): - - log_start("perform_security_operation") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - - # build the APDU - - - perform_security_operation_apdu = '80 2A' + key_version_number + key_identifier + lv(crt_data) - - error_status, rapdu = send_APDU(perform_security_operation_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - - - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - -def registry_update(security_domain_AID, application_aid, application_privileges = "00", registry_parameter_field = None, install_token = None): - log_start("registry_update") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("registry_update", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - registry_update_apdu_data = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + lv(application_privileges) - # optionnal fields - parameter_field = '' - if registry_parameter_field != None: - parameter_field = parameter_field + 'EF' + lv(remove_space(registry_parameter_field)) - else: - parameter_field =parameter_field + 'EF00' - - if install_token != None: - install_token = lv(install_token) - else: - install_token = '00' #no token - - registry_update_apdu = '80 E6 40 00' + lv(registry_update_apdu_data + lv(parameter_field) + install_token) - - error_status, rapdu = send_APDU(registry_update_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("registry_update", error_status['errorStatus']) - return error_status - - - log_end("registry_update", error_status['errorStatus']) - return error_status - - -def install_install(make_selectable, executable_LoadFile_AID, executable_Module_AID, application_AID, application_privileges = "00", application_specific_parameters = None, install_parameters = None, install_token = None): - log_start("install_install") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("install_install", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - install_apdu_data = lv(remove_space(executable_LoadFile_AID)) + lv(remove_space(executable_Module_AID)) + lv(remove_space(application_AID)) + lv(application_privileges) - # optionnal fields - parameter_field = '' - if application_specific_parameters != None: - parameter_field = parameter_field + 'C9' + lv(remove_space(application_specific_parameters)) - else: - parameter_field =parameter_field + 'C903000000' - - if install_parameters != None: - parameter_field = parameter_field + 'EF' + lv(remove_space(install_parameters)) - else: - parameter_field =parameter_field + 'EF00' - - if install_token != None: - install_token = lv(install_token) - else: - install_token = '00' #no token - - - if make_selectable == True: - install_apdu = '80 E6 0C 00' + lv(install_apdu_data + lv(parameter_field) + install_token) - else: - install_apdu = '80 E6 04 00' + lv(install_apdu_data + lv(parameter_field) + install_token) - - error_status, rapdu = send_APDU(install_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("install_install", error_status['errorStatus']) - return error_status - - - log_end("install_install", error_status['errorStatus']) - return error_status - - -def install_load(executable_load_file_aid, security_domain_aid, load_file_data_block_hash = None, load_parameters = None, load_token = None): - log_start("install_load") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("install_load", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - install_apdu = lv(remove_space(executable_load_file_aid)) + lv(remove_space(security_domain_aid)) - # optionnal fields - if load_file_data_block_hash != None: - install_apdu = install_apdu + lv(remove_space(load_file_data_block_hash)) - else: - install_apdu = install_apdu + '00' - - if load_parameters != None: - install_apdu = install_apdu + lv('EF' + lv(remove_space(load_parameters))) - else: - install_apdu = install_apdu + '00' #no parameter - - if load_token != None: - install_apdu = install_apdu + lv(load_token) - else: - install_apdu = install_apdu + '00' #no token - - install_apdu = '80 E6 02 00' + lv(install_apdu) - - error_status, rapdu = send_APDU(install_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("install_load", error_status['errorStatus']) - return error_status - - - log_end("install_load", error_status['errorStatus']) - return error_status - - -def load_blocks(load_file_path, block_size = 32): - log_start("load_blocks") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("load_blocks", error_status['errorStatus']) - return error_status - - block_number = 0x00 - - load_file_obj = loadfile.Loadfile(load_file_path) - - log_debug("load_blocks: load_file_obj: %s" %load_file_obj.__str__()) - - all_blocks_data = load_file_obj.get_load_blocks(block_size) - - for block in all_blocks_data: - blockNumber = intToHexString(block_number) - is_last_block = (block == all_blocks_data[-1]) - - if is_last_block == True: - load_apdu = '80' + 'E8' + '80' + blockNumber + lv(block) - else: - load_apdu = '80' + 'E8' + '00'+ blockNumber + lv(block) - - error_status, rapdu = send_APDU(load_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - block_number = block_number + 1 - - log_end("load_blocks", error_status['errorStatus']) - return error_status - - -def extradite(security_domain_AID, application_aid, identification_number = None, image_Number = None, application_provider_identifier = None, token_identifier = None, extraditeToken = None): - log_start("extradite") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - - strControlReferenceTemplate = '' - str_ExtraditionParametersfield = '' - - if (identification_number != None): - strControlReferenceTemplate += "42" + lv(remove_space(identification_number)) - - if (image_Number != None): - strControlReferenceTemplate += "45" + lv(remove_space(image_Number)) - - if (application_provider_identifier != None): - strControlReferenceTemplate += "5F20" + lv(remove_space(application_provider_identifier)) - - if (strControlReferenceTemplate != ''): - str_ExtraditionParametersfield = str_ExtraditionParametersfield + "B6" - str_ExtraditionParametersfield = str_ExtraditionParametersfield + lv(strControlReferenceTemplate) - - # build the APDU - extradite_apdu = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + '00' + lv(str_ExtraditionParametersfield) - - if extraditeToken != None: - extradite_apdu = extradite_apdu + lv(extraditeToken) - else: - extradite_apdu = extradite_apdu + '00' #no token - - extradite_apdu = '80 E6 10 00' + lv(extradite_apdu) - - error_status, rapdu = send_APDU(extradite_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - - log_end("extradite", error_status['errorStatus']) - return error_status - - -def put_key(key_version_number, key_identifier, key_type, key_value, replace = False ): - - log_start("put_key") - - security_info = securityInfo[securityInfo[4]] - - error_status = __check_security_info__(security_info) - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - # build the extradition parameter fields - - if replace == False: - p1 = '00' - else: - p1 = key_version_number - - # cipher key regarding the SCP protocol - if security_info['secureChannelProtocol'] == GP_SCP03: - cipher_key, cipher_key_kcv = cipher_key_SCP03(key_value, security_info['dataEncryptionSessionKey'] ) - cipher_key_len = intToHexString(int(len(cipher_key)/2)) - put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv( cipher_key_len + cipher_key) + lv(cipher_key_kcv)) - - - elif security_info['secureChannelProtocol'] == GP_SCP02: - - cipher_key, cipher_key_kcv = cipher_key_SCP02(key_value, security_info['dataEncryptionSessionKey'] ) - put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv(cipher_key) + lv(cipher_key_kcv)) - - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status - - - error_status, rapdu = send_APDU(put_key_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - - log_end("put_key", error_status['errorStatus']) - return error_status - - -def put_scp_key(key_version_number, key_list, replace = False ): - - log_start("put_scp_key") - - security_info = securityInfo[securityInfo[4]] - error_status = __check_security_info__(security_info) - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - # build the extradition parameter fields - - if replace == False: - p1 = '00' - else: - p1 = key_version_number - - scp = security_info['secureChannelProtocol'] - apdu_data = key_version_number - # cipher key regarding the SCP protocol - for ( key_vn, key_id, key_type, key_value ) in key_list: - # calculate key check value (refer key_type of key_list) - if (key_type == 'DES') or (key_type == 'AES'): - kcv = compute_key_check_value(key_type, key_value) - else: - error_status = create_error_status(ERROR_PUTKEY_INVALID_KEY_TYPE, runtimeErrorDict[ERROR_PUTKEY_INVALID_KEY_TYPE]) - break - - # encrypt the key (refer the key type of security_info['dataEncryptionSessionKey']) - if (scp == GP_SCP03) or (scp == GP_SCP02): - encryptedkey = cipher_key(key_value, security_info['dataEncryptionSessionKey'], scp) - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - break - - # compose APDU - apdu_data = apdu_data + key_type_coding_dict[key_type] + lv( getLength(encryptedkey) + encryptedkey) + lv(kcv) - - if error_status['errorStatus'] != 0x00: - log_end("put_scp_key", error_status['errorStatus']) - return error_status - - put_scp_key_apdu = '80 D8' + p1 + '81' + lv(apdu_data) - error_status, rapdu = send_APDU(put_scp_key_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("put_scp_key", error_status['errorStatus']) - return error_status - - log_end("put_scp_key", error_status['errorStatus']) - return error_status - -def manage_channel(open_channel, logical_channel): - global securityInfo - - log_start("manage_channel") - - if open_channel == True: - capdu = '00 70 00 00 01' - else: - if logical_channel < 0x01 and logical_channel > 0x03: - # channel number must be between 01 and 03 - error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) - return error_status - capdu = '00 70 80 ' + intToHexString(logical_channel) + '00' - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != 0x00: - log_end("manage_channel", error_status['errorStatus']) - return error_status, None - - if open_channel == True: - byte_list_data = toByteArray(rapdu) - channel_num = byte_list_data[0] - sChannelInfo = securityInfo[channel_num] - sChannelInfo['channelStatus'] = "ON" - else: - # Close working channel then set another chanenl as working channel - if securityInfo[4] == logical_channel: - securityInfo[4] = 0 - securityInfo[logical_channel] = {} - - log_end("manage_channel", error_status['errorStatus']) - - return error_status, rapdu +import time +import pygp.crypto as crypto +from pygp.logger import * +from pygp.connection.connection import * +from pygp.error import * +from pygp.constants import * +from pygp.gp.gp_crypto import * +import pygp.loadfile as loadfile +from pygp.tlv import * + + + +# global variable for gp_functions +# information of each channel. +# [0] for channel 0, [1] for channel 1, [2] for channel 2, [3] for channel 3 +# [4] it save current working channel number. This channel number used when sending APDU. +securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] + +last_apdu_response = None +last_apdu_status = None +apdu_timing = False + +payload_mode_activated = False +payload_list = [] + +total_time = 0.0 + +def clear_securityInfo(): + global securityInfo + securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] + +def get_payload_list(): + global payload_list + return payload_list + +def set_payload_mode(activate): + global payload_mode_activated + global payload_list + payload_mode_activated = activate + # clear the list on activation + if (activate == True): + payload_list.clear() + +def last_response(): + global last_apdu_response + return last_apdu_response + +def last_status(): + global last_apdu_status + return last_apdu_status + +def set_apdu_timing(activated): + global apdu_timing + apdu_timing = activated + +def set_start_timing(): + global total_time + total_time = 0.0 + +def get_total_execution_time(): + return total_time + +def __check_security_info__(security_info): + if security_info == None: + error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) + return error_status + else: + error_status = create_no_error_status(0x00) + return error_status + + +def select_channel(logical_channel): + global securityInfo + + log_start("select_channel") + + # Display All Channel Information + logger.log_info("Channel Status Information [0~3]") + logger.log_info("Channel SCP SCPi SecurityLevel AID") + logger.log_info("------- --- ---- ------------- ------------------") + channel_id = 0 + for sChannelInfo in securityInfo[0:4]: + if (securityInfo[4] == channel_id): + strWorking = '*' + else: + strWorking = ' ' + if (('channelStatus' in sChannelInfo.keys()) and (sChannelInfo['channelStatus'] == "ON")): + if 'selectedAID' in sChannelInfo.keys(): + strAID = sChannelInfo['selectedAID'] + else: + strAID = 'NONE' + + if 'secureChannelProtocolImpl' in sChannelInfo.keys(): + logger.log_info(" %s 0%d %d %s %d %s" \ + %(strWorking, channel_id, sChannelInfo['secureChannelProtocol'],\ + sChannelInfo['secureChannelProtocolImpl'], sChannelInfo['securityLevel'], strAID )) + else: + logger.log_info(" %s 0%d n/a n/a n/a %s" %(strWorking, channel_id, strAID)) + else: + logger.log_info(" 0%d Not Available" %(channel_id)) + + channel_id += 1 + + # check the parameter value + if logical_channel == None or logical_channel < 0x00 or logical_channel > 0x03: + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + log_info("\tInvalid logical channel (%-0.2X)" % securityInfo[4]) + return error_status + + # check the status of logical channel + sChannelInfo = securityInfo[logical_channel] + if sChannelInfo['channelStatus'] == "ON": + # set the working channel value + securityInfo[4] = logical_channel + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_info("\tLogical channel has changed to %-0.2X" % securityInfo[4]) + else: + error_status = create_error_status(ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE, runtimeErrorDict[ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE]) + + log_end("select_channel", error_status) + + return error_status + + +def wrap_command(security_info, capdu): + ''' Wrap APDU according to the security info ''' + #TODO: update doc + + log_start("wrap_command") + + # no security level defined, just return + if (security_info == None): + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + return error_status, capdu + + # security_info level defined + if ('securityLevel' in security_info) == False: + error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) + return error_status, '' + + # trivial case, just return + if security_info['securityLevel'] == SECURITY_LEVEL_NO_SECURE_MESSAGING: + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + return error_status, capdu + + # Determine which type of Exchange between the reader + # Convert capdu from string to list of bytes + bytelist_capdu = toByteArray(capdu) + + ISO_case = -1 + Le = None + + if len(bytelist_capdu) == 4: + ISO_case = CASE_1 + + elif len(bytelist_capdu) == 5: + ISO_case = CASE_2S + Le = bytelist_capdu[4] + + else: + if (bytelist_capdu[4] != 0): + if (bytelist_capdu[4] == len(bytelist_capdu) - 5): + ISO_case = CASE_3S + elif (bytelist_capdu[4] == len(bytelist_capdu) - 6): + ISO_case = CASE_4S + Le = bytelist_capdu[-1:] + else: + pass + + + if ISO_case == -1: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, '' + + # Manage ISO case: Get Le if any and prepare APDU + + apdu_to_wrap = [] + + + # C_MAC on modified APDU + if ( security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or + security_info['secureChannelProtocol'] == GP_SCP03): + + + ############## ISO Case 1 & 2 ############## + if ISO_case == CASE_1: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap.append(0x08) + + elif ISO_case == CASE_2S: + Le = bytelist_capdu[-1:] + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = 0x08 + + elif ISO_case == CASE_3S: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 + + elif ISO_case == CASE_4S: + Le = bytelist_capdu[-1:] + apdu_to_wrap = bytelist_capdu[:-1] + # put lc with the length of the mac + apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 + + else: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, None + + #CLA - indicate security level 1 or 3 + apdu_to_wrap[0] = bytelist_capdu[0] | 0x04 + else: + # C_MAC on unmodified APDU + ############## ISO Case 1 & 2 ############## + if ISO_case == CASE_1: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap.append(0x00) + + elif ISO_case == CASE_2S: + Le = bytelist_capdu[:-1] + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = 0x00 + + elif ISO_case == CASE_4S: + Le = bytelist_capdu[:-1] + apdu_to_wrap = bytelist_capdu[:-1] + + + else: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, None + + # ICV encryption + iv = None + if security_info['secureChannelProtocol'] == GP_SCP02: + if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1A or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1B or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55) : + + iv = encipher_iv_SCP02(security_info['lastC_MAC'], security_info['C_MACSessionKey'][:16]) + + elif(security_info['secureChannelProtocol']== GP_SCP03): + iv = crypto.ISO_9797_M1_Padding_left(intToHexString(security_info['icv_counter'],2), 16) + iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + # Get the data field of the APDU + encData = apdu_to_wrap[5:] + # if we have to encrypt: + if (security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC_R_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC): + # retrieve only the data field (ie: remove APDU header) + apdu_data_field = apdu_to_wrap[5:] + + # cipher data + if security_info['secureChannelProtocol'] == GP_SCP02: + encData = encipher_data_SCP02(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , crypto.ICV_NULL_8) + encData = toByteArray(encData) # type mismatch + + log_debug("wrap_command: encrypted data field: %s" %toHexString(encData)) + + elif(security_info['secureChannelProtocol']== GP_SCP03): + encData = encipher_data_SCP03(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , iv) + + encData = toByteArray(encData) # type mismatch + # re put lc with the length of the encipher data + apdu_to_wrap[4] = len(encData) + 8 # enc data + + + # MAC calculation + if security_info['secureChannelProtocol'] == GP_SCP02: + mac = calculate_mac_SCP02(toHexString(apdu_to_wrap), security_info['C_MACSessionKey'], iv) + # re put lc with the length of the encipher data + apdu_to_wrap[4] = len(encData) + 8# enc data + elif(security_info['secureChannelProtocol']== GP_SCP03): + mac = calculate_mac_SCP03(toHexString(apdu_to_wrap[:5]) + toHexString(encData), security_info['C_MACSessionKey'], security_info['lastC_MAC']) + pass + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + log_debug("wrap_command: Data for MAC computation: %s" %toHexString(apdu_to_wrap)) + log_debug("wrap_command: ICV for MAC: %s" %iv) + log_debug("wrap_command: Generated MAC: %s" %mac) + + #update the last iv + security_info['lastC_MAC'] = mac + + # create the wrapped APDU + if security_info['secureChannelProtocol'] == GP_SCP02: + wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac + + elif(security_info['secureChannelProtocol'] == GP_SCP03): + wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac[:16] + # don't forget tp update the counter for ICV_NULL_8 + log_debug("wrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] ,2)) + security_info['icv_counter'] = security_info['icv_counter'] + 1 + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + # we have to put Le bytes !! and manage RMAC level + if Le != None: + wrappedAPDU = wrappedAPDU + toHexString(Le) + + error_status = create_no_error_status(0x00) + + log_end("wrap_command", error_status) + + return error_status, wrappedAPDU + + +def unwrap_command(security_info, rapdu): + ''' unwrap APDU response according to the security info ''' + #TODO: update doc + + log_start("unwrap_command") + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + + # no security level defined, just return + if (security_info == None): + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_end("unwrap_command") + return error_status, rapdu + + # trivial case, just return + if (security_info['securityLevel'] != SECURITY_LEVEL_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_MAC_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_C_MAC_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC) : + + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_end("unwrap_command") + return error_status, rapdu + + # convert to byte array + bytelist_rapdu = toByteArray(rapdu) + if security_info['secureChannelProtocol'] == GP_SCP02: + # only status word so no RMAC + if len(bytelist_rapdu) == 2: + return error_status, rapdu + #TODO + + elif(security_info['secureChannelProtocol'] == GP_SCP03): + # only status word so no RMAC + if len(bytelist_rapdu) == 2: + return error_status, rapdu + else: + + # get the mac of the command (8 bytes before the rapdu status word) + len_reponse_date_without_sw = len(bytelist_rapdu) - 2 + len_response_data_without_mac = len_reponse_date_without_sw - 8 + response_data_without_mac = bytelist_rapdu[0:len_response_data_without_mac] + response_data_mac = bytelist_rapdu[ len_response_data_without_mac: len_reponse_date_without_sw] + response_data_sw = bytelist_rapdu[len_reponse_date_without_sw:] + log_debug("unwrap_command: Response MAC: %s" %toHexString(response_data_mac)) + # calculate the off card MAC + mac = calculate_mac_SCP03(toHexString(response_data_without_mac) + toHexString(response_data_sw), security_info['R_MACSessionKey'], security_info['lastC_MAC']) + log_debug("unwrap_command: Data for MAC computation: %s" %(toHexString(response_data_without_mac) + toHexString(response_data_sw))) + log_debug("unwrap_command: ICV for Generated MAC: %s" %security_info['lastC_MAC']) + log_debug("unwrap_command: Generated MAC: %s" %mac) + if toHexString(response_data_mac) != mac[:16]: + error_status = create_error_status(ERROR_VALIDATION_R_MAC, runtimeErrorDict[ERROR_VALIDATION_R_MAC]) + log_end("unwrap_command") + return error_status, None + else: + # check if we have to decipher the APDU + if security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC: + if(security_info['secureChannelProtocol']== GP_SCP03): + log_debug("unwrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] - 1,2)) + iv = crypto.ISO_9797_M2_Padding_left(intToHexString(security_info['icv_counter']-1 ,2), 16) + iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) + log_debug("unwrap_command: current ICV : %s " %iv) + + # decipher data + decipher_data = decipher_data_SCP03(toHexString(response_data_without_mac), security_info['encryptionSessionKey'], iv) + decipher_data = crypto.Remove_ISO_9797_M2_Padding(decipher_data) + return error_status, decipher_data + toHexString(response_data_sw) + + pass + else: + return error_status, toHexString(bytelist_rapdu) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("unwrap_command") + return error_status, None + + +def send_APDU(capdu, raw_mode = False, exsw = None, exdata = None): + + global securityInfo + global last_apdu_response + global last_apdu_status + global apdu_timing + global payload_mode_activated + global payload_list + global total_time + + log_start("send_APDU") + #TODO: managing security info wrap the command + + if raw_mode == True: + # no wrapping management, just send the apdu + c_wrapped_apdu = capdu + else: + # get securityInfo of the channel + currentChannel = securityInfo[4] + securityInfoOfChannel = securityInfo[currentChannel] + + # wrap command + error_status, c_wrapped_apdu = wrap_command(securityInfoOfChannel, capdu) + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == True: + payload_list.append(remove_space(c_wrapped_apdu)) + error_status = create_no_error_status(0x00) + return error_status, None + else: + #convert capdu from string to list of bytes + bytelist_capdu = toByteArray(c_wrapped_apdu) + + if raw_mode == False: + # manage the selected logical channel + bytelist_capdu[0] |= securityInfo[4] + + start_time = time.perf_counter() + + error_status, rapdu = send_apdu(bytelist_capdu) + + end_time = time.perf_counter() + total_time = total_time + (end_time - start_time) + + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if raw_mode == True: + c_unwrapped_rapdu = rapdu + else: + error_status, c_unwrapped_rapdu = unwrap_command(securityInfoOfChannel, rapdu) + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if apdu_timing == True: + log_info("execution time: %.3f sec." %(end_time - start_time)) + # update global variables + last_apdu_response = c_unwrapped_rapdu[:-4] # response without status + last_apdu_status = c_unwrapped_rapdu[-4:] # only status + + # check if it is an ISO7816 status word error + if exsw == None: + error_status = check_ISO7816_status_word(rapdu) + else: + # convert exsw to list, compare between expected status word and response. + # The format of exsw "9000, 6Cxx, 6xxx" + exsw = exsw.upper() + exsw = exsw.replace(',', ' ') + exsw = exsw.replace('X', 'F') + byte_list_exsw = toByteArray(exsw) + byte_list_sw = toByteArray(last_apdu_status) + found = False + for offset in range(0, len(byte_list_exsw), 2): + if ((byte_list_exsw[offset] & byte_list_sw[-2]) == byte_list_sw[-2]): + if ((byte_list_exsw[offset+1] & byte_list_sw[-1]) == byte_list_sw[-1]): + # the status word is same as our expectation + error_status = create_no_error_status(last_apdu_status) + found = True + if found == False: + error_status = create_error_status(last_apdu_status, "Differ with expected status word") + log_error("expected status word " + exsw) + #log_end("send_APDU", error_status['errorStatus']) + + if (exdata != None) and (error_status['errorStatus'] == 0x00): + exdata = exdata.upper() + exdata = exdata.replace(' ', '') + for offset in range(0, len(exdata), 1): + if exdata[offset] == 'X': + continue + elif exdata[offset] == rapdu[offset].upper(): + continue + else: + error_status = create_error_status(last_apdu_status, "Differ with expected data") + log_error("expected data " + exdata) + break + + return error_status, rapdu + + +def select_issuerSecurityDomain(logical_channel = 0): + + log_start("select_issuerSecurityDomain") + + if(logical_channel < 0 or logical_channel > 3): + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + return error_status, None + + capdu = intToHexString(logical_channel, 1) + " A4 04 00 00" + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: + log_end("select_issuerSecurityDomain", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so the application is selected. now store its aid + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != '6F': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + for response_tlv in response_tlv.list_childs_tlv(): + if response_tlv.getTAG() == '84': + current_selected_aid = response_tlv.getValue() + else: + current_selected_aid = ISD_APPLICATION_AID + + # there is no error. Do initialize securityInfo of selected channel + securityInfo[4] = logical_channel + securityInfo[logical_channel] = {} + securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING + securityInfo[logical_channel]['channelStatus'] = "ON" + securityInfo[logical_channel]['selectedAID'] = current_selected_aid + log_end("select_issuerSecurityDomain", error_status['errorStatus'], error_status['errorMessage']) + + return error_status, rapdu + + +def select_application(str_AID, logical_channel = 0): + + log_start("select_application") + + if(logical_channel < 0 or logical_channel > 3): + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + return error_status, None + + capdu = intToHexString(logical_channel, 1) + " A4 04 00 " + lv (str_AID) + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: + log_end("select_application", error_status['errorStatus']) + return error_status, None + + # there is no error. Do initialize securityInfo of selected channel + securityInfo[4] = logical_channel + securityInfo[logical_channel] = {} + securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING + securityInfo[logical_channel]['channelStatus'] = "ON" + securityInfo[logical_channel]['selectedAID'] = str_AID + log_end("select_application", error_status['errorStatus']) + + return error_status, rapdu + + +def set_status(cardElement, lifeCycleState, aid): + + log_start("set_status") + # supress blank if any + import re + aid = ''.join( re.split( '\W+', aid.upper() ) ) + + capdu = "80 F0 " + cardElement + lifeCycleState + lv (aid) + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("set_status", error_status['errorStatus']) + return error_status + + log_end("set_status", error_status['errorStatus']) + + return error_status + + +def set_crs_status(status_type, status_value, aid): + + log_start("set_crs_status") + # supress blank if any + import re + aid = ''.join( re.split( '\W+', aid.upper() ) ) + + capdu = "80 F0 " + status_type + status_value + "4F" + lv(aid) + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("set_crs_status", error_status['errorStatus']) + return error_status, None + + log_end("set_crs_status", error_status['errorStatus']) + + #TODO: needs to parse the response data based on the GP Amdt.C Table 3-23 + + return error_status, rapdu + + +def delete_application(str_AID, exsw): + + log_start("delete_application") + + capdu = "80 E4 00 00 " + lv ('4F' + lv(str_AID)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("select_application", error_status['errorStatus']) + return error_status + + log_end("select_application", error_status['errorStatus']) + + return error_status + + +def delete_package(str_AID, exsw): + + log_start("delete_package") + + capdu = "80 E4 00 80 " + lv ('4F' + lv(str_AID)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("delete_package", error_status['errorStatus']) + return error_status + + log_end("delete_package", error_status['errorStatus']) + + return error_status + + +def delete_key(KeyIdentifier, keyVersionNumber, exsw): + + log_start("delete_key") + + capdu = "80 E4 00 00 " + lv('D0' + lv(KeyIdentifier)) + lv('D2' + lv(keyVersionNumber)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("delete_key", error_status['errorStatus']) + return error_status + + log_end("delete_key", error_status['errorStatus']) + + return error_status + + +def get_cplc_data(): + + log_start("get_cplc_data") + + capdu = "80 CA 9F 7F 00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_cplc_data", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so display results + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != '9F7F': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + + log_end("get_cplc_data", error_status['errorStatus']) + + return error_status, response_tlv.getValue() + else: + return error_status, None + + +def get_key_information_template(): + + log_start("get_key_information_template") + + capdu = "80 CA 00 E0 00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_key_information_template", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so display results + # key information is list of Tuple : (Key id, Key version number, KeyLength, KeyType) + keyInformation = [] + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != 'E0': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + # manage the list of TLV with tag C0 into this response_tlv + key_info_list = response_tlv.list_childs_tlv() + for key_info in key_info_list: + + if key_info.getTAG() != 'C0': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + else: + index = 0 + KeyIdentifier = key_info.getValue()[index: index + 2] + index = index + 2 + KeyVersionNumber = key_info.getValue()[index: index + 2] + index = index + 2 + #check key type : if coded with 2 bytes the key is in format 2 + KeyTypeIndex = key_info.getValue()[index: index + 2] + + if (KeyTypeIndex == 'FF'): + p_bool_Iskey_format2 = True + index = index + 2 + while (KeyTypeIndex == 'FF'): + KeyType = key_info.getValue()[index: index + 2] # 1 byte + KeyLength= key_info.getValue()[index + 2: index + 6] # 2 bytes + keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyTypeIndex + KeyType)) + KeyTypeIndex = key_info.getValue()[index + 6: index + 8] + index = index + 8 + #get the key usage and key access + keyUsageLength = KeyTypeIndex + if keyUsageLength != '00': + keyUsage =key_info.getValue()[index: index + 2] + index = index + 2 + + keyAccessLength = key_info.getValue()[index: index + 2] + if keyAccessLength != '00': + keyAccess = key_info.getValue()[index + 2: index + 4] + index = index + 4 + else: + index = index + 2 + + else: + while index < (len(key_info.getValue())): + KeyType = key_info.getValue()[index: index + 2] + index = index + 2 + KeyLength= key_info.getValue()[index: index + 2] + index = index + 2 + keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyType)) + + else: + keyInformation = [] + + + log_end("get_key_information_template", error_status['errorStatus']) + + return error_status, keyInformation + + +def store_data(data): + + log_start("store_data") + block_size =239 # 255 bytes minus 8 byte MAC minus 8 byte encryption padding + block_number = 0x00 + + # supress blank if any + import re + data = ''.join( re.split( '\W+', data.upper() ) ) + # convert to byte array + bytelist_data = toByteArray(data) + remaining_bytes = len(bytelist_data) + read_bytes = 0x00 + while remaining_bytes > 0: + # build the APDU + capdu = "80 E2 " + if remaining_bytes <= block_size: + capdu = capdu + '80' + intToHexString(block_number) + capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + remaining_bytes]) ) + read_bytes = read_bytes + remaining_bytes + remaining_bytes = remaining_bytes - remaining_bytes + else: + capdu = capdu + '00' + intToHexString(block_number) + capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + block_size]) ) + read_bytes = read_bytes + block_size + remaining_bytes = remaining_bytes - block_size + + # send the APDU + error_status, rapdu = send_APDU(capdu) + block_number = block_number + 1 + + if error_status['errorStatus'] != 0x00: + log_end("store_data", error_status['errorStatus']) + return error_status + + + if error_status['errorStatus'] != 0x00: + log_end("get_data", error_status['errorStatus']) + return error_status + + log_end("store_data", error_status['errorStatus']) + + return error_status + + +def get_data(identifier): + + log_start("get_data") + + # build the APDU + # supress blank if any + import re + identifier = ''.join( re.split( '\W+', identifier.upper() ) ) + # check the size of the identifier (it is a string so 2 byte correspond to 4 characters ) + if len(identifier) < 0x00 or len(identifier) > 0x04: + # identifier must be 1 or two byte string + error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) + return error_status, None + + capdu = "80 CA " + + if len(identifier) == 0x04: + capdu = capdu + identifier + '00' + else: + #one byte identifier + capdu = capdu + identifier + '00' + '00' + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_data", error_status['errorStatus']) + return error_status, None + + log_end("get_data", error_status['errorStatus']) + + return error_status, rapdu + + +def get_status(card_element): + + log_start("get_status") + + # build the APDU + # supress blank if any + import re + card_element = ''.join( re.split( '\W+', card_element.upper() ) ) + + capdu = "80 F2 " + card_element + "02 02 4F 00" + "00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_status", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # store the response + card_response = last_response() + # check if more data available + while last_status() == '6310': + # send a get status next occurence + capdu = "80 F2 " + card_element + "03 02 4F 00" + "00" + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_status", error_status['errorStatus']) + return error_status, None + + card_response = card_response + last_response() + else: + card_response = None + error_status = create_no_error_status(0x00) + + + log_end("get_status", error_status['errorStatus']) + + return error_status, card_response + + + +def get_crs_status(aid, tag_list): + + log_start("get_crs_status") + + # build data field tag with given aid and tag list + if aid == '': data_field = "4F00" + else: data_field = "4F" + lv(aid) + + if tag_list == '': pass + else: data_field = data_field + "5C" + lv(tag_list) + + capdu = "80 F2 40 00 " + lv(data_field) + "00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_crs_status", error_status['errorStatus']) + return error_status, None + + # store the response + card_response = last_response() + # check if more data available + while last_status() == '6310': + # send a get status next occurence + capdu = "80 F2 40 01 " + lv(data_field) + "00" + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_crs_status", error_status['errorStatus']) + return error_status, None + + card_response = card_response + last_response() + + # we have the card_response TLV. create the get status response dictionnary + response_tlvs = TLVs(toByteArray(card_response)) + app_info_list = [] + app_aid = None + app_lifecycle = None + uniform_resource_locator = None + app_image_template = None + display_message = None + app_update_counter = None + selection_priority = None + app_group_head = None + app_group_members = None + crel_application_aid_list = None + policy_restricted_app = None + app_discretionary_data = None + application_family = None + display_required_indicator = None + assinged_protocol = None + continuous_processing = None + recognition_algorithm = None + + + for response_tlv in response_tlvs.list_childs_tlv(): + if response_tlv.getTAG() == '61': + # manage the list of TLV into this response_tlv + app_info_tlv_list = response_tlv.list_childs_tlv() + for app_info in app_info_tlv_list: + if app_info.getTAG() == '4F': app_aid = app_info.getValue() + elif app_info.getTAG() == '9F70': app_lifecycle = app_info.getValue() + elif app_info.getTAG() == '7F20': + display_control_tlv_list = app_info.list_childs_tlv() + for display_control_info in display_control_tlv_list: + if display_control_info.getTAG() == '5F50': + uniform_resource_locator = display_control_info.getValue() + elif display_control_info.getTAG() == '6D': + app_image_template = display_control_info.getValue() + elif display_control_info.getTAG() == '5F45': + display_message = display_control_info.getValue() + elif app_info.getTAG() == '80': app_update_counter = app_info.getValue() + elif app_info.getTAG() == '81': selection_priority = app_info.getValue() + elif app_info.getTAG() == 'A2': + app_group_head_tlv_list = app_info.list_childs_tlv() + for app_group_head in app_group_head_tlv_list: + if app_group_head.getTAG() == '4F': + app_group_head = app_group_head.getValue() + # below 3 parameters tag 'A3' to 'A5' can be multiple so need to find a better way to handle + elif app_info.getTAG() == 'A3': + app_group_members_tlv_list = app_info.list_childs_tlv() + for app_group_info in app_group_members_tlv_list: + if app_group_info.getTAG() == '4F': + app_group_members = app_group_info.getValue() + elif app_info.getTAG() == 'A4': + crel_app_aid_list_tlv_list = app_info.list_childs_tlv() + for crel_app_aid_info in crel_app_aid_list_tlv_list: + if crel_app_aid_info.getTAG() == '4F': + crel_app_aid_list = crel_app_aid_info.getValue() + elif app_info.getTAG() == 'A5': + policy_restricted_app_tlv_list = app_info.list_childs_tlv() + for policy_restricted_app_info in policy_restricted_app_tlv_list: + if policy_restricted_app_info.getTAG() == '4F': + policy_restricted_app = policy_restricted_app_info.getValue() + elif app_info.getTAG() == 'A6': app_discretionary_data = app_info.getValue() + elif app_info.getTAG() == '87': app_family = app_info.getValue() + elif app_info.getTAG() == '88': display_required_indicator = app_info.getValue() + elif app_info.getTAG() == '8C': assinged_protocol = app_info.getValue() + elif app_info.getTAG() == '8A': continuous_processing = app_info.getValue() + elif app_info.getTAG() == '8B': recognition_algorithm = app_info.getValue() + app_info_list.append({'aid':app_aid, 'lifecycle':app_lifecycle[:2], \ + 'app update counter':app_update_counter, 'selection priority':selection_priority, \ + 'app group head':app_group_head, 'app group members':app_group_members, \ + 'crel app list':crel_app_aid_list, 'policy restricted app':policy_restricted_app, \ + 'app discretionary data':app_discretionary_data, 'app family':app_family, \ + 'display required indicator':display_required_indicator, 'assinged protocol':assinged_protocol, \ + 'continuous processing':continuous_processing, 'recognition algorithm':recognition_algorithm}) + + else: + error_status = create_no_error_status(0x00) + + log_end("get_crs_status", error_status['errorStatus']) + + return error_status, app_info_list + + +def initialize_update(key_set_version , base_key, enc_key , mac_key , dek_key , scp, scp_implementation, sequence_counter = "000000" ): + + global last_apdu_response + global last_apdu_status + global payload_mode_activated + + log_start("initialize_update") + # create a host challenge + hostChallenge = crypto.RANDOM(8) + + log_debug("initialize_update: Host challenge Data: %s " % hostChallenge) + # create the initialize update APDU command (with Le = Max data) + initUpdateAPDU = '80' + '50' + key_set_version + '00 08' + hostChallenge + '00' + error_status, rapdu = send_APDU(initUpdateAPDU) + + if error_status['errorStatus'] != 0x00: + log_end("initialize_update", error_status['errorStatus']) + return error_status, None + + + # Set the security info structure needed for all GP operations + security_info = securityInfo[securityInfo[4]] + + + # checking payload mode + if payload_mode_activated == True: + # update the security information structure + security_info['secureChannelProtocol'] = int(scp, 16) + + if security_info['secureChannelProtocol'] == GP_SCP02: + # manage init update response + # in SCP02 the card challenge is calculated using the session key. So we need to create session key before. + sequenceCounter = toByteArray(sequence_counter) + # just check the sequence counter length. In SCP02 sequence counter is expressedwith 2 bytes + if len(sequenceCounter) != 2: + error_status = create_error_status(ERROR_INCONSISTENT_SEQUENCE_COUNTER, runtimeErrorDict[ERROR_INCONSISTENT_SEQUENCE_COUNTER]) + return error_status, None + + elif security_info['secureChannelProtocol'] == GP_SCP03: + # manage init update response + sequenceCounter = increment(sequence_counter, 0x01) + cardChallenge = calculate_card_challenge_SCP03(sequenceCounter + security_info['selectedAID'], enc_key) + # type coherency + cardChallenge = toByteArray(cardChallenge) + sequenceCounter= toByteArray(sequenceCounter) + log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) + log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + + security_info['secureChannelProtocolImpl'] = scp_implementation + security_info['keySetVersion'] = key_set_version + # we set it to a dummy value + security_info['keyIndex'] = 0xFF + + + + + + else: + + # managing authentication data + bytearray_initUpdateResponse = toByteArray(last_apdu_response) + # check init_update_response length, it must be 28, 29 or 32 bytes + # SCP01/SCP02 = 30 bytes, SCP03 31 or 34 bytes + if len (bytearray_initUpdateResponse) != 28 and len (bytearray_initUpdateResponse) != 29 and len (bytearray_initUpdateResponse) != 32: + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + # managing response of INITIALIZE UPDATE + keyDiversificationData = bytearray_initUpdateResponse[:10] + keyInformationData = bytearray_initUpdateResponse[10:12] + + # check if a scp has been set by the user, if not take the scp into the init update response + if scp == None: + scp = intToHexString(keyInformationData[1]) + + # test if reported SCP is consistent with passed SCP + if int(str(scp), 16) != keyInformationData[1]: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + # update the security information structure + security_info['secureChannelProtocol'] = int(str(scp), 16) + + + # in SCP03 the scp implementation value is returned by the init update response + # in SCP02 this value is not present so this value should be set by the user. + if security_info['secureChannelProtocol'] == GP_SCP02: + if scp_implementation == None: + scp_implementation = intToHexString(0x55) + # error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + # return error_status, None + + if security_info['secureChannelProtocol'] == GP_SCP03: + # key information data on 3 bytes + keyInformationData = bytearray_initUpdateResponse[10:13] + scpi = keyInformationData[2] + if scp_implementation == None: + scp_implementation = intToHexString(scpi) + else: + #test if reported SCP implementation is consistent with passed SCP implementation + if scp_implementation != intToHexString(scpi): + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + return error_status, None + + security_info['secureChannelProtocolImpl'] = scp_implementation + security_info['keySetVersion'] = keyInformationData[0] + # we set it to a dummy value + security_info['keyIndex'] = 0xFF + + if security_info['secureChannelProtocol'] == GP_SCP02: + # manage init update response + sequenceCounter = bytearray_initUpdateResponse[12:14] + cardChallenge= bytearray_initUpdateResponse[14:20] # 6 bytes + cardCryptogram = bytearray_initUpdateResponse[20:28] # 8 bytes + + if security_info['secureChannelProtocol'] == GP_SCP03: + # manage init update response + cardChallenge= bytearray_initUpdateResponse[13:21] # 8 bytes + cardCryptogram = bytearray_initUpdateResponse[21:29] # 8 bytes + sequenceCounter = bytearray_initUpdateResponse[29:32] # 3 bytes + + + log_debug("initialize_update: Key Diversification Data: %s " % toHexString(keyDiversificationData)) + + log_debug("initialize_update: Key Information Data: %s " % toHexString(keyInformationData)) + + log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) + + log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) + + log_debug("initialize_update: Card Cryptogram %s " % toHexString(cardCryptogram)) + + # create session key regarding the scp implementation + + if security_info['secureChannelProtocol'] == GP_SCP02: + ## Secure Channel base key + if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54): + + # calculation of encryption session key using on base key + security_info['encryptionSessionKey'] = create_session_key_SCP02(base_key, KENC_TYPE, toHexString(sequenceCounter)) + # calculation of C-MAC session key + security_info['C_MACSessionKey'] = create_session_key_SCP02(baseKey, KMAC_TYPE, toHexString(sequenceCounter)) + #calculation of R-MAC session key + security_info['R_MACSessionKey'] = create_session_key_SCP02(baseKey, KRMAC_TYPE, toHexString(sequenceCounter)) + #calculation of data encryption session key + security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(baseKey, KDEK_TYPE, toHexString(sequenceCounter)) + + ## 3 Secure Channel Keys */ + elif (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45): + + # calculation of encryption session key using on 3 static key + security_info['encryptionSessionKey'] = create_session_key_SCP02(enc_key, KENC_TYPE, toHexString(sequenceCounter)) + # calculation of C-MAC session key + security_info['C_MACSessionKey'] = create_session_key_SCP02(mac_key, KMAC_TYPE, toHexString(sequenceCounter)) + #calculation of R-MAC session key + security_info['R_MACSessionKey'] = create_session_key_SCP02(dek_key, KRMAC_TYPE, toHexString(sequenceCounter)) + #calculation of data encryption session key + security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(dek_key, KDEK_TYPE, toHexString(sequenceCounter)) + + if payload_mode_activated == True: + cardChallenge = self.calculate_card_challenge_SCP02(security_info['selectedAID'], security_info['C_MACSessionKey']) + # type coherency + cardChallenge = int(cardChallenge, 16) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + return error_status, None + + elif security_info['secureChannelProtocol'] == GP_SCP03: + + # calculation of encryption session key using on 3 static key + session_key_value = create_session_key_SCP03(enc_key, KENC_TYPE, toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['encryptionSessionKey'] = session_key_value + + # calculation of C-MAC session key + session_key_value = create_session_key_SCP03(mac_key, KMAC_TYPE , toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['C_MACSessionKey'] = session_key_value + #calculation of R-MAC session key + session_key_value = create_session_key_SCP03(mac_key, KRMAC_TYPE, toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['R_MACSessionKey'] = session_key_value + # calculation of data encryption session key + # warning: no session key for data encryption in SCP03 + # session_key_value = create_session_key_SCP03(dek_key, KDEK_TYPE , toHexString(cardChallenge), hostChallenge) + # if session_key_value == None: + # error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + # return error_status, None + # else: + security_info['dataEncryptionSessionKey'] = dek_key + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + log_debug("initialize_update: S-ENC Session Key: %s" %security_info['encryptionSessionKey']) + log_debug("initialize_update: S-MAC Session Key: %s" %security_info['C_MACSessionKey']) + log_debug("initialize_update: DEK Session Key: %s" %security_info['dataEncryptionSessionKey']) + log_debug("initialize_update: R-MAC Session Key: %s" %security_info['R_MACSessionKey']) + + if security_info['secureChannelProtocol'] == GP_SCP02: + offcardCryptogram = calculate_card_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) + elif security_info['secureChannelProtocol'] == GP_SCP03: + offcardCryptogram = calculate_card_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + if payload_mode_activated == False: + # compare cryptograms + if toHexString(cardCryptogram) != offcardCryptogram: + error_status = create_error_status(ERROR_CARD_CRYPTOGRAM_VERIFICATION, runtimeErrorDict[ERROR_CARD_CRYPTOGRAM_VERIFICATION]) + return error_status , None + + + host_cryptogram = None + if security_info['secureChannelProtocol'] == GP_SCP02: + host_cryptogram = calculate_host_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) + + elif security_info['secureChannelProtocol'] == GP_SCP03: + host_cryptogram = calculate_host_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + error_status = create_no_error_status(0x00) + log_end("initialize_update", error_status['errorStatus']) + return error_status, host_cryptogram + +def internal_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): + + log_start("internal_authenticate") + + + + # build the APDU + data = "5F49" + lv(ePK_OCE) + data_field = crt_data + data + + internal_authenticate_apdu = '80 88' + key_version_number + key_identifier + lv(data_field) + + error_status, rapdu = send_APDU(internal_authenticate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("internal_authenticate", error_status['errorStatus']) + return error_status, None + + + log_end("internal_authenticate", error_status['errorStatus']) + return error_status, rapdu + + +def mutual_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): + + log_start("mutual_authenticate") + + + # build the APDU + data = "5F49" + lv(ePK_OCE) + data_field = crt_data + data + + mutual_authenticate_apdu = '80 82' + key_version_number + key_identifier + lv(data_field) + + error_status, rapdu = send_APDU(mutual_authenticate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("mutual_authenticate", error_status['errorStatus']) + return error_status, None + + + log_end("mutual_authenticate", error_status['errorStatus']) + return error_status, rapdu + +def external_authenticate(security_level, host_cryptogram): + + log_start("external_authenticate") + + # get security_info dictionary of working channel + security_info = securityInfo[securityInfo[4]] + # create the external authenticate APDU command + externalAuthAPDU = '84' + '82' + intToHexString(security_level) + '00' + '10' + host_cryptogram + + if security_info['secureChannelProtocol'] == GP_SCP03: + mac = calculate_mac_SCP03(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_16) + security_info['lastC_MAC'] = mac + security_info['icv_counter'] = 0x01 + # add the mac to the command + externalAuthAPDU = externalAuthAPDU + mac[:16] + + elif security_info['secureChannelProtocol'] == GP_SCP02: + + mac = calculate_mac_SCP02(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_8) + security_info['lastC_MAC'] = mac + # add the mac to the command + externalAuthAPDU = externalAuthAPDU + mac + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status + + error_status, rapdu = send_APDU(externalAuthAPDU) + + if error_status['errorStatus'] != 0x00: + log_end("external_authenticate", error_status['errorStatus']) + return error_status + + + #update security info + security_info['securityLevel'] = security_level + + log_end("external_authenticate", error_status['errorStatus']) + return error_status + +def get_certificate(key_version_number, key_identifier): + log_start("get_certificate") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("get_certificate", error_status['errorStatus']) + return error_status + + # build the APDU + + + get_certificate_apdu = '80 CA BF 21' + lv ('A6' + lv ( '83' + lv (key_version_number + key_identifier))) + + error_status, rapdu = send_APDU(get_certificate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_certificate", error_status['errorStatus']) + return error_status, None + + + log_end("get_certificate", error_status['errorStatus']) + return error_status, rapdu + + + +def perform_security_operation(key_version_number, key_identifier , crt_data ): + + log_start("perform_security_operation") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + + # build the APDU + + + perform_security_operation_apdu = '80 2A' + key_version_number + key_identifier + lv(crt_data) + + error_status, rapdu = send_APDU(perform_security_operation_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + + + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + +def registry_update(security_domain_AID, application_aid, application_privileges = "00", registry_parameter_field = None, install_token = None): + log_start("registry_update") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("registry_update", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + registry_update_apdu_data = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + lv(application_privileges) + # optionnal fields + parameter_field = '' + if registry_parameter_field != None: + parameter_field = parameter_field + 'EF' + lv(remove_space(registry_parameter_field)) + else: + parameter_field =parameter_field + 'EF00' + + if install_token != None: + install_token = lv(install_token) + else: + install_token = '00' #no token + + registry_update_apdu = '80 E6 40 00' + lv(registry_update_apdu_data + lv(parameter_field) + install_token) + + error_status, rapdu = send_APDU(registry_update_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("registry_update", error_status['errorStatus']) + return error_status + + + log_end("registry_update", error_status['errorStatus']) + return error_status + + +def install_install(make_selectable, executable_LoadFile_AID, executable_Module_AID, application_AID, application_privileges = "00", application_specific_parameters = None, install_parameters = None, install_token = None): + log_start("install_install") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("install_install", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + install_apdu_data = lv(remove_space(executable_LoadFile_AID)) + lv(remove_space(executable_Module_AID)) + lv(remove_space(application_AID)) + lv(application_privileges) + # optionnal fields + parameter_field = '' + if application_specific_parameters != None: + parameter_field = parameter_field + 'C9' + lv(remove_space(application_specific_parameters)) + else: + parameter_field =parameter_field + 'C903000000' + + if install_parameters != None: + parameter_field = parameter_field + 'EF' + lv(remove_space(install_parameters)) + else: + parameter_field =parameter_field + 'EF00' + + if install_token != None: + install_token = lv(install_token) + else: + install_token = '00' #no token + + + if make_selectable == True: + install_apdu = '80 E6 0C 00' + lv(install_apdu_data + lv(parameter_field) + install_token) + else: + install_apdu = '80 E6 04 00' + lv(install_apdu_data + lv(parameter_field) + install_token) + + error_status, rapdu = send_APDU(install_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("install_install", error_status['errorStatus']) + return error_status + + + log_end("install_install", error_status['errorStatus']) + return error_status + + +def install_load(executable_load_file_aid, security_domain_aid, load_file_data_block_hash = None, load_parameters = None, load_token = None): + log_start("install_load") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("install_load", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + install_apdu = lv(remove_space(executable_load_file_aid)) + lv(remove_space(security_domain_aid)) + # optionnal fields + if load_file_data_block_hash != None: + install_apdu = install_apdu + lv(remove_space(load_file_data_block_hash)) + else: + install_apdu = install_apdu + '00' + + if load_parameters != None: + install_apdu = install_apdu + lv('EF' + lv(remove_space(load_parameters))) + else: + install_apdu = install_apdu + '00' #no parameter + + if load_token != None: + install_apdu = install_apdu + lv(load_token) + else: + install_apdu = install_apdu + '00' #no token + + install_apdu = '80 E6 02 00' + lv(install_apdu) + + error_status, rapdu = send_APDU(install_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("install_load", error_status['errorStatus']) + return error_status + + + log_end("install_load", error_status['errorStatus']) + return error_status + + +def load_blocks(load_file_path, block_size = 32): + log_start("load_blocks") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("load_blocks", error_status['errorStatus']) + return error_status + + block_number = 0x00 + + load_file_obj = loadfile.Loadfile(load_file_path) + + log_debug("load_blocks: load_file_obj: %s" %load_file_obj.__str__()) + + all_blocks_data = load_file_obj.get_load_blocks(block_size) + + for block in all_blocks_data: + blockNumber = intToHexString(block_number) + is_last_block = (block == all_blocks_data[-1]) + + if is_last_block == True: + load_apdu = '80' + 'E8' + '80' + blockNumber + lv(block) + else: + load_apdu = '80' + 'E8' + '00'+ blockNumber + lv(block) + + error_status, rapdu = send_APDU(load_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + block_number = block_number + 1 + if block_number > 255: + block_number = 0 + + log_end("load_blocks", error_status['errorStatus']) + return error_status + + +def extradite(security_domain_AID, application_aid, identification_number = None, image_Number = None, application_provider_identifier = None, token_identifier = None, extraditeToken = None): + log_start("extradite") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + + strControlReferenceTemplate = '' + str_ExtraditionParametersfield = '' + + if (identification_number != None): + strControlReferenceTemplate += "42" + lv(remove_space(identification_number)) + + if (image_Number != None): + strControlReferenceTemplate += "45" + lv(remove_space(image_Number)) + + if (application_provider_identifier != None): + strControlReferenceTemplate += "5F20" + lv(remove_space(application_provider_identifier)) + + if (strControlReferenceTemplate != ''): + str_ExtraditionParametersfield = str_ExtraditionParametersfield + "B6" + str_ExtraditionParametersfield = str_ExtraditionParametersfield + lv(strControlReferenceTemplate) + + # build the APDU + extradite_apdu = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + '00' + lv(str_ExtraditionParametersfield) + + if extraditeToken != None: + extradite_apdu = extradite_apdu + lv(extraditeToken) + else: + extradite_apdu = extradite_apdu + '00' #no token + + extradite_apdu = '80 E6 10 00' + lv(extradite_apdu) + + error_status, rapdu = send_APDU(extradite_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + + log_end("extradite", error_status['errorStatus']) + return error_status + + +def put_key(key_version_number, key_identifier, key_type, key_value, replace = False ): + + log_start("put_key") + + security_info = securityInfo[securityInfo[4]] + + error_status = __check_security_info__(security_info) + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + # build the extradition parameter fields + + if replace == False: + p1 = '00' + else: + p1 = key_version_number + + # cipher key regarding the SCP protocol + if security_info['secureChannelProtocol'] == GP_SCP03: + cipher_key, cipher_key_kcv = cipher_key_SCP03(key_value, security_info['dataEncryptionSessionKey'] ) + cipher_key_len = intToHexString(int(len(cipher_key)/2)) + put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv( cipher_key_len + cipher_key) + lv(cipher_key_kcv)) + + + elif security_info['secureChannelProtocol'] == GP_SCP02: + + cipher_key, cipher_key_kcv = cipher_key_SCP02(key_value, security_info['dataEncryptionSessionKey'] ) + put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv(cipher_key) + lv(cipher_key_kcv)) + + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status + + + error_status, rapdu = send_APDU(put_key_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + + log_end("put_key", error_status['errorStatus']) + return error_status + + +def put_scp_key(key_version_number, key_list, replace = False ): + + log_start("put_scp_key") + + security_info = securityInfo[securityInfo[4]] + error_status = __check_security_info__(security_info) + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + # build the extradition parameter fields + + if replace == False: + p1 = '00' + else: + p1 = key_version_number + + scp = security_info['secureChannelProtocol'] + apdu_data = key_version_number + # cipher key regarding the SCP protocol + for ( key_vn, key_id, key_type, key_value ) in key_list: + # calculate key check value (refer key_type of key_list) + if (key_type == 'DES') or (key_type == 'AES'): + kcv = compute_key_check_value(key_type, key_value) + else: + error_status = create_error_status(ERROR_PUTKEY_INVALID_KEY_TYPE, runtimeErrorDict[ERROR_PUTKEY_INVALID_KEY_TYPE]) + break + + # encrypt the key (refer the key type of security_info['dataEncryptionSessionKey']) + if (scp == GP_SCP03) or (scp == GP_SCP02): + encryptedkey = cipher_key(key_value, security_info['dataEncryptionSessionKey'], scp) + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + break + + # compose APDU + apdu_data = apdu_data + key_type_coding_dict[key_type] + lv( getLength(encryptedkey) + encryptedkey) + lv(kcv) + + if error_status['errorStatus'] != 0x00: + log_end("put_scp_key", error_status['errorStatus']) + return error_status + + put_scp_key_apdu = '80 D8' + p1 + '81' + lv(apdu_data) + error_status, rapdu = send_APDU(put_scp_key_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("put_scp_key", error_status['errorStatus']) + return error_status + + log_end("put_scp_key", error_status['errorStatus']) + return error_status + +def manage_channel(open_channel, logical_channel): + global securityInfo + + log_start("manage_channel") + + if open_channel == True: + capdu = '00 70 00 00 01' + else: + if logical_channel < 0x01 and logical_channel > 0x03: + # channel number must be between 01 and 03 + error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) + return error_status + capdu = '00 70 80 ' + intToHexString(logical_channel) + '00' + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != 0x00: + log_end("manage_channel", error_status['errorStatus']) + return error_status, None + + if open_channel == True: + byte_list_data = toByteArray(rapdu) + channel_num = byte_list_data[0] + sChannelInfo = securityInfo[channel_num] + sChannelInfo['channelStatus'] = "ON" + else: + # Close working channel then set another chanenl as working channel + if securityInfo[4] == logical_channel: + securityInfo[4] = 0 + securityInfo[logical_channel] = {} + + log_end("manage_channel", error_status['errorStatus']) + + return error_status, rapdu From e61402310788ce6db3a58bfb65aa6a7b463a2fa5 Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Fri, 8 Sep 2017 17:07:43 +0900 Subject: [PATCH 5/7] Update the scard module to Release 1.9.6. --- pygp/connection/pcsc/_scard.cp35-win32.pyd | Bin 94720 -> 94720 bytes .../connection/pcsc/_scard.cp35-win_amd64.pyd | Bin 0 -> 112128 bytes pygp/connection/pcsc/_scard.pyd | Bin 94720 -> 0 bytes pygp/connection/pcsc/scard.py | 2859 ++++++++--------- pygp/gp/gp_functions.py | 10 + pygp/pygp.py | 4 +- 6 files changed, 1440 insertions(+), 1433 deletions(-) create mode 100644 pygp/connection/pcsc/_scard.cp35-win_amd64.pyd delete mode 100644 pygp/connection/pcsc/_scard.pyd diff --git a/pygp/connection/pcsc/_scard.cp35-win32.pyd b/pygp/connection/pcsc/_scard.cp35-win32.pyd index 16f826d81b9d988aedd1afda4351a7c421ed2ce7..b85f704f5ba45c13cb395f0f5125569996b03ba4 100644 GIT binary patch delta 9298 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 ZeQTP?@8@Anoew^!(Kf%GH=o|7`fo9B*Sr7# diff --git a/pygp/connection/pcsc/_scard.cp35-win_amd64.pyd b/pygp/connection/pcsc/_scard.cp35-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..131ee4a1cbf2d6191be06505c9122cc747401172 GIT binary patch literal 112128 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 z zSg6mfM-K%MYv1o+C^|9Lg{^9-*ZME5P_3uucX%1%MNZgi)_7(ItE23pMos9*4muXG zpaHDM&eF|ZGNxR~S1Xcz@E`N^ECDZo@%MqwzuY=pZYS1+INXRP&td%9Uru5+pBJ*z zkPB=Rk`h!L#+=A)xSSbWc7?zDah_UXuZ9bVkDv}{f5~oLt5%nr^4qQ zZrqjN~QT zI(JB%UmZgv6Z6NH2^e%{GSLW`00=Dh6egWkgh3=4)IY%Do&gOaY_$HYrXg+Clz|kkVrL~C#Yy#?{TPjL? zN3Xz^MNEBL`YVaMujg&iV}ONzi*Yxl8LnikUHsZG#?z3NJ3+KNuXKwJRj{^NO@5TU zmTBcB|GN9vn>^|>NNJ!S1`oefe+!0wP3Z}L*&-<&(IA}bq#TRkd57)2?90~evmrm( zk6D(wd^v;>=gdZ6L_D|}oLTZ^Yv47`RO%fxub?T^cSF}FF^i`^B*D&4P^Y6KPaACI zImDT;lc5$Tu<}DX#Q0`@tj0AE9hfYyc1WPpeqir=au7eq@dJvnQ60nqYC@R-5u;HZ zH7usjJTyYwIMe{BBTUJFGt4 zp0<|;l^8SrL0v*s=t!%NzKpC%m8sL}`zx~1f}IR?M|dnFv_&(XcvY_SjiM~ZV46CB zz+nQwvl$iaNJnNcM20Ybp-w{`kL6_iJi6T)-EOIgd8$OmIt=5z!4tk zVS)2Ju%87YJaB*oPVm5^6ad1fSmeDt@(mWS@bcbcfj8KjL!-F)a!>Fyg>*l3l&4w2 z0*~-O9Sc}_20j*$cwi3;yw1VyWr0B+c!UDL+FN9MA? zLp-pQ0?4C6-0RrO_xQ_J7C6NN3JX-g_X6-<7Ff#yO^4Rte()tkd4`O)p`%wyjy+H1 z>Hf6TVN1gMoP==9AIH->65c0KZ@ z$-dDLO#~fRpYm(y~D4#~G=E5V&04Yg8c+Fw3Y2ezfL zq|4Wdw@gdgBe{Q~@z0|GVdh_?-lu3U9m(SmEN*#G1G=VI=grLw$rqYYz8AJS!4zyq@dIcMzosC3`UgYGlB6*X|2G6mQ}W?-vyuoGrrF07H}Qg+@9EG^xtk$0O3dDC^ULNl-o9jw9( z%%X$cY6h06gDGZUSvuG~W?)lvu!qdRtUB0}W?)lwu$RohY&zI^Gq7non8jz(3U(bV z-waI3m@3+Ig&A0y4pwIdmac;-W?&gQ*nTrGiw<_!3`{yOMO5Lq8CZr6_PQBZrVjSD z8Q2sZ%+YSr3Q(zcammUz1A}%GU@OePpxp%61~V{d5COK^3=A4XfIVOa2GtOFKwhp$_3=B)o1S9P?1DmOXJ!A%UnGW`(8Q7R@}!!18pk2h708qs5iOQ8Tcsbg)xqU<-7x zH_X63r-NC#Oj^OKgUM!KZ93QrGq7zs*akB&pAObz2G*{FVO1sfwWM$<_&Rj32h6~3 z)4`6KfpzL&r_8{*bg;8#V16C!yct-x4rcY6)bwf{tiTLR*1=YofnB46Z7>77RtM`b z1IyRJ_L_ms*1-;#fnBGA9Weu&qk|2affeXrFPVYO)xpl2fnBeIN!=!mHBSf2F#{{q z!3xd5=IdZ<%)oBY!F*<5MLO6XGq8m^*aIeD9a&ttJ!%5u!_KxnVo@AiV5iJLv6zIc zw=-_v*%(*`E#@e#R5NIIq^wTF|ET6mY$*`KN9J45#Zk4r1Z3&f+o&4GS@oRGlW+T2C*U>%M#f9adt0lABEew_;-!8 zlvnLLh1aw;{2IjjO5sAq+@cF^Wz5RK0i8>)XvP`42sYT3E!hGWb9V24!8k2`p>4nV zl~#lYcBX0TU#+FRC$YG7*yWCXpI$|0J;Ji0B~Ce&>dVLeJv00S#WggZ%X zv?%iA7f^gfeo^?i+<%l6i$x`+SZaaWvH!3T0mDCH^v&-3KDK2M<-^BgJ4|cpi+_jp zv6f}?c@=4)l^gSj5meGF1 zR*nZva4Pc}qyLMw7YL0Vc5FVup0vo8Cvd9&a_rZc;UBVQ90;?T_1y@;pp|Hx@3e`I zn()f|cc8Hw8bh;(GatYsHQJBCPS^!^06}Yj_Q(;;eq1Bz#TV4+XIaIw`@RmS8sCwh zVajvX8P1T0=v{PH1@HqyT^5~n3h~346boQ!#HOVV)%m37DB+U;@a^A&pB#jJREj!= zseeS%0&b%4TGaot8s7@M2T%0!x{usQ3mFy~*DH@)s~%fP8D#ffiyB~&d&#lT@3C(9 z5aB;uW~Fzub2F2P3zp+pmo2%M;J^ANkaO2VUallibv{wz?mrQ{a+9k3Kzf4V_0#B$ zkuOytXThzxY9&?kivHCi2~R#^g*ad_+Ycml3DJ;vT3!5A+BVxFGF_x*X;~IU<{(S9 z768jMu&|(H_ka|#^k|YI}dBwSR{li;F1J8600x z_&2HWgM@GYiX)Y%q@#pa@+((^1vYPDuZAtO_z8=I;@RO;O(S+((-K6ci(I~=ub_<> z%UL+tN!vV%lt9!HTMV4t`!&R2)eo(CKvk#(1rJQ;Jn;CtD14+HYgCp3MEyA?%(fKL zlszD=JP}&?Mv{4v#VDQ~-~v>7j?aDuiXdIz*6oV?`WWp*wPO8@XXB8z2%Lu={}YPx zod)KSA352A(EC}Iv=lR;x~FOy;_KU)TwkR-zOG88p`N3u>si37>Ed;}`#Gu94n<09 zB59frc!Ta7%{_$XrxVbOwF+M^3LJCLCT#7UO9a@a04>0dTyXhvIrgQ1T};i3dlB8S zP=XbkL#-#1Xkc4>to1@O`7#i`l>6KTq(BvKMG~gDNMc41r((&f`USCxBrUs_j!>x8 zm#(W3ofMF25_{I<=e|iPu>&EJ_9~`1&rqL18>i8@j1k7vU!X#=`kc!h`N{Q&S3y!@ z2%T1}3mK@(&%<7RIr19if(1BBT2X=rypiQ%s}RE_X`w7PdxW^jh8Mf@^U^{#6q3gw z@j93F0(qhd3aJVHy4=Vqlm#!sBqOm&CC@lVj(uIkQCM)B&-RD7e4{Tj30i>|@H=ah zd~uiQuC)yb_iPZ#YG{{cW4q`Ze}G(phFo@U0dLNH7wwbyGy-TkT2VqxZAZ{&NA==F z=kSl2=$!jKN{?@6QrttBCAuVJEj5SvTs`ZU@Pt=EJcx$2y*7YH4HG?h;iA7aO8wsF% zG;)jnV??M!#`Ls+MpuVu2TfDN>8$YiT$qN`BH$-oA5cKo2NdA?K!7oJbNP;Q)Jrwg zi4w$cMn!Oyk90ud$}GX?xjOo1Kv}F%d^27ngr@jta|@PXFF6hYuEn}F+L0Fib5!{e zAai26LwOl5@rsN?Y!+6&1vGLar%!<+M#Jw%m9NLx5G>(pKo3+K0%OE2Mg5Ud~0NznKTCrbgS#+Kb{-h%R(N+lPJkfAY1d`R3IWOCvXu0iNl#9k^w4_kTsINi4)St^ zC<4qJgm(M|(~gVrnz5-yed_hc(T2&?&x=uiIoF1{jQXS+4chQ3(uNxnEB$wmbL!Jt z22DIiiN7x>50P|4+MfYA(QhgD>0GJ4NTLPR7p#w$S^YaB8>S~ zvT0Y|Pd3d-Rt-F7#X2fCu5ev{JOz0v2CJsDBCqt)HfR_vw z_miAvvPQF1dPr8w>VnC0S@X%mM6kBiTgb3*$r{LY8?3v}SO$xQvWE zu1ST)FW(~!BAbtc!y{y&95hNvAz@kQs&`}ik&N@1W<8LC{>Y#RRmkPi;)u+A8hLSJ z#1UDHjDc_tAqaE6*t@jweiHUB z?UzJrOsc=Ja%@WtESxz0P3nKjQ)(m9+L~2i7+l1UB=rGcg3fv>wO#x_G@Yec#eWBO z$%&lPIw@6WLBp6>y((2t6+b2({wX-`!x|Hdjw#kZT?GB#eu-IgMVj_c-QSxGF<9?q za6F7V#7HD1@9)1w9Tu5tT7QqtQ*bZ^E%lIL+J2DOC$ODAnq@WyQ!1;58$t$S3p;*E zlkpC4B73%%Q-JK*0#cs1PiWV!8jk~o1<%cim#L#0&pu0Fz@gNg<^vklG$r^yVjQPi zwELit6Ul_y$62%u+%OP!x6uoeh#=%h7?_-)67|dLoL(P=zN0-alhQ8@Y3 z1=wJa`hoo7*IYiC<5^l9#irUq>>bO;y$IK0TuT%A-PjMM?jAkdvd&BF9^WKY$GXP? z);+#HOY0u5(!0k};6mNwS?D$l9>}>lax>E9vUD^U&W${zr`x2byI-U`97~4{*SXZk zp%2q~E3WlIQ&}fVOn3L6s1wWXtAbY*`*sk+wzP+j=iz)O=EK9@X?2WFW+A8D@l;~Q zhCaqZ#*7633D&V|{yW-ZPuYWIE^fSW`b9pPOl*%h7e`8SMX2%=;|&^7w_zBiMzAmZ z4AaWYc?zVs)K`Mq}4O7QjDxhZAqLJQ@SKQxRnK3Y&_-htZ@(3>i| zcN>DfM-z#(`yUyR&Q)<>>Y6|xC`0m|9Y$U_J#|QQI)BTU0N98LndxU|D_9RUdg58;Ft>aD0z{-#?=et;aX?4~A~biK7sht6>nX zhIRHNKfxYeRqkrI@36)Tge&bhh5d^sWKCWMaxTYv8SXKrZj4KfPD&PDhGdVs=4Kej zkb=DDv+-pPnq2o6QNTgHsmu4?3rxa^BV>xfo%OG{Gw(W=e2qi2(Wk>OE@&E$LojEM z-K%g8DPSD3HJ(E@5Ql6@ ziLRG(kR|DJ$2?u1b5&{QigycG1oq%gC-%AlegIx)@Ey9V=l$olw0%H;sV*Y zKaDA=mtc=U_Ka5M)hV?a8EQ0kDb)BJN(pkllr-HpkO}aM8JoG0Zkz+o8X>#C7eb|T z{QW?TIKCT`4{;pN+#8%&Og%W5Xw!E5Bcw;^{H5&PZ`0#?>VUF)>6lAhS!DP2l16<4 zxyKnMRvgt1G|shALYh(h8#2GfK#;{)BCwaVPNbKjM(IEmnw`EKDiG0yk#Axug*xBt z-X|dm=4IzX?f3!=nJE z)$;X8eeO{eVZlWCjEN?&C+^`4^Rd|z({a2w)3Cc@_QYY}KBmf;-@)ihq>oF`=dG(Roj!E<%11$;vp;7F=VRlapWy(CMEZ;Y2jZXS z;VF*moiBoaPG35GCTEXUa6*0T{<8;Q<#2+H>b{2SD_u<+rQyZ;-y z|E}$&;guU`1gX^CoR8mgS$f3pXJHRMK17d=O*n5Lnlw3IYfddmE$cLg4? zC9fBgAI(4GG*IcUF@}aV~?_uFNG%IaJ0Ih(irD-D$a>vi9yHB#Ez5kYu zuWS&47@Yg8SW2Y+xB5%5vjJliR2Jt5hM%zxXVJ^>f2Jwd($A;E|Cz2#^)wx|pl}?( zH+>CcV`PRposPM}@%qy%aBpxCX|RvD$BHjs)xhdH1r-4oITZ;0X=v=oSN|I>9der3 zX&IAVu*?faC8v3g(hglX5pZHW*?a?j>#$SG2dOzUL9VtAuLgzKa&!CUxD?yzOIkTd zm`_&?@e$TI)1h6PX?2LcrULRs>lk(VQ&HMgi@^C zinA1lD_LxG*6YXKk;rcjS z@dbQ%BVc8l8L+5;2@V#;xJB)USF=)qlT5^j&Z1+{Pynq@r8`RBWGsC(FTIITcoUWG zsA1=XQaaI2#2fU7y(OTC`pZW3zaiYQrY}CZtlE0#WyLS3Sp&68%fhYcr~h11ZQVUe zNB!MMC;Z>b>cSeGXT^SNiM>HrTFygsB^uM>7mdagro|Qo8`Spw5KVW!|AgvS7 zz)7FdJiQ*CfWI3U^Ap47aaQL{OZcBbHVMQ4zJRoF7{Y|6X!W94{m7YuPBuq+o z(UC+O`q$XQgBDk94cBtqH_NmhQ@Tw` zp&ZuGnT!Ls0=yCO7_gxHM_Bn{YZ;>-)rFjmS_QTGk=KjjkS{(s@hXmGg;)$KR<*~f z?!=d+iZ2wiu@>So=~Gkb;7l5CLMEcOlOBrRZfR(&C3?~kgfV&&{tW}5A}U}Jb+&{D zt)ww>V)dqhBk51Vj|MLqeiSBS-54h0R10hPla)kqP`9*=NPR1DBN3bVHEtp*K!5hB z#7#sva5DKgRAYaS<4JvIXoC~5kIveQu2nrl2eDe!dCR_R+1?Cs{*e^EGe=U^tJ~>7 z@HIH$6-L+0q-@n{>p1cDl<})ezHA8`Y*MH9q7T4Ppg5KE)4pD|*Xli}J@#>ne)ck{ zsNqaXL(GEDX_@WM`V_ZzV*9gj9Nt0NtEG|q6r6T_d#TsFM7ymXhuLB-Z0V&X$2s+M zFgKlsd$amwoR_XFQ&*Ffun5Zk4X*qVy28`H!M>@f-)9{zWz`-oW!|C9L!M1do6j9Z zTygZ^t*96Dge|gWD!%4+XdeNj1#TnE9zk5p^zV;OrC^PkR3g;`T^N&#Ne<@fD-X=Tm9k1nE zD1X6Mva)Zr7--88EBRIEGPFV#*NxnP2y*;1@@t@p_PwB#(GlWUyxWTiMER|>1BsT} ztJU_BpTa#&k5knbBX{GIv7oq5-HwmQgkC1b6R2rJmPsPi6hlbc5|!QCOuCSdEtrF9 zBhHvs3ZcQFCmBnDk3Y*yhkE)H4V@N#19#su$aHqTlv*8wBo2CU9)+3!Rlsat8$O#(B38{gr!w0oKSI zgY|uI1t7B<2l3smihLKkLTmQSiu~P=F9P6CMgf;Svp4?;0@TF=p6+a9q;Uf7ISLd# z?zyC9aMve0_wQx#`A7D#djVe4`$TW|Vh4ZEyxw@fLco=Zdxf~K5%;w{{Vcya5#Au~ zx6;R!k$%gW+cD~|$Zr+#K5_39_a1Rq#C<2b_vYV2cl-_a=I`fE^muRV@&4E&DHi$z zzybEa9v_N59*#Xe5_=qsJsyueo`^l35|4j;*hkbK&ZM>?y2D=MU5KX%^N#oc;@!t7 z+0an}6VGI>TN~9oEs@LK#}^34up4-_{qYL4j@ZxcSUyt-lPhg`4GGg=I^-!qnD9k7 z*zwwu`YMB;fXZDKZ21IkN|z7uYkC@IOiVvH%BeEH-LVx zH2Ca<;wwNekDd#tH7goOil$xKrN`8++3u@T~~nzzQI)>qA9*W+zZ9M zNZc1Y{))jZE#1yk*Muriq#IWSzCkA;q)>tT{~uOPY842@KZu?9e0_A3OGdW{$I>l3JF7ifh*4MV$B%b7;rRweRBt4@@$-hp2%* z?)(LHmHlyMvrV!G5Fb=IK#c@7tDk!SzdLkxe@_l5^?~rE?;VjtCO3SSBTenj$?jW# zwt~ZA2OGL%B7ie7VEAB^mfd?A&8Tj$lg0fs9-+u&NKg(Q*}|?IV}PddnJi;Cw1-%z zf_+6Nyc3@UT~ed2w_A{W*myi#$|g6C4f9p} z^PA$Jk`Jo1wDj{PR-$60AJa{&{Zr}Q8F4<2xCGQ%)&}z8$&5hur17EF@|<73{L@F#|9eG_GMMFL(Bf4zBuod3~cups7xz)YWoznsbu5Oym!S{ZnN1}Zt?x{A>AcR z{xmD>!QvMhi}A(8t4c<*SHBAPT9G&(Ec>xf*cHy70eeXFs;b@gP3PmX`h4 zSJ*>Y8U{Bde+s>VT#rF4*da!~Ld^22&qh4FZjjVfjqduN&=Eups)v7F@@n-4wx6H^ zhaxcvo*FrV&H|!%Iel31P4zD8-BC&r`Wd7So)2i3AJAv1JaHn%zQ;k|PE^ma^Vd|i z&TP}b&M405a!UIL7GM4R@3BK$rJO5>Y?F0&6zrM`jX!hX&ge+yof!j7X~?eRU$=i( z?Xs41<~u_7>CPpm!l$)Z)V>+LCzUq!8Np9`-rcQO_f@A=;?%i6K0#?po>7WQ zUQr77ER{pk_9K*4ibv(bp5-k3ijuVokIIF8*5$U)wWX^{o(Y{+U!8Ym-oIx2YR}CQ z#3;MsIjmB6Ep#vA?4jb<_8nWEp8f5ifxOMo=+Tj18U(cA#|%$cERK?Am7DgLyrV4I zvrG=n6!;-Bs|*n+|7KSHJ4#k19#MYU@~n`ftg__U(6jTNpZAJq!1C#)p?xQc2ba(A zWPf|GX~5#7;^<4^8=wM+j-UK}Pzs#?CrKK^W&N%seFj$nuEn@2ac#x52iLv04&yqG z>sPqm#%04%h55La(~*Vv`zr4Haovim4VUsg9zTlb2rl|7IwDD{aNUaQPF$bE^%$<_ zaJ`P})42W)7yUhnG&Au0oJjlMxLXkZjd;J}5ty{N+Hh^hwGY<;T;Ij@V_Yxe`YorPxb$m4C~>&NpJT))BdXwa9lxDF%DV<@i^&-dc_0`hj@nf~_Tuw(u- z@$Z?PX^w(j@F(D}XyKyb#Y=9yY3Z`&tu3wIwp6L#yyq6?ubhsTE8wnr^g&$vIV2pI zm;6Ugu_NgHNZp-*oe-B1MdI&=pOK{1<#lW6?Gt+?X@TP1spNP0*~B^$iz{^iMpQNnI;WpBD^&iWbgN+ z@w~USwYLWIcd@wrY0?2aEC>VxE%`+YDeeH`j!-%t$MB+f;~$*NSm1#2#9a%&GL!S4 z7Jez=eeGrAzn}9de6AK3<*TJXV@O|qIfhEO=so@Yhmfc5gN|tkYu90fAHj75*M4As z0(Y#`3br%{S{JnR6fc>-qupP;XhF}eR>`%i%HOVd_)9<){R6@Bzh6PO9X-J5uf+C=;N4&v4z&S6M93TC_?ac2y04(RqRh?5O)MTk3$xHk-NwTSCPT&llM`knu8 z_B%IcxOUb1+grL@y`Hk5(@zhrZY9v}-zI%C&9!T7w|^U6)cb?&+x*^E3QE_3dsppJ zyg|GWpwb}?wldJ&#h|{6oIR^T?VYU+&7C2ylnDsu&KA!)Z&!CabDyu!Hc--w@@z7V|#O_$KAZ$TjvXSn_H#J1kSY~ztY|%(3bv0kf=%3dR~t@Ls{kB{+4F6 zsoPkNsL33vNgQXD`CA#O{|av^+Ig*s5*n|(DM}&)+qZ@k4TCuXuUouX|x&vOxCScZY_XgTJyLWJ~ z-yn8fcWVgj2~rW3urp(|s|t&c@P~wYKt>m85Trax1+` zi%+^amRC7ivohpwQBWNKmj$+=hrKoSo;npNY@jx=L}hoNizxo@v9ik1c#0Q&R4d}D zx_Y_;iU;NMT3}>B{0c7g)S`{iUw;}<)Sm8Lex-RQuk}L$M+gdDd3y*YNqRuUmUnua z15%}-)+A87sWg93Y4*2x*Lf9RcPkTUslouqv5}-700$2kvld3}6+$B@c-iUg@`84x zq?m5_ss-9Yhy<0f#XyZzS=%0LgOGH|)ZJyF7z_|)I(71ffaBMx-+)cbpyTW(`o zN6OXf9|Z65`tLR5YSdoKW93t8Nz+7ruI50{%d?ZtQn@5ag0X}!NjY52!AsjtmX=Ix1iMmUb3^k3y7BH&N3$M()W3Y z@!sF2aSm7qxaMtMr2a}~z2DdDZ|(H9>TOrq-pM$!JQIp#+}Bg7+;UPOepWmc-JDtH z4Rp2p!BK<}!=@{vDkuWofl4r2D1evVAd*S*Ej1x;V3!LrM4Sm;DDQ^)fH>HabFXTZ zW*A~Qr4T8-OtJ1=)F5@xJY_*tz)Mfk7c)o<+M7GkJ-Df(SVNj>8!)QyZVheQ<_&0= ze9>83?OY3!jM00o1ajkFl4eV=%#>D-LNd_qZ|nB7LIVW4cX>LI9jq>?%i9(7;%SNG z@iOlBaC!z7ds@1?x*!S-<+b(Ibye$}3l|sh{9y}vnp+@g?cG~FZIEIqgO}6ZEfRUQ zxAmawRobLnsg2ZzbcNK#6}a>b!p`5)vrGCqf9mRirQg;QQi9S_30Sr(AYqrZEcWd2 zN@cJ=y*^JHYg`Xi^9l)t`Pln7zY_imm|a; zq`AOa3ABcKq~@OX`CUOWoaX~_J{qOHdw%D_`3vWBq9(led7FETuanc0Wn%^r;2oI} zq>@^j2>%!G_q{8AQc?YzQta90;~;J9p`qs7*#p}Eb(EDI-Ewz8R?ts-Wps62LAl*8 zD`Z72kR%T0ZIzp4P@3SIgEIB#-hk|H?jjHaj9;$uD+CdQq2O=jxv)&*;bamIf>V6$ zfkZIkTGls~xy!4nkbk|OI7e2xWya#*0vTwcFM^7b!I;gU_+((V#^K6w=w>$30z(}{ ze4D(L3=G11GI>u1rfBEv1&cPwk}Y+ftLv*OSmAh`s!aBt8b|}8`lKi~E+0etF`8@j zt@5K=X>XNvU^xga`0>+WJp7X4+c+~(gQTeQCGo>#@|hi!LqRWd(kz!}_sGmD_A7Yq z+$npvL)-C3YExtTmSZdri6LGM`I~n%2fQ}P5Qjk$>_j9o#I>Mj*ye4GgA4g@^LOv? zC&V>xZ*K1-nXpOK-7>iiNMyNGYIzzWSQ%~TI<>s@ce+5e%IIQc^iZ9lVEcC99Q1N&vz=_AV2^|V1o(%6z1}0Hg5mEaI)ZRpHcS1qG^ z!jKx|*tU%{Irw^eJIt_akRS%%0*l-q>M;REC(C_R%o@T7jTj8}B@F_+fx7P1f$mTb ztVbT-LWZ7OX%4^^cap&H_*Jk0{B?n5f3TVN{Q|z6?7U76gCU?Fy%e(2!!WL3@rrOo zdl20Wf$;ZEf2%P^R=yKLxE190Av5wRYiVKXZi9si%xM5aMesa$Jg3M)yN+?*))PbHB9cut%N7%qR z$_xiRUPqbdlMo-*CRITUUfWwWim-gvqDQ8zg8~ldn+KCzfHZ_N3jS;IqW3Vn*p^~)C6WGRJ>WW+~?Z)uJ_;79W z4vIKXBssP$k{qMBry)JO*$xM;)3}Nde-`mBJa57M2=04umC{A=cuzZPWr|0*l=8u| z1NS2cpT)C;>omdQ+K)@ZyFtYD;CT>Nb^Y44C|1($&=s@E@!qV+L~BLHGvd6=G=%b) z9h?ojFR(2{Mt!hQ-qx+iewge4gZ|P-y#;f1AZ|NB$)n$Z#mJ1vi_3pDMr^>RtDB=U zAwC4}@%X(vJRTY&?P%Yo55Z^^g*3Yx7iKp^M=Gsw14QO-0J$4R1%zLoGdnm(Mya!V zZl*K@qPYqvtffUqEKvzJh#scZt3dTI+gQ`A-saolaxDZUWHu&*4+1?A>xCCU-$D<- zgjJYP%Y23)7icRdmfhMOq++E27l)vXkT($YwfC@cd|tUna}i0+Ew_i-;e8T!Q&`G0 z;KgX$AG`qrR8v^={^Zoe7>Vvd-71&^>m7vSCMD>EVIGNvmM@HZuS{`r0-y&2z@7ja z%mB+Xfeq8c2mj30!23jcOcdtr>w-rH?Sk}ZRg5?>pGgU25l>AWoE=Px=TYD}CKjS> zo(5T5bePCP>4GTQ)1w5I%fzd#+ zguAMH8&tm+=1q@RTEBk%a;D!CZ5!YY6S|8zCgnLPLFN?3l;Wb8N}zJ&>*rit`r;Jn zHPqtGnB;up-%o^aS+lE!?@ z;9_OO>}P6EvovP!^!!rV&B?S%2{r-Aw01BIdy?9B!hBP3kx)?yqZra%;*G)H*W+j) zA;yjZDrp2N;>8G*<)=kyLz5)@ku42cCZI%2uNsD|x-29NTP0Cw(#Tb>3kYf)BN88I zRW}UJE}1vX_{Qv=z&nNllj`6l58aO%$HPAb9O%df(#f zWJWsdU$?ibS;lO6r+24Z?_MQuU$96nGA4j;2!7bfq=Nq#pgb++kFaV%K|0nL2_1KV z){KRMd@Q9F*gTiv+ukN?xm-ipkleUE?)vg_r`x@pfl$S1DTZKU3@&0`(ss>7CW=@t z&ynYGY!c+DKom3A#xm6k6{sh&`FOZ%4UmZZH7koIMizXfF%p=HHRPo5rPhZf%PomG zAEq&mTXWDuTt4GR0ddkveE0FmqgL8-h;2KZ37xx`pphPIW&#R{gV2&IkNga>-lUjJ ziH+!()R1?<5El$>4YEmokmnLa@=^scfproH9b@`AU0Bu=XeBq}oVm71FnTth)~r3M|g&sBPHJm!pFF#HPqF$i&x+SF-;p&6hhwwC!rRFmWk6d96P}?4UHa zPnuZ^fM+{9wU*%hKV{2GDa29^OIkY98mSj%TLfA%RV!3M9H&j(P zYw=LEuB>(=O_?XWDR;Z5XRhTl2Hdlcu1sUcF1D&wE@<5aac^&dz3aaXKWq`=TxPg7 z!_iHPjeK64jsqGH3Va*f!G7MKk*+|m-x`nR$IHn=F&8q7$O>khqI-4>OuT7xp}Yx} z>qWCLb>>)9M{_@&kZWGjWcqO;SUY52q%hj9g*wd`5e3@g@o%h$X&;XX5{+7UrHMt3 z!qNH0q43cl2R{73h{gA9x;><60@3GDi{qV`{}%cn33wb$z*bd(CNNodY3yFe)>>`a ztW$~FzL2dlLu9iO(4V`SgSY*qsE>q75OXc(i^gd36#b3C1Q)Q{x)Qyjy2MkBmvf#+ zY9bPb&24122{OkIonu+sKpxSnW66Mx2(%p2IlM}?(OX)P!WTgW=Gtf(RBJ0KFLchy$v@)$*7hazl~l26(v29CGm~&W#mv#) zq0V@^GLL3xcsagvEy|dCO2`7HmVr0<31j@*(g=!JIEIT(q>Zcxlm%^A}g%R5X8a zOH14QWjEbaHvh(=^5SJhH{Q5->Efb0FqYB?i#l}{&TC+V~iBbagBf2|m-wRlO3 zd^`${kDa*IA~w@pE)-*nArn7h;PJW#P_G=R5}siSJ)o#o(OOI9Z}8OwUdd6*Bk?qtXSkTnC$wTsYoXp4&J$=`%@GBGvOF@Na(zq6yzs%#1gTnxU$Nac{7f;<#Nd#~?}R zX7f=?ZDO0T_l22E=i2);x2HA*l*%BK2Eo8ro4dcTbmr{JuLiWo> zkJ@-rmN#piGIy0}oq0?ov~IpoPE~%g?@V5qa>h4#B24q0G0RXkPHiWt64p?YQ31LJ zBn|r)=JtbM1K_A%ek9a3;3xhHg~2NrfQ0~WhZ z3wRu9A8;Vm4G9!DbP`pWs6ERIvMlCN3^b8@w1p{bmz)W~wWz!-%B1tg)=tIoF^7;iF?5 z7E>Gs!P6)!RLiZzzts6N7+NhO zv@wqOpwqWmdWp(9ik}@RD<@;Iq`X94Qoa$R& z9}otUTcl=K55Y6z^VP6r*vv8M6%o%|1I(;4&Qtra-pk)@Yvwb~*oMTF4z1h!3spLr zu1VdpF_DW?9I02nedy!r09(JLQJgJ2VPv0-9imN62|ridM$H(*ems1MjsF!QkeLzdnR6QsdTm zBTerdFTp#do!=lxele`Y$F-Omj@yeYR=snI>o1Arpy%1z1_HmRbqz=;6xw(zcFXzd zc`Vdz-NCb8PVE@tUKQ=AF7mc-^D1PxB`_pnF|a~^Cl2+BEz_y0tdQ5RlZ!}$)xn19 z=?(_l=^POq8so?SEy7(n$v;eivp?-_Bb3=;V${!?{b>slWtY3$Ez5e}Y?dbe9jzd~ zbVRxNQdU=2>nU?@tS+Y|A9d@?*RS=|ZFD(-QHsBfhyyp!0ui1NiN)~;DWDNW3Ft2| zVL1+I!0AaIoNki(&m>`OS$Unaw#r>sMRZI28#iHnZ7oa%&#K0{#3VdCQNr@IU%bSG zms;X@v>BhOqK?j07SQ9&ia4Ans$*pwv>`B2!m3KFXixPwiG-=EIbp)};5?Y# zNrg_7usSJ)h?pp0oO~EFnJ8hS5!m=VUcw4zWm)~&I?qM3oR+Yntgg&cn}Ryym)KBM z?(|gFRylEa%0^Fh**d4E%z*1fB{Wuef`nDoG@D~6NPnClp?jmd&bclb(eZUog_G6M zn9zvmgbC}bt1eP?INjx*viiC;wa%J)r#nUB@#;tk%gd_EoojV+#6OrNq%l#7vUmpL zCfrbmmUmW+s~^WpSXEx`slvfLwUuRHAQH*?c&%)fkdNQWQ?~!OT8>JjF3RJ=@s(8;<~a8Rn@NgacMJdiAXrUEK=zvC3?n{#fcI^r>0hoW(i&E8$p$ItJl?GkBrN; zzBaM8VcmL2 z2-zW!uf(L|C2RnTudhY@u!RT~MA=$T^?I!^cArE-;=H&tlTFy*taVqdPwagrD3SDV za%mJ3mvEBC>PpQ@)$TT1H66-=#pg6Y((G++ZU;5zCw9tX;Ts=W(LOZo5LL}93UbAVuQ2PLpfRSFcyT0p%)v(#jhJRY_Nc5AON;t4lVK)Fuwwv^Z>EZ zIn=>V28Tv9{mI}kQQI0cM7bDoU9_dHC;Vh^$on!Q%W1?J*8+;`{8BVW)hC0)WU78L zI5ZLDPX>o3#KEbnp9~Ji(>b3E4ox^`vX13X28W*v4y#(Vf#+Y$;4n6}N*){vX9A7T zv2Jb7YFd%vrJpYPX{Mh}`k`~6=5)`Q+&&6zq!>Hf0fV5}07zTGAl9K`s!Th-ij5DG zPOmcTTdd`aZ({4TMPlQqQACyFdp`Qc!*)ME*!m+IG8ziSr%B<4XyLWOy_M~U#dkki zZ5kS}m8$G0ej4G^``EH5W9sC|L4w8T&`?GfPJm>aq4*Rr`-lPZAol@$6UbhhaR#0g zSuhuFjVhcJDYiY+#tC-D4a>%WKB9ZN{cO|y1b&PUVKTW;o0El~g>6*Cx^|_zrMpve zb7qo)fbVffrpn~lNg8$CORyN~kVg4zQX1UF~L&i5?2p^IZ+BCES-@jso%AGjy z6=>i!{Cv#)N1m0g=G(m37D4;jY58L_6nerZ1K9Ftwnl@0;($v&|27cIj-;#&8zPcZ z(sB)~^6YNGvC8Dc!IBcOkcp~GLBppB_?HO8;F5E&b3^&sdUsWWlgb2&v{0MeKsc3| zA3HBAK!4&jAgSGtajr(e-IEf^k47f3_C=_dYbLXlI;4MAMd-lENRuc)6YV4cVpO;U z(QebfSFn{n3}9sC$)H6o!PY`^x#o*mNSgU6bQdAeJOS3N@P~^zY%WQ@As+aa0eBQl zM@MIq7;PnHriXy{_7D^REfgl^w7E$;!b9z52hQ`;|8casc9sn4XTD5nLKfB9Oc3XT zw-m3QUNbIG!O1pP%&wuXmTVR7_r@aBRNrfe9RbPVIGMz`4CSJ~)*BoPmbjh}D>_?u zxaor=_--An9d=HO7a#Dz7fQ6gGx+haNDQeIOFa!pF6SRmVTvDXqg;VRbm=G;5r-$< z{|A$8p!gm}$T_~^LcIZ)4o>!jQ`jhyBWW`(RhNTbs%1tcRCOJtYsUxBNJgmbC_koH zd$3hCAmiI1)Op~jL-bf(=&WE5(Doe0=4_4zyo?V}@Ks)hN@Hv(ufxYjARTc^oSPx^ zmI_bM`K(|FSoaQ@bsyME2Q!fxn^jQM#c0*U`h@Yat6pD6GCTP!E2?Up+Z0LO!`=4J*g_Arob3Yo{wDmJ^xdn}O^WQ+PK^w`*t=mjC^nbZao67V~- zsOGZDl1(g%v+3r5XkcD0Tiz|Bf8Zr*C!iZvO~=|jzHyQ{4~-DtNCvx*HBs!Rp4l5- zxo%~V&ZMHUu|x}F@a!p;XyLetYRlqRbI6p4?UoQ9VxZ#<7;zyJA5d-T>-9DLnRGo{ zA{H;bYn^2cPPV*4lf#N`vekVtEzA}f#evi^jZQj)O}Q7~`(|CJIn?}IA>Z}Ye02(G zHl8uQ7{j`USSk!zX$!O=mGcWwqj4oU=@Z7RhFn!%iZD}eB@qtFwZBf&lVmS^fTHSov`jhan`*sC}?{< zah!2D*A;?|fliP(hJE%%>rBn^qj4~{2lGR&nQ_ofpR&Fv*&!3hW)w9sR)pz6?n?0u zKy1)s-80)T$OfkDO!Bz?^COnLNei0*{7B>r``WmE(u52C#KbY2aaE2cZwX8-#?M^l zVp{0~NgP>52B*km#15lK^gG&G8Zo{|+JS;0guA7?XYw1w1bl2<302VJP3|W6VJ^In z=;{jj#a9=ZeMg^C65WNX{01z-h8kwOTx{$3mu;9VffJ2_!!31f6ba>rV=UMv*Jd0| zPMH`aE{=711+`8gzSD5)N3&?}*TdXr-)stMhCfOni;PJJ97a@KO@3jm1q4U(oUo*H zT~&1%TLD`Q2_w0R16aLw{VM)iW=nvQ0QgE@j?G2k!Vir`Lzwq022)Eo1}1-a%|#nc zu^9n<{H8I$Bw1MB<74zD#O$X;H-mB9+`5B4j*a2T|Ka`NIv$uQ;0STkrckb`uB^iP zY$x-G;;Y;A4K4H%0Gbc9jhl>V)!~P^Z#w}s_0xz<^RlUMof1~@y()S+)Q;NXf*_;d?6Ja=NSM0^kX0vl!}&@Ez& zNbV$@I842sEZnOQIR;D0f^~jmmp1!%Y2vrOc1|np$%Fez!;Py1W||4VEYE@G+ts|Y zy(`oe^IZ|GksCY{f>)^4G)TokRW!`hTL{XCL{)V1*x}4K8Vme{Hx`3dzXAerBv`>n zP{x2lKoPsg;CJS(Y?uSu7#8y_0p@$?4xr6(WIPT-X4{zQV1O7YuxcE`Zl%Sj+<4_Q z-No(+3eLFGTZe;LU{sehjK=teKJKfLR+t97e!v6$&yMc@-QKsrSzT58{|3FPl9>m=|9) z3O7x$uuzFCF}!KX?V@6-q2BM>`}aC$4iEibtNZzT=5yx!_IvHM*Is+=wbyU$#Sn8l z+Ajr(E=<#PzLS^=1$g15PzxIj5`}Sr0=rEYqJ4Yl2q4%}aD&KTU`f%>HagG{f%+G+ zo4JE`96?u1OM-f%>-2zHa%)7&?{>66`WOInS!?&gkj=6VS5B*`txFgovwAoULZ`wy zf&t){7e@@5m86%vrd7l?>_8Q;0uY+9XQ9h-nfL0st-VX>?M@;z-jliTIDtW9;8AR1 zHR9-tv$C6zL+>!aRo)AplhTbe9kAZu{Z70biXU{6J8MbQ$xNG@dwOuCB_QiTPv9d4 z8!24cK~pj$8#;DiGY1+%uDD{zqyxh|d^OaZS5w9mG+Pf_7 zZ2wMF((9}JyMTKkxr!(c*F;^(_+az`jTnw-Fjhc90mP*kGSBG);^K_u#;(YhqsiZWWG*}*)Y=fS}|0z`OYN<~gDIGJG^v_Nj) zcr4!PAsP(0N|8nb7XneU=zK_|{oBj~H}rxK7Cmf9u8G1FrWaP2I}qBJC;WlnGGuyl zmDJ5Rfb;-wCl~?Q50KO0{)qoTWz$HmKz504x57i@wH22~6}pirl+(lW%vc6a6e{LQhMR!!u|2da8AxpJ-T%w+ z#9P4Z7k9!FHIek;YANmj=J+~xrWAUBp#xmZdSY1#>PzfRB3D(+oyOnw$d0#|bXUcZ z%6-M~Lnr1RkBQ>JWa_`Yk1dJ9JwmKZiq=>V3rQN`$#6Qn*QhkwSTJb7D%crO;M|XG z>Y0BmVq&cNupV% zF1Fx((DLhLK@{lbcqvoW;){BDIzzWEG8hNm#%c2YE zpC|moEg9Ym-G)UxSyoJ%XGi+ySlPU=8w(l-g0}HcK|`a%#xLq1P#su` z(7qOXJD6W!BM-nsT~6;+|x zfUOT<^dD<+QOsb8Z6BFxhTYqO*-Z4{ahxQR z7+4V~LE<>+Y!XK4{qz=gmd%gJ*MLq6sT3^aJwR(d+T%m11jf@xaa>N>>++}b zQ5~66x%YQ$P9+PE&i@_@aBztC9t}~B%1K|K5;>A#dL(($ne7s~*DcRBK4w#S=Z)O( zB~pZN51IW)Ei{RV*&)9v25WW51e4cN{{y5p+Cm9TwhS+^W?^!k5#LUY^G(<~gr+jw zvWCN7&umWv;o<`l5{j(H&MM;77q4ot5e$wNxTs!W&&br6 z+(kE($#7cO3Kb=OiNUaiWCC4H#p#0<9q8+V>i9B)^3Cb;##)%L1dH|ZG##FB1s~NC z={ke+l%&~B7tVayYE{TbN~e$`Q<|dm$%RVDqMA>P3k_bV11u;K9fhziaU5{P7FJGD zVw1T7^QTBR>e3Do%V{|9jMsvv22IpAmR424qYT&KM^F`Of>dOMweUj?uD#;zGIO8k z9G95`MF#{~W(oxS&TS>>1fzi4EJ^g>W~o#S?*}0{7~7U^%x7qT`W?Ekw<7{lVKN`b zSxv>X+FWI*=|+dRZjI2wP{IW)8h0A!rOl8n0ZKMk4?WEMg%C7J5(|-&tthLUS{e8~ zJ6v6X)$BYbU&ZJ5A)WQ`wTJ-_i=hxxBOwN46EQ6ecm5I-uW=$L1!nn3NII$&KNb%X zwv~SuJ-~Ze7+BB3T^enok9c``$e534zZk6!;auTtAd&~bCVIQWog>ZO#pJ>o)c8I& zi?QJsDf_9R#YbfvAE+nlm}VlOm;kS0e&88XVet(3Fghd}mJ*U%mYO8tP47)E;5)boIWHg2HbyqWU%gqLgP3#ujwsaHhU>FcyKnn z7NKWwe4qisZ^0FzJV$@kODyVf4Fl~1nGp)TD?vq%L|SAPTUOmvSxcZzgH~c{1>6wg zW!2D&9Vu&*^6ofA6=As^*pwIRdeC2R`9hqqUUvXZWns@N>gsA?u2V?2&!{&|Tm7#^Xu z6Lv;0jW>G)nGIoVF^QB5Jv*kxnE}k#gZm>-k>qEF~Qw&Cique;5NKG0ikD8|J6Y zS$_U7hVsYHL4HO$5oLrh<;S96VFc?dRG8fY{Uf9}^-J65r{{MpoC~X)CC%OKH2Hfx zVI1n5kQRiq@mn+t($4A`EHrd-Q>e1SI?`j$>KQ8>F}(-PqglG7T{zrxQy1O3EU-M` zb5joqCy1k;Nv*Vi2>gFA_8_V7+D3GP(@PK+TKN{nbKBi0Z$iV99~Y@-;(A$L{>ogK zH`da{?ZgV3I4=2GM_4wtkB{>)+syFNm#{wxYq_v2&llLi<>d>k#0ugfJFL8Tc2FMo zkF^tQ{`2yM21$i*7QqpRl?-SK>Lr_$!tw!DD$1k0(U!9TK7(O+%aQ_EqO_dYKYvc@ zABPwBLVU$#m-WvV_%G|1w>F!rtS+tU7vE5W0}51A2v=#%tbTDhNsHo`w45l%1G(X`urdT|ArzIz|he`|l$VUB2)|uzoHh_<%$Nu*7 z%Y{DS;J+}hy}+qeUR;&FcT zMe8X$M11nLfMh#KuW!kC^1sd5PgzA_e4!`sWO@i+=$tW`2G$j<{rNa3^!1%ALr&5e z6EDD9>^wcct^#lDc{l>?G6G80AGic0x9@p*gLv+_zM$my9;o?2OiNvSWmFdf>7w4imcQD_Bnrp4qD$YHr8heRPsRf`@Md0FpRHxf zYXl2pGELQ$s0^2<#jw=W<`{f&Ut_!gqYd#I4LJF=2TAlt_<^dSI)9v|Mlc4$7r_#a z6((L3M{wloN^9!rf)(^M#39EWi94a0c$~(R6BrQmsR~PQ|1yCW`ec3O4cV&7GP*V| z9F@2n+oogX=DzEbas?lUiaNT)A>+nweS)aWQHiw~bU*(V#+I!xMNv z**K#}JXRP>${MwUa4F|fah^sX4YL}QSe=mXDw?L0lCS>?NH=lbkoF0aVVmJx##l8~ zCwL3m0!y6UR8~=!qr)C_E-I6V%bOfwooqfbiKwa;>Vc>n>O8MvRuk>2OCdWGJB-qZ zo^dS#b!19I{j7jrhY*LHHO!v{-)hK}<8>3NBYeY(o|nSRnD5F20U96nUQF;tXp}Y) znH0HSdi~! zmw1HqdfI|x6AVMyPWTp^OKt|J{ndbVZ4r160yin@OTlMkT}f)s#r@+~b@h)+7In0e z)V9k0d4~7&k4u(gaC30!n1FeKYfhJ)TQ+_?S)2Eg$0?l6;>f18o64;N9#&ADz~@@v zJdNKrz_w#?Nh*i%bfiy5cs=rLS%iG}?m>7m@~y=420ZuS+l#y_kAPEy${*$H5UxITP`cJfdE8zkC&LW=X!f3?N z7!iFD4ZeghjWy9T(JbMB>N#9LsgA>?Q<}ORE(WUc|%cQ6;GfD6{`U z9)4p?NowOb(Clm;ehJTappLE!cz74+>H&l|%;MokrIOU$$g_VY4^PKj6bz&{^6&^e zA3%FM8hCh1Int(Kd~-n<&)ZQxUC+ZiFpq}-rZsgu+;u+SMS5SBhsC;*)Ir3hYI%6( zOyoygR}BwOzOW=U;v)2~I>?XbVA!J{qQfXqQdSy^O|l6~6nC zPW&Q==U#j_1o>Csc|E=fiM6VN90rI)35GI(Mrn-~nuDA^)RLNAu%mp&H@r=wgI%FJga zsF##VSeg&mXam$gE-l;!IP?2015ctL2@?f+lJK8C9)qhksE;sDcnbSMA&$jE!Pg;- z0eo4M=Qx=Mh~i#hAIvb{jo38c;H%L>85K$`6(Mlz&&YEBlmJl{b`kl!5AC zb*MT*JwqL%PE=LZR?F1$)Oxi^ZBrMhJ?iD^HR?_3ch!GW`_!|vm$Xy$HvKdDb^6!z z+x7eP=k>qnZ|lcoK9)Hnb9v@VnIC5E&HOSm#29NR#uVc`W2SM9@nz#{#s=d%#$Co! z#&PBk%^l`$^JVi5^KEmKHO{KGnyt@Tk6Syez1Az%5c?GS7W-Cvll@owIOjyC%&Br_ zIW5itXQi{+x!zgle8bu3-0p00?r|P)9(Epee(vmab~~rIBi+%i;pW_4_ZoM#yVm`V z`#pEF`?9;=eZzg%9qUc-B+u}sdpCKzy%T)J_x%>X*T32SvHzTp;ZF;~3&7_XQ4yzz zi^R`}>&1t~--^E%|0KR44w6olrUR=z(w)*>(tXk+(k|(b(g^uXxlF!XzEb{@e3N{u ze6ReH{Hi=m$tn%XMalx@D&-E~`X|aG%G1hkl)cITHKl$`JzX8GR;W33mfEbgt5>VH zsc)&HHAO4aR%u_+Hfw#_6WVU=&)QqsVEtUZOy8C!434lgvi* zG4pA2zxg^af1)+o@~!i&8P>(t0;}7)%(~h7hV>omPU|V_S?d&Av~}CJYwX$fXYGyl zPwltu0nSI9lblaDs?{3d&GOf+lkS6 z-h0vevvG7vWBe}l7x~Nl&-!2R|JA?V zU+3TAf7id$zuUjhf5`ut{|o8!?8z!`O{cKc#l6*Q@uayVZBpGqtJO zr!ihzwCA+r^mFtYy<5Loe^7rxAC#Gp$!2;p>oN~y_GAVc<1i{qjQ?Y7!)Tmrn&uqy z8uR<+Q|3WX|EH`));jAa)*q}BZ56n_(!R~!0m}b`Q{i+tUv=(to_7Yh=ejf8%iWFc zg^C-mg8uAM4lni~aTfR{u}bzsZ8o0vaAF8e)@pwRoqvQ~aBFrc?=D zaFg_7=|7|)vLas~UjbVEh5Q%!bfsLmRJmTcSJ|U{M4hBIs4LWOsgJ3zs3SCAyF|NI zyIcE>Hb6gH&+1F{Z|MK7@6(56tW0xeRptkor$KY48)q3+Mw@XNX8E^_`;147*Nx%k zC(Uu@6tm7;VD^|-n>U#cnm;w4Fb|l6tdCkFtTQavY5={hvaSWa-C=FP+<(&gPwP$V zT`OguZjZG~?P@z`_ki-Qw6C?lV&7zc-Tt<{*}m6)$bQ6r(tgI?YwxpP1)dJtCC>5A zNN1EY#c6adcDkJ_omI|t&dtvK&O^?V&R%DqV}Lp<-A1>?UE_Yu?E@B{a`(EgyT^DR z^+tGU@Pto!GrX1Fm%Lj+hYxy>c~61@U-91dJbxyr>vGW6W^jo|{e6VvtRVCP$3#Kr zh`r)U@mleh;sG%weM~xCI!Br+)l0L%Cq6ITD*Zq@ARRBCET1NCkZ+fN3hMfm{CjYS zzsUoY6O`FXt8#^MwQ`;EO~xYztDjKEVida7tJDqZx79n<`_!k^z3R*AG1@TgY)#Z^ zw0T;G)~oHun0!JXrH|JQJ*PM8?fMn^2K`>WPyemH7yS5L-ONnM{5-QOb0G63_`+~Q zHf&?I(Pb4r z>war1xWa4JK~U)_b{Zq}8T$tNNA`X8!}e45vCeR3Jh=4v&V|n9&P~n+=eyv?f;-q9 zpkP`0ac&of716D z#$WLN=)d9%b0B{K=L5wvvD%#{&K6t5OU0#PpSWB63n=^;DJ5CbOsQGwmi|?`UfLw> zm)?^8MgF)vN|xm*@_BMjo-MDFx5zu?U(3(S2jtO;qFe~tze?GpJghvf{8HJacswfZRX4l%!nJzd(DS|?=vhD^WXw& zuGM3G%eu?@nYG(`(b{kQ)f#F~x7+Ni>@R}$H`+Tv?L(Z8gYV7(eSd~g_(A71*K&P# z8ZdhUIKz+JKe(>}n{$E3RlwxK-s9e{yyN_j`lt9eGOmb?pfDLY^u#&hCE}IhHR6}W zyTqS}kBEE4VbYn98cM+@8l)>hwOgd0NY6_H}pso;tk zWwJ6&X;!|h+y+VFF<@`6GE6;FHNhbp)j8^X^$PU|>d(~wQ2(U93hGs~DcUS>%ca^% z?HcWy+Ai%4ZMc4_{z+ZcTl5}%z5YFY8!$Nl>)Xkh5t*-K{x9&jIkN*4yf?Ei^J?ZK zW4xgmWyUB8LaY&+#Wr!R_zmFj4CyRMl%`?+&jKaRmljD& z!3Dl9ZIo`8ek$#j_CjJfRvr&ZY?qhIpO>$d|0*A^oUDvc(#qM2sZ3F(D=o?*WvTL2 zQBX*)#5Mkd;BjG zUe^dX1|xntL0l+aB|afOExs)d1x3oh;OC?b;G@q=m2$m23-apXWRqjnXrM0gTgLppgc%kEw2IZ7@-+jPMZntyG#@M z_UsaGz zbIMHczB!<^Hl;)9QhJqT$_iz@(x+@wb|^cQXCV!|fZf4<;Pjv(r~}m@kXwhVBZ1w~ zphOXS1P2%{SF3>IoO&U!+@f{>&%FVjIhF~>8`V41P2i>vVDGR^-433*Q+-z5qrRZ- zQ}?R})Pt&^4b+BcDQ!47ZCV?xO~5**YK}HpE7z((<2mg@ZI0H0b*~G!UjYtsm9|=2 zqpj7}gYq|OcW9d+r97bR&<5%$eY8GKUkPb>r~VRT6#+CqIWs5I0;*q;Sp%MO2c+d~ zneE^#J2THhI(q@!Wj|KQgBigXXbdq@SSv>wqM?F=w;3JAO5-YHow3DufT-f1>hs@! Cz=bye diff --git a/pygp/connection/pcsc/scard.py b/pygp/connection/pcsc/scard.py index ec1ff6f..cf86839 100644 --- a/pygp/connection/pcsc/scard.py +++ b/pygp/connection/pcsc/scard.py @@ -1,1431 +1,1428 @@ -# This file was automatically generated by SWIG (http://www.swig.org). -# Version 3.0.10 -# -# Do not make changes to this file unless you know what you are doing--modify -# the SWIG interface file instead. - - - - -""" -The smartcard.scard module is a simple wrapper on top of the C language -PCSC SCardXXX API. - -The smartcard.scard module is the lower layer of the pyscard -framework that provides a higher level interface. - -You should avoid using the smartcard.scard package directly, and use the -pyscard directly because: - - - smartcard.scard being a C wrapper, the code tends to look like C code - written in python syntax - - - the smartcard package provides higher level abstractions (e.g. - CardType, CardConnection), and makes programming easier since it is - totally written in Python - -You can still use the smartcard.scard package if you want to write your -own framework, or if you want to perform quick-and-dirty port of C -language programs using SCardXXX calls, or if there are features of -SCardXXX API that you want to use and that are not available in the -pyscard library. - -Introduction - -The smartcard.scard module is a Python wrapper around PCSC smart card base -services. On Windows, the wrapper is performed around the smart card base -components winscard library. On linux and OS X, the wrapper is performed -around the PCSC-lite library. - - -The smartcard.scard module provides mapping for the following API functions, -depending on the Operating System: - -=============================== ======= ======= -Function Windows Linux - OS X -=============================== ======= ======= -GetOpenCardName -SCardAddReaderToGroup Y -SCardBeginTransaction Y Y -SCardCancel Y Y -SCardConnect Y Y -SCardControl Y Y -SCardDisconnect Y Y -SCardEndTransaction Y Y -SCardEstablishConteYt Y Y -SCardForgetCardType Y -SCardForgetReader Y -SCardForgetReaderGroup Y -SCardFreeMemory -SCardGetAttrib Y Y -SCardGetCardTypeProviderName Y -SCardGetErrorMessage Y -SCardGetProviderId -SCardGetStatusChange Y Y -SCardIntroduceCardType Y -SCardIntroduceReader Y -SCardIntroduceReaderGroup Y -SCardIsValidConteYt Y Y -SCardListCards Y -SCardListInterfaces Y -SCardListReaderGroups Y Y -SCardListReaders Y Y -SCardLocateCards Y -SCardReconnect Y Y -SCardReleaseConteYt Y Y -SCardRemoveReaderFromGroup Y -SCardSetAttrib Y Y -SCardSetCartTypeProviderName -SCardStatus Y Y -SCardTransmit Y Y -SCardUIDlgSelectCard -=============================== ======= ======= - -Comments, bug reports, improvements welcome. - -------------------------------------------------------------------------------- -Copyright 2001-2012 gemalto -@Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com -@Author: Ludovic Rousseau, mailto:ludovic.rousseau@free.fr - -This file is part of pyscard. - -pyscard is free software; you can redistribute it and/or modify it -under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or (at -your option) any later version. - -pyscard is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with pyscard; if not, write to the Free Software Foundation, -Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -""" - - -from sys import version_info as _swig_python_version_info -if _swig_python_version_info >= (2, 7, 0): - def swig_import_helper(): - import importlib - pkg = __name__.rpartition('.')[0] - mname = '.'.join((pkg, '_scard')).lstrip('.') - try: - return importlib.import_module(mname) - except ImportError: - return importlib.import_module('_scard') - _scard = swig_import_helper() - del swig_import_helper -elif _swig_python_version_info >= (2, 6, 0): - def swig_import_helper(): - from os.path import dirname - import imp - fp = None - try: - fp, pathname, description = imp.find_module('_scard', [dirname(__file__)]) - except ImportError: - import _scard - return _scard - if fp is not None: - try: - _mod = imp.load_module('_scard', fp, pathname, description) - finally: - fp.close() - return _mod - _scard = swig_import_helper() - del swig_import_helper -else: - import _scard -del _swig_python_version_info -try: - _swig_property = property -except NameError: - pass # Python < 2.2 doesn't have 'property'. - -try: - import builtins as __builtin__ -except ImportError: - import __builtin__ - -def _swig_setattr_nondynamic(self, class_type, name, value, static=1): - if (name == "thisown"): - return self.this.own(value) - if (name == "this"): - if type(value).__name__ == 'SwigPyObject': - self.__dict__[name] = value - return - method = class_type.__swig_setmethods__.get(name, None) - if method: - return method(self, value) - if (not static): - if _newclass: - object.__setattr__(self, name, value) - else: - self.__dict__[name] = value - else: - raise AttributeError("You cannot add attributes to %s" % self) - - -def _swig_setattr(self, class_type, name, value): - return _swig_setattr_nondynamic(self, class_type, name, value, 0) - - -def _swig_getattr(self, class_type, name): - if (name == "thisown"): - return self.this.own() - method = class_type.__swig_getmethods__.get(name, None) - if method: - return method(self) - raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) - - -def _swig_repr(self): - try: - strthis = "proxy of " + self.this.__repr__() - except __builtin__.Exception: - strthis = "" - return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) - -try: - _object = object - _newclass = 1 -except __builtin__.Exception: - class _object: - pass - _newclass = 0 - - -def SCardAddReaderToGroup(hcontext, readername, groupname): - """ - SCardAddReaderToGroup( hcontext, readername, groupname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readername: card reader name - groupname: card reader group name - - - - adds a reader to a reader group - - Windows only, not supported by PCSC lite wrapper. - - example: - - from smartcard.scard import * - ... establish context ... - newgroup = 'SCard$MyOwnGroup' - reader = 'SchlumbergerSema Reflex USB v.2 0' - readeralias = 'SchlumbergerSema Reflex USB v.2 0 alias' - hresult = SCardIntroduceReader(hcontext, readeralias, reader]) - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to introduce reader: ' + SCardGetErrorMessage(hresult) - - hresult = SCardAddReaderToGroup(hcontext, readeralias, newgroup) - if hresult!=0: - raise error, 'Unable to add reader to group: ' + SCardGetErrorMessage(hresult) - - ... - - """ - return _scard.SCardAddReaderToGroup(hcontext, readername, groupname) - -def SCardForgetCardType(hcontext, cardname): - """ - SCardForgetCardType( hcontext, cardname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - cardname: friendly name of a card - - - - removes an introduced smart card from the smart card subsystem. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... establish context ... - hresult = SCardForgetCardType(hcontext, 'myCardName') - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to remove card type: ' + SCardGetErrorMessage(hresult) - ... - - - """ - return _scard.SCardForgetCardType(hcontext, cardname) - -def SCardForgetReader(hcontext, readername): - """ - SCardForgetReader( hcontext, readername) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readername: card reader name - - - - Removes a previously introduced smart card reader from the smart - card subsystem. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... establish context ... - ... - hresult = SCardForgetReader(hcontext, dummyreader) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to forget readers ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardForgetReader(hcontext, readername) - -def SCardForgetReaderGroup(hcontext, groupname): - """ - SCardForgetReaderGroup( hcontext, groupname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - groupname: card reader group name - - - - Removes a previously introduced smart card reader group from the smart - card subsystem. Although this function automatically clears all readers - from the group, it does not affect the existence of the individual readers - in the database. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... establish context ... - ... - hresult = SCardForgetReaderGroup(hcontext, newgroup) - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to forget reader group: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardForgetReaderGroup(hcontext, groupname) - -def SCardGetCardTypeProviderName(hcontext, cardname, dwProviderId): - """ - SCardGetCardTypeProviderName( hcontext, cardname, dwProviderId) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - cardname: friendly name of a card - dwProviderId: provider type, SCARD_PROVIDER_PRIMARY or SCARD_PROVIDER_CSP - - - - Returns the name of the module (dynamic link library) containing the - provider for a given card name and provider type. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... establish context ... - hresult, cards = SCardListCards(hcontext, [], []) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failure to list cards: ' + SCardGetErrorMessage(hresult) - for i in cards: - hresult, providername = SCardGetCardTypeProviderName(hcontext, i, SCARD_PROVIDER_PRIMARY) - if hresult == SCARD_S_SUCCESS: - print providername - hresult, providername = SCardGetCardTypeProviderName(hcontext, i, SCARD_PROVIDER_CSP) - if hresult == SCARD_S_SUCCESS: - print providername - ... - - """ - return _scard.SCardGetCardTypeProviderName(hcontext, cardname, dwProviderId) - -def SCardIntroduceCardType(hcontext, cardname, primaryprovider, providerlist, atr, mask): - """ - SCardIntroduceCardType( hcontext, cardname, GUID primaryprovider, GUID[] providerlist, byte[] atr, byte[] mask) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - cardname: friendly name of a card - primaryprovidername: GUID of the smart card primary service provider - providerlist: list of GUIDs of interfaces supported by smart card - atr: card ATR - mask: mask to apply to card ATR - - - - Introduces a smart card to the smart card subsystem (for the active user) - by adding it to the smart card database. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... - znewcardName = 'dummy-card' - znewcardATR = [0x3B, 0x77, 0x94, 0x00, 0x00, 0x82, 0x30, 0x00, 0x13, 0x6C, 0x9F, 0x22] - znewcardMask = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] - znewcardPrimGuid = smartcard.guid.strToGUID('{128F3806-4F70-4ccf-977A-60C390664840}') - znewcardSecGuid = smartcard.guid.strToGUID('{EB7F69EA-BA20-47d0-8C50-11CFDEB63BBE}') - ... - hresult = SCardIntroduceCardType(hcontext, znewcardName, - znewcardPrimGuid, znewcardPrimGuid + znewcardSecGuid, - znewcardATR, znewcardMask) - - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to introduce card type: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardIntroduceCardType(hcontext, cardname, primaryprovider, providerlist, atr, mask) - -def SCardIntroduceReader(hcontext, readername, devicename): - """ - SCardIntroduceReader( hcontext, readername, devicename) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readername: card reader name - devicename: card reader device name - - - - Introduces a reader to the smart card subsystem. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... - dummyreader = readers[0] + ' dummy' - hresult = SCardIntroduceReader(hcontext, dummyreader, readers[0]) - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to introduce reader: ' + dummyreader + ' : ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardIntroduceReader(hcontext, readername, devicename) - -def SCardIntroduceReaderGroup(hcontext, groupname): - """ - SCardIntroduceReaderGroup( hcontext, groupname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - groupname: card reader group name - - - - Introduces a reader group to the smart card subsystem. However, the - reader group is not created until the group is specified when adding - a reader to the smart card database. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult = SCardIntroduceReaderGroup(hcontext, 'SCard$MyOwnGroup') - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to introduce reader group: ' + SCardGetErrorMessage(hresult) - hresult = SCardAddReaderToGroup(hcontext, 'SchlumbergerSema Reflex USB v.2 0', 'SCard$MyOwnGroup') - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to add reader to group: ' + SCardGetErrorMessage(hresult) - - """ - return _scard.SCardIntroduceReaderGroup(hcontext, groupname) - -def SCardListInterfaces(hcontext, cardname): - """ - SCardListInterfaces( hcontext, cardname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - cardname: friendly name of a card - - - - Provides a list of interfaces supplied by a given card. The caller - supplies the name of a smart card previously introduced to the subsystem, - and receives the list of interfaces supported by the card - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, interfaces = SCardListInterfaces(hcontext, 'Schlumberger Cryptoflex 8k v2') - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to list interfaces: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardListInterfaces(hcontext, cardname) - -def SCardListCards(hcontext, atr, providerlist): - """ - SCardListCards( hcontext, byte[] atr, GUID[] providerlist) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - atr: card ATR - providerlist: list of GUIDs of interfaces supported by smart card - - - - Searches the smart card database and provides a list of named cards - previously introduced to the system by the user. The caller specifies an - ATR string, a set of interface identifiers (GUIDs), or both. If both an - ATR string and an identifier array are supplied, the cards returned will - match the ATR string supplied and support the interfaces specified. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - ... - slbCryptoFlex8kv2ATR = [ 0x3B, 0x95, 0x15, 0x40, 0x00, 0x68, 0x01, 0x02, 0x00, 0x00 ] - hresult, card = SCardListCards(hcontext, slbCryptoFlex8kv2ATR, []) - if hresult ! =SCARD_S_SUCCESS: - raise error, 'Failure to locate Schlumberger Cryptoflex 8k v2 card: ' + SCardGetErrorMessage(hresult) - hresult, cards = SCardListCards(hcontext, [], []) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failure to list cards: ' + SCardGetErrorMessage(hresult) - print 'Cards: ', cards - ... - - """ - return _scard.SCardListCards(hcontext, atr, providerlist) - -def SCardLocateCards(hcontext, cards, readerstatelist): - """ - SCardLocateCards( hcontext, cards, tuple[] readerstatelist) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - cards: a list of cards to locate - readerstatelist: in input/output, a list of reader state tuple (readername, state, atr) - - - - Searches the readers listed in the readerstate parameter for a card - with an ATR string that matches one of the card names specified in - mszCards, returning immediately with the result. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, readers = SCardListReaders(hcontext, []) - readerstates = [] - cards = ['Schlumberger Cryptoflex 4k', 'Schlumberger Cryptoflex 8k', 'Schlumberger Cryptoflex 8k v2'] - for i in xrange(len(readers)): - readerstates += [(readers[i], SCARD_STATE_UNAWARE)] - hresult, newstates = SCardLocateCards(hcontext, cards, readerstates) - for i in newstates: - reader, eventstate, atr = i - print reader, - for b in atr: - print '0x%.2X' % b, - print - if eventstate & SCARD_STATE_ATRMATCH: - print 'Card found' - if eventstate & SCARD_STATE_EMPTY: - print 'Reader empty' - if eventstate & SCARD_STATE_PRESENT: - print 'Card present in reader' - ... - - """ - return _scard.SCardLocateCards(hcontext, cards, readerstatelist) - -def SCardRemoveReaderFromGroup(hcontext, readername, groupname): - """ - SCardRemoveReaderFromGroup( hcontext, readername, groupname) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readername: card reader name - groupname: card reader group name - - - - - Removes a reader from an existing reader group. This function has no - affect on the reader. - - Windows only, not supported by PCSC lite wrapper. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult = SCardRemoveReaderFromGroup(hcontext, 'SchlumbergerSema Reflex USB v.2 0', 'SCard$MyOwnGroup') - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to remove reader from group: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardRemoveReaderFromGroup(hcontext, readername, groupname) - -def SCardIsValidContext(hcontext): - """ - SCardIsValidContext( hcontext) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - - - - This function determines whether a smart card context handle is still - valid. After a smart card context handle has been set by - SCardEstablishContext(), it may become not valid if the resource manager - service has been shut down. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult = SCardIsValidContext(hcontext) - if hresult != SCARD_S_SUCCESS: - raise error, 'Invalid context: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardIsValidContext(hcontext) - -def SCardGetAttrib(hcard, dwAttrId): - """ - SCardGetAttrib( hcard, dwAttrId) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwAttrId: value of attribute to get - - - - - This function get an attribute from the IFD Handler. - - The possible attributes are: - - ======================================== ======= ======= - Attribute Windows PSCS - lite - ======================================== ======= ======= - SCARD_ATTR_ASYNC_PROTOCOL_TYPES Y - SCARD_ATTR_ATR_STRING Y Y - SCARD_ATTR_CHANNEL_ID Y Y - SCARD_ATTR_CHARACTERISTICS Y Y - SCARD_ATTR_CURRENT_BWT Y Y - SCARD_ATTR_CURRENT_CLK Y Y - SCARD_ATTR_CURRENT_CWT Y Y - SCARD_ATTR_CURRENT_D Y Y - SCARD_ATTR_CURRENT_EBC_ENCODING Y Y - SCARD_ATTR_CURRENT_F Y Y - SCARD_ATTR_CURRENT_IFSC Y Y - SCARD_ATTR_CURRENT_IFSD Y Y - SCARD_ATTR_CURRENT_IO_STATE Y Y - SCARD_ATTR_CURRENT_N Y Y - SCARD_ATTR_CURRENT_PROTOCOL_TYPE Y Y - SCARD_ATTR_CURRENT_W Y Y - SCARD_ATTR_DEFAULT_CLK Y Y - SCARD_ATTR_DEFAULT_DATA_RATE Y Y - SCARD_ATTR_DEVICE_FRIENDLY_NAME_A Y Y - SCARD_ATTR_DEVICE_FRIENDLY_NAME_W Y Y - SCARD_ATTR_DEVICE_IN_USE Y Y - SCARD_ATTR_DEVICE_SYSTEM_NAME_A Y Y - SCARD_ATTR_DEVICE_SYSTEM_NAME_W Y Y - SCARD_ATTR_DEVICE_UNIT Y Y - SCARD_ATTR_ESC_AUTHREQUEST Y Y - SCARD_ATTR_ESC_CANCEL Y Y - SCARD_ATTR_ESC_RESET Y Y - SCARD_ATTR_EXTENDED_BWT Y Y - SCARD_ATTR_ICC_INTERFACE_STATUS Y Y - SCARD_ATTR_ICC_PRESENCE Y Y - SCARD_ATTR_ICC_TYPE_PER_ATR Y Y - SCARD_ATTR_MAXINPUT Y Y - SCARD_ATTR_MAX_CLK Y Y - SCARD_ATTR_MAX_DATA_RATE Y Y - SCARD_ATTR_MAX_IFSD Y Y - SCARD_ATTR_POWER_MGMT_SUPPORT Y Y - SCARD_ATTR_SUPRESS_T1_IFS_REQUEST Y Y - SCARD_ATTR_SYNC_PROTOCOL_TYPES Y - SCARD_ATTR_USER_AUTH_INPUT_DEVICE Y Y - SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE Y Y - SCARD_ATTR_VENDOR_IFD_SERIAL_NO Y Y - SCARD_ATTR_VENDOR_IFD_TYPE Y Y - SCARD_ATTR_VENDOR_IFD_VERSION Y Y - SCARD_ATTR_VENDOR_NAME Y Y - ======================================== ======= ======= - - Not all the dwAttrId values listed above may be implemented in the IFD - Handler you are using. And some dwAttrId values not listed here may be - implemented. - - - from smartcard.scard import * - ... establish context and connect to card ... - hresult, attrib = SCardGetAttrib(hcard, SCARD_ATTR_ATR_STRING) - if hresult == SCARD_S_SUCCESS: - for j in attrib: - print '0x%.2X' % attrib, - ... - - """ - return _scard.SCardGetAttrib(hcard, dwAttrId) - -def SCardSetAttrib(hcard, dwAttrId, ATTRIBUTESIN): - """ - SCardSetAttrib( hcard, dwAttrId, BYTELIST * ATTRIBUTESIN) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwAttrId: value of attribute to get - ATTRIBUTESIN: BYTELIST * - - - - - This function sets an attribute from the IFD Handler. Not all - attributes are supported by all readers nor can they be set at all - times. - - The possible attributes are: - - ======================================== ======= ======= - Attribute Windows PSCS - lite - ======================================== ======= ======= - SCARD_ATTR_ASYNC_PROTOCOL_TYPES Y - SCARD_ATTR_ATR_STRING Y Y - SCARD_ATTR_CHANNEL_ID Y Y - SCARD_ATTR_CHARACTERISTICS Y Y - SCARD_ATTR_CURRENT_BWT Y Y - SCARD_ATTR_CURRENT_CLK Y Y - SCARD_ATTR_CURRENT_CWT Y Y - SCARD_ATTR_CURRENT_D Y Y - SCARD_ATTR_CURRENT_EBC_ENCODING Y Y - SCARD_ATTR_CURRENT_F Y Y - SCARD_ATTR_CURRENT_IFSC Y Y - SCARD_ATTR_CURRENT_IFSD Y Y - SCARD_ATTR_CURRENT_IO_STATE Y Y - SCARD_ATTR_CURRENT_N Y Y - SCARD_ATTR_CURRENT_PROTOCOL_TYPE Y Y - SCARD_ATTR_CURRENT_W Y Y - SCARD_ATTR_DEFAULT_CLK Y Y - SCARD_ATTR_DEFAULT_DATA_RATE Y Y - SCARD_ATTR_DEVICE_FRIENDLY_NAME_A Y Y - SCARD_ATTR_DEVICE_FRIENDLY_NAME_W Y Y - SCARD_ATTR_DEVICE_IN_USE Y Y - SCARD_ATTR_DEVICE_SYSTEM_NAME_A Y Y - SCARD_ATTR_DEVICE_SYSTEM_NAME_W Y Y - SCARD_ATTR_DEVICE_UNIT Y Y - SCARD_ATTR_ESC_AUTHREQUEST Y Y - SCARD_ATTR_ESC_CANCEL Y Y - SCARD_ATTR_ESC_RESET Y Y - SCARD_ATTR_EXTENDED_BWT Y Y - SCARD_ATTR_ICC_INTERFACE_STATUS Y Y - SCARD_ATTR_ICC_PRESENCE Y Y - SCARD_ATTR_ICC_TYPE_PER_ATR Y Y - SCARD_ATTR_MAXINPUT Y Y - SCARD_ATTR_MAX_CLK Y Y - SCARD_ATTR_MAX_DATA_RATE Y Y - SCARD_ATTR_MAX_IFSD Y Y - SCARD_ATTR_POWER_MGMT_SUPPORT Y Y - SCARD_ATTR_SUPRESS_T1_IFS_REQUEST Y Y - SCARD_ATTR_SYNC_PROTOCOL_TYPES Y - SCARD_ATTR_USER_AUTH_INPUT_DEVICE Y Y - SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE Y Y - SCARD_ATTR_VENDOR_IFD_SERIAL_NO Y Y - SCARD_ATTR_VENDOR_IFD_TYPE Y Y - SCARD_ATTR_VENDOR_IFD_VERSION Y Y - SCARD_ATTR_VENDOR_NAME Y Y - ======================================== ======= ======= - - Not all the dwAttrId values listed above may be implemented in the IFD - Handler you are using. And some dwAttrId values not listed here may be - implemented. - - - from smartcard.scard import * - ... establish context and connect to card ... - hresult, attrib = SCardSetAttrib(hcard, SCARD_ATTR_VENDOR_NAME, ['G', 'e', 'm', 'a', 'l', 't', 'o']) - if hresult != SCARD_S_SUCCESS: - print 'Failed to set attribute' - ... - - """ - return _scard.SCardSetAttrib(hcard, dwAttrId, ATTRIBUTESIN) - -def SCardControl(hcard, dwControlCode, inbuffer): - """ - SCardControl( hcard, dwControlCode, byte[] inbuffer) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwControlCode: the control code to send - inbuffer: list of bytes to send with the control code - - - - This function sends a control command to the reader connected to by - SCardConnect(). It returns a result and the control response. - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - CMD = [0x12, 0x34] - hresult, response = SCardControl(hcard, 42, CMD) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to control: ' + SCardGetErrorMessage(hresult) - - """ - return _scard.SCardControl(hcard, dwControlCode, inbuffer) - -def SCardBeginTransaction(hcard): - """ - SCardBeginTransaction( hcard) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - - - - This function establishes a temporary exclusive access mode for doing a - series of commands or transaction. You might want to use this when you - are selecting a few files and then writing a large file so you can make - sure that another application will not change the current file. If - another application has a lock on this reader or this application is in - SCARD_SHARE_EXCLUSIVE there will be no action taken. - - from smartcard.scard import * - ... establish context ... - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - if hresult!=SCARD_S_SUCCESS: - raise error, 'unable to connect: ' + SCardGetErrorMessage(hresult) - hresult = SCardBeginTransaction(hcard) - if hresult != SCARD_S_SUCCESS: - raise error, 'failed to begin transaction: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardBeginTransaction(hcard) - -def SCardCancel(hcontext): - """ - SCardCancel( hcontext) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - - - - This function cancels all pending blocking requests on the - ScardGetStatusChange() function. - - from smartcard.scard import * - ... establish context ... - hresult = SCardCancel(hcard) - if hresult != SCARD_S_SUCCESS: - raise error, 'failed to cancel pending actions: ' + SCardGetErrorMessage(hresult) - ... - """ - return _scard.SCardCancel(hcontext) - -def SCardConnect(hcontext, readername, dwShareMode, dwPreferredProtocols): - """ - SCardConnect( hcontext, readername, dwShareMode, dwPreferredProtocols) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readername: card reader name - dwShareMode: share mode - dwPreferredProtocols: preferred protocols - - - - This function establishes a connection to the friendly name of the reader - specified in szReader. The first connection will power up and perform a - reset on the card. - - Value of dwShareMode Meaning - SCARD_SHARE_SHARED This application will allow others to share the reader - SCARD_SHARE_EXCLUSIVE This application will NOT allow others to share the reader - SCARD_SHARE_DIRECT Direct control of the reader, even without a card - - SCARD_SHARE_DIRECT can be used before using SCardControl() to send control - commands to the reader even if a card is not present in the reader. - - Value of dwPreferredProtocols Meaning - SCARD_PROTOCOL_T0 Use the T=0 protocol - SCARD_PROTOCOL_T1 Use the T=1 protocol - SCARD_PROTOCOL_RAW Use with memory type cards - - from smartcard.scard import * - ... establish context ... - hresult, readers = SCardListReaders(hcontext, 'NULL') - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to list readers:: ' + SCardGetErrorMessage(hresult) - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, readers[0], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - if hresult != SCARD_S_SUCCESS: - raise error, 'unable to connect: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardConnect(hcontext, readername, dwShareMode, dwPreferredProtocols) - -def SCardDisconnect(hcard, dwDisposition): - """ - SCardDisconnect( hcard, dwDisposition) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwDisposition: card disposition on return - - - - This function terminates a connection to the connection made through - SCardConnect. disposition can have the following values: - - Value of disposition Meaning - SCARD_LEAVE_CARD Do nothing - SCARD_RESET_CARD Reset the card (warm reset) - SCARD_UNPOWER_CARD Unpower the card (cold reset) - SCARD_EJECT_CARD Eject the card - - from smartcard.scard import * - ... establish context and connect to card ... - hresult = SCardDisconnect(hcard, SCARD_UNPOWER_CARD) - if hresult != SCARD_S_SUCCESS: - raise error, 'failed to disconnect: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardDisconnect(hcard, dwDisposition) - -def SCardEndTransaction(hcard, dwDisposition): - """ - SCardEndTransaction( hcard, dwDisposition) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwDisposition: card disposition on return - - - - - This function ends a previously begun transaction. The calling - application must be the owner of the previously begun transaction or an - error will occur. disposition can have the following values: The - disposition action is not currently used in this release. - - Value of disposition Meaning - SCARD_LEAVE_CARD Do nothing - SCARD_RESET_CARD Reset the card - SCARD_UNPOWER_CARD Unpower the card - SCARD_EJECT_CARD Eject the card - - from smartcard.scard import * - ... establish context, connect to card, begin transaction ... - hresult = SCardEndTransaction(hcard, SCARD_LEAVE_CARD) - if hresult != SCARD_S_SUCCESS: - raise error, 'failed to end transaction: ' + SCardGetErrorMessage(hresult) - - """ - return _scard.SCardEndTransaction(hcard, dwDisposition) - -def SCardEstablishContext(dwScope): - """ - SCardEstablishContext( dwScope) -> SCARDRETCODE - - Parameters - ---------- - dwScope: context scope - - - - This function creates a communication context to the PC/SC Resource - Manager. This must be the first function called in a PC/SC application. - - Value of dwScope Meaning - SCARD_SCOPE_USER Operations performed within the scope of the User - SCARD_SCOPE_TERMINAL Not used - SCARD_SCOPE_GLOBAL Not used - SCARD_SCOPE_SYSTEM Operations performed within the scope of the system - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to establish context: ' + SCardGetErrorMessage(hresult) - - """ - return _scard.SCardEstablishContext(dwScope) - -def SCardGetStatusChange(hcontext, dwTimeout, readerstatelist): - """ - SCardGetStatusChange( hcontext, dwTimeout, tuple[] readerstatelist) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - dwTimeout: timeout value, INFINITE for infinite time-out - readerstatelist: in input/output, a list of reader state tuple (readername, state, atr) - - - - - This function receives a structure or list of tuples containing reader - states. A READERSTATE hast three fields (readername, state, atr). - It then blocks for a change in state to occur on any of the OR'd - values contained in the current state for a maximum blocking time of - dwTimeout or forever if INFINITE is used. The new event state will be - contained in state. A status change might be a card insertion or - removal event, a change in ATR, etc. - - Value of state Meaning - SCARD_STATE_UNAWARE The application is unaware of the current state, and would like to know. The use of this value results in an immediate return from state transition monitoring services. This is represented by all bits set to zero - SCARD_STATE_IGNORE This reader should be ignored - SCARD_STATE_CHANGED There is a difference between the state believed by the application, and the state known by the resource manager. When this bit is set, the application may assume a significant state change has occurred on this reader - SCARD_STATE_UNKNOWN The given reader name is not recognized by the resource manager. If this bit is set, then SCARD_STATE_CHANGED and SCARD_STATE_IGNORE will also be set - SCARD_STATE_UNAVAILABLE The actual state of this reader is not available. If this bit is set, then all the following bits are clear - SCARD_STATE_EMPTY There is no card in the reader. If this bit is set, all the following bits will be clear - SCARD_STATE_PRESENT There is a card in the reader - SCARD_STATE_ATRMATCH There is a card in the reader with an ATR matching one of the target cards. If this bit is set, SCARD_STATE_PRESENT will also be set. This bit is only returned on the SCardLocateCards function - SCARD_STATE_EXCLUSIVE The card in the reader is allocated for exclusive use by another application. If this bit is set, SCARD_STATE_PRESENT will also be set - SCARD_STATE_INUSE The card in the reader is in use by one or more other applications, but may be connected to in shared mode. If this bit is set, SCARD_STATE_PRESENT will also be set - SCARD_STATE_MUTE There is an unresponsive card in the reader - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, readers = SCardListReaders(hcontext, []) - readerstates = [] - cards = [ 'Schlumberger Cryptoflex 4k', 'Schlumberger Cryptoflex 8k', 'Schlumberger Cryptoflex 8k v2' ] - for i in xrange(len(readers)): - readerstates += [ (readers[i], SCARD_STATE_UNAWARE) ] - hresult, newstates = SCardLocateCards(hcontext, cards, readerstates) - print '----- Please insert or remove a card ------------' - hresult, newstates = SCardGetStatusChange(hcontext, INFINITE, newstates) - for i in newstates - reader, eventstate, atr = i - if eventstate & SCARD_STATE_ATRMATCH: - print ' Card found' - if eventstate & SCARD_STATE_EMPTY: - print ' Reader empty' - - """ - return _scard.SCardGetStatusChange(hcontext, dwTimeout, readerstatelist) - -def SCardListReaders(hcontext, readergroups): - """ - SCardListReaders( hcontext, [] readergroups) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - readergroups: a list of reader groups to search for readers - - - - This function returns a list of currently available readers on the system. - A list of group can be provided in input to list readers in a given - group only. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, readers = SCardListReaders(hcontext, []) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to list readers: ' + SCardGetErrorMessage(hresult) - print 'PCSC Readers: ', readers - hresult, readers = SCardListReaders(hcontext, ['SCard$T1ProtocolReaders', 'SCard$MyOwnGroup'] - ... - - """ - return _scard.SCardListReaders(hcontext, readergroups) - -def SCardListReaderGroups(hcontext): - """ - SCardListReaderGroups( hcontext) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - - - - This function returns a list of currently available reader groups on the - system. - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, readerGroups = SCardListReaderGroups(hcontext) - if hresult != SCARD_S_SUCCESS: - raise error, 'Unable to list reader groups: ' + SCardGetErrorMessage(hresult) - print 'PCSC Reader groups: ', readerGroups - - """ - return _scard.SCardListReaderGroups(hcontext) - -def SCardReconnect(hcard, dwShareMode, dwPreferredProtocols, dwInitialization): - """ - SCardReconnect( hcard, dwShareMode, dwPreferredProtocols, dwInitialization) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - dwShareMode: share mode - dwPreferredProtocols: preferred protocols - dwInitialization: the type of initialization that should be performed on the card - - - - - This function reestablishes a connection to a reader that was previously - connected to using SCardConnect(). In a multi application environment it - is possible for an application to reset the card in shared mode. When - this occurs any other application trying to access certain commands will - be returned the value SCARD_W_RESET_CARD. When this occurs - SCardReconnect() must be called in order to acknowledge that the card was - reset and allow it to change it's state accordingly. - - Value of dwShareMode Meaning - SCARD_SHARE_SHARED This application will allow others to share the reader - SCARD_SHARE_EXCLUSIVE This application will NOT allow others to share the reader - - Value of dwPreferredProtocols Meaning - SCARD_PROTOCOL_T0 Use the T=0 protocol - SCARD_PROTOCOL_T1 Use the T=1 protocol - SCARD_PROTOCOL_RAW Use with memory type cards - - dwPreferredProtocols is a bit mask of acceptable protocols for the connection. You can use (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) if you do not have a preferred protocol. - - Value of dwInitialization Meaning - SCARD_LEAVE_CARD Do nothing - SCARD_RESET_CARD Reset the card (warm reset) - SCARD_UNPOWER_CARD Unpower the card (cold reset) - SCARD_EJECT_CARD Eject the card - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - hresult, activeProtocol = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE, - SCARD_PROTOCOL_T0, SCARD_RESET_CARD) - ... - - """ - return _scard.SCardReconnect(hcard, dwShareMode, dwPreferredProtocols, dwInitialization) - -def SCardReleaseContext(hcontext): - """ - SCardReleaseContext( hcontext) -> SCARDRETCODE - - Parameters - ---------- - hcontext: context handle return from SCardEstablishContext() - - - - - """ - return _scard.SCardReleaseContext(hcontext) - -def SCardStatus(hcard): - """ - SCardStatus( hcard) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - - - - This function returns the current status of the reader connected to by - hcard. The reader friendly name is returned, as well as the state, - protocol and ATR. The state is a DWORD possibly OR'd with the following - values: - - Value of pdwState Meaning - SCARD_ABSENT There is no card in the reader - SCARD_PRESENT There is a card in the reader, but it has not been moved into position for use - SCARD_SWALLOWED There is a card in the reader in position for use. The card is not powered - SCARD_POWERED Power is being provided to the card, but the reader driver is unaware of the mode of the card - SCARD_NEGOTIABLE The card has been reset and is awaiting PTS negotiation - SCARD_SPECIFIC The card has been reset and specific communication protocols have been established - - Value of pdwProtocol Meaning - SCARD_PROTOCOL_T0 Use the T=0 protocol - SCARD_PROTOCOL_T1 Use the T=1 protocol - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - hresult, reader, state, protocol, atr = SCardStatus(hcard) - if hresult != SCARD_S_SUCCESS: - raise error, 'failed to get status: ' + SCardGetErrorMessage(hresult) - print 'Reader: ', reader - print 'State: ', state - print 'Protocol: ', protocol - print 'ATR: ', - for i in xrange(len(atr)): - print '0x%.2X' % i, - print - ... - - """ - return _scard.SCardStatus(hcard) - -def SCardTransmit(hcard, pioSendPci, apducommand): - """ - SCardTransmit( hcard, unsigned long pioSendPci, byte[] apducommand) -> SCARDRETCODE - - Parameters - ---------- - hcard: card handle return from SCardConnect() - pioSendPci: unsigned long - apducommand: list of APDU bytes to transmit - - - - This function sends an APDU to the smart card contained in the reader - connected to by SCardConnect(). - It returns a result and the card APDU response. - - Value of pioSendPci Meaning - SCARD_PCI_T0 Pre-defined T=0 PCI structure - SCARD_PCI_T1 Pre-defined T=1 PCI structure - - - from smartcard.scard import * - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - hresult, hcard, dwActiveProtocol = SCardConnect( - hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) - SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] - DF_TELECOM = [0x7F, 0x10] - hresult, response = SCardTransmit(hcard, SCARD_PCI_T0, SELECT + DF_TELECOM) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to transmit: ' + SCardGetErrorMessage(hresult) - - """ - return _scard.SCardTransmit(hcard, pioSendPci, apducommand) - -def SCARD_CTL_CODE(code): - """ - SCARD_CTL_CODE(long code) -> long - - Parameters - ---------- - code: long - - - - This function returns the value of a control code - - from smartcard.scard import * - ... - CM_IOCTL_GET_FEATURE_REQUEST = SCARD_CTL_CODE(3400) - ... - - """ - return _scard.SCARD_CTL_CODE(code) - -def SCardGetErrorMessage(lErrCode): - """ - SCardGetErrorMessage(long lErrCode) -> ERRORSTRING * - - Parameters - ---------- - lErrCode: long - - - - This function return a human readable text for the given PC/SC error code. - - from smartcard.scard import * - ... - hresult, response = SCardTransmit(hcard, SCARD_PCI_T0, SELECT + DF_TELECOM) - if hresult != SCARD_S_SUCCESS: - raise error, 'Failed to transmit: ' + SCardGetErrorMessage(hresult) - ... - - """ - return _scard.SCardGetErrorMessage(lErrCode) - -error = _scard.error - -SCARD_SCOPE_USER = _scard.SCARD_SCOPE_USER -SCARD_SCOPE_TERMINAL = _scard.SCARD_SCOPE_TERMINAL -SCARD_SCOPE_SYSTEM = _scard.SCARD_SCOPE_SYSTEM -SCARD_SHARE_SHARED = _scard.SCARD_SHARE_SHARED -SCARD_SHARE_EXCLUSIVE = _scard.SCARD_SHARE_EXCLUSIVE -SCARD_SHARE_DIRECT = _scard.SCARD_SHARE_DIRECT -SCARD_LEAVE_CARD = _scard.SCARD_LEAVE_CARD -SCARD_RESET_CARD = _scard.SCARD_RESET_CARD -SCARD_UNPOWER_CARD = _scard.SCARD_UNPOWER_CARD -SCARD_EJECT_CARD = _scard.SCARD_EJECT_CARD -SCARD_STATE_UNAWARE = _scard.SCARD_STATE_UNAWARE -SCARD_STATE_IGNORE = _scard.SCARD_STATE_IGNORE -SCARD_STATE_CHANGED = _scard.SCARD_STATE_CHANGED -SCARD_STATE_UNKNOWN = _scard.SCARD_STATE_UNKNOWN -SCARD_STATE_UNAVAILABLE = _scard.SCARD_STATE_UNAVAILABLE -SCARD_STATE_EMPTY = _scard.SCARD_STATE_EMPTY -SCARD_STATE_PRESENT = _scard.SCARD_STATE_PRESENT -SCARD_STATE_ATRMATCH = _scard.SCARD_STATE_ATRMATCH -SCARD_STATE_EXCLUSIVE = _scard.SCARD_STATE_EXCLUSIVE -SCARD_STATE_INUSE = _scard.SCARD_STATE_INUSE -SCARD_STATE_MUTE = _scard.SCARD_STATE_MUTE -SCARD_STATE_UNPOWERED = _scard.SCARD_STATE_UNPOWERED -SCARD_PROTOCOL_UNDEFINED = _scard.SCARD_PROTOCOL_UNDEFINED -SCARD_PROTOCOL_T0 = _scard.SCARD_PROTOCOL_T0 -SCARD_PROTOCOL_T1 = _scard.SCARD_PROTOCOL_T1 -SCARD_PROTOCOL_RAW = _scard.SCARD_PROTOCOL_RAW -SCARD_PROTOCOL_Tx = _scard.SCARD_PROTOCOL_Tx -SCARD_PROTOCOL_DEFAULT = _scard.SCARD_PROTOCOL_DEFAULT -SCARD_PROTOCOL_OPTIMAL = _scard.SCARD_PROTOCOL_OPTIMAL -SCARD_PROTOCOL_UNSET = _scard.SCARD_PROTOCOL_UNSET -SCARD_PROTOCOL_ANY = _scard.SCARD_PROTOCOL_ANY -SCARD_PROTOCOL_T15 = _scard.SCARD_PROTOCOL_T15 -SCARD_PCI_T0 = _scard.SCARD_PCI_T0 -SCARD_PCI_T1 = _scard.SCARD_PCI_T1 -SCARD_PCI_RAW = _scard.SCARD_PCI_RAW -SCARD_PROVIDER_PRIMARY = _scard.SCARD_PROVIDER_PRIMARY -SCARD_PROVIDER_CSP = _scard.SCARD_PROVIDER_CSP -SCARD_ATTR_VENDOR_NAME = _scard.SCARD_ATTR_VENDOR_NAME -SCARD_ATTR_VENDOR_IFD_TYPE = _scard.SCARD_ATTR_VENDOR_IFD_TYPE -SCARD_ATTR_VENDOR_IFD_VERSION = _scard.SCARD_ATTR_VENDOR_IFD_VERSION -SCARD_ATTR_VENDOR_IFD_SERIAL_NO = _scard.SCARD_ATTR_VENDOR_IFD_SERIAL_NO -SCARD_ATTR_CHANNEL_ID = _scard.SCARD_ATTR_CHANNEL_ID -SCARD_ATTR_DEFAULT_CLK = _scard.SCARD_ATTR_DEFAULT_CLK -SCARD_ATTR_MAX_CLK = _scard.SCARD_ATTR_MAX_CLK -SCARD_ATTR_DEFAULT_DATA_RATE = _scard.SCARD_ATTR_DEFAULT_DATA_RATE -SCARD_ATTR_MAX_DATA_RATE = _scard.SCARD_ATTR_MAX_DATA_RATE -SCARD_ATTR_MAX_IFSD = _scard.SCARD_ATTR_MAX_IFSD -SCARD_ATTR_POWER_MGMT_SUPPORT = _scard.SCARD_ATTR_POWER_MGMT_SUPPORT -SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE = _scard.SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE -SCARD_ATTR_USER_AUTH_INPUT_DEVICE = _scard.SCARD_ATTR_USER_AUTH_INPUT_DEVICE -SCARD_ATTR_CHARACTERISTICS = _scard.SCARD_ATTR_CHARACTERISTICS -SCARD_ATTR_CURRENT_PROTOCOL_TYPE = _scard.SCARD_ATTR_CURRENT_PROTOCOL_TYPE -SCARD_ATTR_CURRENT_CLK = _scard.SCARD_ATTR_CURRENT_CLK -SCARD_ATTR_CURRENT_F = _scard.SCARD_ATTR_CURRENT_F -SCARD_ATTR_CURRENT_D = _scard.SCARD_ATTR_CURRENT_D -SCARD_ATTR_CURRENT_N = _scard.SCARD_ATTR_CURRENT_N -SCARD_ATTR_CURRENT_W = _scard.SCARD_ATTR_CURRENT_W -SCARD_ATTR_CURRENT_IFSC = _scard.SCARD_ATTR_CURRENT_IFSC -SCARD_ATTR_CURRENT_IFSD = _scard.SCARD_ATTR_CURRENT_IFSD -SCARD_ATTR_CURRENT_BWT = _scard.SCARD_ATTR_CURRENT_BWT -SCARD_ATTR_CURRENT_CWT = _scard.SCARD_ATTR_CURRENT_CWT -SCARD_ATTR_CURRENT_EBC_ENCODING = _scard.SCARD_ATTR_CURRENT_EBC_ENCODING -SCARD_ATTR_EXTENDED_BWT = _scard.SCARD_ATTR_EXTENDED_BWT -SCARD_ATTR_ICC_PRESENCE = _scard.SCARD_ATTR_ICC_PRESENCE -SCARD_ATTR_ICC_INTERFACE_STATUS = _scard.SCARD_ATTR_ICC_INTERFACE_STATUS -SCARD_ATTR_CURRENT_IO_STATE = _scard.SCARD_ATTR_CURRENT_IO_STATE -SCARD_ATTR_ATR_STRING = _scard.SCARD_ATTR_ATR_STRING -SCARD_ATTR_ICC_TYPE_PER_ATR = _scard.SCARD_ATTR_ICC_TYPE_PER_ATR -SCARD_ATTR_ESC_RESET = _scard.SCARD_ATTR_ESC_RESET -SCARD_ATTR_ESC_CANCEL = _scard.SCARD_ATTR_ESC_CANCEL -SCARD_ATTR_ESC_AUTHREQUEST = _scard.SCARD_ATTR_ESC_AUTHREQUEST -SCARD_ATTR_MAXINPUT = _scard.SCARD_ATTR_MAXINPUT -SCARD_ATTR_DEVICE_UNIT = _scard.SCARD_ATTR_DEVICE_UNIT -SCARD_ATTR_DEVICE_IN_USE = _scard.SCARD_ATTR_DEVICE_IN_USE -SCARD_ATTR_DEVICE_FRIENDLY_NAME_A = _scard.SCARD_ATTR_DEVICE_FRIENDLY_NAME_A -SCARD_ATTR_DEVICE_SYSTEM_NAME_A = _scard.SCARD_ATTR_DEVICE_SYSTEM_NAME_A -SCARD_ATTR_DEVICE_FRIENDLY_NAME_W = _scard.SCARD_ATTR_DEVICE_FRIENDLY_NAME_W -SCARD_ATTR_DEVICE_SYSTEM_NAME_W = _scard.SCARD_ATTR_DEVICE_SYSTEM_NAME_W -SCARD_ATTR_SUPRESS_T1_IFS_REQUEST = _scard.SCARD_ATTR_SUPRESS_T1_IFS_REQUEST -ERROR_ALREADY_EXISTS = _scard.ERROR_ALREADY_EXISTS -SCARD_S_SUCCESS = _scard.SCARD_S_SUCCESS -SCARD_F_INTERNAL_ERROR = _scard.SCARD_F_INTERNAL_ERROR -SCARD_E_CANCELLED = _scard.SCARD_E_CANCELLED -SCARD_E_INVALID_HANDLE = _scard.SCARD_E_INVALID_HANDLE -SCARD_E_INVALID_PARAMETER = _scard.SCARD_E_INVALID_PARAMETER -SCARD_E_INVALID_TARGET = _scard.SCARD_E_INVALID_TARGET -SCARD_E_NO_MEMORY = _scard.SCARD_E_NO_MEMORY -SCARD_F_WAITED_TOO_LONG = _scard.SCARD_F_WAITED_TOO_LONG -SCARD_E_INSUFFICIENT_BUFFER = _scard.SCARD_E_INSUFFICIENT_BUFFER -SCARD_E_UNKNOWN_READER = _scard.SCARD_E_UNKNOWN_READER -SCARD_E_TIMEOUT = _scard.SCARD_E_TIMEOUT -SCARD_E_SHARING_VIOLATION = _scard.SCARD_E_SHARING_VIOLATION -SCARD_E_NO_SMARTCARD = _scard.SCARD_E_NO_SMARTCARD -SCARD_E_UNKNOWN_CARD = _scard.SCARD_E_UNKNOWN_CARD -SCARD_E_CANT_DISPOSE = _scard.SCARD_E_CANT_DISPOSE -SCARD_E_PROTO_MISMATCH = _scard.SCARD_E_PROTO_MISMATCH -SCARD_E_NOT_READY = _scard.SCARD_E_NOT_READY -SCARD_E_INVALID_VALUE = _scard.SCARD_E_INVALID_VALUE -SCARD_E_SYSTEM_CANCELLED = _scard.SCARD_E_SYSTEM_CANCELLED -SCARD_F_COMM_ERROR = _scard.SCARD_F_COMM_ERROR -SCARD_F_UNKNOWN_ERROR = _scard.SCARD_F_UNKNOWN_ERROR -SCARD_E_INVALID_ATR = _scard.SCARD_E_INVALID_ATR -SCARD_E_NOT_TRANSACTED = _scard.SCARD_E_NOT_TRANSACTED -SCARD_E_READER_UNAVAILABLE = _scard.SCARD_E_READER_UNAVAILABLE -SCARD_E_PCI_TOO_SMALL = _scard.SCARD_E_PCI_TOO_SMALL -SCARD_E_READER_UNSUPPORTED = _scard.SCARD_E_READER_UNSUPPORTED -SCARD_E_DUPLICATE_READER = _scard.SCARD_E_DUPLICATE_READER -SCARD_E_CARD_UNSUPPORTED = _scard.SCARD_E_CARD_UNSUPPORTED -SCARD_E_NO_SERVICE = _scard.SCARD_E_NO_SERVICE -SCARD_E_SERVICE_STOPPED = _scard.SCARD_E_SERVICE_STOPPED -SCARD_E_NO_READERS_AVAILABLE = _scard.SCARD_E_NO_READERS_AVAILABLE -SCARD_E_UNSUPPORTED_FEATURE = _scard.SCARD_E_UNSUPPORTED_FEATURE -SCARD_W_UNSUPPORTED_CARD = _scard.SCARD_W_UNSUPPORTED_CARD -SCARD_W_UNRESPONSIVE_CARD = _scard.SCARD_W_UNRESPONSIVE_CARD -SCARD_W_UNPOWERED_CARD = _scard.SCARD_W_UNPOWERED_CARD -SCARD_W_RESET_CARD = _scard.SCARD_W_RESET_CARD -SCARD_W_REMOVED_CARD = _scard.SCARD_W_REMOVED_CARD -SCARD_W_SECURITY_VIOLATION = _scard.SCARD_W_SECURITY_VIOLATION -SCARD_W_WRONG_CHV = _scard.SCARD_W_WRONG_CHV -SCARD_W_CHV_BLOCKED = _scard.SCARD_W_CHV_BLOCKED -SCARD_W_EOF = _scard.SCARD_W_EOF -SCARD_W_CANCELLED_BY_USER = _scard.SCARD_W_CANCELLED_BY_USER -SCARD_W_CARD_NOT_AUTHENTICATED = _scard.SCARD_W_CARD_NOT_AUTHENTICATED -SCARD_E_UNEXPECTED = _scard.SCARD_E_UNEXPECTED -SCARD_E_ICC_INSTALLATION = _scard.SCARD_E_ICC_INSTALLATION -SCARD_E_ICC_CREATEORDER = _scard.SCARD_E_ICC_CREATEORDER -SCARD_E_DIR_NOT_FOUND = _scard.SCARD_E_DIR_NOT_FOUND -SCARD_E_FILE_NOT_FOUND = _scard.SCARD_E_FILE_NOT_FOUND -SCARD_E_NO_DIR = _scard.SCARD_E_NO_DIR -SCARD_E_NO_FILE = _scard.SCARD_E_NO_FILE -SCARD_E_NO_ACCESS = _scard.SCARD_E_NO_ACCESS -SCARD_E_WRITE_TOO_MANY = _scard.SCARD_E_WRITE_TOO_MANY -SCARD_E_BAD_SEEK = _scard.SCARD_E_BAD_SEEK -SCARD_E_INVALID_CHV = _scard.SCARD_E_INVALID_CHV -SCARD_E_UNKNOWN_RES_MNG = _scard.SCARD_E_UNKNOWN_RES_MNG -SCARD_E_NO_SUCH_CERTIFICATE = _scard.SCARD_E_NO_SUCH_CERTIFICATE -SCARD_E_CERTIFICATE_UNAVAILABLE = _scard.SCARD_E_CERTIFICATE_UNAVAILABLE -SCARD_E_COMM_DATA_LOST = _scard.SCARD_E_COMM_DATA_LOST -SCARD_E_NO_KEY_CONTAINER = _scard.SCARD_E_NO_KEY_CONTAINER -SCARD_E_SERVER_TOO_BUSY = _scard.SCARD_E_SERVER_TOO_BUSY -ERROR_INVALID_HANDLE = _scard.ERROR_INVALID_HANDLE -SCARD_P_SHUTDOWN = _scard.SCARD_P_SHUTDOWN -INFINITE = _scard.INFINITE -resourceManager = _scard.resourceManager -resourceManagerSubType = _scard.resourceManagerSubType -# This file is compatible with both classic and new-style classes. - - +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.9 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + + + + +""" +The smartcard.scard module is a simple wrapper on top of the C language +PCSC SCardXXX API. + +The smartcard.scard module is the lower layer of the pyscard +framework that provides a higher level interface. + +You should avoid using the smartcard.scard package directly, and use the +pyscard directly because: + + - smartcard.scard being a C wrapper, the code tends to look like C code + written in python syntax + + - the smartcard package provides higher level abstractions (e.g. + CardType, CardConnection), and makes programming easier since it is + totally written in Python + +You can still use the smartcard.scard package if you want to write your +own framework, or if you want to perform quick-and-dirty port of C +language programs using SCardXXX calls, or if there are features of +SCardXXX API that you want to use and that are not available in the +pyscard library. + +Introduction + +The smartcard.scard module is a Python wrapper around PCSC smart card base +services. On Windows, the wrapper is performed around the smart card base +components winscard library. On linux and OS X, the wrapper is performed +around the PCSC-lite library. + + +The smartcard.scard module provides mapping for the following API functions, +depending on the Operating System: + +=============================== ======= ======= +Function Windows Linux + OS X +=============================== ======= ======= +GetOpenCardName +SCardAddReaderToGroup Y +SCardBeginTransaction Y Y +SCardCancel Y Y +SCardConnect Y Y +SCardControl Y Y +SCardDisconnect Y Y +SCardEndTransaction Y Y +SCardEstablishConteYt Y Y +SCardForgetCardType Y +SCardForgetReader Y +SCardForgetReaderGroup Y +SCardFreeMemory +SCardGetAttrib Y Y +SCardGetCardTypeProviderName Y +SCardGetErrorMessage Y +SCardGetProviderId +SCardGetStatusChange Y Y +SCardIntroduceCardType Y +SCardIntroduceReader Y +SCardIntroduceReaderGroup Y +SCardIsValidConteYt Y Y +SCardListCards Y +SCardListInterfaces Y +SCardListReaderGroups Y Y +SCardListReaders Y Y +SCardLocateCards Y +SCardReconnect Y Y +SCardReleaseConteYt Y Y +SCardRemoveReaderFromGroup Y +SCardSetAttrib Y Y +SCardSetCartTypeProviderName +SCardStatus Y Y +SCardTransmit Y Y +SCardUIDlgSelectCard +=============================== ======= ======= + +Comments, bug reports, improvements welcome. + +------------------------------------------------------------------------------- +Copyright 2001-2012 gemalto +@Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com +@Author: Ludovic Rousseau, mailto:ludovic.rousseau@free.fr + +This file is part of pyscard. + +pyscard is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at +your option) any later version. + +pyscard is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with pyscard; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +""" + + +from sys import version_info +if version_info >= (2, 7, 0): + def swig_import_helper(): + import importlib + pkg = __name__.rpartition('.')[0] + mname = '.'.join((pkg, '_scard')).lstrip('.') + return importlib.import_module(mname) + _scard = swig_import_helper() + del swig_import_helper +elif version_info >= (2, 6, 0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_scard', [dirname(__file__)]) + except ImportError: + import _scard + return _scard + if fp is not None: + try: + _mod = imp.load_module('_scard', fp, pathname, description) + finally: + fp.close() + return _mod + _scard = swig_import_helper() + del swig_import_helper +else: + import _scard +del version_info +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if (name == "thisown"): + return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if (not static): + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr(self, class_type, name): + if (name == "thisown"): + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except __builtin__.Exception: + class _object: + pass + _newclass = 0 + + +def SCardAddReaderToGroup(hcontext, readername, groupname): + """ + SCardAddReaderToGroup( hcontext, readername, groupname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readername: card reader name + groupname: card reader group name + + + + adds a reader to a reader group + + Windows only, not supported by PCSC lite wrapper. + + example: + + from smartcard.scard import * + ... establish context ... + newgroup = 'SCard$MyOwnGroup' + reader = 'SchlumbergerSema Reflex USB v.2 0' + readeralias = 'SchlumbergerSema Reflex USB v.2 0 alias' + hresult = SCardIntroduceReader(hcontext, readeralias, reader]) + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to introduce reader: ' + SCardGetErrorMessage(hresult) + + hresult = SCardAddReaderToGroup(hcontext, readeralias, newgroup) + if hresult!=0: + raise error, 'Unable to add reader to group: ' + SCardGetErrorMessage(hresult) + + ... + + """ + return _scard.SCardAddReaderToGroup(hcontext, readername, groupname) + +def SCardForgetCardType(hcontext, cardname): + """ + SCardForgetCardType( hcontext, cardname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + cardname: friendly name of a card + + + + removes an introduced smart card from the smart card subsystem. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... establish context ... + hresult = SCardForgetCardType(hcontext, 'myCardName') + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to remove card type: ' + SCardGetErrorMessage(hresult) + ... + + + """ + return _scard.SCardForgetCardType(hcontext, cardname) + +def SCardForgetReader(hcontext, readername): + """ + SCardForgetReader( hcontext, readername) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readername: card reader name + + + + Removes a previously introduced smart card reader from the smart + card subsystem. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... establish context ... + ... + hresult = SCardForgetReader(hcontext, dummyreader) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to forget readers ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardForgetReader(hcontext, readername) + +def SCardForgetReaderGroup(hcontext, groupname): + """ + SCardForgetReaderGroup( hcontext, groupname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + groupname: card reader group name + + + + Removes a previously introduced smart card reader group from the smart + card subsystem. Although this function automatically clears all readers + from the group, it does not affect the existence of the individual readers + in the database. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... establish context ... + ... + hresult = SCardForgetReaderGroup(hcontext, newgroup) + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to forget reader group: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardForgetReaderGroup(hcontext, groupname) + +def SCardGetCardTypeProviderName(hcontext, cardname, dwProviderId): + """ + SCardGetCardTypeProviderName( hcontext, cardname, dwProviderId) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + cardname: friendly name of a card + dwProviderId: provider type, SCARD_PROVIDER_PRIMARY or SCARD_PROVIDER_CSP + + + + Returns the name of the module (dynamic link library) containing the + provider for a given card name and provider type. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... establish context ... + hresult, cards = SCardListCards(hcontext, [], []) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failure to list cards: ' + SCardGetErrorMessage(hresult) + for i in cards: + hresult, providername = SCardGetCardTypeProviderName(hcontext, i, SCARD_PROVIDER_PRIMARY) + if hresult == SCARD_S_SUCCESS: + print providername + hresult, providername = SCardGetCardTypeProviderName(hcontext, i, SCARD_PROVIDER_CSP) + if hresult == SCARD_S_SUCCESS: + print providername + ... + + """ + return _scard.SCardGetCardTypeProviderName(hcontext, cardname, dwProviderId) + +def SCardIntroduceCardType(hcontext, cardname, primaryprovider, providerlist, atr, mask): + """ + SCardIntroduceCardType( hcontext, cardname, GUID primaryprovider, GUID[] providerlist, byte[] atr, byte[] mask) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + cardname: friendly name of a card + primaryprovidername: GUID of the smart card primary service provider + providerlist: list of GUIDs of interfaces supported by smart card + atr: card ATR + mask: mask to apply to card ATR + + + + Introduces a smart card to the smart card subsystem (for the active user) + by adding it to the smart card database. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... + znewcardName = 'dummy-card' + znewcardATR = [0x3B, 0x77, 0x94, 0x00, 0x00, 0x82, 0x30, 0x00, 0x13, 0x6C, 0x9F, 0x22] + znewcardMask = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + znewcardPrimGuid = smartcard.guid.strToGUID('{128F3806-4F70-4ccf-977A-60C390664840}') + znewcardSecGuid = smartcard.guid.strToGUID('{EB7F69EA-BA20-47d0-8C50-11CFDEB63BBE}') + ... + hresult = SCardIntroduceCardType(hcontext, znewcardName, + znewcardPrimGuid, znewcardPrimGuid + znewcardSecGuid, + znewcardATR, znewcardMask) + + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to introduce card type: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardIntroduceCardType(hcontext, cardname, primaryprovider, providerlist, atr, mask) + +def SCardIntroduceReader(hcontext, readername, devicename): + """ + SCardIntroduceReader( hcontext, readername, devicename) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readername: card reader name + devicename: card reader device name + + + + Introduces a reader to the smart card subsystem. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... + dummyreader = readers[0] + ' dummy' + hresult = SCardIntroduceReader(hcontext, dummyreader, readers[0]) + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to introduce reader: ' + dummyreader + ' : ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardIntroduceReader(hcontext, readername, devicename) + +def SCardIntroduceReaderGroup(hcontext, groupname): + """ + SCardIntroduceReaderGroup( hcontext, groupname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + groupname: card reader group name + + + + Introduces a reader group to the smart card subsystem. However, the + reader group is not created until the group is specified when adding + a reader to the smart card database. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult = SCardIntroduceReaderGroup(hcontext, 'SCard$MyOwnGroup') + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to introduce reader group: ' + SCardGetErrorMessage(hresult) + hresult = SCardAddReaderToGroup(hcontext, 'SchlumbergerSema Reflex USB v.2 0', 'SCard$MyOwnGroup') + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to add reader to group: ' + SCardGetErrorMessage(hresult) + + """ + return _scard.SCardIntroduceReaderGroup(hcontext, groupname) + +def SCardListInterfaces(hcontext, cardname): + """ + SCardListInterfaces( hcontext, cardname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + cardname: friendly name of a card + + + + Provides a list of interfaces supplied by a given card. The caller + supplies the name of a smart card previously introduced to the subsystem, + and receives the list of interfaces supported by the card + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, interfaces = SCardListInterfaces(hcontext, 'Schlumberger Cryptoflex 8k v2') + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to list interfaces: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardListInterfaces(hcontext, cardname) + +def SCardListCards(hcontext, atr, providerlist): + """ + SCardListCards( hcontext, byte[] atr, GUID[] providerlist) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + atr: card ATR + providerlist: list of GUIDs of interfaces supported by smart card + + + + Searches the smart card database and provides a list of named cards + previously introduced to the system by the user. The caller specifies an + ATR string, a set of interface identifiers (GUIDs), or both. If both an + ATR string and an identifier array are supplied, the cards returned will + match the ATR string supplied and support the interfaces specified. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + ... + slbCryptoFlex8kv2ATR = [ 0x3B, 0x95, 0x15, 0x40, 0x00, 0x68, 0x01, 0x02, 0x00, 0x00 ] + hresult, card = SCardListCards(hcontext, slbCryptoFlex8kv2ATR, []) + if hresult ! =SCARD_S_SUCCESS: + raise error, 'Failure to locate Schlumberger Cryptoflex 8k v2 card: ' + SCardGetErrorMessage(hresult) + hresult, cards = SCardListCards(hcontext, [], []) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failure to list cards: ' + SCardGetErrorMessage(hresult) + print 'Cards: ', cards + ... + + """ + return _scard.SCardListCards(hcontext, atr, providerlist) + +def SCardLocateCards(hcontext, cards, readerstatelist): + """ + SCardLocateCards( hcontext, cards, tuple[] readerstatelist) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + cards: a list of cards to locate + readerstatelist: in input/output, a list of reader state tuple (readername, state, atr) + + + + Searches the readers listed in the readerstate parameter for a card + with an ATR string that matches one of the card names specified in + mszCards, returning immediately with the result. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, readers = SCardListReaders(hcontext, []) + readerstates = [] + cards = ['Schlumberger Cryptoflex 4k', 'Schlumberger Cryptoflex 8k', 'Schlumberger Cryptoflex 8k v2'] + for i in xrange(len(readers)): + readerstates += [(readers[i], SCARD_STATE_UNAWARE)] + hresult, newstates = SCardLocateCards(hcontext, cards, readerstates) + for i in newstates: + reader, eventstate, atr = i + print reader, + for b in atr: + print '0x%.2X' % b, + print + if eventstate & SCARD_STATE_ATRMATCH: + print 'Card found' + if eventstate & SCARD_STATE_EMPTY: + print 'Reader empty' + if eventstate & SCARD_STATE_PRESENT: + print 'Card present in reader' + ... + + """ + return _scard.SCardLocateCards(hcontext, cards, readerstatelist) + +def SCardRemoveReaderFromGroup(hcontext, readername, groupname): + """ + SCardRemoveReaderFromGroup( hcontext, readername, groupname) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readername: card reader name + groupname: card reader group name + + + + + Removes a reader from an existing reader group. This function has no + affect on the reader. + + Windows only, not supported by PCSC lite wrapper. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult = SCardRemoveReaderFromGroup(hcontext, 'SchlumbergerSema Reflex USB v.2 0', 'SCard$MyOwnGroup') + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to remove reader from group: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardRemoveReaderFromGroup(hcontext, readername, groupname) + +def SCardIsValidContext(hcontext): + """ + SCardIsValidContext( hcontext) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + + + + This function determines whether a smart card context handle is still + valid. After a smart card context handle has been set by + SCardEstablishContext(), it may become not valid if the resource manager + service has been shut down. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult = SCardIsValidContext(hcontext) + if hresult != SCARD_S_SUCCESS: + raise error, 'Invalid context: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardIsValidContext(hcontext) + +def SCardGetAttrib(hcard, dwAttrId): + """ + SCardGetAttrib( hcard, dwAttrId) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwAttrId: value of attribute to get + + + + + This function get an attribute from the IFD Handler. + + The possible attributes are: + + ======================================== ======= ======= + Attribute Windows PSCS + lite + ======================================== ======= ======= + SCARD_ATTR_ASYNC_PROTOCOL_TYPES Y + SCARD_ATTR_ATR_STRING Y Y + SCARD_ATTR_CHANNEL_ID Y Y + SCARD_ATTR_CHARACTERISTICS Y Y + SCARD_ATTR_CURRENT_BWT Y Y + SCARD_ATTR_CURRENT_CLK Y Y + SCARD_ATTR_CURRENT_CWT Y Y + SCARD_ATTR_CURRENT_D Y Y + SCARD_ATTR_CURRENT_EBC_ENCODING Y Y + SCARD_ATTR_CURRENT_F Y Y + SCARD_ATTR_CURRENT_IFSC Y Y + SCARD_ATTR_CURRENT_IFSD Y Y + SCARD_ATTR_CURRENT_IO_STATE Y Y + SCARD_ATTR_CURRENT_N Y Y + SCARD_ATTR_CURRENT_PROTOCOL_TYPE Y Y + SCARD_ATTR_CURRENT_W Y Y + SCARD_ATTR_DEFAULT_CLK Y Y + SCARD_ATTR_DEFAULT_DATA_RATE Y Y + SCARD_ATTR_DEVICE_FRIENDLY_NAME_A Y Y + SCARD_ATTR_DEVICE_FRIENDLY_NAME_W Y Y + SCARD_ATTR_DEVICE_IN_USE Y Y + SCARD_ATTR_DEVICE_SYSTEM_NAME_A Y Y + SCARD_ATTR_DEVICE_SYSTEM_NAME_W Y Y + SCARD_ATTR_DEVICE_UNIT Y Y + SCARD_ATTR_ESC_AUTHREQUEST Y Y + SCARD_ATTR_ESC_CANCEL Y Y + SCARD_ATTR_ESC_RESET Y Y + SCARD_ATTR_EXTENDED_BWT Y Y + SCARD_ATTR_ICC_INTERFACE_STATUS Y Y + SCARD_ATTR_ICC_PRESENCE Y Y + SCARD_ATTR_ICC_TYPE_PER_ATR Y Y + SCARD_ATTR_MAXINPUT Y Y + SCARD_ATTR_MAX_CLK Y Y + SCARD_ATTR_MAX_DATA_RATE Y Y + SCARD_ATTR_MAX_IFSD Y Y + SCARD_ATTR_POWER_MGMT_SUPPORT Y Y + SCARD_ATTR_SUPRESS_T1_IFS_REQUEST Y Y + SCARD_ATTR_SYNC_PROTOCOL_TYPES Y + SCARD_ATTR_USER_AUTH_INPUT_DEVICE Y Y + SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE Y Y + SCARD_ATTR_VENDOR_IFD_SERIAL_NO Y Y + SCARD_ATTR_VENDOR_IFD_TYPE Y Y + SCARD_ATTR_VENDOR_IFD_VERSION Y Y + SCARD_ATTR_VENDOR_NAME Y Y + ======================================== ======= ======= + + Not all the dwAttrId values listed above may be implemented in the IFD + Handler you are using. And some dwAttrId values not listed here may be + implemented. + + + from smartcard.scard import * + ... establish context and connect to card ... + hresult, attrib = SCardGetAttrib(hcard, SCARD_ATTR_ATR_STRING) + if hresult == SCARD_S_SUCCESS: + for j in attrib: + print '0x%.2X' % attrib, + ... + + """ + return _scard.SCardGetAttrib(hcard, dwAttrId) + +def SCardSetAttrib(hcard, dwAttrId, ATTRIBUTESIN): + """ + SCardSetAttrib( hcard, dwAttrId, BYTELIST * ATTRIBUTESIN) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwAttrId: value of attribute to get + ATTRIBUTESIN: BYTELIST * + + + + + This function sets an attribute from the IFD Handler. Not all + attributes are supported by all readers nor can they be set at all + times. + + The possible attributes are: + + ======================================== ======= ======= + Attribute Windows PSCS + lite + ======================================== ======= ======= + SCARD_ATTR_ASYNC_PROTOCOL_TYPES Y + SCARD_ATTR_ATR_STRING Y Y + SCARD_ATTR_CHANNEL_ID Y Y + SCARD_ATTR_CHARACTERISTICS Y Y + SCARD_ATTR_CURRENT_BWT Y Y + SCARD_ATTR_CURRENT_CLK Y Y + SCARD_ATTR_CURRENT_CWT Y Y + SCARD_ATTR_CURRENT_D Y Y + SCARD_ATTR_CURRENT_EBC_ENCODING Y Y + SCARD_ATTR_CURRENT_F Y Y + SCARD_ATTR_CURRENT_IFSC Y Y + SCARD_ATTR_CURRENT_IFSD Y Y + SCARD_ATTR_CURRENT_IO_STATE Y Y + SCARD_ATTR_CURRENT_N Y Y + SCARD_ATTR_CURRENT_PROTOCOL_TYPE Y Y + SCARD_ATTR_CURRENT_W Y Y + SCARD_ATTR_DEFAULT_CLK Y Y + SCARD_ATTR_DEFAULT_DATA_RATE Y Y + SCARD_ATTR_DEVICE_FRIENDLY_NAME_A Y Y + SCARD_ATTR_DEVICE_FRIENDLY_NAME_W Y Y + SCARD_ATTR_DEVICE_IN_USE Y Y + SCARD_ATTR_DEVICE_SYSTEM_NAME_A Y Y + SCARD_ATTR_DEVICE_SYSTEM_NAME_W Y Y + SCARD_ATTR_DEVICE_UNIT Y Y + SCARD_ATTR_ESC_AUTHREQUEST Y Y + SCARD_ATTR_ESC_CANCEL Y Y + SCARD_ATTR_ESC_RESET Y Y + SCARD_ATTR_EXTENDED_BWT Y Y + SCARD_ATTR_ICC_INTERFACE_STATUS Y Y + SCARD_ATTR_ICC_PRESENCE Y Y + SCARD_ATTR_ICC_TYPE_PER_ATR Y Y + SCARD_ATTR_MAXINPUT Y Y + SCARD_ATTR_MAX_CLK Y Y + SCARD_ATTR_MAX_DATA_RATE Y Y + SCARD_ATTR_MAX_IFSD Y Y + SCARD_ATTR_POWER_MGMT_SUPPORT Y Y + SCARD_ATTR_SUPRESS_T1_IFS_REQUEST Y Y + SCARD_ATTR_SYNC_PROTOCOL_TYPES Y + SCARD_ATTR_USER_AUTH_INPUT_DEVICE Y Y + SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE Y Y + SCARD_ATTR_VENDOR_IFD_SERIAL_NO Y Y + SCARD_ATTR_VENDOR_IFD_TYPE Y Y + SCARD_ATTR_VENDOR_IFD_VERSION Y Y + SCARD_ATTR_VENDOR_NAME Y Y + ======================================== ======= ======= + + Not all the dwAttrId values listed above may be implemented in the IFD + Handler you are using. And some dwAttrId values not listed here may be + implemented. + + + from smartcard.scard import * + ... establish context and connect to card ... + hresult, attrib = SCardSetAttrib(hcard, SCARD_ATTR_VENDOR_NAME, ['G', 'e', 'm', 'a', 'l', 't', 'o']) + if hresult != SCARD_S_SUCCESS: + print 'Failed to set attribute' + ... + + """ + return _scard.SCardSetAttrib(hcard, dwAttrId, ATTRIBUTESIN) + +def SCardControl(hcard, dwControlCode, inbuffer): + """ + SCardControl( hcard, dwControlCode, byte[] inbuffer) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwControlCode: the control code to send + inbuffer: list of bytes to send with the control code + + + + This function sends a control command to the reader connected to by + SCardConnect(). It returns a result and the control response. + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + CMD = [0x12, 0x34] + hresult, response = SCardControl(hcard, 42, CMD) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to control: ' + SCardGetErrorMessage(hresult) + + """ + return _scard.SCardControl(hcard, dwControlCode, inbuffer) + +def SCardBeginTransaction(hcard): + """ + SCardBeginTransaction( hcard) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + + + + This function establishes a temporary exclusive access mode for doing a + series of commands or transaction. You might want to use this when you + are selecting a few files and then writing a large file so you can make + sure that another application will not change the current file. If + another application has a lock on this reader or this application is in + SCARD_SHARE_EXCLUSIVE there will be no action taken. + + from smartcard.scard import * + ... establish context ... + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + if hresult!=SCARD_S_SUCCESS: + raise error, 'unable to connect: ' + SCardGetErrorMessage(hresult) + hresult = SCardBeginTransaction(hcard) + if hresult != SCARD_S_SUCCESS: + raise error, 'failed to begin transaction: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardBeginTransaction(hcard) + +def SCardCancel(hcontext): + """ + SCardCancel( hcontext) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + + + + This function cancels all pending blocking requests on the + ScardGetStatusChange() function. + + from smartcard.scard import * + ... establish context ... + hresult = SCardCancel(hcard) + if hresult != SCARD_S_SUCCESS: + raise error, 'failed to cancel pending actions: ' + SCardGetErrorMessage(hresult) + ... + """ + return _scard.SCardCancel(hcontext) + +def SCardConnect(hcontext, readername, dwShareMode, dwPreferredProtocols): + """ + SCardConnect( hcontext, readername, dwShareMode, dwPreferredProtocols) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readername: card reader name + dwShareMode: share mode + dwPreferredProtocols: preferred protocols + + + + This function establishes a connection to the friendly name of the reader + specified in szReader. The first connection will power up and perform a + reset on the card. + + Value of dwShareMode Meaning + SCARD_SHARE_SHARED This application will allow others to share the reader + SCARD_SHARE_EXCLUSIVE This application will NOT allow others to share the reader + SCARD_SHARE_DIRECT Direct control of the reader, even without a card + + SCARD_SHARE_DIRECT can be used before using SCardControl() to send control + commands to the reader even if a card is not present in the reader. + + Value of dwPreferredProtocols Meaning + SCARD_PROTOCOL_T0 Use the T=0 protocol + SCARD_PROTOCOL_T1 Use the T=1 protocol + SCARD_PROTOCOL_RAW Use with memory type cards + + from smartcard.scard import * + ... establish context ... + hresult, readers = SCardListReaders(hcontext, 'NULL') + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to list readers:: ' + SCardGetErrorMessage(hresult) + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, readers[0], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + if hresult != SCARD_S_SUCCESS: + raise error, 'unable to connect: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardConnect(hcontext, readername, dwShareMode, dwPreferredProtocols) + +def SCardDisconnect(hcard, dwDisposition): + """ + SCardDisconnect( hcard, dwDisposition) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwDisposition: card disposition on return + + + + This function terminates a connection to the connection made through + SCardConnect. disposition can have the following values: + + Value of disposition Meaning + SCARD_LEAVE_CARD Do nothing + SCARD_RESET_CARD Reset the card (warm reset) + SCARD_UNPOWER_CARD Unpower the card (cold reset) + SCARD_EJECT_CARD Eject the card + + from smartcard.scard import * + ... establish context and connect to card ... + hresult = SCardDisconnect(hcard, SCARD_UNPOWER_CARD) + if hresult != SCARD_S_SUCCESS: + raise error, 'failed to disconnect: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardDisconnect(hcard, dwDisposition) + +def SCardEndTransaction(hcard, dwDisposition): + """ + SCardEndTransaction( hcard, dwDisposition) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwDisposition: card disposition on return + + + + + This function ends a previously begun transaction. The calling + application must be the owner of the previously begun transaction or an + error will occur. disposition can have the following values: The + disposition action is not currently used in this release. + + Value of disposition Meaning + SCARD_LEAVE_CARD Do nothing + SCARD_RESET_CARD Reset the card + SCARD_UNPOWER_CARD Unpower the card + SCARD_EJECT_CARD Eject the card + + from smartcard.scard import * + ... establish context, connect to card, begin transaction ... + hresult = SCardEndTransaction(hcard, SCARD_LEAVE_CARD) + if hresult != SCARD_S_SUCCESS: + raise error, 'failed to end transaction: ' + SCardGetErrorMessage(hresult) + + """ + return _scard.SCardEndTransaction(hcard, dwDisposition) + +def SCardEstablishContext(dwScope): + """ + SCardEstablishContext( dwScope) -> SCARDRETCODE + + Parameters + ---------- + dwScope: context scope + + + + This function creates a communication context to the PC/SC Resource + Manager. This must be the first function called in a PC/SC application. + + Value of dwScope Meaning + SCARD_SCOPE_USER Operations performed within the scope of the User + SCARD_SCOPE_TERMINAL Not used + SCARD_SCOPE_GLOBAL Not used + SCARD_SCOPE_SYSTEM Operations performed within the scope of the system + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to establish context: ' + SCardGetErrorMessage(hresult) + + """ + return _scard.SCardEstablishContext(dwScope) + +def SCardGetStatusChange(hcontext, dwTimeout, readerstatelist): + """ + SCardGetStatusChange( hcontext, dwTimeout, tuple[] readerstatelist) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + dwTimeout: timeout value, INFINITE for infinite time-out + readerstatelist: in input/output, a list of reader state tuple (readername, state, atr) + + + + + This function receives a structure or list of tuples containing reader + states. A READERSTATE hast three fields (readername, state, atr). + It then blocks for a change in state to occur on any of the OR'd + values contained in the current state for a maximum blocking time of + dwTimeout or forever if INFINITE is used. The new event state will be + contained in state. A status change might be a card insertion or + removal event, a change in ATR, etc. + + Value of state Meaning + SCARD_STATE_UNAWARE The application is unaware of the current state, and would like to know. The use of this value results in an immediate return from state transition monitoring services. This is represented by all bits set to zero + SCARD_STATE_IGNORE This reader should be ignored + SCARD_STATE_CHANGED There is a difference between the state believed by the application, and the state known by the resource manager. When this bit is set, the application may assume a significant state change has occurred on this reader + SCARD_STATE_UNKNOWN The given reader name is not recognized by the resource manager. If this bit is set, then SCARD_STATE_CHANGED and SCARD_STATE_IGNORE will also be set + SCARD_STATE_UNAVAILABLE The actual state of this reader is not available. If this bit is set, then all the following bits are clear + SCARD_STATE_EMPTY There is no card in the reader. If this bit is set, all the following bits will be clear + SCARD_STATE_PRESENT There is a card in the reader + SCARD_STATE_ATRMATCH There is a card in the reader with an ATR matching one of the target cards. If this bit is set, SCARD_STATE_PRESENT will also be set. This bit is only returned on the SCardLocateCards function + SCARD_STATE_EXCLUSIVE The card in the reader is allocated for exclusive use by another application. If this bit is set, SCARD_STATE_PRESENT will also be set + SCARD_STATE_INUSE The card in the reader is in use by one or more other applications, but may be connected to in shared mode. If this bit is set, SCARD_STATE_PRESENT will also be set + SCARD_STATE_MUTE There is an unresponsive card in the reader + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, readers = SCardListReaders(hcontext, []) + readerstates = [] + cards = [ 'Schlumberger Cryptoflex 4k', 'Schlumberger Cryptoflex 8k', 'Schlumberger Cryptoflex 8k v2' ] + for i in xrange(len(readers)): + readerstates += [ (readers[i], SCARD_STATE_UNAWARE) ] + hresult, newstates = SCardLocateCards(hcontext, cards, readerstates) + print '----- Please insert or remove a card ------------' + hresult, newstates = SCardGetStatusChange(hcontext, INFINITE, newstates) + for i in newstates + reader, eventstate, atr = i + if eventstate & SCARD_STATE_ATRMATCH: + print ' Card found' + if eventstate & SCARD_STATE_EMPTY: + print ' Reader empty' + + """ + return _scard.SCardGetStatusChange(hcontext, dwTimeout, readerstatelist) + +def SCardListReaders(hcontext, readergroups): + """ + SCardListReaders( hcontext, [] readergroups) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + readergroups: a list of reader groups to search for readers + + + + This function returns a list of currently available readers on the system. + A list of group can be provided in input to list readers in a given + group only. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, readers = SCardListReaders(hcontext, []) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to list readers: ' + SCardGetErrorMessage(hresult) + print 'PCSC Readers: ', readers + hresult, readers = SCardListReaders(hcontext, ['SCard$T1ProtocolReaders', 'SCard$MyOwnGroup'] + ... + + """ + return _scard.SCardListReaders(hcontext, readergroups) + +def SCardListReaderGroups(hcontext): + """ + SCardListReaderGroups( hcontext) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + + + + This function returns a list of currently available reader groups on the + system. + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, readerGroups = SCardListReaderGroups(hcontext) + if hresult != SCARD_S_SUCCESS: + raise error, 'Unable to list reader groups: ' + SCardGetErrorMessage(hresult) + print 'PCSC Reader groups: ', readerGroups + + """ + return _scard.SCardListReaderGroups(hcontext) + +def SCardReconnect(hcard, dwShareMode, dwPreferredProtocols, dwInitialization): + """ + SCardReconnect( hcard, dwShareMode, dwPreferredProtocols, dwInitialization) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + dwShareMode: share mode + dwPreferredProtocols: preferred protocols + dwInitialization: the type of initialization that should be performed on the card + + + + + This function reestablishes a connection to a reader that was previously + connected to using SCardConnect(). In a multi application environment it + is possible for an application to reset the card in shared mode. When + this occurs any other application trying to access certain commands will + be returned the value SCARD_W_RESET_CARD. When this occurs + SCardReconnect() must be called in order to acknowledge that the card was + reset and allow it to change it's state accordingly. + + Value of dwShareMode Meaning + SCARD_SHARE_SHARED This application will allow others to share the reader + SCARD_SHARE_EXCLUSIVE This application will NOT allow others to share the reader + + Value of dwPreferredProtocols Meaning + SCARD_PROTOCOL_T0 Use the T=0 protocol + SCARD_PROTOCOL_T1 Use the T=1 protocol + SCARD_PROTOCOL_RAW Use with memory type cards + + dwPreferredProtocols is a bit mask of acceptable protocols for the connection. You can use (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) if you do not have a preferred protocol. + + Value of dwInitialization Meaning + SCARD_LEAVE_CARD Do nothing + SCARD_RESET_CARD Reset the card (warm reset) + SCARD_UNPOWER_CARD Unpower the card (cold reset) + SCARD_EJECT_CARD Eject the card + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + hresult, activeProtocol = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE, + SCARD_PROTOCOL_T0, SCARD_RESET_CARD) + ... + + """ + return _scard.SCardReconnect(hcard, dwShareMode, dwPreferredProtocols, dwInitialization) + +def SCardReleaseContext(hcontext): + """ + SCardReleaseContext( hcontext) -> SCARDRETCODE + + Parameters + ---------- + hcontext: context handle return from SCardEstablishContext() + + + + + """ + return _scard.SCardReleaseContext(hcontext) + +def SCardStatus(hcard): + """ + SCardStatus( hcard) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + + + + This function returns the current status of the reader connected to by + hcard. The reader friendly name is returned, as well as the state, + protocol and ATR. The state is a DWORD possibly OR'd with the following + values: + + Value of pdwState Meaning + SCARD_ABSENT There is no card in the reader + SCARD_PRESENT There is a card in the reader, but it has not been moved into position for use + SCARD_SWALLOWED There is a card in the reader in position for use. The card is not powered + SCARD_POWERED Power is being provided to the card, but the reader driver is unaware of the mode of the card + SCARD_NEGOTIABLE The card has been reset and is awaiting PTS negotiation + SCARD_SPECIFIC The card has been reset and specific communication protocols have been established + + Value of pdwProtocol Meaning + SCARD_PROTOCOL_T0 Use the T=0 protocol + SCARD_PROTOCOL_T1 Use the T=1 protocol + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + hresult, reader, state, protocol, atr = SCardStatus(hcard) + if hresult != SCARD_S_SUCCESS: + raise error, 'failed to get status: ' + SCardGetErrorMessage(hresult) + print 'Reader: ', reader + print 'State: ', state + print 'Protocol: ', protocol + print 'ATR: ', + for i in xrange(len(atr)): + print '0x%.2X' % i, + print + ... + + """ + return _scard.SCardStatus(hcard) + +def SCardTransmit(hcard, pioSendPci, apducommand): + """ + SCardTransmit( hcard, unsigned long pioSendPci, byte[] apducommand) -> SCARDRETCODE + + Parameters + ---------- + hcard: card handle return from SCardConnect() + pioSendPci: unsigned long + apducommand: list of APDU bytes to transmit + + + + This function sends an APDU to the smart card contained in the reader + connected to by SCardConnect(). + It returns a result and the card APDU response. + + Value of pioSendPci Meaning + SCARD_PCI_T0 Pre-defined T=0 PCI structure + SCARD_PCI_T1 Pre-defined T=1 PCI structure + + + from smartcard.scard import * + hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) + hresult, hcard, dwActiveProtocol = SCardConnect( + hcontext, 'SchlumbergerSema Reflex USB v.2 0', SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0) + SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02] + DF_TELECOM = [0x7F, 0x10] + hresult, response = SCardTransmit(hcard, SCARD_PCI_T0, SELECT + DF_TELECOM) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to transmit: ' + SCardGetErrorMessage(hresult) + + """ + return _scard.SCardTransmit(hcard, pioSendPci, apducommand) + +def SCARD_CTL_CODE(code): + """ + SCARD_CTL_CODE(long code) -> long + + Parameters + ---------- + code: long + + + + This function returns the value of a control code + + from smartcard.scard import * + ... + CM_IOCTL_GET_FEATURE_REQUEST = SCARD_CTL_CODE(3400) + ... + + """ + return _scard.SCARD_CTL_CODE(code) + +def SCardGetErrorMessage(lErrCode): + """ + SCardGetErrorMessage(long lErrCode) -> ERRORSTRING * + + Parameters + ---------- + lErrCode: long + + + + This function return a human readable text for the given PC/SC error code. + + from smartcard.scard import * + ... + hresult, response = SCardTransmit(hcard, SCARD_PCI_T0, SELECT + DF_TELECOM) + if hresult != SCARD_S_SUCCESS: + raise error, 'Failed to transmit: ' + SCardGetErrorMessage(hresult) + ... + + """ + return _scard.SCardGetErrorMessage(lErrCode) + +error = _scard.error + +SCARD_SCOPE_USER = _scard.SCARD_SCOPE_USER +SCARD_SCOPE_TERMINAL = _scard.SCARD_SCOPE_TERMINAL +SCARD_SCOPE_SYSTEM = _scard.SCARD_SCOPE_SYSTEM +SCARD_SHARE_SHARED = _scard.SCARD_SHARE_SHARED +SCARD_SHARE_EXCLUSIVE = _scard.SCARD_SHARE_EXCLUSIVE +SCARD_SHARE_DIRECT = _scard.SCARD_SHARE_DIRECT +SCARD_LEAVE_CARD = _scard.SCARD_LEAVE_CARD +SCARD_RESET_CARD = _scard.SCARD_RESET_CARD +SCARD_UNPOWER_CARD = _scard.SCARD_UNPOWER_CARD +SCARD_EJECT_CARD = _scard.SCARD_EJECT_CARD +SCARD_STATE_UNAWARE = _scard.SCARD_STATE_UNAWARE +SCARD_STATE_IGNORE = _scard.SCARD_STATE_IGNORE +SCARD_STATE_CHANGED = _scard.SCARD_STATE_CHANGED +SCARD_STATE_UNKNOWN = _scard.SCARD_STATE_UNKNOWN +SCARD_STATE_UNAVAILABLE = _scard.SCARD_STATE_UNAVAILABLE +SCARD_STATE_EMPTY = _scard.SCARD_STATE_EMPTY +SCARD_STATE_PRESENT = _scard.SCARD_STATE_PRESENT +SCARD_STATE_ATRMATCH = _scard.SCARD_STATE_ATRMATCH +SCARD_STATE_EXCLUSIVE = _scard.SCARD_STATE_EXCLUSIVE +SCARD_STATE_INUSE = _scard.SCARD_STATE_INUSE +SCARD_STATE_MUTE = _scard.SCARD_STATE_MUTE +SCARD_STATE_UNPOWERED = _scard.SCARD_STATE_UNPOWERED +SCARD_PROTOCOL_UNDEFINED = _scard.SCARD_PROTOCOL_UNDEFINED +SCARD_PROTOCOL_T0 = _scard.SCARD_PROTOCOL_T0 +SCARD_PROTOCOL_T1 = _scard.SCARD_PROTOCOL_T1 +SCARD_PROTOCOL_RAW = _scard.SCARD_PROTOCOL_RAW +SCARD_PROTOCOL_Tx = _scard.SCARD_PROTOCOL_Tx +SCARD_PROTOCOL_DEFAULT = _scard.SCARD_PROTOCOL_DEFAULT +SCARD_PROTOCOL_OPTIMAL = _scard.SCARD_PROTOCOL_OPTIMAL +SCARD_PROTOCOL_UNSET = _scard.SCARD_PROTOCOL_UNSET +SCARD_PROTOCOL_ANY = _scard.SCARD_PROTOCOL_ANY +SCARD_PROTOCOL_T15 = _scard.SCARD_PROTOCOL_T15 +SCARD_PCI_T0 = _scard.SCARD_PCI_T0 +SCARD_PCI_T1 = _scard.SCARD_PCI_T1 +SCARD_PCI_RAW = _scard.SCARD_PCI_RAW +SCARD_PROVIDER_PRIMARY = _scard.SCARD_PROVIDER_PRIMARY +SCARD_PROVIDER_CSP = _scard.SCARD_PROVIDER_CSP +SCARD_ATTR_VENDOR_NAME = _scard.SCARD_ATTR_VENDOR_NAME +SCARD_ATTR_VENDOR_IFD_TYPE = _scard.SCARD_ATTR_VENDOR_IFD_TYPE +SCARD_ATTR_VENDOR_IFD_VERSION = _scard.SCARD_ATTR_VENDOR_IFD_VERSION +SCARD_ATTR_VENDOR_IFD_SERIAL_NO = _scard.SCARD_ATTR_VENDOR_IFD_SERIAL_NO +SCARD_ATTR_CHANNEL_ID = _scard.SCARD_ATTR_CHANNEL_ID +SCARD_ATTR_DEFAULT_CLK = _scard.SCARD_ATTR_DEFAULT_CLK +SCARD_ATTR_MAX_CLK = _scard.SCARD_ATTR_MAX_CLK +SCARD_ATTR_DEFAULT_DATA_RATE = _scard.SCARD_ATTR_DEFAULT_DATA_RATE +SCARD_ATTR_MAX_DATA_RATE = _scard.SCARD_ATTR_MAX_DATA_RATE +SCARD_ATTR_MAX_IFSD = _scard.SCARD_ATTR_MAX_IFSD +SCARD_ATTR_POWER_MGMT_SUPPORT = _scard.SCARD_ATTR_POWER_MGMT_SUPPORT +SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE = _scard.SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE +SCARD_ATTR_USER_AUTH_INPUT_DEVICE = _scard.SCARD_ATTR_USER_AUTH_INPUT_DEVICE +SCARD_ATTR_CHARACTERISTICS = _scard.SCARD_ATTR_CHARACTERISTICS +SCARD_ATTR_CURRENT_PROTOCOL_TYPE = _scard.SCARD_ATTR_CURRENT_PROTOCOL_TYPE +SCARD_ATTR_CURRENT_CLK = _scard.SCARD_ATTR_CURRENT_CLK +SCARD_ATTR_CURRENT_F = _scard.SCARD_ATTR_CURRENT_F +SCARD_ATTR_CURRENT_D = _scard.SCARD_ATTR_CURRENT_D +SCARD_ATTR_CURRENT_N = _scard.SCARD_ATTR_CURRENT_N +SCARD_ATTR_CURRENT_W = _scard.SCARD_ATTR_CURRENT_W +SCARD_ATTR_CURRENT_IFSC = _scard.SCARD_ATTR_CURRENT_IFSC +SCARD_ATTR_CURRENT_IFSD = _scard.SCARD_ATTR_CURRENT_IFSD +SCARD_ATTR_CURRENT_BWT = _scard.SCARD_ATTR_CURRENT_BWT +SCARD_ATTR_CURRENT_CWT = _scard.SCARD_ATTR_CURRENT_CWT +SCARD_ATTR_CURRENT_EBC_ENCODING = _scard.SCARD_ATTR_CURRENT_EBC_ENCODING +SCARD_ATTR_EXTENDED_BWT = _scard.SCARD_ATTR_EXTENDED_BWT +SCARD_ATTR_ICC_PRESENCE = _scard.SCARD_ATTR_ICC_PRESENCE +SCARD_ATTR_ICC_INTERFACE_STATUS = _scard.SCARD_ATTR_ICC_INTERFACE_STATUS +SCARD_ATTR_CURRENT_IO_STATE = _scard.SCARD_ATTR_CURRENT_IO_STATE +SCARD_ATTR_ATR_STRING = _scard.SCARD_ATTR_ATR_STRING +SCARD_ATTR_ICC_TYPE_PER_ATR = _scard.SCARD_ATTR_ICC_TYPE_PER_ATR +SCARD_ATTR_ESC_RESET = _scard.SCARD_ATTR_ESC_RESET +SCARD_ATTR_ESC_CANCEL = _scard.SCARD_ATTR_ESC_CANCEL +SCARD_ATTR_ESC_AUTHREQUEST = _scard.SCARD_ATTR_ESC_AUTHREQUEST +SCARD_ATTR_MAXINPUT = _scard.SCARD_ATTR_MAXINPUT +SCARD_ATTR_DEVICE_UNIT = _scard.SCARD_ATTR_DEVICE_UNIT +SCARD_ATTR_DEVICE_IN_USE = _scard.SCARD_ATTR_DEVICE_IN_USE +SCARD_ATTR_DEVICE_FRIENDLY_NAME_A = _scard.SCARD_ATTR_DEVICE_FRIENDLY_NAME_A +SCARD_ATTR_DEVICE_SYSTEM_NAME_A = _scard.SCARD_ATTR_DEVICE_SYSTEM_NAME_A +SCARD_ATTR_DEVICE_FRIENDLY_NAME_W = _scard.SCARD_ATTR_DEVICE_FRIENDLY_NAME_W +SCARD_ATTR_DEVICE_SYSTEM_NAME_W = _scard.SCARD_ATTR_DEVICE_SYSTEM_NAME_W +SCARD_ATTR_SUPRESS_T1_IFS_REQUEST = _scard.SCARD_ATTR_SUPRESS_T1_IFS_REQUEST +ERROR_ALREADY_EXISTS = _scard.ERROR_ALREADY_EXISTS +SCARD_S_SUCCESS = _scard.SCARD_S_SUCCESS +SCARD_F_INTERNAL_ERROR = _scard.SCARD_F_INTERNAL_ERROR +SCARD_E_CANCELLED = _scard.SCARD_E_CANCELLED +SCARD_E_INVALID_HANDLE = _scard.SCARD_E_INVALID_HANDLE +SCARD_E_INVALID_PARAMETER = _scard.SCARD_E_INVALID_PARAMETER +SCARD_E_INVALID_TARGET = _scard.SCARD_E_INVALID_TARGET +SCARD_E_NO_MEMORY = _scard.SCARD_E_NO_MEMORY +SCARD_F_WAITED_TOO_LONG = _scard.SCARD_F_WAITED_TOO_LONG +SCARD_E_INSUFFICIENT_BUFFER = _scard.SCARD_E_INSUFFICIENT_BUFFER +SCARD_E_UNKNOWN_READER = _scard.SCARD_E_UNKNOWN_READER +SCARD_E_TIMEOUT = _scard.SCARD_E_TIMEOUT +SCARD_E_SHARING_VIOLATION = _scard.SCARD_E_SHARING_VIOLATION +SCARD_E_NO_SMARTCARD = _scard.SCARD_E_NO_SMARTCARD +SCARD_E_UNKNOWN_CARD = _scard.SCARD_E_UNKNOWN_CARD +SCARD_E_CANT_DISPOSE = _scard.SCARD_E_CANT_DISPOSE +SCARD_E_PROTO_MISMATCH = _scard.SCARD_E_PROTO_MISMATCH +SCARD_E_NOT_READY = _scard.SCARD_E_NOT_READY +SCARD_E_INVALID_VALUE = _scard.SCARD_E_INVALID_VALUE +SCARD_E_SYSTEM_CANCELLED = _scard.SCARD_E_SYSTEM_CANCELLED +SCARD_F_COMM_ERROR = _scard.SCARD_F_COMM_ERROR +SCARD_F_UNKNOWN_ERROR = _scard.SCARD_F_UNKNOWN_ERROR +SCARD_E_INVALID_ATR = _scard.SCARD_E_INVALID_ATR +SCARD_E_NOT_TRANSACTED = _scard.SCARD_E_NOT_TRANSACTED +SCARD_E_READER_UNAVAILABLE = _scard.SCARD_E_READER_UNAVAILABLE +SCARD_E_PCI_TOO_SMALL = _scard.SCARD_E_PCI_TOO_SMALL +SCARD_E_READER_UNSUPPORTED = _scard.SCARD_E_READER_UNSUPPORTED +SCARD_E_DUPLICATE_READER = _scard.SCARD_E_DUPLICATE_READER +SCARD_E_CARD_UNSUPPORTED = _scard.SCARD_E_CARD_UNSUPPORTED +SCARD_E_NO_SERVICE = _scard.SCARD_E_NO_SERVICE +SCARD_E_SERVICE_STOPPED = _scard.SCARD_E_SERVICE_STOPPED +SCARD_E_NO_READERS_AVAILABLE = _scard.SCARD_E_NO_READERS_AVAILABLE +SCARD_E_UNSUPPORTED_FEATURE = _scard.SCARD_E_UNSUPPORTED_FEATURE +SCARD_W_UNSUPPORTED_CARD = _scard.SCARD_W_UNSUPPORTED_CARD +SCARD_W_UNRESPONSIVE_CARD = _scard.SCARD_W_UNRESPONSIVE_CARD +SCARD_W_UNPOWERED_CARD = _scard.SCARD_W_UNPOWERED_CARD +SCARD_W_RESET_CARD = _scard.SCARD_W_RESET_CARD +SCARD_W_REMOVED_CARD = _scard.SCARD_W_REMOVED_CARD +SCARD_W_SECURITY_VIOLATION = _scard.SCARD_W_SECURITY_VIOLATION +SCARD_W_WRONG_CHV = _scard.SCARD_W_WRONG_CHV +SCARD_W_CHV_BLOCKED = _scard.SCARD_W_CHV_BLOCKED +SCARD_W_EOF = _scard.SCARD_W_EOF +SCARD_W_CANCELLED_BY_USER = _scard.SCARD_W_CANCELLED_BY_USER +SCARD_W_CARD_NOT_AUTHENTICATED = _scard.SCARD_W_CARD_NOT_AUTHENTICATED +SCARD_E_UNEXPECTED = _scard.SCARD_E_UNEXPECTED +SCARD_E_ICC_INSTALLATION = _scard.SCARD_E_ICC_INSTALLATION +SCARD_E_ICC_CREATEORDER = _scard.SCARD_E_ICC_CREATEORDER +SCARD_E_DIR_NOT_FOUND = _scard.SCARD_E_DIR_NOT_FOUND +SCARD_E_FILE_NOT_FOUND = _scard.SCARD_E_FILE_NOT_FOUND +SCARD_E_NO_DIR = _scard.SCARD_E_NO_DIR +SCARD_E_NO_FILE = _scard.SCARD_E_NO_FILE +SCARD_E_NO_ACCESS = _scard.SCARD_E_NO_ACCESS +SCARD_E_WRITE_TOO_MANY = _scard.SCARD_E_WRITE_TOO_MANY +SCARD_E_BAD_SEEK = _scard.SCARD_E_BAD_SEEK +SCARD_E_INVALID_CHV = _scard.SCARD_E_INVALID_CHV +SCARD_E_UNKNOWN_RES_MNG = _scard.SCARD_E_UNKNOWN_RES_MNG +SCARD_E_NO_SUCH_CERTIFICATE = _scard.SCARD_E_NO_SUCH_CERTIFICATE +SCARD_E_CERTIFICATE_UNAVAILABLE = _scard.SCARD_E_CERTIFICATE_UNAVAILABLE +SCARD_E_COMM_DATA_LOST = _scard.SCARD_E_COMM_DATA_LOST +SCARD_E_NO_KEY_CONTAINER = _scard.SCARD_E_NO_KEY_CONTAINER +SCARD_E_SERVER_TOO_BUSY = _scard.SCARD_E_SERVER_TOO_BUSY +ERROR_INVALID_HANDLE = _scard.ERROR_INVALID_HANDLE +SCARD_P_SHUTDOWN = _scard.SCARD_P_SHUTDOWN +INFINITE = _scard.INFINITE +resourceManager = _scard.resourceManager +resourceManagerSubType = _scard.resourceManagerSubType +# This file is compatible with both classic and new-style classes. + + diff --git a/pygp/gp/gp_functions.py b/pygp/gp/gp_functions.py index e95feca..503aeaa 100644 --- a/pygp/gp/gp_functions.py +++ b/pygp/gp/gp_functions.py @@ -29,6 +29,16 @@ def clear_securityInfo(): global securityInfo securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] +def get_securityInfo(channel = None): + global securityInfo + if channel == None: + return securityInfo[securityInfo[4]] + else: + if channel >= 0 and channel <=3: + return securityInfo[channel] + else: + pass + def get_payload_list(): global payload_list return payload_list diff --git a/pygp/pygp.py b/pygp/pygp.py index e4bb7ba..16d0756 100644 --- a/pygp/pygp.py +++ b/pygp/pygp.py @@ -363,9 +363,9 @@ def terminal(readerName = None): if len(list_readernames) > 0: for readers in list_readernames: # then perform a card connect to verify the card connection - error_status = conn.card_connect(str(readers.decode()), current_protocol) + error_status = conn.card_connect(readers, current_protocol) if error_status['errorStatus'] == error.ERROR_STATUS_SUCCESS: - readerName = readers.decode() + readerName = readers break if readerName == None: From ea574ed6220b7712bf00bbea94ba9fa27366608d Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Fri, 8 Sep 2017 17:43:28 +0900 Subject: [PATCH 6/7] Change the file format to Unix. --- pygp/gp/gp_functions.py | 3522 +++++++++++++++++++-------------------- 1 file changed, 1761 insertions(+), 1761 deletions(-) diff --git a/pygp/gp/gp_functions.py b/pygp/gp/gp_functions.py index 503aeaa..f7cdcee 100644 --- a/pygp/gp/gp_functions.py +++ b/pygp/gp/gp_functions.py @@ -1,1761 +1,1761 @@ -import time -import pygp.crypto as crypto -from pygp.logger import * -from pygp.connection.connection import * -from pygp.error import * -from pygp.constants import * -from pygp.gp.gp_crypto import * -import pygp.loadfile as loadfile -from pygp.tlv import * - - - -# global variable for gp_functions -# information of each channel. -# [0] for channel 0, [1] for channel 1, [2] for channel 2, [3] for channel 3 -# [4] it save current working channel number. This channel number used when sending APDU. -securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] - -last_apdu_response = None -last_apdu_status = None -apdu_timing = False - -payload_mode_activated = False -payload_list = [] - -total_time = 0.0 - -def clear_securityInfo(): - global securityInfo - securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] - -def get_securityInfo(channel = None): - global securityInfo - if channel == None: - return securityInfo[securityInfo[4]] - else: - if channel >= 0 and channel <=3: - return securityInfo[channel] - else: - pass - -def get_payload_list(): - global payload_list - return payload_list - -def set_payload_mode(activate): - global payload_mode_activated - global payload_list - payload_mode_activated = activate - # clear the list on activation - if (activate == True): - payload_list.clear() - -def last_response(): - global last_apdu_response - return last_apdu_response - -def last_status(): - global last_apdu_status - return last_apdu_status - -def set_apdu_timing(activated): - global apdu_timing - apdu_timing = activated - -def set_start_timing(): - global total_time - total_time = 0.0 - -def get_total_execution_time(): - return total_time - -def __check_security_info__(security_info): - if security_info == None: - error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) - return error_status - else: - error_status = create_no_error_status(0x00) - return error_status - - -def select_channel(logical_channel): - global securityInfo - - log_start("select_channel") - - # Display All Channel Information - logger.log_info("Channel Status Information [0~3]") - logger.log_info("Channel SCP SCPi SecurityLevel AID") - logger.log_info("------- --- ---- ------------- ------------------") - channel_id = 0 - for sChannelInfo in securityInfo[0:4]: - if (securityInfo[4] == channel_id): - strWorking = '*' - else: - strWorking = ' ' - if (('channelStatus' in sChannelInfo.keys()) and (sChannelInfo['channelStatus'] == "ON")): - if 'selectedAID' in sChannelInfo.keys(): - strAID = sChannelInfo['selectedAID'] - else: - strAID = 'NONE' - - if 'secureChannelProtocolImpl' in sChannelInfo.keys(): - logger.log_info(" %s 0%d %d %s %d %s" \ - %(strWorking, channel_id, sChannelInfo['secureChannelProtocol'],\ - sChannelInfo['secureChannelProtocolImpl'], sChannelInfo['securityLevel'], strAID )) - else: - logger.log_info(" %s 0%d n/a n/a n/a %s" %(strWorking, channel_id, strAID)) - else: - logger.log_info(" 0%d Not Available" %(channel_id)) - - channel_id += 1 - - # check the parameter value - if logical_channel == None or logical_channel < 0x00 or logical_channel > 0x03: - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - log_info("\tInvalid logical channel (%-0.2X)" % securityInfo[4]) - return error_status - - # check the status of logical channel - sChannelInfo = securityInfo[logical_channel] - if sChannelInfo['channelStatus'] == "ON": - # set the working channel value - securityInfo[4] = logical_channel - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_info("\tLogical channel has changed to %-0.2X" % securityInfo[4]) - else: - error_status = create_error_status(ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE, runtimeErrorDict[ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE]) - - log_end("select_channel", error_status) - - return error_status - - -def wrap_command(security_info, capdu): - ''' Wrap APDU according to the security info ''' - #TODO: update doc - - log_start("wrap_command") - - # no security level defined, just return - if (security_info == None): - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - return error_status, capdu - - # security_info level defined - if ('securityLevel' in security_info) == False: - error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) - return error_status, '' - - # trivial case, just return - if security_info['securityLevel'] == SECURITY_LEVEL_NO_SECURE_MESSAGING: - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - return error_status, capdu - - # Determine which type of Exchange between the reader - # Convert capdu from string to list of bytes - bytelist_capdu = toByteArray(capdu) - - ISO_case = -1 - Le = None - - if len(bytelist_capdu) == 4: - ISO_case = CASE_1 - - elif len(bytelist_capdu) == 5: - ISO_case = CASE_2S - Le = bytelist_capdu[4] - - else: - if (bytelist_capdu[4] != 0): - if (bytelist_capdu[4] == len(bytelist_capdu) - 5): - ISO_case = CASE_3S - elif (bytelist_capdu[4] == len(bytelist_capdu) - 6): - ISO_case = CASE_4S - Le = bytelist_capdu[-1:] - else: - pass - - - if ISO_case == -1: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, '' - - # Manage ISO case: Get Le if any and prepare APDU - - apdu_to_wrap = [] - - - # C_MAC on modified APDU - if ( security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or - security_info['secureChannelProtocol'] == GP_SCP03): - - - ############## ISO Case 1 & 2 ############## - if ISO_case == CASE_1: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap.append(0x08) - - elif ISO_case == CASE_2S: - Le = bytelist_capdu[-1:] - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = 0x08 - - elif ISO_case == CASE_3S: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 - - elif ISO_case == CASE_4S: - Le = bytelist_capdu[-1:] - apdu_to_wrap = bytelist_capdu[:-1] - # put lc with the length of the mac - apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 - - else: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, None - - #CLA - indicate security level 1 or 3 - apdu_to_wrap[0] = bytelist_capdu[0] | 0x04 - else: - # C_MAC on unmodified APDU - ############## ISO Case 1 & 2 ############## - if ISO_case == CASE_1: - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap.append(0x00) - - elif ISO_case == CASE_2S: - Le = bytelist_capdu[:-1] - apdu_to_wrap = bytelist_capdu - # put lc with the length of the mac - apdu_to_wrap[4] = 0x00 - - elif ISO_case == CASE_4S: - Le = bytelist_capdu[:-1] - apdu_to_wrap = bytelist_capdu[:-1] - - - else: - # create the status error structure - error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) - return error_status, None - - # ICV encryption - iv = None - if security_info['secureChannelProtocol'] == GP_SCP02: - if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1A or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1B or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55) : - - iv = encipher_iv_SCP02(security_info['lastC_MAC'], security_info['C_MACSessionKey'][:16]) - - elif(security_info['secureChannelProtocol']== GP_SCP03): - iv = crypto.ISO_9797_M1_Padding_left(intToHexString(security_info['icv_counter'],2), 16) - iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - # Get the data field of the APDU - encData = apdu_to_wrap[5:] - # if we have to encrypt: - if (security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC_R_MAC or - security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC): - # retrieve only the data field (ie: remove APDU header) - apdu_data_field = apdu_to_wrap[5:] - - # cipher data - if security_info['secureChannelProtocol'] == GP_SCP02: - encData = encipher_data_SCP02(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , crypto.ICV_NULL_8) - encData = toByteArray(encData) # type mismatch - - log_debug("wrap_command: encrypted data field: %s" %toHexString(encData)) - - elif(security_info['secureChannelProtocol']== GP_SCP03): - encData = encipher_data_SCP03(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , iv) - - encData = toByteArray(encData) # type mismatch - # re put lc with the length of the encipher data - apdu_to_wrap[4] = len(encData) + 8 # enc data - - - # MAC calculation - if security_info['secureChannelProtocol'] == GP_SCP02: - mac = calculate_mac_SCP02(toHexString(apdu_to_wrap), security_info['C_MACSessionKey'], iv) - # re put lc with the length of the encipher data - apdu_to_wrap[4] = len(encData) + 8# enc data - elif(security_info['secureChannelProtocol']== GP_SCP03): - mac = calculate_mac_SCP03(toHexString(apdu_to_wrap[:5]) + toHexString(encData), security_info['C_MACSessionKey'], security_info['lastC_MAC']) - pass - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - log_debug("wrap_command: Data for MAC computation: %s" %toHexString(apdu_to_wrap)) - log_debug("wrap_command: ICV for MAC: %s" %iv) - log_debug("wrap_command: Generated MAC: %s" %mac) - - #update the last iv - security_info['lastC_MAC'] = mac - - # create the wrapped APDU - if security_info['secureChannelProtocol'] == GP_SCP02: - wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac - - elif(security_info['secureChannelProtocol'] == GP_SCP03): - wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac[:16] - # don't forget tp update the counter for ICV_NULL_8 - log_debug("wrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] ,2)) - security_info['icv_counter'] = security_info['icv_counter'] + 1 - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("wrap_command") - return error_status, None - - - # we have to put Le bytes !! and manage RMAC level - if Le != None: - wrappedAPDU = wrappedAPDU + toHexString(Le) - - error_status = create_no_error_status(0x00) - - log_end("wrap_command", error_status) - - return error_status, wrappedAPDU - - -def unwrap_command(security_info, rapdu): - ''' unwrap APDU response according to the security info ''' - #TODO: update doc - - log_start("unwrap_command") - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - - # no security level defined, just return - if (security_info == None): - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_end("unwrap_command") - return error_status, rapdu - - # trivial case, just return - if (security_info['securityLevel'] != SECURITY_LEVEL_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_MAC_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_C_MAC_R_MAC and - security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC) : - - error_status = create_no_error_status(ERROR_STATUS_SUCCESS) - log_end("unwrap_command") - return error_status, rapdu - - # convert to byte array - bytelist_rapdu = toByteArray(rapdu) - if security_info['secureChannelProtocol'] == GP_SCP02: - # only status word so no RMAC - if len(bytelist_rapdu) == 2: - return error_status, rapdu - #TODO - - elif(security_info['secureChannelProtocol'] == GP_SCP03): - # only status word so no RMAC - if len(bytelist_rapdu) == 2: - return error_status, rapdu - else: - - # get the mac of the command (8 bytes before the rapdu status word) - len_reponse_date_without_sw = len(bytelist_rapdu) - 2 - len_response_data_without_mac = len_reponse_date_without_sw - 8 - response_data_without_mac = bytelist_rapdu[0:len_response_data_without_mac] - response_data_mac = bytelist_rapdu[ len_response_data_without_mac: len_reponse_date_without_sw] - response_data_sw = bytelist_rapdu[len_reponse_date_without_sw:] - log_debug("unwrap_command: Response MAC: %s" %toHexString(response_data_mac)) - # calculate the off card MAC - mac = calculate_mac_SCP03(toHexString(response_data_without_mac) + toHexString(response_data_sw), security_info['R_MACSessionKey'], security_info['lastC_MAC']) - log_debug("unwrap_command: Data for MAC computation: %s" %(toHexString(response_data_without_mac) + toHexString(response_data_sw))) - log_debug("unwrap_command: ICV for Generated MAC: %s" %security_info['lastC_MAC']) - log_debug("unwrap_command: Generated MAC: %s" %mac) - if toHexString(response_data_mac) != mac[:16]: - error_status = create_error_status(ERROR_VALIDATION_R_MAC, runtimeErrorDict[ERROR_VALIDATION_R_MAC]) - log_end("unwrap_command") - return error_status, None - else: - # check if we have to decipher the APDU - if security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC: - if(security_info['secureChannelProtocol']== GP_SCP03): - log_debug("unwrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] - 1,2)) - iv = crypto.ISO_9797_M2_Padding_left(intToHexString(security_info['icv_counter']-1 ,2), 16) - iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) - log_debug("unwrap_command: current ICV : %s " %iv) - - # decipher data - decipher_data = decipher_data_SCP03(toHexString(response_data_without_mac), security_info['encryptionSessionKey'], iv) - decipher_data = crypto.Remove_ISO_9797_M2_Padding(decipher_data) - return error_status, decipher_data + toHexString(response_data_sw) - - pass - else: - return error_status, toHexString(bytelist_rapdu) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - log_end("unwrap_command") - return error_status, None - - -def send_APDU(capdu, raw_mode = False, exsw = None, exdata = None): - - global securityInfo - global last_apdu_response - global last_apdu_status - global apdu_timing - global payload_mode_activated - global payload_list - global total_time - - log_start("send_APDU") - #TODO: managing security info wrap the command - - if raw_mode == True: - # no wrapping management, just send the apdu - c_wrapped_apdu = capdu - else: - # get securityInfo of the channel - currentChannel = securityInfo[4] - securityInfoOfChannel = securityInfo[currentChannel] - - # wrap command - error_status, c_wrapped_apdu = wrap_command(securityInfoOfChannel, capdu) - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == True: - payload_list.append(remove_space(c_wrapped_apdu)) - error_status = create_no_error_status(0x00) - return error_status, None - else: - #convert capdu from string to list of bytes - bytelist_capdu = toByteArray(c_wrapped_apdu) - - if raw_mode == False: - # manage the selected logical channel - bytelist_capdu[0] |= securityInfo[4] - - start_time = time.perf_counter() - - error_status, rapdu = send_apdu(bytelist_capdu) - - end_time = time.perf_counter() - total_time = total_time + (end_time - start_time) - - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if raw_mode == True: - c_unwrapped_rapdu = rapdu - else: - error_status, c_unwrapped_rapdu = unwrap_command(securityInfoOfChannel, rapdu) - if error_status['errorStatus'] != 0x00: - log_end("send_APDU", error_status['errorStatus']) - return error_status, None - - if apdu_timing == True: - log_info("execution time: %.3f sec." %(end_time - start_time)) - # update global variables - last_apdu_response = c_unwrapped_rapdu[:-4] # response without status - last_apdu_status = c_unwrapped_rapdu[-4:] # only status - - # check if it is an ISO7816 status word error - if exsw == None: - error_status = check_ISO7816_status_word(rapdu) - else: - # convert exsw to list, compare between expected status word and response. - # The format of exsw "9000, 6Cxx, 6xxx" - exsw = exsw.upper() - exsw = exsw.replace(',', ' ') - exsw = exsw.replace('X', 'F') - byte_list_exsw = toByteArray(exsw) - byte_list_sw = toByteArray(last_apdu_status) - found = False - for offset in range(0, len(byte_list_exsw), 2): - if ((byte_list_exsw[offset] & byte_list_sw[-2]) == byte_list_sw[-2]): - if ((byte_list_exsw[offset+1] & byte_list_sw[-1]) == byte_list_sw[-1]): - # the status word is same as our expectation - error_status = create_no_error_status(last_apdu_status) - found = True - if found == False: - error_status = create_error_status(last_apdu_status, "Differ with expected status word") - log_error("expected status word " + exsw) - #log_end("send_APDU", error_status['errorStatus']) - - if (exdata != None) and (error_status['errorStatus'] == 0x00): - exdata = exdata.upper() - exdata = exdata.replace(' ', '') - for offset in range(0, len(exdata), 1): - if exdata[offset] == 'X': - continue - elif exdata[offset] == rapdu[offset].upper(): - continue - else: - error_status = create_error_status(last_apdu_status, "Differ with expected data") - log_error("expected data " + exdata) - break - - return error_status, rapdu - - -def select_issuerSecurityDomain(logical_channel = 0): - - log_start("select_issuerSecurityDomain") - - if(logical_channel < 0 or logical_channel > 3): - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - return error_status, None - - capdu = intToHexString(logical_channel, 1) + " A4 04 00 00" - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: - log_end("select_issuerSecurityDomain", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so the application is selected. now store its aid - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != '6F': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - for response_tlv in response_tlv.list_childs_tlv(): - if response_tlv.getTAG() == '84': - current_selected_aid = response_tlv.getValue() - else: - current_selected_aid = ISD_APPLICATION_AID - - # there is no error. Do initialize securityInfo of selected channel - securityInfo[4] = logical_channel - securityInfo[logical_channel] = {} - securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING - securityInfo[logical_channel]['channelStatus'] = "ON" - securityInfo[logical_channel]['selectedAID'] = current_selected_aid - log_end("select_issuerSecurityDomain", error_status['errorStatus'], error_status['errorMessage']) - - return error_status, rapdu - - -def select_application(str_AID, logical_channel = 0): - - log_start("select_application") - - if(logical_channel < 0 or logical_channel > 3): - error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) - return error_status, None - - capdu = intToHexString(logical_channel, 1) + " A4 04 00 " + lv (str_AID) - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: - log_end("select_application", error_status['errorStatus']) - return error_status, None - - # there is no error. Do initialize securityInfo of selected channel - securityInfo[4] = logical_channel - securityInfo[logical_channel] = {} - securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING - securityInfo[logical_channel]['channelStatus'] = "ON" - securityInfo[logical_channel]['selectedAID'] = str_AID - log_end("select_application", error_status['errorStatus']) - - return error_status, rapdu - - -def set_status(cardElement, lifeCycleState, aid): - - log_start("set_status") - # supress blank if any - import re - aid = ''.join( re.split( '\W+', aid.upper() ) ) - - capdu = "80 F0 " + cardElement + lifeCycleState + lv (aid) - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("set_status", error_status['errorStatus']) - return error_status - - log_end("set_status", error_status['errorStatus']) - - return error_status - - -def set_crs_status(status_type, status_value, aid): - - log_start("set_crs_status") - # supress blank if any - import re - aid = ''.join( re.split( '\W+', aid.upper() ) ) - - capdu = "80 F0 " + status_type + status_value + "4F" + lv(aid) - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("set_crs_status", error_status['errorStatus']) - return error_status, None - - log_end("set_crs_status", error_status['errorStatus']) - - #TODO: needs to parse the response data based on the GP Amdt.C Table 3-23 - - return error_status, rapdu - - -def delete_application(str_AID, exsw): - - log_start("delete_application") - - capdu = "80 E4 00 00 " + lv ('4F' + lv(str_AID)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("select_application", error_status['errorStatus']) - return error_status - - log_end("select_application", error_status['errorStatus']) - - return error_status - - -def delete_package(str_AID, exsw): - - log_start("delete_package") - - capdu = "80 E4 00 80 " + lv ('4F' + lv(str_AID)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("delete_package", error_status['errorStatus']) - return error_status - - log_end("delete_package", error_status['errorStatus']) - - return error_status - - -def delete_key(KeyIdentifier, keyVersionNumber, exsw): - - log_start("delete_key") - - capdu = "80 E4 00 00 " + lv('D0' + lv(KeyIdentifier)) + lv('D2' + lv(keyVersionNumber)) - - error_status, rapdu = send_APDU(capdu, exsw = exsw) - - if error_status['errorStatus'] != 0x00: - log_end("delete_key", error_status['errorStatus']) - return error_status - - log_end("delete_key", error_status['errorStatus']) - - return error_status - - -def get_cplc_data(): - - log_start("get_cplc_data") - - capdu = "80 CA 9F 7F 00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_cplc_data", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so display results - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != '9F7F': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - - log_end("get_cplc_data", error_status['errorStatus']) - - return error_status, response_tlv.getValue() - else: - return error_status, None - - -def get_key_information_template(): - - log_start("get_key_information_template") - - capdu = "80 CA 00 E0 00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_key_information_template", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # no error so display results - # key information is list of Tuple : (Key id, Key version number, KeyLength, KeyType) - keyInformation = [] - response_tlv = TLV(toByteArray(last_response())) - - # check the tag - if response_tlv.getTAG() != 'E0': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - # manage the list of TLV with tag C0 into this response_tlv - key_info_list = response_tlv.list_childs_tlv() - for key_info in key_info_list: - - if key_info.getTAG() != 'C0': - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - else: - index = 0 - KeyIdentifier = key_info.getValue()[index: index + 2] - index = index + 2 - KeyVersionNumber = key_info.getValue()[index: index + 2] - index = index + 2 - #check key type : if coded with 2 bytes the key is in format 2 - KeyTypeIndex = key_info.getValue()[index: index + 2] - - if (KeyTypeIndex == 'FF'): - p_bool_Iskey_format2 = True - index = index + 2 - while (KeyTypeIndex == 'FF'): - KeyType = key_info.getValue()[index: index + 2] # 1 byte - KeyLength= key_info.getValue()[index + 2: index + 6] # 2 bytes - keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyTypeIndex + KeyType)) - KeyTypeIndex = key_info.getValue()[index + 6: index + 8] - index = index + 8 - #get the key usage and key access - keyUsageLength = KeyTypeIndex - if keyUsageLength != '00': - keyUsage =key_info.getValue()[index: index + 2] - index = index + 2 - - keyAccessLength = key_info.getValue()[index: index + 2] - if keyAccessLength != '00': - keyAccess = key_info.getValue()[index + 2: index + 4] - index = index + 4 - else: - index = index + 2 - - else: - while index < (len(key_info.getValue())): - KeyType = key_info.getValue()[index: index + 2] - index = index + 2 - KeyLength= key_info.getValue()[index: index + 2] - index = index + 2 - keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyType)) - - else: - keyInformation = [] - - - log_end("get_key_information_template", error_status['errorStatus']) - - return error_status, keyInformation - - -def store_data(data): - - log_start("store_data") - block_size =239 # 255 bytes minus 8 byte MAC minus 8 byte encryption padding - block_number = 0x00 - - # supress blank if any - import re - data = ''.join( re.split( '\W+', data.upper() ) ) - # convert to byte array - bytelist_data = toByteArray(data) - remaining_bytes = len(bytelist_data) - read_bytes = 0x00 - while remaining_bytes > 0: - # build the APDU - capdu = "80 E2 " - if remaining_bytes <= block_size: - capdu = capdu + '80' + intToHexString(block_number) - capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + remaining_bytes]) ) - read_bytes = read_bytes + remaining_bytes - remaining_bytes = remaining_bytes - remaining_bytes - else: - capdu = capdu + '00' + intToHexString(block_number) - capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + block_size]) ) - read_bytes = read_bytes + block_size - remaining_bytes = remaining_bytes - block_size - - # send the APDU - error_status, rapdu = send_APDU(capdu) - block_number = block_number + 1 - - if error_status['errorStatus'] != 0x00: - log_end("store_data", error_status['errorStatus']) - return error_status - - - if error_status['errorStatus'] != 0x00: - log_end("get_data", error_status['errorStatus']) - return error_status - - log_end("store_data", error_status['errorStatus']) - - return error_status - - -def get_data(identifier): - - log_start("get_data") - - # build the APDU - # supress blank if any - import re - identifier = ''.join( re.split( '\W+', identifier.upper() ) ) - # check the size of the identifier (it is a string so 2 byte correspond to 4 characters ) - if len(identifier) < 0x00 or len(identifier) > 0x04: - # identifier must be 1 or two byte string - error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) - return error_status, None - - capdu = "80 CA " - - if len(identifier) == 0x04: - capdu = capdu + identifier + '00' - else: - #one byte identifier - capdu = capdu + identifier + '00' + '00' - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_data", error_status['errorStatus']) - return error_status, None - - log_end("get_data", error_status['errorStatus']) - - return error_status, rapdu - - -def get_status(card_element): - - log_start("get_status") - - # build the APDU - # supress blank if any - import re - card_element = ''.join( re.split( '\W+', card_element.upper() ) ) - - capdu = "80 F2 " + card_element + "02 02 4F 00" + "00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_status", error_status['errorStatus']) - return error_status, None - - if payload_mode_activated == False: - # store the response - card_response = last_response() - # check if more data available - while last_status() == '6310': - # send a get status next occurence - capdu = "80 F2 " + card_element + "03 02 4F 00" + "00" - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_status", error_status['errorStatus']) - return error_status, None - - card_response = card_response + last_response() - else: - card_response = None - error_status = create_no_error_status(0x00) - - - log_end("get_status", error_status['errorStatus']) - - return error_status, card_response - - - -def get_crs_status(aid, tag_list): - - log_start("get_crs_status") - - # build data field tag with given aid and tag list - if aid == '': data_field = "4F00" - else: data_field = "4F" + lv(aid) - - if tag_list == '': pass - else: data_field = data_field + "5C" + lv(tag_list) - - capdu = "80 F2 40 00 " + lv(data_field) + "00" - - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_crs_status", error_status['errorStatus']) - return error_status, None - - # store the response - card_response = last_response() - # check if more data available - while last_status() == '6310': - # send a get status next occurence - capdu = "80 F2 40 01 " + lv(data_field) + "00" - error_status, rapdu = send_APDU(capdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_crs_status", error_status['errorStatus']) - return error_status, None - - card_response = card_response + last_response() - - # we have the card_response TLV. create the get status response dictionnary - response_tlvs = TLVs(toByteArray(card_response)) - app_info_list = [] - app_aid = None - app_lifecycle = None - uniform_resource_locator = None - app_image_template = None - display_message = None - app_update_counter = None - selection_priority = None - app_group_head = None - app_group_members = None - crel_application_aid_list = None - policy_restricted_app = None - app_discretionary_data = None - application_family = None - display_required_indicator = None - assinged_protocol = None - continuous_processing = None - recognition_algorithm = None - - - for response_tlv in response_tlvs.list_childs_tlv(): - if response_tlv.getTAG() == '61': - # manage the list of TLV into this response_tlv - app_info_tlv_list = response_tlv.list_childs_tlv() - for app_info in app_info_tlv_list: - if app_info.getTAG() == '4F': app_aid = app_info.getValue() - elif app_info.getTAG() == '9F70': app_lifecycle = app_info.getValue() - elif app_info.getTAG() == '7F20': - display_control_tlv_list = app_info.list_childs_tlv() - for display_control_info in display_control_tlv_list: - if display_control_info.getTAG() == '5F50': - uniform_resource_locator = display_control_info.getValue() - elif display_control_info.getTAG() == '6D': - app_image_template = display_control_info.getValue() - elif display_control_info.getTAG() == '5F45': - display_message = display_control_info.getValue() - elif app_info.getTAG() == '80': app_update_counter = app_info.getValue() - elif app_info.getTAG() == '81': selection_priority = app_info.getValue() - elif app_info.getTAG() == 'A2': - app_group_head_tlv_list = app_info.list_childs_tlv() - for app_group_head in app_group_head_tlv_list: - if app_group_head.getTAG() == '4F': - app_group_head = app_group_head.getValue() - # below 3 parameters tag 'A3' to 'A5' can be multiple so need to find a better way to handle - elif app_info.getTAG() == 'A3': - app_group_members_tlv_list = app_info.list_childs_tlv() - for app_group_info in app_group_members_tlv_list: - if app_group_info.getTAG() == '4F': - app_group_members = app_group_info.getValue() - elif app_info.getTAG() == 'A4': - crel_app_aid_list_tlv_list = app_info.list_childs_tlv() - for crel_app_aid_info in crel_app_aid_list_tlv_list: - if crel_app_aid_info.getTAG() == '4F': - crel_app_aid_list = crel_app_aid_info.getValue() - elif app_info.getTAG() == 'A5': - policy_restricted_app_tlv_list = app_info.list_childs_tlv() - for policy_restricted_app_info in policy_restricted_app_tlv_list: - if policy_restricted_app_info.getTAG() == '4F': - policy_restricted_app = policy_restricted_app_info.getValue() - elif app_info.getTAG() == 'A6': app_discretionary_data = app_info.getValue() - elif app_info.getTAG() == '87': app_family = app_info.getValue() - elif app_info.getTAG() == '88': display_required_indicator = app_info.getValue() - elif app_info.getTAG() == '8C': assinged_protocol = app_info.getValue() - elif app_info.getTAG() == '8A': continuous_processing = app_info.getValue() - elif app_info.getTAG() == '8B': recognition_algorithm = app_info.getValue() - app_info_list.append({'aid':app_aid, 'lifecycle':app_lifecycle[:2], \ - 'app update counter':app_update_counter, 'selection priority':selection_priority, \ - 'app group head':app_group_head, 'app group members':app_group_members, \ - 'crel app list':crel_app_aid_list, 'policy restricted app':policy_restricted_app, \ - 'app discretionary data':app_discretionary_data, 'app family':app_family, \ - 'display required indicator':display_required_indicator, 'assinged protocol':assinged_protocol, \ - 'continuous processing':continuous_processing, 'recognition algorithm':recognition_algorithm}) - - else: - error_status = create_no_error_status(0x00) - - log_end("get_crs_status", error_status['errorStatus']) - - return error_status, app_info_list - - -def initialize_update(key_set_version , base_key, enc_key , mac_key , dek_key , scp, scp_implementation, sequence_counter = "000000" ): - - global last_apdu_response - global last_apdu_status - global payload_mode_activated - - log_start("initialize_update") - # create a host challenge - hostChallenge = crypto.RANDOM(8) - - log_debug("initialize_update: Host challenge Data: %s " % hostChallenge) - # create the initialize update APDU command (with Le = Max data) - initUpdateAPDU = '80' + '50' + key_set_version + '00 08' + hostChallenge + '00' - error_status, rapdu = send_APDU(initUpdateAPDU) - - if error_status['errorStatus'] != 0x00: - log_end("initialize_update", error_status['errorStatus']) - return error_status, None - - - # Set the security info structure needed for all GP operations - security_info = securityInfo[securityInfo[4]] - - - # checking payload mode - if payload_mode_activated == True: - # update the security information structure - security_info['secureChannelProtocol'] = int(scp, 16) - - if security_info['secureChannelProtocol'] == GP_SCP02: - # manage init update response - # in SCP02 the card challenge is calculated using the session key. So we need to create session key before. - sequenceCounter = toByteArray(sequence_counter) - # just check the sequence counter length. In SCP02 sequence counter is expressedwith 2 bytes - if len(sequenceCounter) != 2: - error_status = create_error_status(ERROR_INCONSISTENT_SEQUENCE_COUNTER, runtimeErrorDict[ERROR_INCONSISTENT_SEQUENCE_COUNTER]) - return error_status, None - - elif security_info['secureChannelProtocol'] == GP_SCP03: - # manage init update response - sequenceCounter = increment(sequence_counter, 0x01) - cardChallenge = calculate_card_challenge_SCP03(sequenceCounter + security_info['selectedAID'], enc_key) - # type coherency - cardChallenge = toByteArray(cardChallenge) - sequenceCounter= toByteArray(sequenceCounter) - log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) - log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - - security_info['secureChannelProtocolImpl'] = scp_implementation - security_info['keySetVersion'] = key_set_version - # we set it to a dummy value - security_info['keyIndex'] = 0xFF - - - - - - else: - - # managing authentication data - bytearray_initUpdateResponse = toByteArray(last_apdu_response) - # check init_update_response length, it must be 28, 29 or 32 bytes - # SCP01/SCP02 = 30 bytes, SCP03 31 or 34 bytes - if len (bytearray_initUpdateResponse) != 28 and len (bytearray_initUpdateResponse) != 29 and len (bytearray_initUpdateResponse) != 32: - error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) - return error_status, None - - # managing response of INITIALIZE UPDATE - keyDiversificationData = bytearray_initUpdateResponse[:10] - keyInformationData = bytearray_initUpdateResponse[10:12] - - # check if a scp has been set by the user, if not take the scp into the init update response - if scp == None: - scp = intToHexString(keyInformationData[1]) - - # test if reported SCP is consistent with passed SCP - if int(str(scp), 16) != keyInformationData[1]: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - # update the security information structure - security_info['secureChannelProtocol'] = int(str(scp), 16) - - - # in SCP03 the scp implementation value is returned by the init update response - # in SCP02 this value is not present so this value should be set by the user. - if security_info['secureChannelProtocol'] == GP_SCP02: - if scp_implementation == None: - scp_implementation = intToHexString(0x55) - # error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - # return error_status, None - - if security_info['secureChannelProtocol'] == GP_SCP03: - # key information data on 3 bytes - keyInformationData = bytearray_initUpdateResponse[10:13] - scpi = keyInformationData[2] - if scp_implementation == None: - scp_implementation = intToHexString(scpi) - else: - #test if reported SCP implementation is consistent with passed SCP implementation - if scp_implementation != intToHexString(scpi): - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - return error_status, None - - security_info['secureChannelProtocolImpl'] = scp_implementation - security_info['keySetVersion'] = keyInformationData[0] - # we set it to a dummy value - security_info['keyIndex'] = 0xFF - - if security_info['secureChannelProtocol'] == GP_SCP02: - # manage init update response - sequenceCounter = bytearray_initUpdateResponse[12:14] - cardChallenge= bytearray_initUpdateResponse[14:20] # 6 bytes - cardCryptogram = bytearray_initUpdateResponse[20:28] # 8 bytes - - if security_info['secureChannelProtocol'] == GP_SCP03: - # manage init update response - cardChallenge= bytearray_initUpdateResponse[13:21] # 8 bytes - cardCryptogram = bytearray_initUpdateResponse[21:29] # 8 bytes - sequenceCounter = bytearray_initUpdateResponse[29:32] # 3 bytes - - - log_debug("initialize_update: Key Diversification Data: %s " % toHexString(keyDiversificationData)) - - log_debug("initialize_update: Key Information Data: %s " % toHexString(keyInformationData)) - - log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) - - log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) - - log_debug("initialize_update: Card Cryptogram %s " % toHexString(cardCryptogram)) - - # create session key regarding the scp implementation - - if security_info['secureChannelProtocol'] == GP_SCP02: - ## Secure Channel base key - if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54): - - # calculation of encryption session key using on base key - security_info['encryptionSessionKey'] = create_session_key_SCP02(base_key, KENC_TYPE, toHexString(sequenceCounter)) - # calculation of C-MAC session key - security_info['C_MACSessionKey'] = create_session_key_SCP02(baseKey, KMAC_TYPE, toHexString(sequenceCounter)) - #calculation of R-MAC session key - security_info['R_MACSessionKey'] = create_session_key_SCP02(baseKey, KRMAC_TYPE, toHexString(sequenceCounter)) - #calculation of data encryption session key - security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(baseKey, KDEK_TYPE, toHexString(sequenceCounter)) - - ## 3 Secure Channel Keys */ - elif (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or - security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45): - - # calculation of encryption session key using on 3 static key - security_info['encryptionSessionKey'] = create_session_key_SCP02(enc_key, KENC_TYPE, toHexString(sequenceCounter)) - # calculation of C-MAC session key - security_info['C_MACSessionKey'] = create_session_key_SCP02(mac_key, KMAC_TYPE, toHexString(sequenceCounter)) - #calculation of R-MAC session key - security_info['R_MACSessionKey'] = create_session_key_SCP02(dek_key, KRMAC_TYPE, toHexString(sequenceCounter)) - #calculation of data encryption session key - security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(dek_key, KDEK_TYPE, toHexString(sequenceCounter)) - - if payload_mode_activated == True: - cardChallenge = self.calculate_card_challenge_SCP02(security_info['selectedAID'], security_info['C_MACSessionKey']) - # type coherency - cardChallenge = int(cardChallenge, 16) - - else: - error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) - return error_status, None - - elif security_info['secureChannelProtocol'] == GP_SCP03: - - # calculation of encryption session key using on 3 static key - session_key_value = create_session_key_SCP03(enc_key, KENC_TYPE, toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['encryptionSessionKey'] = session_key_value - - # calculation of C-MAC session key - session_key_value = create_session_key_SCP03(mac_key, KMAC_TYPE , toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['C_MACSessionKey'] = session_key_value - #calculation of R-MAC session key - session_key_value = create_session_key_SCP03(mac_key, KRMAC_TYPE, toHexString(cardChallenge), hostChallenge) - if session_key_value == None: - error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - return error_status, None - else: - security_info['R_MACSessionKey'] = session_key_value - # calculation of data encryption session key - # warning: no session key for data encryption in SCP03 - # session_key_value = create_session_key_SCP03(dek_key, KDEK_TYPE , toHexString(cardChallenge), hostChallenge) - # if session_key_value == None: - # error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) - # return error_status, None - # else: - security_info['dataEncryptionSessionKey'] = dek_key - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - log_debug("initialize_update: S-ENC Session Key: %s" %security_info['encryptionSessionKey']) - log_debug("initialize_update: S-MAC Session Key: %s" %security_info['C_MACSessionKey']) - log_debug("initialize_update: DEK Session Key: %s" %security_info['dataEncryptionSessionKey']) - log_debug("initialize_update: R-MAC Session Key: %s" %security_info['R_MACSessionKey']) - - if security_info['secureChannelProtocol'] == GP_SCP02: - offcardCryptogram = calculate_card_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) - elif security_info['secureChannelProtocol'] == GP_SCP03: - offcardCryptogram = calculate_card_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - if payload_mode_activated == False: - # compare cryptograms - if toHexString(cardCryptogram) != offcardCryptogram: - error_status = create_error_status(ERROR_CARD_CRYPTOGRAM_VERIFICATION, runtimeErrorDict[ERROR_CARD_CRYPTOGRAM_VERIFICATION]) - return error_status , None - - - host_cryptogram = None - if security_info['secureChannelProtocol'] == GP_SCP02: - host_cryptogram = calculate_host_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) - - elif security_info['secureChannelProtocol'] == GP_SCP03: - host_cryptogram = calculate_host_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status, None - - - error_status = create_no_error_status(0x00) - log_end("initialize_update", error_status['errorStatus']) - return error_status, host_cryptogram - -def internal_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): - - log_start("internal_authenticate") - - - - # build the APDU - data = "5F49" + lv(ePK_OCE) - data_field = crt_data + data - - internal_authenticate_apdu = '80 88' + key_version_number + key_identifier + lv(data_field) - - error_status, rapdu = send_APDU(internal_authenticate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("internal_authenticate", error_status['errorStatus']) - return error_status, None - - - log_end("internal_authenticate", error_status['errorStatus']) - return error_status, rapdu - - -def mutual_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): - - log_start("mutual_authenticate") - - - # build the APDU - data = "5F49" + lv(ePK_OCE) - data_field = crt_data + data - - mutual_authenticate_apdu = '80 82' + key_version_number + key_identifier + lv(data_field) - - error_status, rapdu = send_APDU(mutual_authenticate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("mutual_authenticate", error_status['errorStatus']) - return error_status, None - - - log_end("mutual_authenticate", error_status['errorStatus']) - return error_status, rapdu - -def external_authenticate(security_level, host_cryptogram): - - log_start("external_authenticate") - - # get security_info dictionary of working channel - security_info = securityInfo[securityInfo[4]] - # create the external authenticate APDU command - externalAuthAPDU = '84' + '82' + intToHexString(security_level) + '00' + '10' + host_cryptogram - - if security_info['secureChannelProtocol'] == GP_SCP03: - mac = calculate_mac_SCP03(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_16) - security_info['lastC_MAC'] = mac - security_info['icv_counter'] = 0x01 - # add the mac to the command - externalAuthAPDU = externalAuthAPDU + mac[:16] - - elif security_info['secureChannelProtocol'] == GP_SCP02: - - mac = calculate_mac_SCP02(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_8) - security_info['lastC_MAC'] = mac - # add the mac to the command - externalAuthAPDU = externalAuthAPDU + mac - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status - - error_status, rapdu = send_APDU(externalAuthAPDU) - - if error_status['errorStatus'] != 0x00: - log_end("external_authenticate", error_status['errorStatus']) - return error_status - - - #update security info - security_info['securityLevel'] = security_level - - log_end("external_authenticate", error_status['errorStatus']) - return error_status - -def get_certificate(key_version_number, key_identifier): - log_start("get_certificate") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("get_certificate", error_status['errorStatus']) - return error_status - - # build the APDU - - - get_certificate_apdu = '80 CA BF 21' + lv ('A6' + lv ( '83' + lv (key_version_number + key_identifier))) - - error_status, rapdu = send_APDU(get_certificate_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("get_certificate", error_status['errorStatus']) - return error_status, None - - - log_end("get_certificate", error_status['errorStatus']) - return error_status, rapdu - - - -def perform_security_operation(key_version_number, key_identifier , crt_data ): - - log_start("perform_security_operation") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - - # build the APDU - - - perform_security_operation_apdu = '80 2A' + key_version_number + key_identifier + lv(crt_data) - - error_status, rapdu = send_APDU(perform_security_operation_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - - - log_end("perform_security_operation", error_status['errorStatus']) - return error_status - -def registry_update(security_domain_AID, application_aid, application_privileges = "00", registry_parameter_field = None, install_token = None): - log_start("registry_update") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("registry_update", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - registry_update_apdu_data = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + lv(application_privileges) - # optionnal fields - parameter_field = '' - if registry_parameter_field != None: - parameter_field = parameter_field + 'EF' + lv(remove_space(registry_parameter_field)) - else: - parameter_field =parameter_field + 'EF00' - - if install_token != None: - install_token = lv(install_token) - else: - install_token = '00' #no token - - registry_update_apdu = '80 E6 40 00' + lv(registry_update_apdu_data + lv(parameter_field) + install_token) - - error_status, rapdu = send_APDU(registry_update_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("registry_update", error_status['errorStatus']) - return error_status - - - log_end("registry_update", error_status['errorStatus']) - return error_status - - -def install_install(make_selectable, executable_LoadFile_AID, executable_Module_AID, application_AID, application_privileges = "00", application_specific_parameters = None, install_parameters = None, install_token = None): - log_start("install_install") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("install_install", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - install_apdu_data = lv(remove_space(executable_LoadFile_AID)) + lv(remove_space(executable_Module_AID)) + lv(remove_space(application_AID)) + lv(application_privileges) - # optionnal fields - parameter_field = '' - if application_specific_parameters != None: - parameter_field = parameter_field + 'C9' + lv(remove_space(application_specific_parameters)) - else: - parameter_field =parameter_field + 'C903000000' - - if install_parameters != None: - parameter_field = parameter_field + 'EF' + lv(remove_space(install_parameters)) - else: - parameter_field =parameter_field + 'EF00' - - if install_token != None: - install_token = lv(install_token) - else: - install_token = '00' #no token - - - if make_selectable == True: - install_apdu = '80 E6 0C 00' + lv(install_apdu_data + lv(parameter_field) + install_token) - else: - install_apdu = '80 E6 04 00' + lv(install_apdu_data + lv(parameter_field) + install_token) - - error_status, rapdu = send_APDU(install_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("install_install", error_status['errorStatus']) - return error_status - - - log_end("install_install", error_status['errorStatus']) - return error_status - - -def install_load(executable_load_file_aid, security_domain_aid, load_file_data_block_hash = None, load_parameters = None, load_token = None): - log_start("install_load") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("install_load", error_status['errorStatus']) - return error_status - - # build the APDU - # mandatory fields - install_apdu = lv(remove_space(executable_load_file_aid)) + lv(remove_space(security_domain_aid)) - # optionnal fields - if load_file_data_block_hash != None: - install_apdu = install_apdu + lv(remove_space(load_file_data_block_hash)) - else: - install_apdu = install_apdu + '00' - - if load_parameters != None: - install_apdu = install_apdu + lv('EF' + lv(remove_space(load_parameters))) - else: - install_apdu = install_apdu + '00' #no parameter - - if load_token != None: - install_apdu = install_apdu + lv(load_token) - else: - install_apdu = install_apdu + '00' #no token - - install_apdu = '80 E6 02 00' + lv(install_apdu) - - error_status, rapdu = send_APDU(install_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("install_load", error_status['errorStatus']) - return error_status - - - log_end("install_load", error_status['errorStatus']) - return error_status - - -def load_blocks(load_file_path, block_size = 32): - log_start("load_blocks") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("load_blocks", error_status['errorStatus']) - return error_status - - block_number = 0x00 - - load_file_obj = loadfile.Loadfile(load_file_path) - - log_debug("load_blocks: load_file_obj: %s" %load_file_obj.__str__()) - - all_blocks_data = load_file_obj.get_load_blocks(block_size) - - for block in all_blocks_data: - blockNumber = intToHexString(block_number) - is_last_block = (block == all_blocks_data[-1]) - - if is_last_block == True: - load_apdu = '80' + 'E8' + '80' + blockNumber + lv(block) - else: - load_apdu = '80' + 'E8' + '00'+ blockNumber + lv(block) - - error_status, rapdu = send_APDU(load_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - block_number = block_number + 1 - if block_number > 255: - block_number = 0 - - log_end("load_blocks", error_status['errorStatus']) - return error_status - - -def extradite(security_domain_AID, application_aid, identification_number = None, image_Number = None, application_provider_identifier = None, token_identifier = None, extraditeToken = None): - log_start("extradite") - - error_status = __check_security_info__(securityInfo[securityInfo[4]]) - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - - strControlReferenceTemplate = '' - str_ExtraditionParametersfield = '' - - if (identification_number != None): - strControlReferenceTemplate += "42" + lv(remove_space(identification_number)) - - if (image_Number != None): - strControlReferenceTemplate += "45" + lv(remove_space(image_Number)) - - if (application_provider_identifier != None): - strControlReferenceTemplate += "5F20" + lv(remove_space(application_provider_identifier)) - - if (strControlReferenceTemplate != ''): - str_ExtraditionParametersfield = str_ExtraditionParametersfield + "B6" - str_ExtraditionParametersfield = str_ExtraditionParametersfield + lv(strControlReferenceTemplate) - - # build the APDU - extradite_apdu = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + '00' + lv(str_ExtraditionParametersfield) - - if extraditeToken != None: - extradite_apdu = extradite_apdu + lv(extraditeToken) - else: - extradite_apdu = extradite_apdu + '00' #no token - - extradite_apdu = '80 E6 10 00' + lv(extradite_apdu) - - error_status, rapdu = send_APDU(extradite_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("extradite", error_status['errorStatus']) - return error_status - - - log_end("extradite", error_status['errorStatus']) - return error_status - - -def put_key(key_version_number, key_identifier, key_type, key_value, replace = False ): - - log_start("put_key") - - security_info = securityInfo[securityInfo[4]] - - error_status = __check_security_info__(security_info) - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - # build the extradition parameter fields - - if replace == False: - p1 = '00' - else: - p1 = key_version_number - - # cipher key regarding the SCP protocol - if security_info['secureChannelProtocol'] == GP_SCP03: - cipher_key, cipher_key_kcv = cipher_key_SCP03(key_value, security_info['dataEncryptionSessionKey'] ) - cipher_key_len = intToHexString(int(len(cipher_key)/2)) - put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv( cipher_key_len + cipher_key) + lv(cipher_key_kcv)) - - - elif security_info['secureChannelProtocol'] == GP_SCP02: - - cipher_key, cipher_key_kcv = cipher_key_SCP02(key_value, security_info['dataEncryptionSessionKey'] ) - put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv(cipher_key) + lv(cipher_key_kcv)) - - - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - return error_status - - - error_status, rapdu = send_APDU(put_key_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - - log_end("put_key", error_status['errorStatus']) - return error_status - - -def put_scp_key(key_version_number, key_list, replace = False ): - - log_start("put_scp_key") - - security_info = securityInfo[securityInfo[4]] - error_status = __check_security_info__(security_info) - if error_status['errorStatus'] != 0x00: - log_end("put_key", error_status['errorStatus']) - return error_status - - # build the extradition parameter fields - - if replace == False: - p1 = '00' - else: - p1 = key_version_number - - scp = security_info['secureChannelProtocol'] - apdu_data = key_version_number - # cipher key regarding the SCP protocol - for ( key_vn, key_id, key_type, key_value ) in key_list: - # calculate key check value (refer key_type of key_list) - if (key_type == 'DES') or (key_type == 'AES'): - kcv = compute_key_check_value(key_type, key_value) - else: - error_status = create_error_status(ERROR_PUTKEY_INVALID_KEY_TYPE, runtimeErrorDict[ERROR_PUTKEY_INVALID_KEY_TYPE]) - break - - # encrypt the key (refer the key type of security_info['dataEncryptionSessionKey']) - if (scp == GP_SCP03) or (scp == GP_SCP02): - encryptedkey = cipher_key(key_value, security_info['dataEncryptionSessionKey'], scp) - else: - error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) - break - - # compose APDU - apdu_data = apdu_data + key_type_coding_dict[key_type] + lv( getLength(encryptedkey) + encryptedkey) + lv(kcv) - - if error_status['errorStatus'] != 0x00: - log_end("put_scp_key", error_status['errorStatus']) - return error_status - - put_scp_key_apdu = '80 D8' + p1 + '81' + lv(apdu_data) - error_status, rapdu = send_APDU(put_scp_key_apdu) - - if error_status['errorStatus'] != 0x00: - log_end("put_scp_key", error_status['errorStatus']) - return error_status - - log_end("put_scp_key", error_status['errorStatus']) - return error_status - -def manage_channel(open_channel, logical_channel): - global securityInfo - - log_start("manage_channel") - - if open_channel == True: - capdu = '00 70 00 00 01' - else: - if logical_channel < 0x01 and logical_channel > 0x03: - # channel number must be between 01 and 03 - error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) - return error_status - capdu = '00 70 80 ' + intToHexString(logical_channel) + '00' - - error_status, rapdu = send_APDU(capdu, raw_mode = True) - - if error_status['errorStatus'] != 0x00: - log_end("manage_channel", error_status['errorStatus']) - return error_status, None - - if open_channel == True: - byte_list_data = toByteArray(rapdu) - channel_num = byte_list_data[0] - sChannelInfo = securityInfo[channel_num] - sChannelInfo['channelStatus'] = "ON" - else: - # Close working channel then set another chanenl as working channel - if securityInfo[4] == logical_channel: - securityInfo[4] = 0 - securityInfo[logical_channel] = {} - - log_end("manage_channel", error_status['errorStatus']) - - return error_status, rapdu +import time +import pygp.crypto as crypto +from pygp.logger import * +from pygp.connection.connection import * +from pygp.error import * +from pygp.constants import * +from pygp.gp.gp_crypto import * +import pygp.loadfile as loadfile +from pygp.tlv import * + + + +# global variable for gp_functions +# information of each channel. +# [0] for channel 0, [1] for channel 1, [2] for channel 2, [3] for channel 3 +# [4] it save current working channel number. This channel number used when sending APDU. +securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] + +last_apdu_response = None +last_apdu_status = None +apdu_timing = False + +payload_mode_activated = False +payload_list = [] + +total_time = 0.0 + +def clear_securityInfo(): + global securityInfo + securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] + +def get_securityInfo(channel = None): + global securityInfo + if channel == None: + return securityInfo[securityInfo[4]] + else: + if channel >= 0 and channel <=3: + return securityInfo[channel] + else: + pass + +def get_payload_list(): + global payload_list + return payload_list + +def set_payload_mode(activate): + global payload_mode_activated + global payload_list + payload_mode_activated = activate + # clear the list on activation + if (activate == True): + payload_list.clear() + +def last_response(): + global last_apdu_response + return last_apdu_response + +def last_status(): + global last_apdu_status + return last_apdu_status + +def set_apdu_timing(activated): + global apdu_timing + apdu_timing = activated + +def set_start_timing(): + global total_time + total_time = 0.0 + +def get_total_execution_time(): + return total_time + +def __check_security_info__(security_info): + if security_info == None: + error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) + return error_status + else: + error_status = create_no_error_status(0x00) + return error_status + + +def select_channel(logical_channel): + global securityInfo + + log_start("select_channel") + + # Display All Channel Information + logger.log_info("Channel Status Information [0~3]") + logger.log_info("Channel SCP SCPi SecurityLevel AID") + logger.log_info("------- --- ---- ------------- ------------------") + channel_id = 0 + for sChannelInfo in securityInfo[0:4]: + if (securityInfo[4] == channel_id): + strWorking = '*' + else: + strWorking = ' ' + if (('channelStatus' in sChannelInfo.keys()) and (sChannelInfo['channelStatus'] == "ON")): + if 'selectedAID' in sChannelInfo.keys(): + strAID = sChannelInfo['selectedAID'] + else: + strAID = 'NONE' + + if 'secureChannelProtocolImpl' in sChannelInfo.keys(): + logger.log_info(" %s 0%d %d %s %d %s" \ + %(strWorking, channel_id, sChannelInfo['secureChannelProtocol'],\ + sChannelInfo['secureChannelProtocolImpl'], sChannelInfo['securityLevel'], strAID )) + else: + logger.log_info(" %s 0%d n/a n/a n/a %s" %(strWorking, channel_id, strAID)) + else: + logger.log_info(" 0%d Not Available" %(channel_id)) + + channel_id += 1 + + # check the parameter value + if logical_channel == None or logical_channel < 0x00 or logical_channel > 0x03: + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + log_info("\tInvalid logical channel (%-0.2X)" % securityInfo[4]) + return error_status + + # check the status of logical channel + sChannelInfo = securityInfo[logical_channel] + if sChannelInfo['channelStatus'] == "ON": + # set the working channel value + securityInfo[4] = logical_channel + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_info("\tLogical channel has changed to %-0.2X" % securityInfo[4]) + else: + error_status = create_error_status(ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE, runtimeErrorDict[ERROR_LOGICAL_CHANNEL_NOT_AVAILABLE]) + + log_end("select_channel", error_status) + + return error_status + + +def wrap_command(security_info, capdu): + ''' Wrap APDU according to the security info ''' + #TODO: update doc + + log_start("wrap_command") + + # no security level defined, just return + if (security_info == None): + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + return error_status, capdu + + # security_info level defined + if ('securityLevel' in security_info) == False: + error_status = create_error_status(ERROR_NO_SECURITY_INFO_INITIALIZED, runtimeErrorDict[ERROR_NO_SECURITY_INFO_INITIALIZED]) + return error_status, '' + + # trivial case, just return + if security_info['securityLevel'] == SECURITY_LEVEL_NO_SECURE_MESSAGING: + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + return error_status, capdu + + # Determine which type of Exchange between the reader + # Convert capdu from string to list of bytes + bytelist_capdu = toByteArray(capdu) + + ISO_case = -1 + Le = None + + if len(bytelist_capdu) == 4: + ISO_case = CASE_1 + + elif len(bytelist_capdu) == 5: + ISO_case = CASE_2S + Le = bytelist_capdu[4] + + else: + if (bytelist_capdu[4] != 0): + if (bytelist_capdu[4] == len(bytelist_capdu) - 5): + ISO_case = CASE_3S + elif (bytelist_capdu[4] == len(bytelist_capdu) - 6): + ISO_case = CASE_4S + Le = bytelist_capdu[-1:] + else: + pass + + + if ISO_case == -1: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, '' + + # Manage ISO case: Get Le if any and prepare APDU + + apdu_to_wrap = [] + + + # C_MAC on modified APDU + if ( security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or + security_info['secureChannelProtocol'] == GP_SCP03): + + + ############## ISO Case 1 & 2 ############## + if ISO_case == CASE_1: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap.append(0x08) + + elif ISO_case == CASE_2S: + Le = bytelist_capdu[-1:] + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = 0x08 + + elif ISO_case == CASE_3S: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 + + elif ISO_case == CASE_4S: + Le = bytelist_capdu[-1:] + apdu_to_wrap = bytelist_capdu[:-1] + # put lc with the length of the mac + apdu_to_wrap[4] = apdu_to_wrap[4] + 0x08 + + else: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, None + + #CLA - indicate security level 1 or 3 + apdu_to_wrap[0] = bytelist_capdu[0] | 0x04 + else: + # C_MAC on unmodified APDU + ############## ISO Case 1 & 2 ############## + if ISO_case == CASE_1: + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap.append(0x00) + + elif ISO_case == CASE_2S: + Le = bytelist_capdu[:-1] + apdu_to_wrap = bytelist_capdu + # put lc with the length of the mac + apdu_to_wrap[4] = 0x00 + + elif ISO_case == CASE_4S: + Le = bytelist_capdu[:-1] + apdu_to_wrap = bytelist_capdu[:-1] + + + else: + # create the status error structure + error_status = create_error_status(ERROR_UNRECOGNIZED_APDU_COMMAND, runtimeErrorDict[ERROR_UNRECOGNIZED_APDU_COMMAND]) + return error_status, None + + # ICV encryption + iv = None + if security_info['secureChannelProtocol'] == GP_SCP02: + if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1A or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i1B or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55) : + + iv = encipher_iv_SCP02(security_info['lastC_MAC'], security_info['C_MACSessionKey'][:16]) + + elif(security_info['secureChannelProtocol']== GP_SCP03): + iv = crypto.ISO_9797_M1_Padding_left(intToHexString(security_info['icv_counter'],2), 16) + iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + # Get the data field of the APDU + encData = apdu_to_wrap[5:] + # if we have to encrypt: + if (security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_C_MAC_R_MAC or + security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC): + # retrieve only the data field (ie: remove APDU header) + apdu_data_field = apdu_to_wrap[5:] + + # cipher data + if security_info['secureChannelProtocol'] == GP_SCP02: + encData = encipher_data_SCP02(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , crypto.ICV_NULL_8) + encData = toByteArray(encData) # type mismatch + + log_debug("wrap_command: encrypted data field: %s" %toHexString(encData)) + + elif(security_info['secureChannelProtocol']== GP_SCP03): + encData = encipher_data_SCP03(toHexString(apdu_data_field), security_info['encryptionSessionKey'] , iv) + + encData = toByteArray(encData) # type mismatch + # re put lc with the length of the encipher data + apdu_to_wrap[4] = len(encData) + 8 # enc data + + + # MAC calculation + if security_info['secureChannelProtocol'] == GP_SCP02: + mac = calculate_mac_SCP02(toHexString(apdu_to_wrap), security_info['C_MACSessionKey'], iv) + # re put lc with the length of the encipher data + apdu_to_wrap[4] = len(encData) + 8# enc data + elif(security_info['secureChannelProtocol']== GP_SCP03): + mac = calculate_mac_SCP03(toHexString(apdu_to_wrap[:5]) + toHexString(encData), security_info['C_MACSessionKey'], security_info['lastC_MAC']) + pass + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + log_debug("wrap_command: Data for MAC computation: %s" %toHexString(apdu_to_wrap)) + log_debug("wrap_command: ICV for MAC: %s" %iv) + log_debug("wrap_command: Generated MAC: %s" %mac) + + #update the last iv + security_info['lastC_MAC'] = mac + + # create the wrapped APDU + if security_info['secureChannelProtocol'] == GP_SCP02: + wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac + + elif(security_info['secureChannelProtocol'] == GP_SCP03): + wrappedAPDU = toHexString(apdu_to_wrap[:5]) + toHexString(encData) + mac[:16] + # don't forget tp update the counter for ICV_NULL_8 + log_debug("wrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] ,2)) + security_info['icv_counter'] = security_info['icv_counter'] + 1 + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("wrap_command") + return error_status, None + + + # we have to put Le bytes !! and manage RMAC level + if Le != None: + wrappedAPDU = wrappedAPDU + toHexString(Le) + + error_status = create_no_error_status(0x00) + + log_end("wrap_command", error_status) + + return error_status, wrappedAPDU + + +def unwrap_command(security_info, rapdu): + ''' unwrap APDU response according to the security info ''' + #TODO: update doc + + log_start("unwrap_command") + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + + # no security level defined, just return + if (security_info == None): + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_end("unwrap_command") + return error_status, rapdu + + # trivial case, just return + if (security_info['securityLevel'] != SECURITY_LEVEL_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_MAC_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_C_MAC_R_MAC and + security_info['securityLevel'] != SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC) : + + error_status = create_no_error_status(ERROR_STATUS_SUCCESS) + log_end("unwrap_command") + return error_status, rapdu + + # convert to byte array + bytelist_rapdu = toByteArray(rapdu) + if security_info['secureChannelProtocol'] == GP_SCP02: + # only status word so no RMAC + if len(bytelist_rapdu) == 2: + return error_status, rapdu + #TODO + + elif(security_info['secureChannelProtocol'] == GP_SCP03): + # only status word so no RMAC + if len(bytelist_rapdu) == 2: + return error_status, rapdu + else: + + # get the mac of the command (8 bytes before the rapdu status word) + len_reponse_date_without_sw = len(bytelist_rapdu) - 2 + len_response_data_without_mac = len_reponse_date_without_sw - 8 + response_data_without_mac = bytelist_rapdu[0:len_response_data_without_mac] + response_data_mac = bytelist_rapdu[ len_response_data_without_mac: len_reponse_date_without_sw] + response_data_sw = bytelist_rapdu[len_reponse_date_without_sw:] + log_debug("unwrap_command: Response MAC: %s" %toHexString(response_data_mac)) + # calculate the off card MAC + mac = calculate_mac_SCP03(toHexString(response_data_without_mac) + toHexString(response_data_sw), security_info['R_MACSessionKey'], security_info['lastC_MAC']) + log_debug("unwrap_command: Data for MAC computation: %s" %(toHexString(response_data_without_mac) + toHexString(response_data_sw))) + log_debug("unwrap_command: ICV for Generated MAC: %s" %security_info['lastC_MAC']) + log_debug("unwrap_command: Generated MAC: %s" %mac) + if toHexString(response_data_mac) != mac[:16]: + error_status = create_error_status(ERROR_VALIDATION_R_MAC, runtimeErrorDict[ERROR_VALIDATION_R_MAC]) + log_end("unwrap_command") + return error_status, None + else: + # check if we have to decipher the APDU + if security_info['securityLevel'] == SECURITY_LEVEL_C_DEC_R_ENC_C_MAC_R_MAC: + if(security_info['secureChannelProtocol']== GP_SCP03): + log_debug("unwrap_command: current ICV counter: %s" %intToHexString(security_info['icv_counter'] - 1,2)) + iv = crypto.ISO_9797_M2_Padding_left(intToHexString(security_info['icv_counter']-1 ,2), 16) + iv = encipher_iv_SCP03(iv, security_info['encryptionSessionKey']) + log_debug("unwrap_command: current ICV : %s " %iv) + + # decipher data + decipher_data = decipher_data_SCP03(toHexString(response_data_without_mac), security_info['encryptionSessionKey'], iv) + decipher_data = crypto.Remove_ISO_9797_M2_Padding(decipher_data) + return error_status, decipher_data + toHexString(response_data_sw) + + pass + else: + return error_status, toHexString(bytelist_rapdu) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + log_end("unwrap_command") + return error_status, None + + +def send_APDU(capdu, raw_mode = False, exsw = None, exdata = None): + + global securityInfo + global last_apdu_response + global last_apdu_status + global apdu_timing + global payload_mode_activated + global payload_list + global total_time + + log_start("send_APDU") + #TODO: managing security info wrap the command + + if raw_mode == True: + # no wrapping management, just send the apdu + c_wrapped_apdu = capdu + else: + # get securityInfo of the channel + currentChannel = securityInfo[4] + securityInfoOfChannel = securityInfo[currentChannel] + + # wrap command + error_status, c_wrapped_apdu = wrap_command(securityInfoOfChannel, capdu) + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == True: + payload_list.append(remove_space(c_wrapped_apdu)) + error_status = create_no_error_status(0x00) + return error_status, None + else: + #convert capdu from string to list of bytes + bytelist_capdu = toByteArray(c_wrapped_apdu) + + if raw_mode == False: + # manage the selected logical channel + bytelist_capdu[0] |= securityInfo[4] + + start_time = time.perf_counter() + + error_status, rapdu = send_apdu(bytelist_capdu) + + end_time = time.perf_counter() + total_time = total_time + (end_time - start_time) + + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if raw_mode == True: + c_unwrapped_rapdu = rapdu + else: + error_status, c_unwrapped_rapdu = unwrap_command(securityInfoOfChannel, rapdu) + if error_status['errorStatus'] != 0x00: + log_end("send_APDU", error_status['errorStatus']) + return error_status, None + + if apdu_timing == True: + log_info("execution time: %.3f sec." %(end_time - start_time)) + # update global variables + last_apdu_response = c_unwrapped_rapdu[:-4] # response without status + last_apdu_status = c_unwrapped_rapdu[-4:] # only status + + # check if it is an ISO7816 status word error + if exsw == None: + error_status = check_ISO7816_status_word(rapdu) + else: + # convert exsw to list, compare between expected status word and response. + # The format of exsw "9000, 6Cxx, 6xxx" + exsw = exsw.upper() + exsw = exsw.replace(',', ' ') + exsw = exsw.replace('X', 'F') + byte_list_exsw = toByteArray(exsw) + byte_list_sw = toByteArray(last_apdu_status) + found = False + for offset in range(0, len(byte_list_exsw), 2): + if ((byte_list_exsw[offset] & byte_list_sw[-2]) == byte_list_sw[-2]): + if ((byte_list_exsw[offset+1] & byte_list_sw[-1]) == byte_list_sw[-1]): + # the status word is same as our expectation + error_status = create_no_error_status(last_apdu_status) + found = True + if found == False: + error_status = create_error_status(last_apdu_status, "Differ with expected status word") + log_error("expected status word " + exsw) + #log_end("send_APDU", error_status['errorStatus']) + + if (exdata != None) and (error_status['errorStatus'] == 0x00): + exdata = exdata.upper() + exdata = exdata.replace(' ', '') + for offset in range(0, len(exdata), 1): + if exdata[offset] == 'X': + continue + elif exdata[offset] == rapdu[offset].upper(): + continue + else: + error_status = create_error_status(last_apdu_status, "Differ with expected data") + log_error("expected data " + exdata) + break + + return error_status, rapdu + + +def select_issuerSecurityDomain(logical_channel = 0): + + log_start("select_issuerSecurityDomain") + + if(logical_channel < 0 or logical_channel > 3): + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + return error_status, None + + capdu = intToHexString(logical_channel, 1) + " A4 04 00 00" + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: + log_end("select_issuerSecurityDomain", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so the application is selected. now store its aid + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != '6F': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + for response_tlv in response_tlv.list_childs_tlv(): + if response_tlv.getTAG() == '84': + current_selected_aid = response_tlv.getValue() + else: + current_selected_aid = ISD_APPLICATION_AID + + # there is no error. Do initialize securityInfo of selected channel + securityInfo[4] = logical_channel + securityInfo[logical_channel] = {} + securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING + securityInfo[logical_channel]['channelStatus'] = "ON" + securityInfo[logical_channel]['selectedAID'] = current_selected_aid + log_end("select_issuerSecurityDomain", error_status['errorStatus'], error_status['errorMessage']) + + return error_status, rapdu + + +def select_application(str_AID, logical_channel = 0): + + log_start("select_application") + + if(logical_channel < 0 or logical_channel > 3): + error_status = create_error_status(INVALID_LOGICAL_CHANNEL_NUMBER, runtimeErrorDict[INVALID_LOGICAL_CHANNEL_NUMBER]) + return error_status, None + + capdu = intToHexString(logical_channel, 1) + " A4 04 00 " + lv (str_AID) + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != ERROR_STATUS_SUCCESS: + log_end("select_application", error_status['errorStatus']) + return error_status, None + + # there is no error. Do initialize securityInfo of selected channel + securityInfo[4] = logical_channel + securityInfo[logical_channel] = {} + securityInfo[logical_channel]['securityLevel'] = SECURITY_LEVEL_NO_SECURE_MESSAGING + securityInfo[logical_channel]['channelStatus'] = "ON" + securityInfo[logical_channel]['selectedAID'] = str_AID + log_end("select_application", error_status['errorStatus']) + + return error_status, rapdu + + +def set_status(cardElement, lifeCycleState, aid): + + log_start("set_status") + # supress blank if any + import re + aid = ''.join( re.split( '\W+', aid.upper() ) ) + + capdu = "80 F0 " + cardElement + lifeCycleState + lv (aid) + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("set_status", error_status['errorStatus']) + return error_status + + log_end("set_status", error_status['errorStatus']) + + return error_status + + +def set_crs_status(status_type, status_value, aid): + + log_start("set_crs_status") + # supress blank if any + import re + aid = ''.join( re.split( '\W+', aid.upper() ) ) + + capdu = "80 F0 " + status_type + status_value + "4F" + lv(aid) + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("set_crs_status", error_status['errorStatus']) + return error_status, None + + log_end("set_crs_status", error_status['errorStatus']) + + #TODO: needs to parse the response data based on the GP Amdt.C Table 3-23 + + return error_status, rapdu + + +def delete_application(str_AID, exsw): + + log_start("delete_application") + + capdu = "80 E4 00 00 " + lv ('4F' + lv(str_AID)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("select_application", error_status['errorStatus']) + return error_status + + log_end("select_application", error_status['errorStatus']) + + return error_status + + +def delete_package(str_AID, exsw): + + log_start("delete_package") + + capdu = "80 E4 00 80 " + lv ('4F' + lv(str_AID)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("delete_package", error_status['errorStatus']) + return error_status + + log_end("delete_package", error_status['errorStatus']) + + return error_status + + +def delete_key(KeyIdentifier, keyVersionNumber, exsw): + + log_start("delete_key") + + capdu = "80 E4 00 00 " + lv('D0' + lv(KeyIdentifier)) + lv('D2' + lv(keyVersionNumber)) + + error_status, rapdu = send_APDU(capdu, exsw = exsw) + + if error_status['errorStatus'] != 0x00: + log_end("delete_key", error_status['errorStatus']) + return error_status + + log_end("delete_key", error_status['errorStatus']) + + return error_status + + +def get_cplc_data(): + + log_start("get_cplc_data") + + capdu = "80 CA 9F 7F 00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_cplc_data", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so display results + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != '9F7F': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + + log_end("get_cplc_data", error_status['errorStatus']) + + return error_status, response_tlv.getValue() + else: + return error_status, None + + +def get_key_information_template(): + + log_start("get_key_information_template") + + capdu = "80 CA 00 E0 00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_key_information_template", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # no error so display results + # key information is list of Tuple : (Key id, Key version number, KeyLength, KeyType) + keyInformation = [] + response_tlv = TLV(toByteArray(last_response())) + + # check the tag + if response_tlv.getTAG() != 'E0': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + # manage the list of TLV with tag C0 into this response_tlv + key_info_list = response_tlv.list_childs_tlv() + for key_info in key_info_list: + + if key_info.getTAG() != 'C0': + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + else: + index = 0 + KeyIdentifier = key_info.getValue()[index: index + 2] + index = index + 2 + KeyVersionNumber = key_info.getValue()[index: index + 2] + index = index + 2 + #check key type : if coded with 2 bytes the key is in format 2 + KeyTypeIndex = key_info.getValue()[index: index + 2] + + if (KeyTypeIndex == 'FF'): + p_bool_Iskey_format2 = True + index = index + 2 + while (KeyTypeIndex == 'FF'): + KeyType = key_info.getValue()[index: index + 2] # 1 byte + KeyLength= key_info.getValue()[index + 2: index + 6] # 2 bytes + keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyTypeIndex + KeyType)) + KeyTypeIndex = key_info.getValue()[index + 6: index + 8] + index = index + 8 + #get the key usage and key access + keyUsageLength = KeyTypeIndex + if keyUsageLength != '00': + keyUsage =key_info.getValue()[index: index + 2] + index = index + 2 + + keyAccessLength = key_info.getValue()[index: index + 2] + if keyAccessLength != '00': + keyAccess = key_info.getValue()[index + 2: index + 4] + index = index + 4 + else: + index = index + 2 + + else: + while index < (len(key_info.getValue())): + KeyType = key_info.getValue()[index: index + 2] + index = index + 2 + KeyLength= key_info.getValue()[index: index + 2] + index = index + 2 + keyInformation.append((KeyIdentifier, KeyVersionNumber, KeyLength, KeyType)) + + else: + keyInformation = [] + + + log_end("get_key_information_template", error_status['errorStatus']) + + return error_status, keyInformation + + +def store_data(data): + + log_start("store_data") + block_size =239 # 255 bytes minus 8 byte MAC minus 8 byte encryption padding + block_number = 0x00 + + # supress blank if any + import re + data = ''.join( re.split( '\W+', data.upper() ) ) + # convert to byte array + bytelist_data = toByteArray(data) + remaining_bytes = len(bytelist_data) + read_bytes = 0x00 + while remaining_bytes > 0: + # build the APDU + capdu = "80 E2 " + if remaining_bytes <= block_size: + capdu = capdu + '80' + intToHexString(block_number) + capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + remaining_bytes]) ) + read_bytes = read_bytes + remaining_bytes + remaining_bytes = remaining_bytes - remaining_bytes + else: + capdu = capdu + '00' + intToHexString(block_number) + capdu = capdu + lv(toHexString(bytelist_data[read_bytes:read_bytes + block_size]) ) + read_bytes = read_bytes + block_size + remaining_bytes = remaining_bytes - block_size + + # send the APDU + error_status, rapdu = send_APDU(capdu) + block_number = block_number + 1 + + if error_status['errorStatus'] != 0x00: + log_end("store_data", error_status['errorStatus']) + return error_status + + + if error_status['errorStatus'] != 0x00: + log_end("get_data", error_status['errorStatus']) + return error_status + + log_end("store_data", error_status['errorStatus']) + + return error_status + + +def get_data(identifier): + + log_start("get_data") + + # build the APDU + # supress blank if any + import re + identifier = ''.join( re.split( '\W+', identifier.upper() ) ) + # check the size of the identifier (it is a string so 2 byte correspond to 4 characters ) + if len(identifier) < 0x00 or len(identifier) > 0x04: + # identifier must be 1 or two byte string + error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) + return error_status, None + + capdu = "80 CA " + + if len(identifier) == 0x04: + capdu = capdu + identifier + '00' + else: + #one byte identifier + capdu = capdu + identifier + '00' + '00' + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_data", error_status['errorStatus']) + return error_status, None + + log_end("get_data", error_status['errorStatus']) + + return error_status, rapdu + + +def get_status(card_element): + + log_start("get_status") + + # build the APDU + # supress blank if any + import re + card_element = ''.join( re.split( '\W+', card_element.upper() ) ) + + capdu = "80 F2 " + card_element + "02 02 4F 00" + "00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_status", error_status['errorStatus']) + return error_status, None + + if payload_mode_activated == False: + # store the response + card_response = last_response() + # check if more data available + while last_status() == '6310': + # send a get status next occurence + capdu = "80 F2 " + card_element + "03 02 4F 00" + "00" + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_status", error_status['errorStatus']) + return error_status, None + + card_response = card_response + last_response() + else: + card_response = None + error_status = create_no_error_status(0x00) + + + log_end("get_status", error_status['errorStatus']) + + return error_status, card_response + + + +def get_crs_status(aid, tag_list): + + log_start("get_crs_status") + + # build data field tag with given aid and tag list + if aid == '': data_field = "4F00" + else: data_field = "4F" + lv(aid) + + if tag_list == '': pass + else: data_field = data_field + "5C" + lv(tag_list) + + capdu = "80 F2 40 00 " + lv(data_field) + "00" + + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_crs_status", error_status['errorStatus']) + return error_status, None + + # store the response + card_response = last_response() + # check if more data available + while last_status() == '6310': + # send a get status next occurence + capdu = "80 F2 40 01 " + lv(data_field) + "00" + error_status, rapdu = send_APDU(capdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_crs_status", error_status['errorStatus']) + return error_status, None + + card_response = card_response + last_response() + + # we have the card_response TLV. create the get status response dictionnary + response_tlvs = TLVs(toByteArray(card_response)) + app_info_list = [] + app_aid = None + app_lifecycle = None + uniform_resource_locator = None + app_image_template = None + display_message = None + app_update_counter = None + selection_priority = None + app_group_head = None + app_group_members = None + crel_application_aid_list = None + policy_restricted_app = None + app_discretionary_data = None + application_family = None + display_required_indicator = None + assinged_protocol = None + continuous_processing = None + recognition_algorithm = None + + + for response_tlv in response_tlvs.list_childs_tlv(): + if response_tlv.getTAG() == '61': + # manage the list of TLV into this response_tlv + app_info_tlv_list = response_tlv.list_childs_tlv() + for app_info in app_info_tlv_list: + if app_info.getTAG() == '4F': app_aid = app_info.getValue() + elif app_info.getTAG() == '9F70': app_lifecycle = app_info.getValue() + elif app_info.getTAG() == '7F20': + display_control_tlv_list = app_info.list_childs_tlv() + for display_control_info in display_control_tlv_list: + if display_control_info.getTAG() == '5F50': + uniform_resource_locator = display_control_info.getValue() + elif display_control_info.getTAG() == '6D': + app_image_template = display_control_info.getValue() + elif display_control_info.getTAG() == '5F45': + display_message = display_control_info.getValue() + elif app_info.getTAG() == '80': app_update_counter = app_info.getValue() + elif app_info.getTAG() == '81': selection_priority = app_info.getValue() + elif app_info.getTAG() == 'A2': + app_group_head_tlv_list = app_info.list_childs_tlv() + for app_group_head in app_group_head_tlv_list: + if app_group_head.getTAG() == '4F': + app_group_head = app_group_head.getValue() + # below 3 parameters tag 'A3' to 'A5' can be multiple so need to find a better way to handle + elif app_info.getTAG() == 'A3': + app_group_members_tlv_list = app_info.list_childs_tlv() + for app_group_info in app_group_members_tlv_list: + if app_group_info.getTAG() == '4F': + app_group_members = app_group_info.getValue() + elif app_info.getTAG() == 'A4': + crel_app_aid_list_tlv_list = app_info.list_childs_tlv() + for crel_app_aid_info in crel_app_aid_list_tlv_list: + if crel_app_aid_info.getTAG() == '4F': + crel_app_aid_list = crel_app_aid_info.getValue() + elif app_info.getTAG() == 'A5': + policy_restricted_app_tlv_list = app_info.list_childs_tlv() + for policy_restricted_app_info in policy_restricted_app_tlv_list: + if policy_restricted_app_info.getTAG() == '4F': + policy_restricted_app = policy_restricted_app_info.getValue() + elif app_info.getTAG() == 'A6': app_discretionary_data = app_info.getValue() + elif app_info.getTAG() == '87': app_family = app_info.getValue() + elif app_info.getTAG() == '88': display_required_indicator = app_info.getValue() + elif app_info.getTAG() == '8C': assinged_protocol = app_info.getValue() + elif app_info.getTAG() == '8A': continuous_processing = app_info.getValue() + elif app_info.getTAG() == '8B': recognition_algorithm = app_info.getValue() + app_info_list.append({'aid':app_aid, 'lifecycle':app_lifecycle[:2], \ + 'app update counter':app_update_counter, 'selection priority':selection_priority, \ + 'app group head':app_group_head, 'app group members':app_group_members, \ + 'crel app list':crel_app_aid_list, 'policy restricted app':policy_restricted_app, \ + 'app discretionary data':app_discretionary_data, 'app family':app_family, \ + 'display required indicator':display_required_indicator, 'assinged protocol':assinged_protocol, \ + 'continuous processing':continuous_processing, 'recognition algorithm':recognition_algorithm}) + + else: + error_status = create_no_error_status(0x00) + + log_end("get_crs_status", error_status['errorStatus']) + + return error_status, app_info_list + + +def initialize_update(key_set_version , base_key, enc_key , mac_key , dek_key , scp, scp_implementation, sequence_counter = "000000" ): + + global last_apdu_response + global last_apdu_status + global payload_mode_activated + + log_start("initialize_update") + # create a host challenge + hostChallenge = crypto.RANDOM(8) + + log_debug("initialize_update: Host challenge Data: %s " % hostChallenge) + # create the initialize update APDU command (with Le = Max data) + initUpdateAPDU = '80' + '50' + key_set_version + '00 08' + hostChallenge + '00' + error_status, rapdu = send_APDU(initUpdateAPDU) + + if error_status['errorStatus'] != 0x00: + log_end("initialize_update", error_status['errorStatus']) + return error_status, None + + + # Set the security info structure needed for all GP operations + security_info = securityInfo[securityInfo[4]] + + + # checking payload mode + if payload_mode_activated == True: + # update the security information structure + security_info['secureChannelProtocol'] = int(scp, 16) + + if security_info['secureChannelProtocol'] == GP_SCP02: + # manage init update response + # in SCP02 the card challenge is calculated using the session key. So we need to create session key before. + sequenceCounter = toByteArray(sequence_counter) + # just check the sequence counter length. In SCP02 sequence counter is expressedwith 2 bytes + if len(sequenceCounter) != 2: + error_status = create_error_status(ERROR_INCONSISTENT_SEQUENCE_COUNTER, runtimeErrorDict[ERROR_INCONSISTENT_SEQUENCE_COUNTER]) + return error_status, None + + elif security_info['secureChannelProtocol'] == GP_SCP03: + # manage init update response + sequenceCounter = increment(sequence_counter, 0x01) + cardChallenge = calculate_card_challenge_SCP03(sequenceCounter + security_info['selectedAID'], enc_key) + # type coherency + cardChallenge = toByteArray(cardChallenge) + sequenceCounter= toByteArray(sequenceCounter) + log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) + log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + + security_info['secureChannelProtocolImpl'] = scp_implementation + security_info['keySetVersion'] = key_set_version + # we set it to a dummy value + security_info['keyIndex'] = 0xFF + + + + + + else: + + # managing authentication data + bytearray_initUpdateResponse = toByteArray(last_apdu_response) + # check init_update_response length, it must be 28, 29 or 32 bytes + # SCP01/SCP02 = 30 bytes, SCP03 31 or 34 bytes + if len (bytearray_initUpdateResponse) != 28 and len (bytearray_initUpdateResponse) != 29 and len (bytearray_initUpdateResponse) != 32: + error_status = create_error_status(ERROR_INVALID_RESPONSE_DATA, runtimeErrorDict[ERROR_INVALID_RESPONSE_DATA]) + return error_status, None + + # managing response of INITIALIZE UPDATE + keyDiversificationData = bytearray_initUpdateResponse[:10] + keyInformationData = bytearray_initUpdateResponse[10:12] + + # check if a scp has been set by the user, if not take the scp into the init update response + if scp == None: + scp = intToHexString(keyInformationData[1]) + + # test if reported SCP is consistent with passed SCP + if int(str(scp), 16) != keyInformationData[1]: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + # update the security information structure + security_info['secureChannelProtocol'] = int(str(scp), 16) + + + # in SCP03 the scp implementation value is returned by the init update response + # in SCP02 this value is not present so this value should be set by the user. + if security_info['secureChannelProtocol'] == GP_SCP02: + if scp_implementation == None: + scp_implementation = intToHexString(0x55) + # error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + # return error_status, None + + if security_info['secureChannelProtocol'] == GP_SCP03: + # key information data on 3 bytes + keyInformationData = bytearray_initUpdateResponse[10:13] + scpi = keyInformationData[2] + if scp_implementation == None: + scp_implementation = intToHexString(scpi) + else: + #test if reported SCP implementation is consistent with passed SCP implementation + if scp_implementation != intToHexString(scpi): + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + return error_status, None + + security_info['secureChannelProtocolImpl'] = scp_implementation + security_info['keySetVersion'] = keyInformationData[0] + # we set it to a dummy value + security_info['keyIndex'] = 0xFF + + if security_info['secureChannelProtocol'] == GP_SCP02: + # manage init update response + sequenceCounter = bytearray_initUpdateResponse[12:14] + cardChallenge= bytearray_initUpdateResponse[14:20] # 6 bytes + cardCryptogram = bytearray_initUpdateResponse[20:28] # 8 bytes + + if security_info['secureChannelProtocol'] == GP_SCP03: + # manage init update response + cardChallenge= bytearray_initUpdateResponse[13:21] # 8 bytes + cardCryptogram = bytearray_initUpdateResponse[21:29] # 8 bytes + sequenceCounter = bytearray_initUpdateResponse[29:32] # 3 bytes + + + log_debug("initialize_update: Key Diversification Data: %s " % toHexString(keyDiversificationData)) + + log_debug("initialize_update: Key Information Data: %s " % toHexString(keyInformationData)) + + log_debug("initialize_update: Card Challenge: %s " % toHexString(cardChallenge)) + + log_debug("initialize_update: Sequence Counter: %s " % toHexString(sequenceCounter)) + + log_debug("initialize_update: Card Cryptogram %s " % toHexString(cardCryptogram)) + + # create session key regarding the scp implementation + + if security_info['secureChannelProtocol'] == GP_SCP02: + ## Secure Channel base key + if (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i04 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i14 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i44 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i54): + + # calculation of encryption session key using on base key + security_info['encryptionSessionKey'] = create_session_key_SCP02(base_key, KENC_TYPE, toHexString(sequenceCounter)) + # calculation of C-MAC session key + security_info['C_MACSessionKey'] = create_session_key_SCP02(baseKey, KMAC_TYPE, toHexString(sequenceCounter)) + #calculation of R-MAC session key + security_info['R_MACSessionKey'] = create_session_key_SCP02(baseKey, KRMAC_TYPE, toHexString(sequenceCounter)) + #calculation of data encryption session key + security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(baseKey, KDEK_TYPE, toHexString(sequenceCounter)) + + ## 3 Secure Channel Keys */ + elif (security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i05 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i15 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i55 or + security_info['secureChannelProtocolImpl'] == SCP02_IMPL_i45): + + # calculation of encryption session key using on 3 static key + security_info['encryptionSessionKey'] = create_session_key_SCP02(enc_key, KENC_TYPE, toHexString(sequenceCounter)) + # calculation of C-MAC session key + security_info['C_MACSessionKey'] = create_session_key_SCP02(mac_key, KMAC_TYPE, toHexString(sequenceCounter)) + #calculation of R-MAC session key + security_info['R_MACSessionKey'] = create_session_key_SCP02(dek_key, KRMAC_TYPE, toHexString(sequenceCounter)) + #calculation of data encryption session key + security_info['dataEncryptionSessionKey'] = create_session_key_SCP02(dek_key, KDEK_TYPE, toHexString(sequenceCounter)) + + if payload_mode_activated == True: + cardChallenge = self.calculate_card_challenge_SCP02(security_info['selectedAID'], security_info['C_MACSessionKey']) + # type coherency + cardChallenge = int(cardChallenge, 16) + + else: + error_status = create_error_status(ERROR_INVALID_SCP_IMPL, runtimeErrorDict[ERROR_INVALID_SCP_IMPL]) + return error_status, None + + elif security_info['secureChannelProtocol'] == GP_SCP03: + + # calculation of encryption session key using on 3 static key + session_key_value = create_session_key_SCP03(enc_key, KENC_TYPE, toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['encryptionSessionKey'] = session_key_value + + # calculation of C-MAC session key + session_key_value = create_session_key_SCP03(mac_key, KMAC_TYPE , toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['C_MACSessionKey'] = session_key_value + #calculation of R-MAC session key + session_key_value = create_session_key_SCP03(mac_key, KRMAC_TYPE, toHexString(cardChallenge), hostChallenge) + if session_key_value == None: + error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + return error_status, None + else: + security_info['R_MACSessionKey'] = session_key_value + # calculation of data encryption session key + # warning: no session key for data encryption in SCP03 + # session_key_value = create_session_key_SCP03(dek_key, KDEK_TYPE , toHexString(cardChallenge), hostChallenge) + # if session_key_value == None: + # error_status = create_error_status(ERROR_SESSION_KEY_CREATION, runtimeErrorDict[ERROR_SESSION_KEY_CREATION]) + # return error_status, None + # else: + security_info['dataEncryptionSessionKey'] = dek_key + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + log_debug("initialize_update: S-ENC Session Key: %s" %security_info['encryptionSessionKey']) + log_debug("initialize_update: S-MAC Session Key: %s" %security_info['C_MACSessionKey']) + log_debug("initialize_update: DEK Session Key: %s" %security_info['dataEncryptionSessionKey']) + log_debug("initialize_update: R-MAC Session Key: %s" %security_info['R_MACSessionKey']) + + if security_info['secureChannelProtocol'] == GP_SCP02: + offcardCryptogram = calculate_card_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) + elif security_info['secureChannelProtocol'] == GP_SCP03: + offcardCryptogram = calculate_card_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + if payload_mode_activated == False: + # compare cryptograms + if toHexString(cardCryptogram) != offcardCryptogram: + error_status = create_error_status(ERROR_CARD_CRYPTOGRAM_VERIFICATION, runtimeErrorDict[ERROR_CARD_CRYPTOGRAM_VERIFICATION]) + return error_status , None + + + host_cryptogram = None + if security_info['secureChannelProtocol'] == GP_SCP02: + host_cryptogram = calculate_host_cryptogram_SCP02(toHexString(sequenceCounter), toHexString(cardChallenge), hostChallenge, security_info['encryptionSessionKey']) + + elif security_info['secureChannelProtocol'] == GP_SCP03: + host_cryptogram = calculate_host_cryptogram_SCP03(toHexString(cardChallenge), hostChallenge, security_info['C_MACSessionKey']) + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status, None + + + error_status = create_no_error_status(0x00) + log_end("initialize_update", error_status['errorStatus']) + return error_status, host_cryptogram + +def internal_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): + + log_start("internal_authenticate") + + + + # build the APDU + data = "5F49" + lv(ePK_OCE) + data_field = crt_data + data + + internal_authenticate_apdu = '80 88' + key_version_number + key_identifier + lv(data_field) + + error_status, rapdu = send_APDU(internal_authenticate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("internal_authenticate", error_status['errorStatus']) + return error_status, None + + + log_end("internal_authenticate", error_status['errorStatus']) + return error_status, rapdu + + +def mutual_authenticate(key_version_number, key_identifier , crt_data , ePK_OCE ): + + log_start("mutual_authenticate") + + + # build the APDU + data = "5F49" + lv(ePK_OCE) + data_field = crt_data + data + + mutual_authenticate_apdu = '80 82' + key_version_number + key_identifier + lv(data_field) + + error_status, rapdu = send_APDU(mutual_authenticate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("mutual_authenticate", error_status['errorStatus']) + return error_status, None + + + log_end("mutual_authenticate", error_status['errorStatus']) + return error_status, rapdu + +def external_authenticate(security_level, host_cryptogram): + + log_start("external_authenticate") + + # get security_info dictionary of working channel + security_info = securityInfo[securityInfo[4]] + # create the external authenticate APDU command + externalAuthAPDU = '84' + '82' + intToHexString(security_level) + '00' + '10' + host_cryptogram + + if security_info['secureChannelProtocol'] == GP_SCP03: + mac = calculate_mac_SCP03(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_16) + security_info['lastC_MAC'] = mac + security_info['icv_counter'] = 0x01 + # add the mac to the command + externalAuthAPDU = externalAuthAPDU + mac[:16] + + elif security_info['secureChannelProtocol'] == GP_SCP02: + + mac = calculate_mac_SCP02(externalAuthAPDU, security_info['C_MACSessionKey'], crypto.ICV_NULL_8) + security_info['lastC_MAC'] = mac + # add the mac to the command + externalAuthAPDU = externalAuthAPDU + mac + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status + + error_status, rapdu = send_APDU(externalAuthAPDU) + + if error_status['errorStatus'] != 0x00: + log_end("external_authenticate", error_status['errorStatus']) + return error_status + + + #update security info + security_info['securityLevel'] = security_level + + log_end("external_authenticate", error_status['errorStatus']) + return error_status + +def get_certificate(key_version_number, key_identifier): + log_start("get_certificate") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("get_certificate", error_status['errorStatus']) + return error_status + + # build the APDU + + + get_certificate_apdu = '80 CA BF 21' + lv ('A6' + lv ( '83' + lv (key_version_number + key_identifier))) + + error_status, rapdu = send_APDU(get_certificate_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("get_certificate", error_status['errorStatus']) + return error_status, None + + + log_end("get_certificate", error_status['errorStatus']) + return error_status, rapdu + + + +def perform_security_operation(key_version_number, key_identifier , crt_data ): + + log_start("perform_security_operation") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + + # build the APDU + + + perform_security_operation_apdu = '80 2A' + key_version_number + key_identifier + lv(crt_data) + + error_status, rapdu = send_APDU(perform_security_operation_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + + + log_end("perform_security_operation", error_status['errorStatus']) + return error_status + +def registry_update(security_domain_AID, application_aid, application_privileges = "00", registry_parameter_field = None, install_token = None): + log_start("registry_update") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("registry_update", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + registry_update_apdu_data = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + lv(application_privileges) + # optionnal fields + parameter_field = '' + if registry_parameter_field != None: + parameter_field = parameter_field + 'EF' + lv(remove_space(registry_parameter_field)) + else: + parameter_field =parameter_field + 'EF00' + + if install_token != None: + install_token = lv(install_token) + else: + install_token = '00' #no token + + registry_update_apdu = '80 E6 40 00' + lv(registry_update_apdu_data + lv(parameter_field) + install_token) + + error_status, rapdu = send_APDU(registry_update_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("registry_update", error_status['errorStatus']) + return error_status + + + log_end("registry_update", error_status['errorStatus']) + return error_status + + +def install_install(make_selectable, executable_LoadFile_AID, executable_Module_AID, application_AID, application_privileges = "00", application_specific_parameters = None, install_parameters = None, install_token = None): + log_start("install_install") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("install_install", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + install_apdu_data = lv(remove_space(executable_LoadFile_AID)) + lv(remove_space(executable_Module_AID)) + lv(remove_space(application_AID)) + lv(application_privileges) + # optionnal fields + parameter_field = '' + if application_specific_parameters != None: + parameter_field = parameter_field + 'C9' + lv(remove_space(application_specific_parameters)) + else: + parameter_field =parameter_field + 'C903000000' + + if install_parameters != None: + parameter_field = parameter_field + 'EF' + lv(remove_space(install_parameters)) + else: + parameter_field =parameter_field + 'EF00' + + if install_token != None: + install_token = lv(install_token) + else: + install_token = '00' #no token + + + if make_selectable == True: + install_apdu = '80 E6 0C 00' + lv(install_apdu_data + lv(parameter_field) + install_token) + else: + install_apdu = '80 E6 04 00' + lv(install_apdu_data + lv(parameter_field) + install_token) + + error_status, rapdu = send_APDU(install_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("install_install", error_status['errorStatus']) + return error_status + + + log_end("install_install", error_status['errorStatus']) + return error_status + + +def install_load(executable_load_file_aid, security_domain_aid, load_file_data_block_hash = None, load_parameters = None, load_token = None): + log_start("install_load") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("install_load", error_status['errorStatus']) + return error_status + + # build the APDU + # mandatory fields + install_apdu = lv(remove_space(executable_load_file_aid)) + lv(remove_space(security_domain_aid)) + # optionnal fields + if load_file_data_block_hash != None: + install_apdu = install_apdu + lv(remove_space(load_file_data_block_hash)) + else: + install_apdu = install_apdu + '00' + + if load_parameters != None: + install_apdu = install_apdu + lv('EF' + lv(remove_space(load_parameters))) + else: + install_apdu = install_apdu + '00' #no parameter + + if load_token != None: + install_apdu = install_apdu + lv(load_token) + else: + install_apdu = install_apdu + '00' #no token + + install_apdu = '80 E6 02 00' + lv(install_apdu) + + error_status, rapdu = send_APDU(install_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("install_load", error_status['errorStatus']) + return error_status + + + log_end("install_load", error_status['errorStatus']) + return error_status + + +def load_blocks(load_file_path, block_size = 32): + log_start("load_blocks") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("load_blocks", error_status['errorStatus']) + return error_status + + block_number = 0x00 + + load_file_obj = loadfile.Loadfile(load_file_path) + + log_debug("load_blocks: load_file_obj: %s" %load_file_obj.__str__()) + + all_blocks_data = load_file_obj.get_load_blocks(block_size) + + for block in all_blocks_data: + blockNumber = intToHexString(block_number) + is_last_block = (block == all_blocks_data[-1]) + + if is_last_block == True: + load_apdu = '80' + 'E8' + '80' + blockNumber + lv(block) + else: + load_apdu = '80' + 'E8' + '00'+ blockNumber + lv(block) + + error_status, rapdu = send_APDU(load_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + block_number = block_number + 1 + if block_number > 255: + block_number = 0 + + log_end("load_blocks", error_status['errorStatus']) + return error_status + + +def extradite(security_domain_AID, application_aid, identification_number = None, image_Number = None, application_provider_identifier = None, token_identifier = None, extraditeToken = None): + log_start("extradite") + + error_status = __check_security_info__(securityInfo[securityInfo[4]]) + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + + strControlReferenceTemplate = '' + str_ExtraditionParametersfield = '' + + if (identification_number != None): + strControlReferenceTemplate += "42" + lv(remove_space(identification_number)) + + if (image_Number != None): + strControlReferenceTemplate += "45" + lv(remove_space(image_Number)) + + if (application_provider_identifier != None): + strControlReferenceTemplate += "5F20" + lv(remove_space(application_provider_identifier)) + + if (strControlReferenceTemplate != ''): + str_ExtraditionParametersfield = str_ExtraditionParametersfield + "B6" + str_ExtraditionParametersfield = str_ExtraditionParametersfield + lv(strControlReferenceTemplate) + + # build the APDU + extradite_apdu = lv(remove_space(security_domain_AID)) + '00' + lv(remove_space(application_aid)) + '00' + lv(str_ExtraditionParametersfield) + + if extraditeToken != None: + extradite_apdu = extradite_apdu + lv(extraditeToken) + else: + extradite_apdu = extradite_apdu + '00' #no token + + extradite_apdu = '80 E6 10 00' + lv(extradite_apdu) + + error_status, rapdu = send_APDU(extradite_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("extradite", error_status['errorStatus']) + return error_status + + + log_end("extradite", error_status['errorStatus']) + return error_status + + +def put_key(key_version_number, key_identifier, key_type, key_value, replace = False ): + + log_start("put_key") + + security_info = securityInfo[securityInfo[4]] + + error_status = __check_security_info__(security_info) + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + # build the extradition parameter fields + + if replace == False: + p1 = '00' + else: + p1 = key_version_number + + # cipher key regarding the SCP protocol + if security_info['secureChannelProtocol'] == GP_SCP03: + cipher_key, cipher_key_kcv = cipher_key_SCP03(key_value, security_info['dataEncryptionSessionKey'] ) + cipher_key_len = intToHexString(int(len(cipher_key)/2)) + put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv( cipher_key_len + cipher_key) + lv(cipher_key_kcv)) + + + elif security_info['secureChannelProtocol'] == GP_SCP02: + + cipher_key, cipher_key_kcv = cipher_key_SCP02(key_value, security_info['dataEncryptionSessionKey'] ) + put_key_apdu = '80 D8' + p1 + '01' + lv(key_version_number + key_type_coding_dict[key_type] + lv(cipher_key) + lv(cipher_key_kcv)) + + + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + return error_status + + + error_status, rapdu = send_APDU(put_key_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + + log_end("put_key", error_status['errorStatus']) + return error_status + + +def put_scp_key(key_version_number, key_list, replace = False ): + + log_start("put_scp_key") + + security_info = securityInfo[securityInfo[4]] + error_status = __check_security_info__(security_info) + if error_status['errorStatus'] != 0x00: + log_end("put_key", error_status['errorStatus']) + return error_status + + # build the extradition parameter fields + + if replace == False: + p1 = '00' + else: + p1 = key_version_number + + scp = security_info['secureChannelProtocol'] + apdu_data = key_version_number + # cipher key regarding the SCP protocol + for ( key_vn, key_id, key_type, key_value ) in key_list: + # calculate key check value (refer key_type of key_list) + if (key_type == 'DES') or (key_type == 'AES'): + kcv = compute_key_check_value(key_type, key_value) + else: + error_status = create_error_status(ERROR_PUTKEY_INVALID_KEY_TYPE, runtimeErrorDict[ERROR_PUTKEY_INVALID_KEY_TYPE]) + break + + # encrypt the key (refer the key type of security_info['dataEncryptionSessionKey']) + if (scp == GP_SCP03) or (scp == GP_SCP02): + encryptedkey = cipher_key(key_value, security_info['dataEncryptionSessionKey'], scp) + else: + error_status = create_error_status(ERROR_INCONSISTENT_SCP, runtimeErrorDict[ERROR_INCONSISTENT_SCP]) + break + + # compose APDU + apdu_data = apdu_data + key_type_coding_dict[key_type] + lv( getLength(encryptedkey) + encryptedkey) + lv(kcv) + + if error_status['errorStatus'] != 0x00: + log_end("put_scp_key", error_status['errorStatus']) + return error_status + + put_scp_key_apdu = '80 D8' + p1 + '81' + lv(apdu_data) + error_status, rapdu = send_APDU(put_scp_key_apdu) + + if error_status['errorStatus'] != 0x00: + log_end("put_scp_key", error_status['errorStatus']) + return error_status + + log_end("put_scp_key", error_status['errorStatus']) + return error_status + +def manage_channel(open_channel, logical_channel): + global securityInfo + + log_start("manage_channel") + + if open_channel == True: + capdu = '00 70 00 00 01' + else: + if logical_channel < 0x01 and logical_channel > 0x03: + # channel number must be between 01 and 03 + error_status = create_error_status(ERROR_WRONG_DATA, runtimeErrorDict[ERROR_WRONG_DATA]) + return error_status + capdu = '00 70 80 ' + intToHexString(logical_channel) + '00' + + error_status, rapdu = send_APDU(capdu, raw_mode = True) + + if error_status['errorStatus'] != 0x00: + log_end("manage_channel", error_status['errorStatus']) + return error_status, None + + if open_channel == True: + byte_list_data = toByteArray(rapdu) + channel_num = byte_list_data[0] + sChannelInfo = securityInfo[channel_num] + sChannelInfo['channelStatus'] = "ON" + else: + # Close working channel then set another chanenl as working channel + if securityInfo[4] == logical_channel: + securityInfo[4] = 0 + securityInfo[logical_channel] = {} + + log_end("manage_channel", error_status['errorStatus']) + + return error_status, rapdu From 51a7fe8a7ff0b22d16d3fcd24fd194a7122817f9 Mon Sep 17 00:00:00 2001 From: LEE Jaekuk Date: Fri, 8 Sep 2017 17:49:59 +0900 Subject: [PATCH 7/7] Remove temporary script. --- pygp/gp/gp_functions.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pygp/gp/gp_functions.py b/pygp/gp/gp_functions.py index f7cdcee..71f36a4 100644 --- a/pygp/gp/gp_functions.py +++ b/pygp/gp/gp_functions.py @@ -29,16 +29,6 @@ def clear_securityInfo(): global securityInfo securityInfo = [{'channelStatus':'ON'}, {}, {}, {}, 0] -def get_securityInfo(channel = None): - global securityInfo - if channel == None: - return securityInfo[securityInfo[4]] - else: - if channel >= 0 and channel <=3: - return securityInfo[channel] - else: - pass - def get_payload_list(): global payload_list return payload_list