2006-05-26 04:51:31 +00:00
import utils
2010-10-15 06:20:22 +00:00
from iso_card import *
2011-03-23 22:21:26 +00:00
import building_blocks
2006-05-26 04:51:31 +00:00
2011-03-23 22:21:26 +00:00
class GSM_Card ( building_blocks . Card_with_read_binary , ISO_Card ) :
2007-03-18 23:38:24 +00:00
DRIVER_NAME = [ " GSM " ]
2010-10-15 14:37:26 +00:00
COMMAND_GET_RESPONSE = C_APDU ( " \xa0 \xC0 \x00 \x00 " )
2011-03-23 22:21:26 +00:00
CLA = 0xA0
APDU_GET_RESPONSE = C_APDU ( cla = CLA , ins = 0xC0 )
APDU_RUN_GSM_ALGO = C_APDU ( cla = CLA , ins = 0x88 )
APDU_VERIFY_PIN = C_APDU ( cla = CLA , ins = 0x20 )
APDU_SELECT_FILE = C_APDU ( cla = CLA , ins = 0xA4 )
APDU_READ_BINARY = C_APDU ( cla = CLA , ins = 0xB0 )
APDU_STATUS = C_APDU ( cla = CLA , ins = 0xF2 )
APDU_UPD_BINARY = C_APDU ( cla = CLA , ins = 0xD6 )
APDU_UPD_RECORD = C_APDU ( cla = CLA , ins = 0xDC )
# TS 11.14 / STK related
APDU_TERMINAL_PROFILE = C_APDU ( cla = CLA , ins = 0x10 )
APDU_ENVELOPE = C_APDU ( cla = CLA , ins = 0xC2 )
APDU_FETCH = C_APDU ( cla = CLA , ins = 0x12 )
APDU_TERMINAL_RESPONSE = C_APDU ( cla = CLA , ins = 0x14 )
2007-02-13 00:32:04 +00:00
STATUS_MAP = {
2010-10-15 06:20:22 +00:00
Card . PURPOSE_GET_RESPONSE : ( " 9F?? " , )
2007-02-13 00:32:04 +00:00
}
2011-03-23 22:21:26 +00:00
INTERESTING_DFS = [
( " DF.GSM " , " \x7f \x20 " ) ,
( " DF.TELECOM " , " \x7f \x10 " ) ,
]
# Files at the GSM application level
GSM_DFS = [
( " LP " , " \x6f \x05 " , " Language Preference " ) ,
( " IMSI " , " \x6f \x07 " , " IMSI " ) ,
( " Kc " , " \x6f \x20 " , " Ciphering Key Kc " ) ,
( " PLMNsel " , " \x6f \x30 " , " PLMN selector " ) ,
( " HPPLMN " , " \x6f \x31 " , " Higher Priority PLMN search period " ) ,
( " ACMmax " , " \x6f \x37 " , " ACM maximum value " ) ,
( " SST " , " \x6f \x38 " , " SIM service table " ) ,
( " ACM " , " \x6f \x39 " , " Accumulated call meter " ) ,
( " GID1 " , " \x6f \x3e " , " Group Identifier Level 1 " ) ,
( " GID2 " , " \x6f \x3f " , " Group Identifier Level 2 " ) ,
( " SPN " , " \x6f \x46 " , " Service Provider Name " ) ,
( " PUCT " , " \x6f \x41 " , " Price per unit and currency table " ) ,
( " CBMI " , " \x6f \x45 " , " Cell broadcast message identifier selection " ) ,
( " BCCH " , " \x6f \x74 " , " Broadcast control channels " ) ,
( " ACC " , " \x6f \x78 " , " Access control class " ) ,
( " FPLMN " , " \x6f \x7b " , " Forbidden PLMNs " ) ,
( " LOCI " , " \x6f \x7e " , " Location Information " ) ,
( " AD " , " \x6f \xAD " , " Administrative Data " ) ,
( " Phase " , " \x6f \xAE " , " Phase identification " ) ,
( " VGCS " , " \x6f \xB1 " , " Voice Group Call Service " ) ,
( " VGCSS " , " \x6f \xB2 " , " Voice Group Call Service Status " ) ,
( " VBS " , " \x6f \xB3 " , " Voice Broadcast Service " ) ,
( " VBSS " , " \x6f \xB4 " , " Voice Broadcast Service Status " ) ,
( " eMLPP " , " \x6f \xB5 " , " enhanced Multi Level Pre-emptyion and Priority " ) ,
( " AAeM " , " \x6f \xB5 " , " Authomatic Answer for eEMLPP Service " ) ,
( " CBMID " , " \x6f \x48 " , " Cell Broadcast Message Identifier for Data Download " ) ,
( " ECC " , " \x6f \xB7 " , " Emergency Call Codes " ) ,
( " CBMIR " , " \x6f \x50 " , " Cell broadcast message identifier range selection " ) ,
( " DCK " , " \x6f \x2C " , " De-personalization Control Keys " ) ,
( " CNL " , " \x6f \x32 " , " Co-operative Network List " ) ,
( " NIA " , " \x6f \x51 " , " Network ' s Indication of Alerting " ) ,
( " KcGPRS " , " \x6f \x52 " , " GPRS Ciphering Key KcGPRS " ) ,
( " LOCIGPRS " , " \x6f \x53 " , " GPRS Location Information " ) ,
( " SUME " , " \x6f \x54 " , " SetUpMenu Elements " ) ,
( " PLMNwAcT " , " \x6f \x60 " , " User controlled PLMN Selector with Access Technology " ) ,
( " OPLMNwAcT " , " \x6f \x51 " , " Operator controlled PLMN Selector with Access Technology " ) ,
( " HPLMNwAcT " , " \x6f \x62 " , " HPLMN Selector with Access Technology " ) ,
( " CPBCCH " , " \x6f \x63 " , " CPBCCH Information " ) ,
( " InvScan " , " \x6f \x64 " , " Investigation Scan " ) ,
]
# According to TS 11.11 Chapter 9.3
type_of_file_names = { 0 : ' RFU ' , 1 : ' MF ' , 2 : ' DF ' , 4 : ' EF ' }
struct_of_file_names = { 0 : ' transparent ' ,
1 : ' linear fixed ' ,
2 : ' transparent ' ,
3 : ' cyclic ' }
acc_cond_names = { 0 : ' ALWAYS ' ,
1 : ' CHV1 ' ,
2 : ' CHV2 ' ,
3 : ' RFU ' ,
4 : ' ADM1 ' ,
5 : ' ADM2 ' ,
6 : ' ADM3 ' ,
7 : ' ADM4 ' ,
8 : ' ADM5 ' ,
9 : ' ADM6 ' ,
10 : ' ADM7 ' ,
11 : ' ADM8 ' ,
12 : ' ADM9 ' ,
13 : ' ADM10 ' ,
14 : ' ADM11 ' ,
15 : ' NEW ' }
2006-05-26 04:51:31 +00:00
ATRS = [
( " 3bff9500ffc00a1f438031e073f62113574a334861324147d6 " , None ) ,
2011-03-23 22:21:26 +00:00
( " 3b9a940092027593110001020200 " , None ) ,
( " 3b989400939114010c020102 " , None ) ,
#("3b991800118822334455667760", None),
#("3bdf96ff80b1fe451fc78031e073fe21136791150103040404ef", None),
2006-05-26 04:51:31 +00:00
]
2011-03-23 22:21:26 +00:00
def before_send ( self , apdu ) :
if apdu . cla == 0x00 :
apdu . CLA = self . CLA
return apdu
def cmd_run_gsm_algo ( self , rand ) :
""" Perform the GSM A3/A8 algorithm.
RAND is the random challenge to be sent to the card . """
if len ( rand ) != 16 :
rand2 = binascii . a2b_hex ( " " . join ( rand . split ( ) ) )
else :
rand2 = rand
if len ( rand2 ) != 16 :
raise TypeError , " Need either exactly 16 binary bytes or 16 hexedecimal bytes for the RAND argument. "
apdu = C_APDU ( self . APDU_RUN_GSM_ALGO ,
p1 = 0 , p2 = 0 , data = rand2 )
result = self . send_apdu ( apdu )
return result
def cmd_cd_gsm ( self ) :
""" Change into DF.GSM. """
apdu = C_APDU ( self . APDU_SELECT_FILE , data = " \x7f \x20 " )
result = self . send_apdu ( apdu )
return result
# def cmd_cd(self, dir = None):
# fid = None
# for n, f in self.INTERESTING_FILES:
# if n == dir:
# fid = f
# break
# if fid is None:
# return ISO_7816_4_Card.cmd_cd(self, dir)
# else:
# return ISO_7816_4_Card.change_dir(self, fid)
def change_dir ( self , fid = None ) :
" Change to a child DF. Alternatively, change to MF if fid is None. "
if fid is None :
return self . select_file ( 0 , 0 , " \x3f \x00 " )
else :
return self . select_file ( 0 , 0 , fid )
def cmd_status ( self ) :
""" STATUS Command. """
apdu = C_APDU ( self . APDU_STATUS , data = " \xff " )
result = self . send_apdu ( apdu )
if len ( result . data ) > 0 :
print utils . hexdump ( result . data )
print self . sel_ret_decode ( result . data )
def cmd_term_prof ( self , data ) :
""" TERMINAL PROFILE Command. """
apdu = C_APDU ( self . APDU_TERMINAL_PROFILE , data = data )
return self . send_apdu ( apdu )
def cmd_envelope ( self , data ) :
""" ENVELOPE Command. """
apdu = C_APDU ( self . APDU_ENVELOPE , data = data )
return self . send_apdu ( apdu )
def cmd_fetch ( self , data ) :
""" FETCH Command. """
apdu = C_APDU ( self . APDU_FETCH , data = data )
return self . send_apdu ( apdu )
def cmd_term_resp ( self , data ) :
""" TERMINAL RESPONSE Command. """
apdu = C_APDU ( self . APDU_TERMINAL_RESPONSE , data = data )
return self . send_apdu ( apdu )
def cmd_upd_binary ( self , data ) :
""" Write to a transparent binary file. """
data_bin = binascii . a2b_hex ( " " . join ( data . split ( ) ) )
apdu = C_APDU ( self . APDU_UPD_BINARY , data = data_bin )
return self . send_apdu ( apdu )
def cmd_selectfile ( self , fid ) :
""" Select a file on the card. """
fid = binascii . a2b_hex ( " " . join ( fid . split ( ) ) )
result = self . select_file ( 0 , 0 , fid )
if len ( result . data ) > 0 :
print utils . hexdump ( result . data )
print self . sel_ret_decode ( result . data )
def sel_ret_decode ( self , data ) :
#print "File: %s" % (data[
type_of_file = ord ( data [ 6 ] )
print " Type of File: %s " % ( self . type_of_file_names [ type_of_file ] )
if type_of_file == 4 :
structure = ord ( data [ 12 ] )
print " Structure of File: %s " % ( self . struct_of_file_names [ structure ] )
if structure == 3 :
if ord ( data [ 7 ] ) & 0x80 :
print " INCREASE allowed "
else :
print " INCREASE not allowed "
if structure == 1 or structure == 3 :
print " Record size: %u bytes " % ord ( data [ 14 ] )
#print "File Size: %u bytes" %
acc_cond = data [ 8 : 11 ]
self . acc_cond_decode ( acc_cond )
status = ord ( data [ 11 ] )
if not status & 0x1 :
print " Status: invalidated, "
if not status & 0x4 :
print " not "
print " readable and updatable \n "
elif type_of_file == 2 :
#print "Total unallocated memory in DF: %u bytes"
gsm_spec_data = data [ 13 : ]
print " Number of DFs : %u " % ( ord ( gsm_spec_data [ 1 ] ) )
print " Number of EFs : %u " % ( ord ( gsm_spec_data [ 2 ] ) )
print " Number of CHV : %u " % ( ord ( gsm_spec_data [ 3 ] ) )
print " CHV1 status : %s " % ( self . chv_status_decode ( gsm_spec_data [ 5 ] ) )
print " UNBLOCK CHV1 status : %s " % ( self . chv_status_decode ( gsm_spec_data [ 6 ] ) )
print " CHV2 status : %s " % ( self . chv_status_decode ( gsm_spec_data [ 7 ] ) )
print " UNBLOCK CHV2 status : %s " % ( self . chv_status_decode ( gsm_spec_data [ 8 ] ) )
def chv_status_decode ( self , s ) :
status = ord ( s )
if status & 0x80 :
initialized = 1
else :
initialized = 0
return " Initialized: %u , Retries remaining: %u " % ( initialized , status & 0xf )
def acc_cond_decode ( self , acc_cond ) :
print " Access cond. READ/SEEK : %s " % ( self . acc_cond_names [ ord ( acc_cond [ 0 ] ) >> 4 ] )
print " Access cond. UPDATE : %s " % ( self . acc_cond_names [ ord ( acc_cond [ 0 ] ) & 0xf ] )
print " Access cond. INCREASE : %s " % ( self . acc_cond_names [ ord ( acc_cond [ 1 ] ) >> 4 ] )
print " Access cond. REHABILITATE: %s " % ( self . acc_cond_names [ ord ( acc_cond [ 2 ] ) >> 4 ] )
print " Access cond. INVALIDATE : %s " % ( self . acc_cond_names [ ord ( acc_cond [ 2 ] ) & 0xf ] )
COMMANDS = dict ( Card . COMMANDS )
COMMANDS . update ( building_blocks . Card_with_read_binary . COMMANDS )
COMMANDS . update ( {
" gsm_run_algo " : cmd_run_gsm_algo ,
#"cd" : cmd_cd,
" cd_df_gsm " : cmd_cd_gsm ,
" gsm_status " : cmd_status ,
# STK
" gsm_terminal_profile " : cmd_term_prof ,
" gsm_envelope " : cmd_envelope ,
" gsm_fetch " : cmd_fetch ,
" gsm_terminal_response " : cmd_term_resp ,
" gsm_select_file " : cmd_selectfile ,
" update_binary " : cmd_upd_binary ,
} )
2006-05-26 04:51:31 +00:00
STATUS_WORDS = {
2011-03-23 22:21:26 +00:00
# TS 11.11 Chapter 9.4.1
#'9000': "Normal ending of the command"
#'91??': "Normal ending of the command, with extra information from proactive SIM"
' 9E?? ' : " Length ' $(SW2)i (0x$(SW2)02x) ' of the response data in case of SIM data dl error " ,
2006-05-26 04:51:31 +00:00
' 9F?? ' : " Length ' %(SW2)i (0x %(SW2)02x ) ' of the response data " ,
2011-03-23 22:21:26 +00:00
# TS 11.11 Chapter 9.4.2
' 9300 ' : " SIM Application Toolkit busy " ,
# TS 11.11 Chapter 9.4.3
2006-05-26 04:51:31 +00:00
' 920? ' : lambda sw1 , sw2 : " Update successful but after using an internal retry routine ' %i ' times " % ( sw2 % 16 ) ,
' 9240 ' : " Memory problem " ,
2011-03-23 22:21:26 +00:00
# TS 11.11 Chapter 9.4.4
2006-05-26 04:51:31 +00:00
' 9400 ' : " No EF selected " ,
' 9402 ' : " Out of range (invalid address) " ,
' 9404 ' : " - File ID not found \n - Pattern not found " ,
' 9408 ' : " File is inconsistent with the command " ,
2011-03-23 22:21:26 +00:00
# TS 11.11 Chapter 9.4.5
2006-05-26 04:51:31 +00:00
' 9802 ' : " No CHV initialized " ,
' 9804 ' : " - Access condition not fulfilled \n - Unsuccessful CHV verification, at least one attempt left \n - unsuccesful UNBLOCK CHV verification, at least one attempt left \n - authentication failed " ,
' 9808 ' : " In contradiction with CHV status " ,
' 9810 ' : " In contradiction with invalidation status " ,
' 9840 ' : " - Unsuccessful CHV verification, no attempt left \n - unsuccesful UNBLOCK CHV verification, no attempt left \n - CHV blocked \n - UNBLOCK CHV blocked " ,
' 9850 ' : " Increase cannot be performed, Max value reached " ,
2011-03-23 22:21:26 +00:00
# TS 11.11 Chapter 9.4.6
2006-05-26 04:51:31 +00:00
" 67?? " : " Incorrect parameter P3 " ,
" \x67 \x00 " : " Incorrect parameter P3 (ISO:Wrong length) " ,
" 6B?? " : " Incorrect parameter P1 or P2 " ,
" \x6B \x00 " : " Incorrect parameter P1 or P2 (ISO:Wrong parameter(s) P1-P2) " ,
" 6D?? " : " Unknown instruction code given in the command " ,
" \x6D \x00 " : " Unknown instruction code given in the command (ISO: Instruction code not supported or invalid) " ,
" 6E?? " : " Wrong instruction class given in the command " ,
" \x6E \x00 " : " Wrong instruction class given in the command (ISO: Class not supported) " ,
" 6F?? " : " Technical problem with no diagnostic given " ,
" \x6F \x00 " : " Technical problem with no diagnostic given (ISO: No precise diagnosis) " ,
}