def check_fci_9102(buffer): tlvs = utils.parse_tlv(buffer) sub_tlvs = [] sub_fci_9102 = [] for tlv in tlvs: for fci_item in fci_9102: if tlv.tag == fci_item[0]: sub_fci_9102.append(fci_item) sub_tlvs.append(tlv) # 判断集合中的顺序是否一致 has_ordered = True for index in range(len(sub_fci_9102)): if sub_fci_9102[index][0] != sub_tlvs[index].tag or sub_fci_9102[index][1] != sub_tlvs[index].level: Log.warn('tag%s is not ordered.',sub_tlvs[index].tag) has_ordered = False if not has_ordered: order_tags = [item[0] for item in sub_fci_9102] Log.warn('the minium order list should be %s',str(order_tags)) # 判断是否缺少必须的tag for item in fci_9102: if item not in sub_fci_9102: if item[2] == TagCondition.M: Log.error('FCI 9102 should contains tag%s',item[0]) elif item[2] == TagCondition.C: Log.warn('FCI 9102 recommand that tag%s should be existed.',item[0]) # 判断是否包含多余的tag for item in tlvs: if item not in sub_tlvs: Log.warn('FCI 9102 should not contains tag%s',item.tag)
def create_check_aid_case(self, check_aid_list): case_node = self.smart_qc.create_case_node('AID检测-非接', 'SSQCaseEx3.dll') # <DeviceList> device_list_node = self.smart_qc.create_node(case_node, 'DeviceList') self.smart_qc.create_device_node(device_list_node, 'PC Twin2') # <SpecialInput> special_input_node = self.smart_qc.create_node(case_node, 'SpecialInput') # <CardStruct> card_struct_node = self.smart_qc.create_node(special_input_node, 'CardSturct') # <MF> self.smart_qc.create_node(card_struct_node, 'MF', fid='3F00') # <ApduList> app_nodes = self.dp_xml_handle.get_child_nodes( self.dp_xml_handle.root_element, 'App') cur_aids = [] for app_node in app_nodes: aid = self.dp_xml_handle.get_attribute(app_node, 'aid') if not aid: Log.error( 'can not get aid from dp xml, check wether the dp xml is correct.' ) return cur_aids.append(aid) need_check_aids = [] for aid in check_aid_list: if aid not in cur_aids: need_check_aids.append(aid) apdu_list_node = self.smart_qc.create_node(card_struct_node, 'ApduList') for aid in need_check_aids: apdu = '00A40400' + utils.get_strlen(aid) + aid self.smart_qc.create_node(apdu_list_node, 'Apdu ', value=apdu, sw12="6A82") test_process_list_node = self.smart_qc.create_node( case_node, 'TestProcessList') self.smart_qc.create_node(test_process_list_node, 'Card_sturct_test', enable='false') self.smart_qc.create_node(test_process_list_node, 'Compare_IC_info', enable='false') self.smart_qc.create_node(test_process_list_node, 'Special_file_test', enable='false') self.smart_qc.create_node(test_process_list_node, 'Transaction_test', enable='false') self.smart_qc.create_node(test_process_list_node, 'Check_atr', enable='false') self.smart_qc.create_node(test_process_list_node, 'ApduList_test', enable='true')
def check_startswith_70(apdu_resps): ''' 检测读记录响应数据是否以70模板开头 ''' for resp in apdu_resps: if helper.case_startswith('70', resp.response) != CR.OK: Log.error('read record response not start with template 70') return CR.ERROR return CR.OK
def _parse_set_status_cmd(self, req, resp): if req[0:6] == '80F080': if req[6:8] == '07': Log.info('current card lifecycle: OP_INITIALIZE') elif req[6:8] == '0F': Log.info('current card lifecycle: SECURED') else: Log.error('Unknown card lifecycle') if resp != '9000': Log.error('set current lifecyle Failed.')
def check_empty_tag(trans_obj): ''' 检测读记录之前的数据是否有空值的现象 ''' has_empty_tag = False for tag_info in trans_obj.tags_info: if not tag_info.value: Log.error('tag%s is empty', tag_info.tag) has_empty_tag = True if has_empty_tag: return CR.ERROR return CR.OK
def _filter(self, data): if data and data.startswith('APDU'): start_index = data.find('[') end_index = data.find(']', start_index) resp_start_index = data.find('[', end_index) resp_end_index = data.find(']', resp_start_index) req = data[start_index + 1:end_index] #取[]中间的APDU指令 resp = data[resp_start_index + 1:resp_end_index] if req[0:4] in ('00A4', '80E2', '80E6', '80D8', '80F0'): if not req.endswith('00'): Log.error('data error: %s not end with 00' % data) return req[0:-2], resp return '', ''
def _comapre_dgi_list(self, mock_cps, prod_cps): Log.info('===================== Check DGI List =====================') mock_cps_dgis = [item.name for item in mock_cps.dgi_list] prod_cps_dgis = [item.name for item in prod_cps.dgi_list] for mock_cps_dgi_name in mock_cps_dgis: if mock_cps_dgi_name not in prod_cps_dgis: Log.error('should perso DGI %s in product environment', mock_cps_dgi_name) for prod_cps_dgi_name in prod_cps_dgis: if prod_cps_dgi_name not in mock_cps_dgis: Log.error('should NOT perso DGI %s in product environment', prod_cps_dgi_name) Log.info("\nmock dgi list:%s\n", mock_cps_dgis) Log.info('prod dgi list:%s\n', str(prod_cps_dgis))
def _parse_install_cmd(self, data, resp): if data.startswith('80E60C00'): data = data[10:] # 去掉命令头及长度部分 data, pkg = self._get_install_param(data) data, applet = self._get_install_param(data) data, inst = self._get_install_param(data) data, priviliage = self._get_install_param(data) token, param = self._get_install_param(data) if resp != '6101': Log.error('Install Instance:%s Failed.', inst) else: Log.info('Install Instance:%s', inst) Log.info('Package:%s', pkg) Log.info('Applet:%s', applet) Log.info('Priviliage:%s', priviliage) Log.info('Install param:%s', param) Log.info('Token:%s\n', token)
def read_record(self,tag94): ''' 读记录数据 ''' resps = [] afls = utils.parse_afl(tag94) for afl in afls: Log.info('read record: %02X%02X',afl.sfi,afl.record_no) resp = apdu.read_record(afl.sfi,afl.record_no) if resp.sw != 0x9000: Log.error('read record wrong') return resps tools.output_apdu_info(resp) resps.append(resp) if afl.is_static_sign_data: self.sig_data += utils.remove_template70(resp.response) return resps
def split_rsa(xml, goldpac_dgi_list, is_second_app): rule_file_handle = RuleXml(xml.file_name) _, key = rule_file_handle.get_decrypted_attribute('RSA') sddf_tag = '' _, sddf_tag, _ = rule_file_handle.get_tag_link_attribute( 'EMVDataName', 'Icc_KeyPair') if is_second_app: sddf_tag = sddf_tag[0:4] + get_second_app_index() + sddf_tag[5:8] else: sddf_tag = sddf_tag[0:4] + get_first_app_index() + sddf_tag[5:8] encrypted_data = get_goldpac_data(goldpac_dgi_list, sddf_tag, is_second_app) if encrypted_data is None: Log.error('无法获取RSA数据[tag' + sddf_tag + ']缺少数据') decrypted_data = algorithm.des3_ecb_decrypt(key, encrypted_data) if len(decrypted_data) <= 2 or decrypted_data[0:2] != '30': Log.error('RSA解密失败') return None decrypted_data = decrypted_data[2:] _, decrypted_data = get_rsa_dgi_len(decrypted_data) dgi_list = [] for i in range(9): decrypted_data = decrypted_data[2:] #remove flag '02' dgi_len, decrypted_data = get_rsa_dgi_len(decrypted_data) dgi_data = get_rsa_dgi_value(decrypted_data, dgi_len) decrypted_data = decrypted_data[dgi_len:] dgi = Dgi() if is_second_app: dgi.name = '_2' if i == 4: dgi.name = '8205' + dgi.name elif i == 5: dgi.name = '8204' + dgi.name elif i == 6: dgi.name = '8203' + dgi.name elif i == 7: dgi.name = '8202' + dgi.name elif i == 8: dgi.name = '8201' + dgi.name else: continue dgi.add_tag_value(dgi.name[0:4], dgi_data) dgi_list.append(dgi) Log.info(dgi.name[0:4] + '=' + dgi_data) return dgi_list
def check_duplicate_tag(trans_obj): ''' 检测GPO和读记录数据中是否有重复tag出现 ''' tags = [] has_duplicate = False for tag_info in trans_obj.tags_info: if tag_info.step in (PROCESS_STEP.GPO, PROCESS_STEP.READ_RECORD): tags.append(tag_info.tag) tag_counts = len(tags) for current in range(tag_counts): for index in range(current + 1, tag_counts): if tags[current] == tags[index]: Log.error('tag%s has duplicated', tags[current]) has_duplicate = True if has_duplicate: return CR.ERROR return CR.OK
def check_tag_len(trans_obj): has_error = False for tag_info in trans_obj.tags_info: tag_len = len(tag_info.value) // 2 len_type, lens = get_tag_len(tag_info.tag, trans_obj.aid) if len_type == LenType.Range: if tag_len < lens[0] or tag_len > lens[1]: Log.error( 'tag%s length is not correct, should be in range%d-%d. current len: %d', tag_info.tag, lens[0], lens[1], tag_len) has_error = True elif len_type == LenType.Fixed: if tag_len not in lens: Log.error( 'tag%s length is not correct, should be any of %s. current len: %d', tag_info.tag, str(lens), tag_len) has_error = True if has_error: return CR.ERROR return CR.OK
def first_gac_cda(self): tag8C = self.get_tag(PROCESS_STEP.READ_RECORD, '8C') if not tag8C: Log.error('first gac cda faild since tag8C is tempty') return False tls = utils.parse_tl(tag8C) data = '' self.unpredicatble_number = '' for tl in tls: data += terminal.get_terminal(tl.tag, tl.len) if tl.tag == '9F37': self.unpredicatble_number = terminal.get_terminal( tl.tag, tl.len) resp = super().gac(Crypto_Type.TC_CDA, data) if resp.sw != 0x9000: Log.error('send first gac command failed. SW:%04X', resp.sw) return tlvs = utils.parse_tlv(resp.response) tools.output_apdu_info(resp) self.store_tag_group(PROCESS_STEP.FIRST_GAC, tlvs)
def get_pse_and_ppse_dgi_list(sddf_dgi_list): global _aid_list_info pse_dgi_list = [] ppse_dgi_list = [] pse_index, _ = _aid_list_info.get( '315041592E5359532E4444463031', ('F', '')) #_aid_list_info['315041592E5359532E4444463031'] ppse_index, _ = _aid_list_info.get( '325041592E5359532E4444463031', ('F', '')) #_aid_list_info['325041592E5359532E4444463031'] if pse_index == 'F': Log.error('无法获取PSE DGI相关列表') if ppse_index == 'F': Log.error('无法获取PPSE DGI相关列表') for dgi in sddf_dgi_list: if dgi[4] == pse_index: pse_dgi_list.append(dgi) elif dgi[4] == ppse_index: ppse_dgi_list.append(dgi) return pse_dgi_list, ppse_dgi_list
def _parse_tlv(dgi_name, data): dgi = Dgi() data = utils.remove_dgi(data, dgi_name) int_dgi = utils.str_to_int(dgi_name) if int_dgi < 0x0B01: if data[0:2] != '70': Log.error('数据有误,小于0B01的DGI应包含70模板') return None data = utils.remove_template70(data) if not utils.is_rsa(dgi_name) and utils.is_tlv(data): tlvs = utils.parse_tlv(data) if len(tlvs) > 0 and tlvs[0].is_template is True: value = utils.assemble_tlv(tlvs[0].tag, tlvs[0].value) dgi.add_tag_value(dgi_name, value) else: for tlv in tlvs: value = utils.assemble_tlv(tlv.tag, tlv.value) dgi.add_tag_value(tlv.tag, value) else: dgi.add_tag_value(dgi_name, data) return dgi
def issuer_auth(self): tag9F26 = self.get_tag(PROCESS_STEP.FIRST_GAC, '9F26') if not self.cvn: Log.error('can not get CVN, check tag9F10 wether existed.') return if self.cvn == '0A': # CVN10处理流程 arc = '3030' key = self.key_ac if self.key_flag == App_Master_Key.MDK: tag5A = self.get_tag(PROCESS_STEP.READ_RECORD, '5A') tag5F34 = self.get_tag(PROCESS_STEP.READ_RECORD, '5F34') key = auth.gen_udk(key, tag5A, tag5F34) arpc = auth.gen_arpc_by_des3(key, tag9F26, arc) resp = apdu.external_auth(arpc, arc) if resp.sw == 0x9000: return True elif self.cvn == '12': # CVN18处理流程 csu = '00820000' self.divert_key() #需要使用AC session key arpc = auth.gen_arpc_by_mac(self.session_key_ac, tag9F26, csu) terminal.set_terminal('91', arpc + csu) return True return False
def gen_9F4B(self,ddol): ''' 发送内部认证命令,卡片生成tag9F4B动态签名数据 ''' resp = apdu.internal_auth(ddol) if resp.sw != 0x9000: Log.error('SW: %0X',resp.sw) Log.error('send internal authentication apdu command failed whereby dda/cda failed') return '' tlvs = utils.parse_tlv(resp.response) if not tlvs: Log.error('response: %s',resp.response) Log.error('internal authentication response data can not be breaked up to tlv format') return '' # 返回的APDU可能是模板77或者80 if resp.response.startswith('77'): if len(tlvs) != 2: Log.error('response: %s',resp.response) Log.error('internal authentication response data is not correct') return '' return tlvs[1].value elif resp.response.startswith('80'): return tlvs[0].value else: Log.error('response: %s',resp.response) Log.error('internal authentication response data is not startwith 70/80') return ''
def do_cda(self): tag8C = self.get_tag(PROCESS_STEP.READ_RECORD,'8C') tag9F47 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F47') tag9F4B = self.get_tag(PROCESS_STEP.FIRST_GAC,'9F4B') if not tag8C: Log.error('tag8C: %s',tag8C) Log.error('require tag8C failed whereby dda failed') return False if not tag9F47: Log.error('tag9F47: %s',tag9F47) Log.error('require tag9F47 failed whereby dda failed') return False if not tag9F4B: Log.error('tag9F4B: %s',tag9F4B) Log.error('require tag9F4B failed whereby dda failed') return False issuer_pub_key = self._get_issuer_pub_key() if not issuer_pub_key: Log.error('get issuer public key failed where by cda failed.') return False icc_pub_key = self._get_icc_pub_key(issuer_pub_key) if not icc_pub_key: Log.error('get icc public key failed where by cda failed.') return False sig_data = self.unpredicatble_number if not auth.validate_9F4B(icc_pub_key,tag9F47,sig_data,tag9F4B): Log.error('icc public key: %s',icc_pub_key) Log.error('tag9F47: %s',tag9F47) Log.error('tag9F4B: %s',tag9F4B) Log.error('validate tag9F4B failed whereby cda failed') return False Log.info('cda authentication sucess.') return True
def do_dda(self,fDDA=False): tag9F47 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F47') if not tag9F47: Log.error('tag9F47: %s',tag9F47) Log.error('require tag9F47 failed whereby dda failed') return False tag9F4B = '' ddol = '' if fDDA: tag9F4B = self.get_tag(PROCESS_STEP.GPO,'9F4B') tag9F69 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F69') if not tag9F69: Log.error('tag9F69: %s',tag9F69) Log.error('require tag9F69 failed whereby dda failed') return False # 这里无需再判断终端数据是否存在,在GPO阶段已经验证过 tag9F37 = terminal.get_terminal('9F37') tag9F02 = terminal.get_terminal('9F02') tag5F2A = terminal.get_terminal('5F2A') # 这里默认使用ddol代替fDDA的签名数据 # 签名使用的tag9F36自动包含在了tag9F4B恢复数据中,这里无需重复包含 ddol = tag9F37 + tag9F02 + tag5F2A + tag9F69 else: tag9F49 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F49') if not tag9F49: Log.error('tag9F49: %s',tag9F49) Log.error('require tag9F49 failed whereby dda failed') return False ddol = tools.assemble_dol(tag9F49) if not ddol: Log.error('tag9F49: %s',tag9F49) Log.error('can not get terminal ddol data whereby dda failed') return False tag9F4B = self.gen_9F4B(ddol) if not tag9F4B: Log.error('can not get tag9F4B data whereby dda failed') return False issuer_pub_key = self._get_issuer_pub_key() if not issuer_pub_key: Log.error('get issuer public key failed where by dda failed.') return False icc_pub_key = self._get_icc_pub_key(issuer_pub_key) if not icc_pub_key: Log.error('get icc public key failed where by dda failed.') return False if not auth.validate_9F4B(icc_pub_key,tag9F47,ddol,tag9F4B): Log.error('icc public key: %s',icc_pub_key) Log.error('tag9F47: %s',tag9F47) Log.error('sig data: %s',ddol) Log.error('tag9F4B: %s',tag9F4B) Log.error('validate tag9F4B failed whereby dda failed') return False Log.info('dda authentication sucess.') return True
def _get_icc_pub_key(self,issuer_pub_key): icc_pub_key = '' tag9F32 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F32') tag9F46 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F46') tag9F48 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F48') tag9F47 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F47') tag5A = self.get_tag(PROCESS_STEP.READ_RECORD,'5A') tag5F24 = self.get_tag(PROCESS_STEP.READ_RECORD,'5F24') if not tag9F46: Log.error('tag9F46: %s',tag9F46) Log.error('require tag9F46 failed whereby dda failed') return icc_pub_key if not tag9F47: Log.error('tag9F47: %s',tag9F47) Log.error('require tag9F47 failed whereby dda failed') return icc_pub_key if not tag5A: Log.error('tag5A: %s',tag5A) Log.error('require tag5A failed whereby dda failed') return icc_pub_key if not tag5F24: Log.error('tag5F24: %s',tag5F24) Log.error('require tag5F24 failed whereby dda failed') return icc_pub_key if not tag9F32: Log.error('tag9F32: %s',tag9F32) Log.info('require tag9F32 failed whereby dda failed') return issuer_pub_key tag9F4A = self.get_tag(PROCESS_STEP.READ_RECORD,'9F4A') if tag9F4A: tag82 = self.get_tag(PROCESS_STEP.GPO,'82') if not tag82: Log.error('require tag82 failed whereby dda failed') return icc_pub_key self.sig_data += tag82 icc_pub_key = auth.get_icc_pub_key(issuer_pub_key,tag9F32,tag9F46,tag9F48,tag9F47,self.sig_data,tag5A,tag5F24) if not icc_pub_key: Log.error('issuer public key: %s',issuer_pub_key) Log.error('tag9F32: %s',tag9F32) Log.error('tag9F46: %s',tag9F46) Log.error('tag9F47: %s',tag9F47) Log.error('sig data: %s',self.sig_data) Log.error('tag5A: %s',tag5A) Log.error('tag5F24: %s',tag5F24) Log.error('can not get icc public key whereby dda failed') return '' return icc_pub_key
def _get_issuer_pub_key(self): ''' 恢复发卡行公钥 ''' issuer_pub_key = '' tag84 = self.get_tag(PROCESS_STEP.SELECT,'84') tag8F = self.get_tag(PROCESS_STEP.READ_RECORD,'8F') tag90 = self.get_tag(PROCESS_STEP.READ_RECORD,'90') tag92 = self.get_tag(PROCESS_STEP.READ_RECORD,'92') tag9F32 = self.get_tag(PROCESS_STEP.READ_RECORD,'9F32') tag5A = self.get_tag(PROCESS_STEP.READ_RECORD,'5A') tag5F24 = self.get_tag(PROCESS_STEP.READ_RECORD,'5F24') if not tag84 or len(tag84) < 10: Log.error('tag84: %s',tag84) Log.error('tag84 is empty or length less than 10 bytes whereby dda failed') return issuer_pub_key if not tag8F: Log.error('tag8F: %s',tag8F) Log.info('require tag8F failed whereby dda failed') return issuer_pub_key if not tag90: Log.error('tag90: %s',tag90) Log.info('require tag90 failed whereby dda failed') return issuer_pub_key if not tag9F32: Log.error('tag9F32: %s',tag9F32) Log.info('require tag9F32 failed whereby dda failed') return issuer_pub_key if not tag5A: Log.error('tag5A: %s',tag5A) Log.info('require tag5A failed whereby dda failed') return issuer_pub_key if not tag5F24: Log.error('tag5F24: %s',tag5F24) Log.info('require tag5F24 failed whereby dda failed') return issuer_pub_key # 验证tag90,获取发卡行公钥 # 获取CA 公钥及CA指数 ca_pub_key,ca_exp = auth.get_ca_pub_key(tag84[0:10],tag8F) if not ca_pub_key or not ca_exp: Log.error('rid: %s index: %s ',tag84[0:10],tag8F) Log.error('can not get ca public key, make sure this root ca existed, please check pcsc.db file') return issuer_pub_key issuer_pub_key = auth.get_issuer_pub_key(ca_pub_key,ca_exp,tag90,tag92,tag9F32,tag5A,tag5F24) if not issuer_pub_key: Log.error('CA public key: %s',ca_pub_key) Log.error('CA exp: %s',ca_exp) Log.error('tag90: %s',tag90) Log.error('tag92: %s',tag92) Log.error('tag9F32: %s',tag9F32) Log.error('tag5A: %s',tag5A) Log.error('tag5F24: %s',tag5F24) Log.info('can not get issuer public key whereby dda failed') return '' return issuer_pub_key
def get_icc_pub_key(issuer_pub_key,tag9F32,tag9F46,tag9F48,tag9F47,sig_data,tag5A,tag5F24): ''' 获取IC卡公钥 ''' icc_pub_key = '' recovery_data = gen_recovery_data(issuer_pub_key,tag9F32,tag9F46) if not recovery_data: Log.error('can not require recovery data through public cert tag90') return icc_pub_key Log.info('icc recovery data: %s',recovery_data) if recovery_data[0:4] != '6A04' or recovery_data[-2:] != 'BC': Log.error('recovery data format incorrect, it should start with 6A02 and end with BC') return icc_pub_key pan = recovery_data[4:24].replace('F','') if len(pan) < 16 or tag5A.replace('F','') != pan: Log.error('PAN: %s',pan) Log.error('tag5A: %s',tag5A) Log.error('pan required from recovery data is not accord with tag5A') return icc_pub_key expiry_date = int(recovery_data[26:28] + recovery_data[24:26]) now = int(time.strftime('%y%m')) if expiry_date < now: Log.warn('expiry date: %s',recovery_data[26:28] + recovery_data[24:26]) Log.warn('icc cert has been overdue') if expiry_date < int(tag5F24[0:4]): Log.warn('expiry date: %s',recovery_data[26:28] + recovery_data[24:26]) Log.warn('tag5F24: %s',tag5F24) Log.warn('icc cert overdue earlier than application expiry date') if recovery_data[34:38] != '0101': Log.error('hash algo flag: %s',recovery_data[34:36]) Log.error('ipk algo flag: %s',recovery_data[36:38]) hash_input = recovery_data[2:-42] + tag9F48 + tag9F47 + sig_data hash_cert = recovery_data[-42:-2] hash_output = algorithm.gen_hash(hash_input) if hash_cert != hash_output: Log.error('hash input: %s',hash_input) Log.error('hash: %s',hash_output) Log.error('hash cert: %s',hash_cert) Log.error('hash compared failed whereby gen icc recovery data failed ') return icc_pub_key icc_cert_len = int(recovery_data[38:40],16) * 2 if icc_cert_len <= len(issuer_pub_key) - 84: icc_pub_key = recovery_data[42:42 + icc_cert_len] else: icc_pub_key = recovery_data[42:-42] + tag9F48 Log.info('icc pub key: %s',icc_pub_key) return icc_pub_key
def gen_cps(self, session_key=None, second_session_key=None, delete80_list=[]): cps = Cps() start_perso = False # 个人化起始行 app_type = None # 个人化应用类型 prevous_req = '' #保存上一次的数据,对于一条超长数据,需要使用两条store data指令 prevous_req_len = 0 # is_jetco_app = False #判断是否有jetco应用 parse_perso_complete = False #是否已经完成个人化 has_check_status = False #是否有检测卡片状态 has_check_kmc = False # 是否有检测修改过卡片KMC if not delete80_list: delete80_list = ['8201', '8202', '8203', '8204', '8205'] while not self.log_handle.EOF: is_encrypted = False data = self.log_handle.read_line() #每次读取一行数据进行分析 req, resp = self._filter(data) #过滤非指定的APDU信息,返回请求和响应数据 if req.startswith('80E60C00'): #解析安装参数信息 Log.info( '===================== Check Install Param =====================' ) self._parse_install_cmd(req, resp) if req.startswith('00A40400') and not req.startswith( '00A4040008A000000003000000'): start_perso = True #从此行开始认定个人化开始 if start_perso: if req.startswith('00A404000E315041592E5359532E4444463031'): app_type = 'PSE' #说明接下来是个人化PSE continue elif req.startswith('00A404000E325041592E5359532E4444463031'): app_type = 'PPSE' #接下来个人化PPSE continue elif req.startswith('00A40400'): #应用 app_type = 'APP' if req.startswith('00A4040009A00000047400000001'): app_type = "JETCO" is_jetco_app = True # 这里判断,是因为表示是双应用数据 continue if app_type: if req.startswith('80E2') and req[4:6] in ( '00', '60', '80', 'E0'): #处理store data 数据 if resp != '9000': Log.error('perso error: [APDU: %s][%s]', req, resp) continue #判断是否为加密数据 if req[4:6] in ('60', 'E0'): is_encrypted = True # 判断是否已经解析完个人化数据,接下来要判断卡片安全状态和修改KMC if req[4:6] in ('80', 'E0') and app_type == 'APP': parse_perso_complete = True req = req[10:] # 去掉命令头及长度 # 处理仅一条APDU指令发送store data的情况 if not prevous_req: dgi_name = req[0:4] # DGI名称 if is_jetco_app: #默认Jetco为第二应用 dgi_name += '_2' req = req[4:] # 去掉DGI名称 # 去掉DGI后面跟随的长度 data_len = 0 if req[0:4] == 'FF00': data_len = int(req[4:6], 16) req = req[6:] else: data_len = int(req[0:2], 16) req = req[2:] # 如果长度和后面的数据不一致,则说明分两条APDU发送 if data_len * 2 != len(req): prevous_req = req prevous_req_len = data_len continue else: # 处理需要两条APDU指令发送的情况 req = prevous_req + req if prevous_req_len * 2 != len(req): Log.error("data len error") return None prevous_req = '' prevous_req_len = 0 dgi = None if is_jetco_app: dgi = self.parse_store_data( app_type, dgi_name, req, second_session_key, is_encrypted, delete80_list) else: dgi = self.parse_store_data( app_type, dgi_name, req, session_key, is_encrypted, delete80_list) cps.add_dgi(dgi) if parse_perso_complete: if req.startswith('80F080'): # 检测卡片是否设置为安全状态 Log.info( '===================== Check Card Status =====================' ) has_check_status = True self._parse_set_status_cmd(req, resp) if req.startswith('80D8'): # 检测卡片是否修改KMC Log.info( '===================== Check KMC =====================' ) has_check_kmc = True self._parse_modify_kmc(req, resp) if not has_check_status: Log.info( '===================== Check Card Status =====================' ) Log.error('Not set card status during perso card.\n') if not has_check_kmc: Log.info('===================== Check KMC =====================') Log.error('Not modify kmc during perso card.\n') return cps
def _parse_modify_kmc(self, req, resp): if req[0:4] == '80D8' and resp == '610A': Log.info('Check Modify KMC Sucess.') else: Log.error('Check Modify KMC Failed.')
def compare_cps(self, product_cps, mock_cps, ignore_list=[]): self._comapre_dgi_list(mock_cps, product_cps) self._display_dki(product_cps) self._display_key_info(product_cps) mock_dgis = mock_cps.get_all_dgis() if not ignore_list: # 涉及KMS相关的值,不比较 ignore_list = [ '8F', '90', '92', '9F32', '9F46', '9F48', '93', '8201', '8202', '8203', '8204', '8205', '8000', '9000', 'A006', 'A016', '8400', '8401', '8001', '9001', 'B010', 'B023' ] Log.info('===================== Compare Data =====================') has_err = False for mock_dgi in mock_dgis: Log.info( '===================== compare dgi %s =====================', mock_dgi.name) product_dgi = product_cps.get_dgi(mock_dgi.name) if not product_dgi: Log.error('cannot find DGI %s in product enviromnent' % mock_dgi.name) has_err = True continue if product_dgi.name in ('9115', '9116', '9117'): product_value = product_dgi.get_value(product_dgi.name) mock_value = '' for tag, value in mock_dgi.get_all_tags().items(): mock_value += value if mock_value != product_value: has_err = True Log.error('compare dgi %s failed.' % product_dgi.name) Log.error('Prod_value: %s' % product_value) Log.error('Mock_value: %s\n' % mock_value) else: Log.info('compare dgi %s sucess.' % product_dgi.name) Log.info('Prod_value: %s' % product_value) Log.info('Mock_value: %s\n' % mock_value) else: for tag, value in mock_dgi.get_all_tags().items(): if tag in ignore_list: continue product_value = product_dgi.get_value(tag) if product_value != value: has_err = True Log.error('compare tag %s failed.' % tag) Log.error('Prod_value: %s' % product_value) Log.error('Mock_value: %s\n' % value) else: Log.info('compare tag %s sucess.' % tag) Log.info('Prod_value: %s' % product_value) Log.info('Mock_value: %s\n' % value) if has_err: Log.error('Comapare data failed.') else: Log.info('Compared data sucess.')
def get_issuer_pub_key(ca_pub_key,ca_exp,tag90,tag92,tag9F32,tag5A,tag5F24): ''' 获取发卡行公钥 ''' issuer_pub_key = '' recovery_data = gen_recovery_data(ca_pub_key,ca_exp,tag90) if not recovery_data: Log.error('can not require recovery data through public cert tag90') return issuer_pub_key Log.info('issuer recovery data: %s',recovery_data) if recovery_data[0:4] != '6A02' or recovery_data[-2:] != 'BC': Log.error('recovery data format incorrect, it should start with 6A02 and end with BC') return issuer_pub_key issuer_bin = recovery_data[4:12].replace('F','') if len(issuer_bin) < 3 or not tag5A.startswith(issuer_bin): Log.error('issuer_bin: %s',issuer_bin) Log.error('tag5A: %s',tag5A) Log.error('issuer_bin required from recovery data is not accord with tag5A') return issuer_pub_key expiry_date = int(recovery_data[14:16] + recovery_data[12:14]) now = int(time.strftime('%y%m')) if expiry_date < now: Log.warn('expiry date: %s',recovery_data[14:16] + recovery_data[12:14]) Log.warn('issuer cert has been overdue') if expiry_date < int(tag5F24[0:4]): Log.warn('expiry date: %s',recovery_data[14:16] + recovery_data[12:14]) Log.warn('tag5F24: %s',tag5F24) Log.warn('issuer cert overdue earlier than application expiry date') if recovery_data[22:26] != '0101': Log.error('hash algo flag: %s',recovery_data[22:24]) Log.error('ipk algo flag: %s',recovery_data[24:26]) return issuer_pub_key hash_input = recovery_data[2:-42] + tag92 + tag9F32 hash_cert = recovery_data[-42:-2] hash_output = algorithm.gen_hash(hash_input) if hash_cert != hash_output: Log.error('hash input: %s',hash_input) Log.error('hash: %s',hash_output) Log.error('hash cert: %s',hash_cert) Log.error('hash compared failed whereby gen issuer recovery data failed ') return issuer_pub_key issuer_cert_len = int(recovery_data[26:28],16) * 2 if issuer_cert_len <= len(ca_pub_key) - 72: issuer_pub_key = recovery_data[30:30 + issuer_cert_len] else: issuer_pub_key = recovery_data[30:-42] + tag92 Log.info('issuer pub key: %s',issuer_pub_key) return issuer_pub_key
def create_case(self, configs, check_aid_list=None): app_nodes = self.dp_xml_handle.get_child_nodes( self.dp_xml_handle.root_element, 'App') # handle contact case for app_node in app_nodes: aid = self.dp_xml_handle.get_attribute(app_node, 'aid') if not aid: Log.error( 'can not get aid from dp xml, check wether the dp xml is correct.' ) return app_type = self.dp_xml_handle.get_attribute(app_node, 'type') if not app_type: Log.warn('can not recognize app type') app_type = '' app_type += '_conatct_test' if aid[:10] not in configs: Log.error('can not get configuration of aid %s', aid) return config = configs[aid[:10]] # get this aid's configuration # create header case_node = self._create_header(app_type, aid, config['contact_device'], config['contact_dll'], config['contact_spec']) if 'dki_cvn' in config: self.smart_qc.create_node(case_node, 'VerifyTag9F10', pattern=config['dki_cvn']) # <CompareData> compare_data_node = self.smart_qc.create_node( case_node, 'CompareData') gpo_node = self.dp_xml_handle.get_node_by_attribute( app_node, 'DGI', name=config['gpo_dgi']) if not gpo_node: Log.error('can not found dgi %s in app node,aid=%s', config['gpo_dgi'], aid) return afl_node = self.dp_xml_handle.get_node_by_attribute( gpo_node, 'Tag', name=config['afl_tag']) if not afl_node: Log.error('can not found tag %s in dgi %s', config['afl_tag'], config['gpo_dgi']) return afl_tag = self.dp_xml_handle.get_attribute(afl_node, 'value') if not afl_tag: Log.error('tag %s is empty', config['afl_tag']) return dgi_names = self._get_dgis(afl_tag) # pse tags pse_nodes = self._get_pse_nodes() for node in pse_nodes: self.create_tag_node('pse', node, compare_data_node, '315041592E5359532E4444463031') fci_node = self.dp_xml_handle.get_node_by_attribute( app_node, 'DGI', name=config['contact_fci_dgi']) if not fci_node: Log.error('can not found dgi %s in app node', config['contact_fci_dgi']) return # app fci tags self.create_tag_node(app_type, fci_node, compare_data_node, aid) # app gpo tags self.create_tag_node(app_type, gpo_node, compare_data_node, aid) # app record tags for dgi_name in dgi_names: node = self.dp_xml_handle.get_node_by_attribute(app_node, 'DGI', name=dgi_name) self.create_tag_node(app_type, node, compare_data_node, aid) # risk management tags internal_dgis = config['risk_dgi_list'] get_data_tags = [[tag, False] for tag in config['get_data_tags']] risk_tags = '' for dgi_name in internal_dgis: node = self.dp_xml_handle.get_node_by_attribute(app_node, 'DGI', name=dgi_name) if node: self.create_tag_node(app_type, node, compare_data_node, aid, get_data_tags) for item in get_data_tags: tag = item[0] has_check = item[1] if not has_check: Log.error( 'can not get tag %s in dp xml,check the "get_data_tags" configuration', tag) else: if len(tag) == 2: tag = '00' + tag risk_tags += tag + ',' if not risk_tags: Log.error('MustContain node is empty') return risk_tags = risk_tags[0:-1] # <MustConatin> self.smart_qc.create_text_node(case_node, 'MustContain', risk_tags) # <ShowCardFace> self.smart_qc.create_node(case_node, 'ShowCardFace') # handle contactless case for app_node in app_nodes: aid = self.dp_xml_handle.get_attribute(app_node, 'aid') if not aid: Log.error( 'can not get aid from dp xml, check wether the dp xml is correct.' ) return if aid in ('315041592E5359532E4444463031', '325041592E5359532E4444463031'): continue app_type = self.dp_xml_handle.get_attribute(app_node, 'type') if not app_type: Log.warn('can not recognize app type') app_type = '' app_type += '_conatctless_test' if aid[:10] not in configs: Log.error('can not configuration of aid %s', aid) return config = configs[aid[:10]] # get this aid's configuration # 判断是否有非接数据,并生成非接case fci_dgi_node = self.dp_xml_handle.get_node_by_attribute( app_node, 'DGI', name=config['contactless_fci_dgi']) if fci_dgi_node: case_node = self._create_header(app_type, aid, config['contactless_device'], config['contactless_dll'], config['contactless_spec']) compare_data_node = self.smart_qc.create_node( case_node, 'CompareData') ppse_child_node = self._get_ppse_node() self.create_tag_node('ppse', ppse_child_node, compare_data_node, '325041592E5359532E4444463031') self.create_tag_node(app_type, fci_dgi_node, compare_data_node, aid) # check aid list if check_aid_list: self.create_check_aid_case(check_aid_list)
def validate_9F4B(icc_pub_key,icc_exp,sig_data,tag9F4B): ''' 校验tag9F4B的正确性 ''' recovery_data = gen_recovery_data(icc_pub_key,icc_exp,tag9F4B) if not recovery_data: Log.error('can not require recovery data through public cert tag90') return False Log.info('tag9F4B recovery data: %s',recovery_data) if recovery_data[0:4] not in ('6A05','6A95') or recovery_data[-2:] != 'BC': Log.error('recovery data format incorrect, it should start with 6A05/6A95 and end with BC') return False hash_input = recovery_data[2:-42] + sig_data hash_9F4B = recovery_data[-42:-2] hash_output = algorithm.gen_hash(hash_input) if hash_9F4B != hash_output: Log.error('hash in tag9F4B: %s',hash_9F4B) Log.error('hash input: %s',hash_input) Log.error('hash calc: %s',hash_output) Log.error('hash compared failed whereby tag9F4B is not correct ') return False icc_dynamic_data_len = int(recovery_data[8:10],16) * 2 tag9F4C = recovery_data[10:10 + icc_dynamic_data_len] terminal.set_terminal('9F4C',tag9F4C) return True