590 lines
23 KiB
Python
590 lines
23 KiB
Python
# -*- coding: UTF-8 -*-
|
|
"""
|
|
card: Library adapted to request (U)SIM cards and other types of telco cards.
|
|
Copyright (C) 2010 Benoit Michau
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
"""
|
|
|
|
|
|
#################################
|
|
# Python library to work on
|
|
# USIM card
|
|
# communication based on ISO7816 card
|
|
# and commands and formats based on UICC card
|
|
#
|
|
# needs pyscard from:
|
|
# http://pyscard.sourceforge.net/
|
|
#################################
|
|
|
|
from card.ICC import UICC, ISO7816
|
|
from card.SIM import SIM
|
|
from card.FS import USIM_app_FS
|
|
from card.utils import *
|
|
|
|
USIM_service_table = {
|
|
1 : 'Local Phone Book',
|
|
2 : 'Fixed Dialling Numbers (FDN)',
|
|
3 : 'Extension 2',
|
|
4 : 'Service Dialling Numbers (SDN)',
|
|
5 : 'Extension3',
|
|
6 : 'Barred Dialling Numbers (BDN)',
|
|
7 : 'Extension4',
|
|
8 : 'Outgoing Call Information (OCI and OCT)',
|
|
9 : 'Incoming Call Information (ICI and ICT)',
|
|
10 : 'Short Message Storage (SMS)',
|
|
11 : 'Short Message Status Reports (SMSR)',
|
|
12 : 'Short Message Service Parameters (SMSP)',
|
|
13 : 'Advice of Charge (AoC)',
|
|
14 : 'Capability Configuration Parameters 2 (CCP2)',
|
|
15 : 'Cell Broadcast Message Identifier ',
|
|
16 : 'Cell Broadcast Message Identifier Ranges ',
|
|
17 : 'Group Identifier Level 1',
|
|
18 : 'Group Identifier Level 2',
|
|
19 : 'Service Provider Name',
|
|
20 : 'User controlled PLMN selector with Access Technology',
|
|
21 : 'MSISDN',
|
|
22 : 'Image (IMG)',
|
|
23 : 'Support of Localised Service Areas (SoLSA) ',
|
|
24 : 'Enhanced Multi-Level Precedence and Pre-emption Service',
|
|
25 : 'Automatic Answer for eMLPP',
|
|
26 : 'RFU',
|
|
27 : 'GSM Access',
|
|
28 : 'Data download via SMS-PP',
|
|
29 : 'Data download via SMS-CB',
|
|
30 : 'Call Control by USIM',
|
|
31 : 'MO-SMS Control by USIM',
|
|
32 : 'RUN AT COMMAND command',
|
|
33 : 'shall be set to \'1\'',
|
|
34 : 'Enabled Services Table',
|
|
35 : 'APN Control List (ACL)',
|
|
36 : 'Depersonalisation Control Keys',
|
|
37 : 'Co-operative Network List',
|
|
38 : 'GSM security context ',
|
|
39 : 'CPBCCH Information',
|
|
40 : 'Investigation Scan',
|
|
41 : 'MexE',
|
|
42 : 'Operator controlled PLMN selector with Access Technology',
|
|
43 : 'HPLMN selector with Access Technology',
|
|
44 : 'Extension 5',
|
|
45 : 'PLMN Network Name',
|
|
46 : 'Operator PLMN List',
|
|
47 : 'Mailbox Dialling Numbers ',
|
|
48 : 'Message Waiting Indication Status',
|
|
49 : 'Call Forwarding Indication Status',
|
|
50 : 'Reserved and shall be ignored',
|
|
51 : 'Service Provider Display Information',
|
|
52 : 'Multimedia Messaging Service (MMS)',
|
|
53 : 'Extension 8',
|
|
54 : 'Call control on GPRS by USIM',
|
|
55 : 'MMS User Connectivity Parameters',
|
|
56 : 'Network\'s indication of alerting in the MS (NIA)',
|
|
57 : 'VGCS Group Identifier List (EFVGCS and EFVGCSS)',
|
|
58 : 'VBS Group Identifier List (EFVBS and EFVBSS)',
|
|
59 : 'Pseudonym',
|
|
60 : 'User Controlled PLMN selector for I-WLAN access',
|
|
61 : 'Operator Controlled PLMN selector for I-WLAN access',
|
|
62 : 'User controlled WSID list',
|
|
63 : 'Operator controlled WSID list',
|
|
64 : 'VGCS security',
|
|
65 : 'VBS security',
|
|
66 : 'WLAN Reauthentication Identity',
|
|
67 : 'Multimedia Messages Storage',
|
|
68 : 'Generic Bootstrapping Architecture (GBA)',
|
|
69 : 'MBMS security',
|
|
70 : 'Data download via USSD and USSD application mode',
|
|
71 : 'Equivalent HPLMN',
|
|
72 : 'Additional TERMINAL PROFILE after UICC activation',
|
|
73 : 'Equivalent HPLMN Presentation Indication',
|
|
74 : 'Last RPLMN Selection Indication',
|
|
75 : 'OMA BCAST Smart Card Profile',
|
|
76 : 'GBA-based Local Key Establishment Mechanism',
|
|
77 : 'Terminal Applications',
|
|
78 : 'Service Provider Name Icon',
|
|
79 : 'PLMN Network Name Icon',
|
|
80 : 'Connectivity Parameters for USIM IP connections',
|
|
81 : 'Home I-WLAN Specific Identifier List',
|
|
82 : 'I-WLAN Equivalent HPLMN Presentation Indication',
|
|
83 : 'I-WLAN HPLMN Priority Indication',
|
|
84 : 'I-WLAN Last Registered PLMN',
|
|
85 : 'EPS Mobility Management Information',
|
|
86 : 'Allowed CSG Lists and corresponding indications',
|
|
87 : 'Call control on EPS PDN connection by USIM',
|
|
88 : 'HPLMN Direct Access',
|
|
89 : 'eCall Data',
|
|
90 : 'Operator CSG Lists and corresponding indications',
|
|
91 : 'Support for SM-over-IP',
|
|
92 : 'Support of CSG Display Control',
|
|
93 : 'Communication Control for IMS by USIM',
|
|
94 : 'Extended Terminal Applications',
|
|
95 : 'Support of UICC access to IMS',
|
|
96 : 'Non-Access Stratum configuration by USIM',
|
|
97 : 'PWS configuration by USIM',
|
|
98 : 'RFU',
|
|
99 : 'URI support by UICC',
|
|
100: 'Extended EARFCN support',
|
|
101: 'ProSe',
|
|
102: 'USAT Application Pairing',
|
|
103: 'Media Type support',
|
|
104: 'IMS call disconnection cause',
|
|
105: 'URI support for MO SHORT MESSAGE CONTROL',
|
|
106: 'ePDG configuration Information support',
|
|
107: 'ePDG configuration Information configured',
|
|
108: 'ACDC support',
|
|
109: 'Mission Critical Services',
|
|
110: 'ePDG configuration Information for Emergency Service support',
|
|
111: 'ePDG configuration Information for Emergency Service configured',
|
|
112: 'eCall Data over IMS',
|
|
113: 'URI support for SMS-PP DOWNLOAD as defined in 3GPP TS 31.111',
|
|
114: 'From Preferred',
|
|
115: 'IMS configuration data',
|
|
116: 'TV configuration',
|
|
117: '3GPP PS Data Off',
|
|
118: '3GPP PS Data Off Service List',
|
|
119: 'V2X',
|
|
120: 'XCAP Configuration Data',
|
|
121: 'EARFCN list for MTC/NB-IOT UEs',
|
|
122: '5GS Mobility Management Information',
|
|
123: '5G Security Parameters',
|
|
124: 'Subscription identifier privacy support',
|
|
125: 'SUCI calculation by the USIM',
|
|
126: 'UAC Access Identities support',
|
|
127: 'Control plane-based steering of UE in VPLMN',
|
|
128: 'Call control on PDU Session by USIM',
|
|
129: '5GS Operator PLMN List',
|
|
130: 'Support for SUPI of type network specific identifier',
|
|
}
|
|
|
|
|
|
class USIM(UICC):
|
|
"""
|
|
defines attributes, methods and facilities for ETSI / 3GPP USIM card
|
|
check USIM specifications in 3GPP TS 31.102
|
|
|
|
inherits (eventually overrides) methods and objects from UICC class
|
|
use self.dbg = 1 or more to print live debugging information
|
|
"""
|
|
|
|
def __init__(self, atr = None):
|
|
"""
|
|
initializes like an ISO7816-4 card with CLA=0x00
|
|
and checks available AID (Application ID) read from EF_DIR
|
|
|
|
initializes on the MF
|
|
"""
|
|
# initialize like a UICC
|
|
ISO7816.__init__(self, atr, CLA=0x00)
|
|
self.AID = []
|
|
|
|
if self.dbg >= 2:
|
|
log(3, '(UICC.__init__) type definition: %s' % type(self))
|
|
log(3, '(UICC.__init__) CLA definition: %s' % hex(self.CLA))
|
|
|
|
self.SELECT_ADF_USIM()
|
|
|
|
def SELECT_ADF_USIM(self):
|
|
# USIM selection from AID
|
|
if self.dbg:
|
|
log(3, '(USIM.__init__) UICC AID found:')
|
|
self.get_AID()
|
|
for aid in self.AID:
|
|
if tuple(aid[0:5]) == (0xA0, 0x00, 0x00, 0x00, 0x87) \
|
|
and tuple(aid[5:7]) == (0x10, 0x02) :
|
|
usim = self.select(addr=aid, type='aid')
|
|
if usim is None and self.dbg:
|
|
log(2, '(USIM.__init__) USIM AID selection failed')
|
|
if usim is not None:
|
|
self.USIM_AID = aid
|
|
if self.dbg:
|
|
log(3, '(USIM.__init__) USIM AID selection succeeded\n')
|
|
|
|
def SELECT_ADF_ISIM(self):
|
|
# USIM selection from AID
|
|
if self.dbg:
|
|
log(3, '(ISIM.__init__) UICC AID found:')
|
|
self.get_AID()
|
|
for aid in self.AID:
|
|
if tuple(aid[0:5]) == (0xA0, 0x00, 0x00, 0x00, 0x87) \
|
|
and tuple(aid[5:7]) == (0x10, 0x04) :
|
|
usim = self.select(addr=aid, type='aid')
|
|
if usim is None and self.dbg:
|
|
log(2, '(ISIM.__init__) ISIM AID selection failed')
|
|
if usim is not None:
|
|
self.USIM_AID = aid
|
|
if self.dbg:
|
|
log(3, '(ISIM.__init__) ISIM AID selection succeeded\n')
|
|
|
|
@staticmethod
|
|
def sw_status(sw1, sw2):
|
|
status = SIM.sw_status(sw1, sw2)
|
|
if sw1 == 0x98 and sw2 in (0x62, 0x64, 0x65, 0x66, 0x67):
|
|
status = 'security management'
|
|
if sw2 == 0x62: status += ': authentication error, ' \
|
|
'incorrect MAC'
|
|
elif sw2 == 0x64: status += ': authentication error, ' \
|
|
'security context not supported'
|
|
elif sw2 == 0x65: status += ': key freshness failure'
|
|
elif sw2 == 0x66: status += ': authentication error, ' \
|
|
'no memory space available'
|
|
elif sw2 == 0x67: status += ': authentication error, ' \
|
|
'no memory space available in EF_MUK'
|
|
return status
|
|
|
|
def get_imsi(self):
|
|
"""
|
|
get_imsi() -> string(IMSI)
|
|
|
|
reads IMSI value at address [0x6F, 0x07]
|
|
returns IMSI string on success or None on error
|
|
"""
|
|
# select IMSI file
|
|
imsi = self.select([0x6F, 0x07])
|
|
if imsi is None:
|
|
return None
|
|
# and parse the received data into the IMSI structure
|
|
if 'Data' in imsi.keys() and len(imsi['Data']) == 9:
|
|
return decode_BCD(imsi['Data'])[3:]
|
|
|
|
# if issue with the content of the DF_IMSI file
|
|
if self.dbg >= 2:
|
|
log(3, '(get_imsi) %s' % self.coms())
|
|
return None
|
|
|
|
def get_CS_keys(self):
|
|
"""
|
|
get_CS_keys() -> [KSI, CK, IK]
|
|
|
|
reads CS UMTS keys at address [0x6F, 0x08]
|
|
returns list of 3 keys, each are list of bytes, on success
|
|
(or eventually the whole file dict if the format is strange)
|
|
or None on error
|
|
"""
|
|
EF_KEYS = self.select( [0x6F, 0x08] )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
if len(EF_KEYS['Data']) == 33:
|
|
KSI, CK, IK = ( EF_KEYS['Data'][0:1],
|
|
EF_KEYS['Data'][1:17],
|
|
EF_KEYS['Data'][17:33])
|
|
log(3, '(get_CS_keys) successful CS keys selection: ' \
|
|
'Get [KSI, CK, IK]')
|
|
return [KSI, CK, IK]
|
|
else:
|
|
return EF_KEYS
|
|
return None
|
|
|
|
def get_PS_keys(self):
|
|
"""
|
|
get_PS_keys() -> [KSI, CK_PS, IK_PS]
|
|
|
|
reads PS UMTS keys at address [0x6F, 0x09]
|
|
returns list of 3 keys, each are list of bytes, on success
|
|
(or eventually the whole file dict if the format is strange)
|
|
or None on error
|
|
"""
|
|
EF_KEYSPS = self.select( [0x6F, 0x09] )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
if len(EF_KEYSPS['Data']) == 33:
|
|
KSI, CK, IK = ( EF_KEYSPS['Data'][0:1],
|
|
EF_KEYSPS['Data'][1:17],
|
|
EF_KEYSPS['Data'][17:33] )
|
|
log(3, '(get_PS_keys) successful PS keys selection: ' \
|
|
'Get [KSI, CK, IK]')
|
|
return [KSI, CK, IK]
|
|
else:
|
|
return EF_KEYSPS
|
|
return None
|
|
|
|
def get_GBA_BP(self):
|
|
"""
|
|
get_GBA_BP() -> [[RAND, B-TID, KeyLifetime], ...],
|
|
Length-Value parsing style
|
|
|
|
reads EF_GBABP file at address [0x6F, 0xD6],
|
|
containing RAND and associated B-TID and KeyLifetime
|
|
returns list of list of bytes on success
|
|
(or eventually the whole file dict if the format is strange)
|
|
or None on error
|
|
"""
|
|
EF_GBABP = self.select( [0x6F, 0xD6] )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
if len(EF_GBABP['Data']) > 2:
|
|
#RAND, B_TID, Lifetime = LV_parser( EF_GBABP['Data'] )
|
|
log(3, '(get_GBA_BP) successful GBA_BP selection: ' \
|
|
'Get list of [RAND, B-TID, KeyLifetime]')
|
|
#return (RAND, B_TID, Lifetime)
|
|
return LV_parser( EF_GBABP['Data'] )
|
|
else:
|
|
return EF_GBABP
|
|
return None
|
|
|
|
def update_GBA_BP(self, RAND, B_TID, key_lifetime):
|
|
"""
|
|
update_GBA_BP([RAND], [B_TID], [key_lifetime])
|
|
-> void (or EF_GBABP file dict if RAND not found)
|
|
|
|
reads EF_GBABP file at address [0x6F, 0xD6],
|
|
checks if RAND provided is referenced,
|
|
and updates the file structure with provided B-TID and KeyLifetime
|
|
returns nothing (or eventually the whole file dict
|
|
if the RAND is not found)
|
|
"""
|
|
GBA_BP = self.get_GBA_BP()
|
|
for i in GBA_BP:
|
|
if i == RAND:
|
|
log(3, '(update_GBA_BP) RAND found in GBA_BP')
|
|
# update transparent file with B_TID and key lifetime
|
|
self.coms.push( self.UPDATE_BINARY( P2=len(RAND)+1,
|
|
Data=[len(B_TID)] + B_TID + \
|
|
[len(key_lifetime)] + key_lifetime ))
|
|
if self.dbg >= 2:
|
|
log(3, '(update_GBA_BP) %s' % self.coms())
|
|
if self.coms()[2] == 0x90 and self.dbg:
|
|
log(3, '(update_GBA_BP) successful GBA_BP update with ' \
|
|
'B-TID and key lifetime')
|
|
if self.dbg >= 3:
|
|
log(3, '(update_GBA_BP) new value of EF_GBA_BP:\n%s' \
|
|
% self.get_GBA_BP())
|
|
else:
|
|
if self.dbg:
|
|
log(2, '(update_GBA_BP) RAND not found in GBA_BP')
|
|
return GBA_BP
|
|
|
|
def get_GBA_NL(self):
|
|
"""
|
|
get_GBA_NL() -> [[NAF_ID, B-TID], ...] , TLV parsing style
|
|
|
|
reads EF_GBANL file at address [0x6F, 0xDA], containing NAF_ID and B-TID
|
|
returns list of list of bytes vector on success
|
|
(or eventually the whole file dict if the format is strange)
|
|
or None on error
|
|
"""
|
|
EF_GBANL = self.select( [0x6F, 0xDA] )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
if len(EF_GBANL['Data'][0]) > 2:
|
|
# This is Tag-Length-Value parsing,
|
|
# with 0x80 for NAF_ID and 0x81 for B-TID
|
|
values = []
|
|
|
|
for rec in EF_GBANL['Data']:
|
|
NAF_ID, B_TID = [], []
|
|
while len(rec) > 0:
|
|
tlv = first_TLV_parser( rec )
|
|
if tlv[1] > 0xFF:
|
|
rec = rec[ tlv[1]+4 : ]
|
|
else:
|
|
rec = rec[ tlv[1]+2 : ]
|
|
if tlv[0] == 0x80:
|
|
NAF_ID = tlv[2]
|
|
elif tlv[0] == 0x81:
|
|
B_TID = tlv[2]
|
|
values.append( [NAF_ID, B_TID] )
|
|
|
|
log(3, '(get_GBA_NL) Successful GBA_NL selection: ' \
|
|
'Get list of [NAF_ID, B-TID]')
|
|
#return (NAF_ID, B_TID)
|
|
return values
|
|
else:
|
|
return EF_GBANL
|
|
return None
|
|
|
|
def authenticate(self, RAND=[], AUTN=[], ctx='3G'):
|
|
"""
|
|
self.authenticate(RAND, AUTN, ctx='3G') -> [key1, key2...],
|
|
LV parsing style
|
|
|
|
runs the INTERNAL AUTHENTICATE command in the USIM
|
|
with the right context:
|
|
ctx = '2G', '3G', 'GBA' ('MBMS' or other not supported at this time)
|
|
RAND and AUTN are list of bytes; for '2G' context, AUTN is not used
|
|
returns a list containing the keys (list of bytes) computed in the USIM,
|
|
on success:
|
|
[RES, CK, IK (, Kc)] or [AUTS] for '3G'
|
|
[RES] or [AUTS] for 'GBA'
|
|
[RES, Kc] for '2G'
|
|
or None on error
|
|
"""
|
|
# prepare input data for authentication
|
|
if ctx in ('3G', 'VGCS', 'GBA', 'MBMS') and len(RAND) != 16 \
|
|
and len(AUTN) != 16:
|
|
log(1, '(authenticate) bad AUTN parameter: aborting')
|
|
return None
|
|
|
|
inp = []
|
|
if ctx == '3G':
|
|
P2 = 0x81
|
|
elif ctx == 'VGCS':
|
|
P2 = 0x82
|
|
log(1, '(authenticate) VGCS auth not implemented: aborting')
|
|
return None
|
|
elif ctx == 'MBMS':
|
|
log(1, '(authenticate) MBMS auth not implemented: aborting')
|
|
return None
|
|
elif ctx == 'GBA':
|
|
P2 = 0x84
|
|
inp = [0xDD]
|
|
inp.extend( [len(RAND)] + RAND + [len(AUTN)] + AUTN )
|
|
if ctx not in ['3G', 'VGCS', 'MBMS', 'GBA']:
|
|
# and also, if ctx == '2G'... the safe way
|
|
# to avoid desynchronizing our USIM counter
|
|
P2 = 0x80
|
|
if len(RAND) != 16:
|
|
log(1, '(authenticate) bad RAND parameter: aborting')
|
|
return None
|
|
# override input value for 2G authent
|
|
inp = [len(RAND)] + RAND
|
|
|
|
self.coms.push( self.INTERNAL_AUTHENTICATE(P2=P2, Data=inp) )
|
|
if self.coms()[2][0] in (0x9F, 0x61):
|
|
self.coms.push( self.GET_RESPONSE(Le=self.coms()[2][1]) )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
val = self.coms()[3]
|
|
if P2 == 0x80:
|
|
if self.dbg:
|
|
log(3, '(authenticate) successful 2G authentication. ' \
|
|
'Get [RES, Kc]')
|
|
values = LV_parser(val)
|
|
# returned values are (RES, Kc)
|
|
return values
|
|
# not adapted to 2G context with Kc, RES: to be confirmed...
|
|
if val[0] == 0xDB:
|
|
if P2 == 0x81 and self.dbg:
|
|
log(3, '(authenticate) successful 3G authentication. ' \
|
|
'Get [RES, CK, IK(, Kc)]')
|
|
elif P2 == 0x84 and self.dbg:
|
|
log(3, '(authenticate) successful GBA authentication.' \
|
|
' Get [RES]')
|
|
values = LV_parser(val[1:])
|
|
# returned values can be (RES, CK, IK) or (RES, CK, IK, Kc)
|
|
return values
|
|
elif val[0] == 0xDC:
|
|
if self.dbg:
|
|
log(2, '(authenticate) synchronization failure. ' \
|
|
'Get [AUTS]')
|
|
values = LV_parser(val[1:])
|
|
return values
|
|
#else:
|
|
if self.dbg:
|
|
log(1, '(authenticate) error: %s' % self.coms())
|
|
return None
|
|
|
|
def GBA_derivation(self, NAF_ID=[], IMPI=[]):
|
|
"""
|
|
self.GBA_derivation(NAF_ID, IMPI) -> [Ks_ext_naf]
|
|
|
|
runs the INTERNAL AUTHENTICATE command in the USIM
|
|
with the GBA derivation context:
|
|
NAF_ID is a list of bytes (use stringToByte())
|
|
"NAF domain name"||"security protocol id",
|
|
eg: "application.org"||"0x010001000a" (> TLS with RSA and SHA)
|
|
IMPI is a list of bytes
|
|
"IMSI@ims.mncXXX.mccYYY.3gppnetwork.org" if no IMS IMPI
|
|
is specifically defined in the USIM
|
|
returns a list with GBA ext key (list of bytes) computed in the USIM:
|
|
[Ks_ext_naf]
|
|
Ks_int_naf remains available in the USIM
|
|
for further GBA_U key derivation
|
|
or None on error
|
|
|
|
see TS 33.220 for GBA specific formats
|
|
"""
|
|
# need to run 1st an authenicate command with 'GBA' context,
|
|
# so to have the required keys in the USIM
|
|
P2 = 0x84
|
|
inp = [0xDE] + [len(NAF_ID)] + NAF_ID + [len(IMPI)] + IMPI
|
|
|
|
self.coms.push( self.INTERNAL_AUTHENTICATE(P2=P2, Data=inp) )
|
|
if self.coms()[2][0] in (0x9F, 0x61):
|
|
self.coms.push( self.GET_RESPONSE(Le=self.coms()[2][1]) )
|
|
if self.coms()[2] == (0x90, 0x00):
|
|
val = self.coms()[3]
|
|
if val[0] == 0xDB: # not adapted to 2G context with Kc, RES
|
|
if self.dbg:
|
|
log(3, '(GBA_derivation) successful GBA derivation. ' \
|
|
'Get [Ks_EXT_NAF]')
|
|
values = LV_parser(val[1:])
|
|
return values
|
|
if self.dbg:
|
|
log(3, '(GBA_derivation) authentication failure: %s' % self.coms())
|
|
return None
|
|
|
|
def get_services(self):
|
|
"""
|
|
self.get_services() -> None
|
|
|
|
reads USIM Service Table at address [0x6F, 0x38]
|
|
prints services allowed / activated
|
|
returns None
|
|
"""
|
|
# select SST file
|
|
sst = self.select([0x6F, 0x38])
|
|
if self.coms()[2] != (0x90, 0x00):
|
|
if self.dbg >= 2:
|
|
log(3, '(get_services) %s' % self.coms())
|
|
return None
|
|
|
|
# parse data and prints corresponding services
|
|
if 'Data' in sst.keys() and len(sst['Data']) >= 2:
|
|
return self.get_services_from_sst(sst['Data'])
|
|
|
|
def read_services(self):
|
|
serv = self.get_services()
|
|
for s in serv:
|
|
print(s)
|
|
|
|
def get_services_from_sst(self, sst=[0, 0]):
|
|
services = []
|
|
cnt = 0
|
|
for B in sst:
|
|
# 1 bit per service -> 8 services per byte
|
|
for i in range(0, 8):
|
|
cnt += 1
|
|
if B & 2**i:
|
|
if cnt in USIM_service_table:
|
|
services.append('%i : %s : available' \
|
|
% (cnt, USIM_service_table[cnt]))
|
|
else:
|
|
services.append('%i : available' % cnt)
|
|
return services
|
|
|
|
def explore_fs(self, filename='usim_fs', depth=2):
|
|
"""
|
|
self.explore_fs(self, filename='usim_fs') -> void
|
|
filename: file to write in information found
|
|
depth: depth in recursivity, True=infinite
|
|
|
|
brute force all file addresses from 1st USIM AID
|
|
with a maximum recursion level (to avoid infinite looping...)
|
|
write information on existing DF and file in the output file
|
|
"""
|
|
usimfs_entries = USIM_app_FS.keys()
|
|
self.explore_DF([], self.AID.index(self.USIM_AID)+1, depth)
|
|
|
|
fd = open(filename, 'w')
|
|
fd.write('\n### AID %s ###\n' % self.USIM_AID)
|
|
f = self.select_by_aid( self.AID.index(self.USIM_AID)+1 )
|
|
write_dict(f, fd)
|
|
fd.write('\n')
|
|
#
|
|
for f in self.FS:
|
|
path = tuple(f['Absolut Path'])
|
|
if path in usimfs_entries:
|
|
f['Name'] = USIM_app_FS[path]
|
|
write_dict(f, fd)
|
|
fd.write('\n')
|
|
|
|
fd.close()
|
|
#
|
|
|