from utils import print_hex
import config
SIM_CLA = 0xA0
MASTER_FILE = 0x3f00
DF_TELECOM = 0x7f10
EF_ADN = 0x6f3a
class SimError(Exception):
pass
def bcd_swapped_encode(number):
i = 0
bcd = ''
while 1:
if i >= len(number):
break
if number[i] == '*':
c = 0x0a
elif number[i] == '#':
c = 0x0b
elif '0' <= number[i] <= '9':
c = ord(number[i]) - ord('0')
i += 1
if i >= len(number):
bcd += chr(c + 0xF0)
break
if number[i] == '*':
c += 0x0a << 4
elif number[i] == '#':
c += 0x0b << 4
elif '0' <= number[i] <= '9':
c += (ord(number[i]) - ord('0')) << 4
bcd += chr(c)
i += 1
return bcd
def bcd_swapped_decode(bcd):
bcd = [ord(x) for x in bcd]
num_table = '0123456789*#'
number = ''
i = 0
while 1:
if i >= len(bcd) or bcd[i] == 0xFF:
break
x = bcd[i] & 0xF
if x < 12:
number += num_table[x]
x = bcd[i] >> 4
if x < 12:
number += num_table[x]
i += 1
return number
def sim_name_decode(name):
name = [ord(x) for x in name]
if name[0] == 0x81:
import gsm7
len = name[1]
base = name[2] << 7
u = u''
for n in name[3:3+len]:
if n & 0x80:
u += unichr(base + (n & 0x7F))
else:
u += unicode(gsm7.gsm7bit_decode(chr(n)), 'utf-8')
return u.encode('utf-8')
return ''
def sim_name_encode(name, max_len):
u = unicode(name, 'utf-8')
import gsm7
ascii = gsm7.gsm7_ascii(u)
if ascii:
if len(ascii) > max_len:
ascii = ascii[:max_len]
if ascii[-1] == chr(27):
ascii = ascii[:-1]
return ascii
if len(u) + 3 > max_len:
u = u[:max_len-3]
x = '\x81' + chr(len(u)) + '\x01' + gsm7.ucs2_encode_for_sim(u)
if len(x) > max_len:
x = x[:max_len]
if x[-1] == chr(27):
x = x[:-1]
return x
import os
class SIMReader:
def __init__(self, path):
self.sim = os.open(path, os.O_RDWR)
def close(self):
os.close(self.sim)
def write(self, data):
n = 0
while data:
l = os.write(self.sim, data[:16])
if l <= 0:
return l
n += l
data = data[16:]
return n
def read(self, n):
return os.read(self.sim, n)
def send_command(self, ins, p1, p2, data):
message = chr(SIM_CLA) + chr(ins) + chr(p1) + chr(p2)
case4 = type(data) == type('')
if case4:
# case 4: send data
if config.sim_reader_debug:
print 'send command: data len =', len(data)
lc = chr(len(data))
self.write(message + lc)
while data:
ack = ord(self.read(1))
if config.sim_reader_debug:
print 'ins: %02X ack: %02X' % (ins, ack)
if (ack & 0xFE) == (ins & 0xFE):
self.write(data)
break
elif ((~ack & 0xFE) == (ins & 0xFE)):
if config.sim_reader_debug:
print 'inverse ack'
self.write(data[0])
data = data[1:]
else:
if config.sim_reader_debug:
print 'INVALID ACK: ins: %02X ack: %02X' % (ins, ack)
return
resp = self.read(256)
else:
le = chr(data)
self.write(message + le)
resp = self.read(data + 3) # ACK, SW1, SW2
if len(resp) < 2:
if config.sim_reader_debug:
print 'Too short response', resp
return
sw1 = ord(resp[-2])
sw2 = ord(resp[-1])
resp = resp[:-2]
if config.sim_reader_debug:
print 'resp', ''.join(['[%02X]' % ord(x) for x in resp])
print 'sw1: %02X sw2: %02X' % (sw1, sw2)
if sw1 == 0x90:
if config.sim_reader_debug:
print 'normal end'
return resp
elif sw1 == 0x9f:
if config.sim_reader_debug:
print 'has response data', sw2
return self.get_resp(sw2)
elif sw1 == 0x94:
message1 = 'reference management error'
if sw2 == 0x00:
message2 = 'no EF selected'
elif sw2 == 0x02:
message2 = 'out of range(invalid address)'
elif sw2 == 0x04:
message2 = 'file ID not found'
elif sw2 == 0x08:
message2 = 'file is inconsistent with the command'
else:
message2 = 'sw1 : 0x94 but sw2 is invalid???', sw2
raise SimError, (message1, message2)
elif sw1 == 0x98:
message1 = 'Security Managemenet error'
if sw2 == 0x02:
message2 = 'no CHV initialized'
elif sw2 == 0x04:
message2 = 'access condition not fullfilled'
elif sw2 == 0x08:
message2 = 'in contradiction with CHV status'
elif sw2 == 0x10:
message2 = 'in contradiction with invalidation status'
elif sw2 == 0x40:
message2 = 'CHV blocked'
elif sw2 == 0x50:
message2 = 'increase cannot be performed'
raise SimError, (message1, message2)
elif sw1 == 0x67:
message = 'incorrect parament p3'
raise SimError, message
elif sw1 == 0x6b:
message = 'incorrect parament p1, or p2'
raise SimError, message
elif sw1 == 0x6d:
message = 'unknown instruction'
raise SimError, message
elif sw1 == 0x6e:
message = 'wrong instruction class'
raise SimError, message
elif sw1 == 0x6f:
message = 'unknown problem'
raise SimError, message
else:
message = 'unhandled error: sw1 = %02X' % (sw1,)
if config.sim_reader_debug:
print 'wrong instruction class'
print 'unknown instruction'
print 'incorrect parament p1, or p2'
raise SimError, message
def get_resp(self, le):
if config.sim_reader_debug:
print 'get resp', le
ins = 0xc0
p1 = p2 = 0x00
p3 = le
return self.send_command(ins, p1, p2, p3)
def select(self, fid):
ins = 0xa4
p1 = p2 = 0x00
data = chr(fid>>8) + chr(fid&0xff)
return self.send_command(ins, p1, p2, data)
def verify_chv(self, chv_no, pin):
if config.sim_reader_debug:
print 'verify chv', pin
ins = 0x20
p1 = 0x00
p2 = chv_no
if len(pin) < 8:
pin += chr(0xFF) * (8 - len(pin))
data = pin
return self.send_command(ins, p1, p2, data)
def change_chv(self, chv_no, opin, npin):
ins = 0x24
p1 = 0x00
p2 = chv_no
if len(opin) < 8:
opin += chr(0xFF) * (8 - len(opin))
if len(npin) < 8:
npin += chr(0xFF) * (8 - len(npin))
data = opin + npin
return self.send_command(ins, p1, p2, data)
def unblock_chv(self, chv_no, new_chv, unblock_chv):
ins = 0x2c
p1 = 0x00
if chv_no == 1:
p2 = 0x00
else:
p2 = 0x02
if len(unblock_chv) < 8:
unblock_chv += chr(0xFF) * (8 - len(unblock_chv))
if len(new_chv) < 8:
new_chv += chr(0xFF) * (8 - len(new_chv))
data = unblock_chv + new_chv
return self.send_command(ins, p1, p2, data)
def enable_chv(self, pin):
# chv1 only
ins = 0x28
p1 = 0x00
p2 = 0x01
if len(pin) < 8:
pin += chr(0xFF) * (8 - len(pin))
data = pin
return self.send_command(ins, p1, p2, data)
def disable_chv(self, pin):
# chv1 only
ins = 0x26
p1 = 0x00
p2 = 0x01
if len(pin) < 8:
pin += chr(0xFF) * (8 - len(pin))
data = pin
return self.send_command(ins, p1, p2, data)
def read_record(self, record, record_len):
ins = 0xb2
p1 = record
p2 = 0x04 # absolute mode
p3 = record_len
return self.send_command(ins, p1, p2, p3)
def update_record(self, record, data):
ins = 0xdc
p1 = record
p2 = 0x04 # absolute mode
return self.send_command(ins, p1, p2, data)
def check_pin_enabled(self):
return self.pin_enabled
def select_adn(self):
import time, ntptime
if config.sim_reader_debug:
print 'select master'
resp = self.select(MASTER_FILE)
time.sleep(0.2)
if config.sim_reader_debug:
print 'select telecom'
resp = self.select(DF_TELECOM)
time.sleep(0.2)
df = DF(resp)
self.pin_enabled = False
if not df.char & 0x80:
#self.verify_chv(1, '0000')
#pass
self.pin_enabled = True
resp = self.select(EF_ADN)
ef = EF(resp)
self.num_adn = ef.num_record
self.len_adn = ef.len_record
def get_adn(self, n):
record = self.read_record(n, self.len_adn)
record = record[1:] # strip ack byte
name_len = self.len_adn - 14
if config.sim_reader_debug:
print 'get adn: record'
print_hex(record)
name = record[:name_len]
if name[0] in ('\x81', '\x80', '\x82'):
name = sim_name_decode(name)
else:
end = name.find('\xFF')
if end >= 0:
name = name[:end]
import gsm7
name = gsm7.gsm7bit_decode(name)
record = record[name_len:]
len_num = ord(record[0]) - 1
if len_num == 0xFE:
return '',''
if config.sim_reader_debug:
print 'number record part'
print_hex(record)
number_type = ord(record[1])
number = bcd_swapped_decode(record[2:len_num + 2])
if number_type == 0x91:
number = '00' + number
elif number_type == 0x81:
pass
else:
if config.sim_reader_debug:
print 'SIM: invalid number type', number_type
return name, number
# for debug
def get_adn_all(self, n):
record = self.read_record(n, self.len_adn)
record = record[1:] # strip ack byte
all_record = record
name_len = self.len_adn - 14
if config.sim_reader_debug:
print 'get adn: record'
print_hex(record)
name = record[:name_len]
end = name.find('\xFF')
if end >= 0:
name = name[:end]
record = record[name_len:]
len_num = ord(record[0]) - 1
if len_num == 0xFE:
return '','', all_record
if config.sim_reader_debug:
print 'number record part'
print_hex(record)
number_type = ord(record[1])
number = bcd_swapped_decode(record[2:len_num + 2])
if number_type == 0x91:
number = '00' + number
elif number_type == 0x81:
pass
else:
if config.sim_reader_debug:
print 'SIM: invalid number type', number_type
return name, number, all_record
def update_adn(self, n, name, number):
name_len = self.len_adn - 14
name = sim_name_encode(name, name_len)
if len(name) > name_len:
name = name[:name_len]
elif len(name) < name_len:
name += '\xFF' * (name_len - len(name))
if number.startswith('00'):
number_type = chr(0x91)
number = number[2:]
else:
number_type = chr(0x81)
len_num = 1 + (len(number) + 1)/2
bcd_number = bcd_swapped_encode(number)
if len(bcd_number) < 10:
bcd_number += chr(0xFF) * (10 - len(bcd_number))
record = name + chr(len_num) + number_type + bcd_number + chr(0xFF) + chr(0xFF)
if config.sim_reader_debug:
print 'update adn: record'
print_hex(record)
#assert len(record) == self.len_adn
self.update_record(n, record)
def __del__(self):
#os.close(self.sim)
pass
class EF:
def __init__(self, data):
#assert data
data = [ord(x) for x in data]
self.size = 256 * data[3] + data[4]
self.id = data[5] * 256 + data[6]
self.type = data[7]
self.access_condition = "%02X%02X%02X" % tuple(data[9:12])
self.status = data[12]
self.structure = data[14]
self.len_record = data[15]
if config.sim_reader_debug:
print 'num of record', self.size / self.len_record
self.num_record = self.size / self.len_record
class DF:
def __init__(self, data):
#assert data
data = [ord(x) for x in data]
self.size = 256 * data[3] + data[4]
self.id = data[5] * 256 + data[6]
self.type = data[7]
self.char = data[14]
self.num_of_df = data[15]
self.num_of_ef = data[16]
self.num_chv = data[17]
if config.sim_reader_debug:
print 'num of chv', self.num_chv
self.chv1_status = data[19]
self.unblock_chv1_status = data[20]
self.chv2_status = data[21]
self.unblock_chv2_status = data[22]
def unblock_chv():
sim.unblock_chv(1, '0000', '39779971')
def test_adn(sim):
sim.select_adn()
print 'maximum name length', sim.len_adn - 14
print 'maximum record count', sim.num_adn
sim.verify_chv(1, '0000')
for i in range(5):
adn = sim.get_adn(i+1)
if adn:
name, number = adn
print i, '- name:', name, 'number:', number
def test1(sim):
print 'select MF'
resp = sim.select(MASTER_FILE)
df = DF(resp)
print 'mf: chv1 %02X %02X' % (df.chv1_status, df.unblock_chv1_status)
print 'mf: chv2 %02X %02X' % (df.chv2_status, df.unblock_chv2_status)
print 'select DF TELECOM'
resp = sim.select(DF_TELECOM)
df = DF(resp)
if df.char & 0x80:
print 'chv1 disabled'
else:
print 'chv1 enabed'
print 'telecom: mf: chv1 %02X %02X' % (df.chv1_status, df.unblock_chv1_status)
print 'telecom: mf: chv2 %02X %02X' % (df.chv2_status, df.unblock_chv2_status)
#if df.char & 0x80:
# sim.enable_chv('0000')
#else:
# sim.disable_chv('0000')
print 'select EF ADN'
resp = sim.select(EF_ADN)
ef = EF(resp)
print 'record size', ef.len_record
print 'access condition', ef.access_condition
print 'verify chv'
#resp = sim.verify_chv(1, '0000')
print_hex(resp)
for i in range(ef.num_record/50):
resp = sim.read_record(i + 1, ef.len_record)
if resp[1:] == chr(0xFF) * (ef.len_record-1):
print_hex(resp)
def remove_all_adn(sim):
sim.select_adn()
print 'maximum name length', sim.len_adn - 14
print 'maximum record count', sim.num_adn
if sim.check_pin_enabled():
sim.verify_chv(1, '0000')
for i in range(sim.num_adn):
sim.update_adn(i+1, '', '')
print 'Remove All OK'
def read_all_adn(sim):
sim.select_adn()
print 'maximum name length', sim.len_adn - 14
print 'maximum record count', sim.num_adn
if sim.check_pin_enabled():
sim.verify_chv(1, '0000')
for i in range(sim.num_adn):
adn = sim.get_adn(i+1)
name, number = adn
if name:
print i+1, '- name:', name, 'number:', number
def insert_adn(sim):
sim.select_adn()
print 'maximum name length', sim.len_adn - 14
print 'maximum record count', sim.num_adn
if sim.check_pin_enabled():
sim.verify_chv(1, '0000')
sim.update_adn(1, 'minho', '0168625457')
sim.update_adn(3, 'megamino', '105')
sim.update_adn(5, 'softon', '107')
sim.update_adn(7, 'king', '156')
sim.update_adn(10, 'mapae', '128')
sim.update_adn(15, 'metallica', '125')
sim.update_adn(30, 'antizm', '124')
print 'Insert data OK'
def toggle_pin_setting(sim):
sim.select_adn()
print '### current PIN enabled:', sim.check_pin_enabled()
if sim.check_pin_enabled():
sim.disable_chv('0000')
if config.sim_reader_debug:
print '### changed PIN enabled: False'
else:
sim.enable_chv('0000')
print '### changed PIN enabled: True'
def dump_sim(sim):
f = open('/tmp/simdata.txt', 'w')
sim.select_adn()
print 'dump sim....'
for i in range(sim.num_adn):
name, num, record = sim.get_adn_all(i+1)
f.write('%d %s %s\n' % (i+1, name,num))
for i, c in enumerate(record):
f.write("[%02X]" % (ord(c)))
if i%16 == 15:
f.write('\n')
f.write('\n\n')
f.close()
print 'ok...check /tmp/simdata.txt'
if __name__ == '__main__':
'''
x = bcd_swapped_encode('498996534264')
assert '498996534264' == bcd_swapped_decode(x)
x = bcd_swapped_encode('08996534264')
assert '08996534264' == bcd_swapped_decode(x)
'''
#import sys
#sys.exit(0)
sim = SIMReader('/dev/misc/usim')
try:
resp = sim.get_resp(80)
except:
pass
#remove_all_adn(sim)
#read_all_adn(sim)
#insert_adn(sim)
#toggle_pin_setting(sim)
dump_sim(sim)