def _display_key_info(self, cps): keys = ['8000', 'A006', '8401', '9000'] dgis = cps.get_all_dgis() Log.info( '===================== First Application Key Info =====================' ) for dgi in dgis: kcv = '' for tag, value in dgi.get_all_tags().items(): if tag in keys: if tag != '9000': kcv = algorithm.gen_kcv(value) Log.info('tag%-5s: value:%s kcv:%s', tag, value, kcv) second_keys = ['8000_2', '9000_2'] Log.info( '\n===================== Second Application Key Info =====================' ) for dgi in dgis: kcv = '' for tag, value in dgi.get_all_tags().items(): if dgi.name in second_keys: Log.info('tag%-5s: value:%s kcv:%s', tag, value, kcv) Log.info('\n')
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
def get_dgi_list(fh): dgi_list_len = fh.read_int(fh.current_offset) dgi_list_str = fh.read_binary(fh.current_offset, dgi_list_len) dgi_list = [] for i in range(0, dgi_list_len * 2, 4): dgi_list.append(dgi_list_str[i:i + 4]) des_encrypt_dgi_list_len = fh.read_int(fh.current_offset) des_encrypt_dgi_list_str = fh.read_binary(fh.current_offset, des_encrypt_dgi_list_len) des_encrypt_dgi_list = [] for i in range(0, des_encrypt_dgi_list_len * 2, 4): des_encrypt_dgi_list.append(des_encrypt_dgi_list_str[i:i + 4]) sm_encrypt_dgi_list_len = fh.read_int(fh.current_offset) sm_encrypt_dgi_list_str = fh.read_binary(fh.current_offset, sm_encrypt_dgi_list_len) sm_encrypt_dgi_list = [] for i in range(0, sm_encrypt_dgi_list_len * 2, 4): sm_encrypt_dgi_list.append(sm_encrypt_dgi_list_str[i:i + 4]) Log.info('sm encrypt dgi list: ', end=' ') Log.info(sm_encrypt_dgi_list) des_encrypt_dgi_list.extend(sm_encrypt_dgi_list) log_dgi_list_len = fh.read_int(fh.current_offset) fh.read_binary(fh.current_offset, log_dgi_list_len) #暂时不需要log DGI记录 return dgi_list, des_encrypt_dgi_list
def read_log(self): Log.info('get trans log') tag9F4D = self.get_tag(PROCESS_STEP.SELECT, '9F4D') if not tag9F4D: Log.info('no tag9F4D,do not support record log') else: record_count = int(tag9F4D[2:4], 16) log_sfi = int(tag9F4D[0:2], 16) buffer = [] for i in range(record_count + 1): resp = apdu.read_record(log_sfi, i + 1, (0x9000, 0x6A83)) if resp.sw == 0x9000: Log.info(resp.response) buffer.append(resp.response) else: break Log.info('%-10s%-15s%-10s%-10s%-8s%-15s%-10s%-10s%-10s', 'result', 'money', 'currency', 'date', 'atc', 'cvr', 'interface', 'time', 'merchant') for resp in buffer: result = resp[0:2] money = resp[2:14] currency = resp[14:18] date = resp[18:24] atc = resp[24:28] cvr = resp[28:40] interface = resp[40:42] trans_time = resp[42:48] merchant = utils.bcd_to_str(resp[48:]) Log.info('%-10s%-15s%-10s%-10s%-8s%-15s%-10s%-10s%-10s', result, money, currency, date, atc, cvr, interface, trans_time, merchant)
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 second_gac(self): tag8D = self.get_tag(PROCESS_STEP.READ_RECORD, '8D') data = tools.assemble_dol(tag8D) resp = super().gac(Crypto_Type.TC, data) if resp.sw != 0x9000: Log.info('send gac1 failed.') return return resp
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 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 second_gac(self): tag8D = self.get_tag(PROCESS_STEP.READ_RECORD, '8D') data = tools.assemble_dol(tag8D) resp = super().gac(Crypto_Type.TC, data) if resp.sw != 0x9000: Log.info('send gac1 failed.') return tlvs = utils.parse_tlv(resp.response) tools.output_apdu_info(resp) self.store_tag_group(PROCESS_STEP.SECOND_GAC, utils.parse_tlv(resp.response)) return resp
def first_gac(self): tag8C = self.get_tag(PROCESS_STEP.READ_RECORD, '8C') data = tools.assemble_dol(tag8C) resp = super().gac(Crypto_Type.ARQC, data) if resp.sw != 0x9000: Log.info('send gac1 failed.') return tlvs = utils.parse_tlv(resp.response) tools.output_apdu_info(resp) self.store_tag_group(PROCESS_STEP.FIRST_GAC, utils.parse_tlv(resp.response)) self.run_case('case_first_gac', 'run_mc', resp) return resp
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 process_card_data(fh, rule_file): cps = Cps() flag = fh.read_str(fh.current_offset, 6) if flag != '000EMV': return False, cps card_data_len = fh.read_int64(fh.current_offset) app_count = utils.hex_str_to_int(fh.read_binary(fh.current_offset, 1)) for app in range(app_count): aid_len = utils.hex_str_to_int(fh.read_binary(fh.current_offset, 1)) aid = fh.read_binary(fh.current_offset, aid_len) app_data_len = fh.read_int64(fh.current_offset) dgi_list, encrypt_dgi_list = get_dgi_list(fh) Log.info('encrypt dgi list :', encrypt_dgi_list) for item in dgi_list: card_dgi = Dgi() dgi = fh.read_binary(fh.current_offset, 2) dgi_len = utils.hex_str_to_int(fh.read_binary( fh.current_offset, 1)) dgi_data = fh.read_binary(fh.current_offset, dgi_len) n_dgi = utils.hex_str_to_int(dgi) card_dgi.dgi = dgi if dgi == '0098' or dgi == '0099': dgi = process_pse(dgi, dgi_data) elif dgi == '0100': dgi = process_ppse(dgi, dgi_data) else: if n_dgi < 0x0B01: if dgi_data[0:2] != '70': return False, cps if dgi_data[2:4] == '81': dgi_data = dgi_data[6:] else: dgi_data = dgi_data[4:] if utils.is_rsa(dgi) is False and utils.is_tlv(dgi_data): tlvs = utils.parse_tlv(dgi_data) if len(tlvs) > 0 and tlvs[0].is_template is True: value = utils.assemble_tlv(tlvs[0].tag, tlvs[0].value) card_dgi.add_tag_value(dgi, value) else: for tlv in tlvs: value = process_tag_decrypt( rule_file, tlv.tag, tlv.value) value = utils.assemble_tlv(tlv.tag, value) card_dgi.add_tag_value(tlv.tag, value) else: card_dgi.add_tag_value(dgi, dgi_data) cps.add_dgi(card_dgi) return True, cps
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 first_gac(self): tag8C = self.get_tag(PROCESS_STEP.READ_RECORD, '8C') data = tools.assemble_dol(tag8C) resp = super().gac(Crypto_Type.ARQC, data) if resp.sw != 0x9000: Log.info('send gac1 failed.') return tlvs = utils.parse_tlv(resp.response) if len(tlvs) != 1 and tlvs[0].tag != '80': Log.info('gac1 response data error') data = tlvs[0].value self.store_tag(PROCESS_STEP.FIRST_GAC, '9F27', data[0:2]) self.store_tag(PROCESS_STEP.FIRST_GAC, '9F36', data[2:6]) self.store_tag(PROCESS_STEP.FIRST_GAC, '9F26', data[6:22]) self.store_tag(PROCESS_STEP.FIRST_GAC, '9F10', data[22:]) return resp
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 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 _display_dki(self, cps): dgis = cps.get_all_dgis() Log.info( '===================== First Application DKI Info =====================' ) for dgi in dgis: for tag, value in dgi.get_all_tags().items(): if tag in ('9F10') and '_2' not in dgi.name: Log.info('tag9F10:%s DKI:%s', value[6:], value[8:10]) Log.info( '\n===================== Second Application DKI Info =====================' ) for dgi in dgis: for tag, value in dgi.get_all_tags().items(): if tag in ('9F10') and '_2' in dgi.name: Log.info('tag9F10:%s DKI:%s', value[6:], value[8:10]) Log.info('\n')
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 send_raw(cmd, resp_sw_list=None): ''' 在连接读卡器后,发送APDU指令。 cmd 表示要发送的APDU命令,包括命令头,数据长度,数据。 resp_sw_list 表示期望的响应返回码列表,若返回不是期望的响应码,则自动退出APDU的交互 ''' apdu_response = ApduResponse() bytes_cmd = str.encode(cmd) resp_len = c_int(2048) resp_data = create_string_buffer(resp_len.value) Log.info('APDU: %s', cmd) apdu_response.sw = _pcsc_lib.SendApdu(bytes_cmd, resp_data, resp_len) apdu_response.response = bytes.decode(resp_data.value) apdu_response.request = cmd Log.info('RESP: %X', apdu_response.sw) if resp_sw_list and apdu_response.sw not in resp_sw_list: sys.exit(1) return apdu_response
def process_mag_data(fh,rule_file_name): rule_file = RuleXml(rule_file_name) mag_node = rule_file.get_first_node(rule_file.root_element,'Magstrip') mag_flag = fh.read_str(fh.current_offset,6) if mag_flag != '000MAG': return False mag_data_len = fh.read_int64(fh.current_offset) track_flag_list = [] track_flag_list.append(fh.read(fh.current_offset,1)) track_flag_list.append(fh.read(fh.current_offset + 1,1)) track_flag_list.append(fh.read(fh.current_offset + 1,1)) mag_data = fh.read_binary(fh.current_offset,mag_data_len - 5) mag_data_list = [x for x in mag_data.split('7C') if len(x) > 0] Log.info('decrypt mag data: ',end='') Log.info(mag_data_list) dgi = Dgi() dgi.name = 'Magstrip' if mag_node is not None: mag_key = rule_file.get_attribute(mag_node,'key') decrypt_mag_list = [] for data in mag_data_list: data = utils.bcd_to_str(data) data = algorithm.des3_ecb_decrypt(mag_key,data) data = utils.bcd_to_str(data) data_len = int(data[0:4]) data = data[4:data_len + 4].rstrip() decrypt_mag_list.append(data) pos = 1 for mag in decrypt_mag_list: for index in range(pos,4): pos += 1 option = 'mag' + str(pos) if track_flag_list[index - 1] == '0': dgi.add_tag_value(option,'') continue dgi.add_tag_value(option,mag) break Log.info('decrypt mag data: ',end='') Log.info(decrypt_mag_list) return dgi
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 _display_emboss_data(self, cps, dp_xml): Log.info( '===================== Show Dynamic Data =====================') tags = [] xml_handle = XmlParser(dp_xml) tag_nodes = xml_handle.get_nodes(xml_handle.root_element, 'Tag') for tag_node in tag_nodes: if xml_handle.get_attribute(tag_node, 'type') == 'file': name = xml_handle.get_attribute(tag_node, 'name') if name not in tags: tags.append(name) dgis = cps.get_all_dgis() for dgi in dgis: for tag, value in dgi.get_all_tags().items(): if value: value = value[len(tag) + 2:] #去掉tag及长度域 if tag in tags: Log.info('tag%-5s: value:%s', tag, value) Log.info('\n')
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 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_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 _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 _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 process_dp(dp_file, rule_file, is_mc_app=False): global _is_mc_app _is_mc_app = is_mc_app cps_list = [] fh = FileHandle(dp_file, 'rb+') dp_header = fh.read_binary(fh.current_offset, 26) dgi_count = fh.read_int(fh.current_offset) goldpac_dgi_list = [] for i in range(dgi_count): #获取sddf dgi个数及所包含数据的长度 item = GoldpacDgi() item.goldpac_dgi = fh.read_binary(fh.current_offset, 4) item.data_len = fh.read_int(fh.current_offset) goldpac_dgi_list.append(item) for item in goldpac_dgi_list: #获取sddf dgi所包含的数据 item.data = fh.read_binary(fh.current_offset, item.data_len) Log.info(item.goldpac_dgi + ' = ' + item.data) xml = XmlParser(rule_file) get_aid_list_info(xml) #获取AID应用列表信息 sddf_dgi_list = [] sddf_element_nodes = xml.get_nodes(xml.root_element, 'SDDFElement') constans_second_app = has_second_app() for node in sddf_element_nodes: sddf_tag = xml.get_attribute(node, 'SDDFTag') sddf_value = xml.get_attribute(node, 'Value') sddf_elements = parse_sddf_element(sddf_tag, sddf_value) sddf_dgi_list.extend(sddf_elements) cps = Cps() cps.dp_file_path = dp_file pse_dgi_list, ppse_dgi_list = get_pse_and_ppse_dgi_list(sddf_dgi_list) for sddf_tag in sddf_dgi_list: if sddf_tag not in pse_dgi_list and sddf_tag not in ppse_dgi_list: #解析TagLink节点,并生成cps数据 cps_dgi = parse_sddf_data(xml, sddf_tag, goldpac_dgi_list) if cps_dgi != None: cps.add_dgi(cps_dgi) dgi_8000 = parse_8000(xml, goldpac_dgi_list, False) dgi_9000 = parse_9000(xml, goldpac_dgi_list, False) cps.add_dgi(dgi_8000) cps.add_dgi(dgi_9000) if is_mc_app: dgi_A006 = parse_A006(xml, goldpac_dgi_list) cps.add_dgi(dgi_A006) dgi_A016 = Dgi() dgi_A016.name = 'A016' dgi_A016.add_tag_value('A016', dgi_A006.get_value('A006')) dgi_8001 = Dgi() dgi_8001.name = '8001' dgi_8001.add_tag_value('8001', dgi_8000.get_value('8000')) dgi_9001 = Dgi() dgi_9001.name = '9001' dgi_9001.add_tag_value('9001', dgi_9000.get_value('9000')) cps.add_dgi(dgi_8001) cps.add_dgi(dgi_9001) cps.add_dgi(dgi_A016) rsa_dgi_list = split_rsa(xml, goldpac_dgi_list, False) for rsa_dgi in rsa_dgi_list: cps.add_dgi(rsa_dgi) pse_dgi, ppse_dgi = parse_pse_and_ppse(xml, pse_dgi_list, ppse_dgi_list, goldpac_dgi_list) if pse_dgi.is_empty() is False: cps.add_dgi(pse_dgi) if ppse_dgi.is_empty() is False: cps.add_dgi(ppse_dgi) if constans_second_app: cps = process_jetco_special_dgi(xml, goldpac_dgi_list, cps) cps.add_dgi(parse_8000(xml, goldpac_dgi_list, True)) cps.add_dgi(parse_9000(xml, goldpac_dgi_list, True)) rsa_dgi_list = split_rsa(xml, goldpac_dgi_list, True) for rsa_dgi in rsa_dgi_list: cps.add_dgi(rsa_dgi) cps_list.append(cps) return cps_list