Newer
Older
Import / projects / LGN-IP3870 / t / new / simreader.py
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)