def _tx_string(self, s): """This is only safe if it's guaranteed the card won't send any data during the time of tx of the string !!!""" self._sl.write(s) r = self._sl.read(len(s)) if r != s: # TX and RX are tied, so we must clear the echo raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
def send_apdu_raw(self, pdu): """see LinkBase.send_apdu_raw""" pdu = h2b(pdu) data_len = ord(pdu[4]) # P3 # Send first CLASS,INS,P1,P2,P3 self._tx_string(pdu[0:5]) # Wait ack which can be # - INS: Command acked -> go ahead # - 0x60: NULL, just wait some more # - SW1: The card can apparently proceed ... while True: b = self._rx_byte() if b == pdu[1]: break elif b != '\x60': # Ok, it 'could' be SW1 sw1 = b sw2 = self._rx_byte() nil = self._rx_byte() if (sw2 and not nil): return '', b2h(sw1+sw2) raise ProtocolError() # Send data (if any) if len(pdu) > 5: self._tx_string(pdu[5:]) # Receive data (including SW !) # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ] to_recv = data_len - len(pdu) + 5 + 2 data = '' while (len(data) < to_recv): b = self._rx_byte() if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?) continue if not b: break; data += b # Split datafield from SW if len(data) < 2: return None, None sw = data[-2:] data = data[0:-2] # Return value return b2h(data), b2h(sw)
def encode_record_hex(self, abstract_data): """Encode abstract representation into raw (hex string) data. Overloaded by specific classes.""" method = getattr(self, '_encode_record_hex', None) if callable(method): return method(abstract_data) method = getattr(self, '_encode_record_bin', None) if callable(method): raw_bin_data = method(abstract_data) return b2h(raww_bin_data) raise NotImplementedError
def decode_record_bin(self, raw_bin_data): """Decode raw (hex string) data into abstract representation. Overloaded by specific classes.""" method = getattr(self, '_decode_record_bin', None) if callable(method): return method(raw_bin_data) raw_hex_data = b2h(raw_bin_data) method = getattr(self, '_decode_record_hex', None) if callable(method): return method(raw_hex_data) return {'raw': raw_hex_data}
def program(self, p): # select MF r = self._scc.select_file(['3f00']) # authenticate as SUPER ADM using default key self._scc.verify_chv(0x0b, h2b("3838383838383838")) # set ADM pin using proprietary command # INS: D4 # P1: 3A for PIN, 3B for PUK # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10) if p['pin_adm']: pin = p['pin_adm'] else: pin = h2b("4444444444444444") pdu = 'A0D43A0508' + b2h(pin) data, sw = self._scc._tp.send_apdu(pdu) # authenticate as ADM (enough to write file, and can set PINs) self._scc.verify_chv(0x05, pin) # write EF.ICCID data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid'])) # select DF_GSM r = self._scc.select_file(['7f20']) # write EF.IMSI data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi'])) # write EF.ACC if p.get('acc') is not None: data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4)) # get size and write EF.HPLMN r = self._scc.select_file(['6f30']) size = int(r[-1][4:8], 16) hplmn = enc_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (size-3)) # set COMP128 version 0 in proprietary file data, sw = self._scc.update_binary('0001', '001000') # set Ki in proprietary file data, sw = self._scc.update_binary('0001', p['ki'], 3) # select DF_TELECOM r = self._scc.select_file(['3f00', '7f10']) # write EF.SMSP data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
def program(self, p): # select MF r = self._scc.select_file(['3f00']) # authenticate as SUPER ADM using default key self._scc.verify_chv(0x0b, h2b("3838383838383838")) # set ADM pin using proprietary command # INS: D4 # P1: 3A for PIN, 3B for PUK # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10) if p['pin_adm']: pin = p['pin_adm'] else: pin = h2b("4444444444444444") pdu = 'A0D43A0508' + b2h(pin) data, sw = self._scc._tp.send_apdu(pdu) # authenticate as ADM (enough to write file, and can set PINs) self._scc.verify_chv(0x05, pin) # write EF.ICCID data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid'])) # select DF_GSM r = self._scc.select_file(['7f20']) # write EF.IMSI data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi'])) # write EF.ACC if p.get('acc') is not None: data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4)) # get size and write EF.HPLMN r = self._scc.select_file(['6f30']) size = int(r[-1][4:8], 16) hplmn = enc_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (size - 3)) # set COMP128 version 0 in proprietary file data, sw = self._scc.update_binary('0001', '001000') # set Ki in proprietary file data, sw = self._scc.update_binary('0001', p['ki'], 3) # select DF_TELECOM r = self._scc.select_file(['3f00', '7f10']) # write EF.SMSP data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
def derive_milenage_opc(ki_hex, op_hex): """ Run the milenage algorithm to calculate OPC from Ki and OP """ from Crypto.Cipher import AES from Crypto.Util.strxor import strxor from pySim.utils import b2h # We pass in hex string and now need to work on bytes aes = AES.new(h2b(ki_hex)) opc_bytes = aes.encrypt(h2b(op_hex)) return b2h(strxor(opc_bytes, h2b(op_hex)))
def derive_milenage_opc(ki_hex, op_hex): """ Run the milenage algorithm. """ from Crypto.Cipher import AES from Crypto.Util.strxor import strxor from pySim.utils import b2h # We pass in hex string and now need to work on bytes aes = AES.new(h2b(ki_hex)) opc_bytes = aes.encrypt(h2b(op_hex)) return b2h(strxor(opc_bytes, h2b(op_hex)))
def program(self, p): # Go to dir self._scc.select_file(['3f00', '7f4d']) # Home PLMN in PLMN_Sel format hplmn = enc_plmn(p['mcc'], p['mnc']) # Operator name ( 3f00/7f4d/8f0c ) self._scc.update_record(self._files['name'][0], 2, rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01' ) # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d ) v = '' # inline Ki if self._ki_file is None: v += p['ki'] # ICCID v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid']) # IMSI v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi']) # Ki if self._ki_file: v += self._ki_file + '10' + p['ki'] # PLMN_Sel v+= '6f30' + '18' + rpad(hplmn, 36) # ACC # This doesn't work with "fake" SuperSIM cards, # but will hopefully work with real SuperSIMs. if p.get('acc') is not None: v+= '6f78' + '02' + lpad(p['acc'], 4) self._scc.update_record(self._files['b_ef'][0], 1, rpad(v, self._files['b_ef'][1]*2) ) # SMSP ( 3f00/7f4d/8f0e ) # FIXME # Write PLMN_Sel forcefully as well r = self._scc.select_file(['3f00', '7f20', '6f30']) tl = int(r[-1][4:8], 16) hplmn = enc_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
def set_phonebook(slot, name, number, capability='ff'): num_records = sc.record_count(['3f00','7f10','6f3a']) record_size = sc.record_size(['3f00','7f10','6f3a']) record_num = int(slot) if (record_num < 1) or (record_num > num_records): raise RuntimeError("Invalid phonebook record number") encoded_name = rpad(b2h(name), (record_size - 14) * 2) if len(encoded_name) > ((record_size - 14) * 2): raise RuntimeError("Name is longer than %s bytes" % ((record_size-14))) if len(number) > 20: raise RuntimeError("Number is too long") encoded_number = swap_nibbles(rpad(number, 20)) record = encoded_name + ('%02x' % len(number)) + capability + encoded_number + 'ffff' sc.update_record(['3f00','7f10','6f3a'], record_num, record)
def program(self, p): # Go to dir self._scc.select_file(['3f00', '7f4d']) # Home PLMN in PLMN_Sel format hplmn = self._e_plmn(p['mcc'], p['mnc']) # Operator name ( 3f00/7f4d/8f0c ) self._scc.update_record(self._files['name'][0], 2, rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01' ) # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d ) v = '' # inline Ki if self._ki_file is None: v += p['ki'] # ICCID v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid']) # IMSI v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi']) # Ki if self._ki_file: v += self._ki_file + '10' + p['ki'] # PLMN_Sel v+= '6f30' + '18' + rpad(hplmn, 36) self._scc.update_record(self._files['b_ef'][0], 1, rpad(v, self._files['b_ef'][1]*2) ) # SMSP ( 3f00/7f4d/8f0e ) # FIXME # Write PLMN_Sel forcefully as well r = self._scc.select_file(['3f00', '7f20', '6f30']) tl = int(r[-1][4:8], 16) hplmn = self._e_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
def program(self, p): # Go to dir self._scc.select_file(['3f00', '7f4d']) # Home PLMN in PLMN_Sel format hplmn = self._e_plmn(p['mcc'], p['mnc']) # Operator name ( 3f00/7f4d/8f0c ) self._scc.update_record( self._files['name'][0], 2, rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01') # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d ) v = '' # inline Ki if self._ki_file is None: v += p['ki'] # ICCID v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid']) # IMSI v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi']) # Ki if self._ki_file: v += self._ki_file + '10' + p['ki'] # PLMN_Sel v += '6f30' + '18' + rpad(hplmn, 36) self._scc.update_record(self._files['b_ef'][0], 1, rpad(v, self._files['b_ef'][1] * 2)) # SMSP ( 3f00/7f4d/8f0e ) # FIXME # Write PLMN_Sel forcefully as well r = self._scc.select_file(['3f00', '7f20', '6f30']) tl = int(r[-1][4:8], 16) hplmn = self._e_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (tl - 3))
def dec_msisdn(ef_msisdn: Hexstr) -> Optional[Tuple[int, int, Optional[str]]]: """ Decode MSISDN from EF.MSISDN or EF.ADN (same structure). See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3. """ # Convert from str to (kind of) 'bytes' ef_msisdn = h2b(ef_msisdn) # Make sure mandatory fields are present if len(ef_msisdn) < 14: raise ValueError("EF.MSISDN is too short") # Skip optional Alpha Identifier xlen = len(ef_msisdn) - 14 msisdn_lhv = ef_msisdn[xlen:] # Parse the length (in bytes) of the BCD encoded number bcd_len = msisdn_lhv[0] # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI if bcd_len == 0xff: return None elif bcd_len > 11 or bcd_len < 1: raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len) # Parse ToN / NPI ton = (msisdn_lhv[1] >> 4) & 0x07 npi = msisdn_lhv[1] & 0x0f bcd_len -= 1 # No MSISDN? if not bcd_len: return (npi, ton, None) msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f') # International number 10.5.118/3GPP TS 24.008 if ton == 0x01: msisdn = '+' + msisdn return (npi, ton, msisdn)
def program(self, p): # Home PLMN r = self._scc.select_file(['3f00', '7f20', '6f30']) tl = int(r[-1][4:8], 16) hplmn = self._e_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (tl - 3)) # Get total number of entries and entry size rec_cnt, rec_len = self._get_infos() # Set first entry entry = ( '81' + # 1b Status: Valid & Active rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name self._e_iccid(p['iccid']) + # 10b ICCID self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI p['ki'] + # 16b Ki lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed) ) self._scc.update_record('000c', 1, entry)
def program(self, p): # Home PLMN r = self._scc.select_file(['3f00', '7f20', '6f30']) tl = int(r[-1][4:8], 16) hplmn = self._e_plmn(p['mcc'], p['mnc']) self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3)) # Get total number of entries and entry size rec_cnt, rec_len = self._get_infos() # Set first entry entry = ( '81' + # 1b Status: Valid & Active rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name self._e_iccid(p['iccid']) + # 10b ICCID self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI p['ki'] + # 16b Ki lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed) ) self._scc.update_record('000c', 1, entry)
def s2h(s: str) -> Hexstr: """convert from an ASCII string to a string of hex nibbles""" b = bytearray() b.extend(map(ord, s)) return b2h(b)
def default(self, o): if isinstance(o, BytesIO) or isinstance(o, bytes) or isinstance( o, bytearray): return b2h(o) return json.JSONEncoder.default(self, o)
sl.send_apdu_checksw('0026000108' + args.disable_pin.encode("hex") + 'ff' * (8 - len(args.disable_pin))) if args.dump_phonebook: num_records = sc.record_count(['3f00', '7f10', '6f3a']) print("Phonebook: %d records available" % num_records) for record_id in range(1, num_records + 1): print sc.read_record(['3f00', '7f10', '6f3a'], record_id) if args.set_phonebook_entry: num_records = sc.record_count(['3f00', '7f10', '6f3a']) record_size = sc.record_size(['3f00', '7f10', '6f3a']) record_num = int(args.set_phonebook_entry[0]) if (record_num < 1) or (record_num > num_records): raise RuntimeError("Invalid phonebook record number") encoded_name = rpad(b2h(args.set_phonebook_entry[1]), (record_size - 14) * 2) if len(encoded_name) > ((record_size - 14) * 2): raise RuntimeError("Name is too long") if len(args.set_phonebook_entry[2]) > 20: raise RuntimeError("Number is too long") encoded_number = swap_nibbles(rpad(args.set_phonebook_entry[2], 20)) record = encoded_name + ( '%02x' % len(args.set_phonebook_entry[2]) ) + args.set_phonebook_entry[3] + encoded_number + 'ffff' sc.update_record(['3f00', '7f10', '6f3a'], record_num, record) if args.list_applets: (data, status) = ac.send_wrapped_apdu_ram('80f21000024f0000c0000000') while status == '6310': (partData,
def verify_chv(self, chv_no, code): fc = rpad(b2h(code), 16) return self._tp.send_apdu_checksw(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)
def change_chv(self, chv_no:int, pin_code:str, new_pin_code:str): """Change a given CHV (Card Holder Verification == PIN)""" fc = rpad(b2h(pin_code), 16) + rpad(b2h(new_pin_code), 16) data, sw = self._tp.send_apdu(self.cla_byte + '2400' + ('%02X' % chv_no) + '10' + fc) self._chv_process_sw('change', chv_no, pin_code, sw) return (data, sw)
def _chv_process_sw(self, op_name, chv_no, pin_code, sw): if sw_match(sw, '63cx'): raise RuntimeError('Failed to %s chv_no 0x%02X with code 0x%s, %i tries left.' % (op_name, chv_no, b2h(pin_code).upper(), int(sw[3]))) elif (sw != '9000'): raise SwMatchError(sw, '9000')
def s2h(s): b = bytearray() b.extend(map(ord, s)) return b2h(b)
if args.disable_pin: sl.send_apdu_checksw('0026000108' + args.disable_pin.encode("hex") + 'ff' * (8 - len(args.disable_pin))) if args.dump_phonebook: num_records = sc.record_count(['3f00','7f10','6f3a']) print ("Phonebook: %d records available" % num_records) for record_id in range(1, num_records + 1): print sc.read_record(['3f00','7f10','6f3a'], record_id) if args.set_phonebook_entry: num_records = sc.record_count(['3f00','7f10','6f3a']) record_size = sc.record_size(['3f00','7f10','6f3a']) record_num = int(args.set_phonebook_entry[0]) if (record_num < 1) or (record_num > num_records): raise RuntimeError("Invalid phonebook record number") encoded_name = rpad(b2h(args.set_phonebook_entry[1]), (record_size - 14) * 2) if len(encoded_name) > ((record_size - 14) * 2): raise RuntimeError("Name is too long") if len(args.set_phonebook_entry[2]) > 20: raise RuntimeError("Number is too long") encoded_number = swap_nibbles(rpad(args.set_phonebook_entry[2], 20)) record = encoded_name + ('%02x' % len(args.set_phonebook_entry[2])) + args.set_phonebook_entry[3] + encoded_number + 'ffff' sc.update_record(['3f00','7f10','6f3a'], record_num, record) if args.list_applets: (data, status) = ac.send_wrapped_apdu_ram('80f21000024f0000c0000000') while status == '6310': (partData, status) = ac.send_wrapped_apdu_ram('80f21001024f0000c0000000') data = data + partData while len(data) > 0:
def _decode(self, obj, context, path): return swap_nibbles(b2h(obj))
def _decode(self, obj, context, path): return b2h(obj)
def verify_chv(self, chv_no:int, code:str): """Verify a given CHV (Card Holder Verification == PIN)""" fc = rpad(b2h(code), 16) data, sw = self._tp.send_apdu(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc) self._chv_process_sw('verify', chv_no, code, sw) return (data, sw)
def unblock_chv(self, chv_no:int, puk_code:str, pin_code:str): """Unblock a given CHV (Card Holder Verification == PIN)""" fc = rpad(b2h(puk_code), 16) + rpad(b2h(pin_code), 16) data, sw = self._tp.send_apdu(self.cla_byte + '2C00' + ('%02X' % chv_no) + '10' + fc) self._chv_process_sw('unblock', chv_no, pin_code, sw) return (data, sw)
def export(self, filename, context): """ Select and export a single file """ context['COUNT'] += 1 df = self._cmd.rs.selected_file if not isinstance(df, CardDF): raise RuntimeError( "currently selected file %s is not a DF or ADF" % str(df)) df_path_list = df.fully_qualified_path(True) df_path_list_fid = df.fully_qualified_path(False) file_str = '/'.join(df_path_list) + "/" + str(filename) self._cmd.poutput(boxed_heading_str(file_str)) self._cmd.poutput("# directory: %s (%s)" % ('/'.join(df_path_list), '/'.join(df_path_list_fid))) try: fcp_dec = self._cmd.rs.select(filename, self._cmd) self._cmd.poutput("# file: %s (%s)" % (self._cmd.rs.selected_file.name, self._cmd.rs.selected_file.fid)) fd = fcp_dec['file_descriptor'] structure = fd['structure'] self._cmd.poutput("# structure: %s" % str(structure)) for f in df_path_list: self._cmd.poutput("select " + str(f)) self._cmd.poutput("select " + self._cmd.rs.selected_file.name) if structure == 'transparent': result = self._cmd.rs.read_binary() self._cmd.poutput("update_binary " + str(result[0])) elif structure == 'cyclic' or structure == 'linear_fixed': num_of_rec = fd['num_of_rec'] for r in range(1, num_of_rec + 1): result = self._cmd.rs.read_record(r) self._cmd.poutput("update_record %d %s" % (r, str(result[0]))) elif structure == 'ber_tlv': tags = self._cmd.rs.retrieve_tags() for t in tags: result = self._cmd.rs.retrieve_data(t) (tag, l, val, remainer) = bertlv_parse_one(h2b(result[0])) self._cmd.poutput("set_data 0x%02x %s" % (t, b2h(val))) else: raise RuntimeError('Unsupported structure "%s" of file "%s"' % (structure, filename)) except Exception as e: bad_file_str = '/'.join(df_path_list) + "/" + str( filename) + ", " + str(e) self._cmd.poutput("# bad file: %s" % bad_file_str) context['ERR'] += 1 context['BAD'].append(bad_file_str) # When reading the file is done, make sure the parent file is # selected again. This will be the usual case, however we need # to check before since we must not select the same DF twice if df != self._cmd.rs.selected_file: self._cmd.rs.select(df.fid or df.aid, self._cmd) self._cmd.poutput("#")
def enable_chv(self, chv_no:int, pin_code:str): """Enable a given CHV (Card Holder Verification == PIN)""" fc = rpad(b2h(pin_code), 16) data, sw = self._tp.send_apdu(self.cla_byte + '2800' + ('%02X' % chv_no) + '08' + fc) self._chv_process_sw('enable', chv_no, pin_code, sw) return (data, sw)
def s2h(s): return b2h(s)
if args.disable_pin: sl.send_apdu_checksw('0026000108' + args.disable_pin.encode("hex") + 'ff' * (8 - len(args.disable_pin))) if args.dump_phonebook: num_records = sc.record_count(['3f00','7f10','6f3a']) print ("Phonebook: %d records available" % num_records) for record_id in range(1, num_records + 1): print sc.read_record(['3f00','7f10','6f3a'], record_id) if args.set_phonebook_entry: num_records = sc.record_count(['3f00','7f10','6f3a']) record_size = sc.record_size(['3f00','7f10','6f3a']) record_num = int(args.set_phonebook_entry[0]) if (record_num < 1) or (record_num > num_records): raise RuntimeError("Invalid phonebook record number") encoded_name = rpad(b2h(args.set_phonebook_entry[1]), (record_size - 14) * 2) if len(encoded_name) > ((record_size - 14) * 2): raise RuntimeError("Name is too long") if len(args.set_phonebook_entry[2]) > 20: raise RuntimeError("Number is too long") encoded_number = swap_nibbles(rpad(args.set_phonebook_entry[2], 20)) record = encoded_name + ('%02x' % len(args.set_phonebook_entry[2])) + args.set_phonebook_entry[3] + encoded_number + 'ffff' sc.update_record(['3f00','7f10','6f3a'], record_num, record) if args.list_applets: (data, status) = ac.send_wrapped_apdu('80f21000024f0000c0000000') while status == '6310': (partData, status) = ac.send_wrapped_apdu('80f21001024f0000c0000000') data = data + partData while len(data) > 0: