#!/usr/bin/env python3 # Copyright 2023 Sergey Stolyarov # # Distributed under New BSD License. # # https://opensource.org/license/bsd-3-clause/ import bertlv from smartcard.System import readers from smartcard.CardRequest import CardRequest from smartcard.CardConnection import CardConnection from smartcard.util import toHexString, toBytes, PACK, HexListToBinString def main() -> int: reader = readers()[0] print('Connected reader: {0}'.format(reader)) cardrequest = CardRequest(timeout=None, readers=[reader]) print('Waiting for card ...') cardservice = cardrequest.waitforcard() cardservice.connection.connect() print('Card connected.') # select MF and fetch FCP # CLA INS P1 P2 Lc DATA apdu = '00 A4 00 04 02 3F 00' response, sw1, sw2 = transmit_wrapper(cardservice.connection, toBytes(apdu)) if (sw1,sw2) != (0x90,0x00): print('Failed to read MF FCP Template') return 1 parts = bertlv.parse_bytes(response) mf_fcp = bertlv.find_tag(0x62, parts) if mf_fcp is None: print('Failed to parse MF FCP Template') return 1 print('MF FCP data:') for tlv in mf_fcp.value: if tlv.tag == 0x82: print(' File descriptor: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x83: print(' File identifier: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x84: print(' DF name (AID): {}'.format(toHexString(tlv.value))) elif tlv.tag == 0xA5: print(' Proprietary information:') for x in tlv.value: print(' 0x{:X}: {}'.format(x.tag, toHexString(x.raw_value))) elif tlv.tag == 0x8A: print(' Life Cycle Status Integer: {}'.format(toHexString(tlv.value))) elif tlv.tag in (0x8B, 0x8C, 0xAB): print(' Security attributes: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0xC6: print(' PIN Status Template DO: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x81: print(' Total file size: {}'.format(toHexString(tlv.value))) else: print(' 0x{X}: {}'.format(tlv.tag, tlv.raw_value)) # read EF.DIR # CLA INS P1 P2 Lc DATA apdu = '00 A4 00 04 02 2F 00' response, sw1, sw2 = transmit_wrapper(cardservice.connection, toBytes(apdu)) if (sw1,sw2) != (0x90,0x00): print('Failed to select EF.DIR') return 1 print('\nEF.DIR:') parts = bertlv.parse_bytes(response) efdir_fcp = bertlv.find_tag(0x62, parts) if efdir_fcp is None: print('Failed to parse EF.DIR FCP Template') return 1 for tlv in efdir_fcp.value: if tlv.tag == 0x82: print(' File descriptor: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x83: print(' File identifier: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0xA5: print(' Proprietary information:') for x in tlv.value: print(' 0x{:X}: {}'.format(x.tag, toHexString(x.raw_value))) elif tlv.tag == 0x8A: print(' Life Cycle Status Integer: {}'.format(toHexString(tlv.value))) elif tlv.tag in (0x8B, 0x8C, 0xAB): print(' Security attributes: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x80: print(' File size: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x81: print(' Total file size: {}'.format(toHexString(tlv.value))) elif tlv.tag == 0x88: print(' Short file identifier: {}'.format(toHexString(tlv.value))) else: print(' 0x{:X}: {}'.format(tlv.tag, toHexString(tlv.raw_value))) # read records efdir_records = [] while True: # CLA INS P1 P2 Lc apduBytes = toBytes('00 B2 00 02 00') response, sw1, sw2 = transmit_wrapper(cardservice.connection, apduBytes) if sw1 == 0x6C: # set new Le and repeat command apduBytes[4] = sw2 response, sw1, sw2 = transmit_wrapper(cardservice.connection, apduBytes) if (sw1,sw2) == (0x6A,0x83): break elif (sw1,sw2) != (0x90,0x00): print('Failed to read record') break parts = bertlv.parse_bytes(response) app_record = bertlv.find_tag(0x61, parts) if app_record is not None: efdir_records.append(app_record) print('EF.DIR records:') for i,tlv in enumerate(efdir_records, 1): print(f'Record {i}') for x in tlv.value: if x.tag == 0x4F: print(' AID: {}'.format(toHexString(x.value))) elif x.tag == 0x50: print(' Application label: {}'.format(HexListToBinString(x.value))) else: print(' 0x{:X}: {}'.format(x.tag, toHexString(x.raw_value))) def transmit_wrapper(connection, apdu): response, sw1, sw2 = connection.transmit(apdu) if sw1 == 0x61: response_data = [] ne = sw2 while True: gr_apdu = '00 C0 00 00 {:02X}'.format(ne) response, sw1, sw2 = connection.transmit(toBytes(gr_apdu)) if (sw1,sw2) == (0x90,0x00): response_data.extend(response) break elif sw1 == 0x61: response_data.extend(response) ne = sw2 continue else: # error, pass sw1, sw2 back to caller response_data = [] break return response_data, 0x90, 0x00 else: return response, sw1, sw2 if __name__ == '__main__': main()