Import slightly modified version of mitshell/card
the modifications can be found in https://github.com/mitshell/card/pull/2 and I wouuld hope that once Benoit has merged this we can remove the local copy here and simply rely on pypi pulling it from the repo.
This commit is contained in:
parent
5666cd6818
commit
4086758393
|
@ -0,0 +1,455 @@
|
|||
# -*- 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.
|
||||
"""
|
||||
|
||||
#################################
|
||||
# 3GPP SIM and USIM File-System #
|
||||
# see TS 51.11 for SIM #
|
||||
# see TS 31.102 for USIM #
|
||||
#################################
|
||||
|
||||
# (U)SIM file-system dictionnaries
|
||||
# (absolut_file_address) : 'file_name'
|
||||
|
||||
SIM_FS = {
|
||||
(0x3F, 0x00) : 'MF',
|
||||
(0x2F, 0xE2) : 'EF_ICCID',
|
||||
(0x2F, 0x05) : 'EF_PL',
|
||||
(0x7F, 0x23) : 'DF_FP-CTS',
|
||||
(0x7F, 0x22) : 'DF_IS-41',
|
||||
(0x7F, 0x10) : 'DF_TELECOM',
|
||||
(0x7F, 0x10, 0x6F, 0x3A) : 'EF_ADN',
|
||||
(0x7F, 0x10, 0x6F, 0x3B) : 'EF_FDN',
|
||||
(0x7F, 0x10, 0x6F, 0x3C) : 'EF_SMS',
|
||||
(0x7F, 0x10, 0x6F, 0x3D) : 'EF_CCP',
|
||||
(0x7F, 0x10, 0x6F, 0x40) : 'EF_MSISDN',
|
||||
(0x7F, 0x10, 0x6F, 0x42) : 'EF_SMSP',
|
||||
(0x7F, 0x10, 0x6F, 0x43) : 'EF_SMSS',
|
||||
(0x7F, 0x10, 0x6F, 0x44) : 'EF_LND',
|
||||
(0x7F, 0x10, 0x6F, 0x47) : 'EF_SMSR',
|
||||
(0x7F, 0x10, 0x6F, 0x49) : 'EF_SDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4A) : 'EF_EXT1',
|
||||
(0x7F, 0x10, 0x6F, 0x4B) : 'EF_EXT2',
|
||||
(0x7F, 0x10, 0x6F, 0x4C) : 'EF_EXT3',
|
||||
(0x7F, 0x10, 0x6F, 0x4D) : 'EF_BDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4E) : 'EF_EXT4',
|
||||
(0x7F, 0x10, 0x6F, 0x58) : 'EF_CMI',
|
||||
(0x7F, 0x10, 0x6F, 0x4F) : 'EF_ECCP',
|
||||
(0x7F, 0x10, 0x5F, 0x50) : 'DF_GRAPHICS',
|
||||
(0x7F, 0x10, 0x5F, 0x50, 0x4F, 0x20) : 'EF_IMG',
|
||||
(0x7F, 0x20) : 'DF_GSM',
|
||||
(0x7F, 0x20, 0x5F, 0x3C) : 'DF_MExE',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x40) : 'EF_MExE-ST',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x41) : 'EF_ORPK',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x42) : 'EF_ARPK',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x43) : 'EF_TPRPK',
|
||||
(0x7F, 0x20, 0x5F, 0x30) : 'DF_IRIDIUM',
|
||||
(0x7F, 0x20, 0x5F, 0x31) : 'DF_GLOBALSTAR',
|
||||
(0x7F, 0x20, 0x5F, 0x32) : 'DF_ICO',
|
||||
(0x7F, 0x20, 0x5F, 0x33) : 'DF_ACeS',
|
||||
(0x7F, 0x20, 0x5F, 0x40) : 'DF_EIA/TIA-553',
|
||||
(0x7F, 0x20, 0x5F, 0x60) : 'DF_CTS',
|
||||
(0x7F, 0x20, 0x5F, 0x70) : 'DF_SoLSA',
|
||||
(0x7F, 0x20, 0x5F, 0x70, 0x4F, 0x30) : 'EF_SAI',
|
||||
(0x7F, 0x20, 0x5F, 0x70, 0x4F, 0x31) : 'EF_SLL',
|
||||
(0x7F, 0x20, 0x6F, 0x05) : 'EF_LP',
|
||||
(0x7F, 0x20, 0x6F, 0x07) : 'EF_IMSI',
|
||||
(0x7F, 0x20, 0x6F, 0x20) : 'EF_Kc',
|
||||
(0x7F, 0x20, 0x6F, 0x2C) : 'EF_DCK',
|
||||
(0x7F, 0x20, 0x6F, 0x30) : 'EF_PLMNsel',
|
||||
(0x7F, 0x20, 0x6F, 0x31) : 'EF_HPLMN',
|
||||
(0x7F, 0x20, 0x6F, 0x32) : 'EF_CNL',
|
||||
(0x7F, 0x20, 0x6F, 0x37) : 'EF_ACMmax',
|
||||
(0x7F, 0x20, 0x6F, 0x38) : 'EF_SST',
|
||||
(0x7F, 0x20, 0x6F, 0x39) : 'EF_ACM',
|
||||
(0x7F, 0x20, 0x6F, 0x3E) : 'EF_GID1',
|
||||
(0x7F, 0x20, 0x6F, 0x3F) : 'EF_GID2',
|
||||
(0x7F, 0x20, 0x6F, 0x41) : 'EF_PUCT',
|
||||
(0x7F, 0x20, 0x6F, 0x45) : 'EF_CBMI',
|
||||
(0x7F, 0x20, 0x6F, 0x46) : 'EF_SPN',
|
||||
(0x7F, 0x20, 0x6F, 0x48) : 'EF_CBMID',
|
||||
(0x7F, 0x20, 0x6F, 0x74) : 'EF_BCCH',
|
||||
(0x7F, 0x20, 0x6F, 0x78) : 'EF_ACC',
|
||||
(0x7F, 0x20, 0x6F, 0x7B) : 'EF_FPLMN',
|
||||
(0x7F, 0x20, 0x6F, 0x7E) : 'EF_LOCI',
|
||||
(0x7F, 0x20, 0x6F, 0xAD) : 'EF_AD',
|
||||
(0x7F, 0x20, 0x6F, 0xAE) : 'EF_PHASE',
|
||||
(0x7F, 0x20, 0x6F, 0xB1) : 'EF_VGCS',
|
||||
(0x7F, 0x20, 0x6F, 0xB2) : 'EF_VGCSS',
|
||||
(0x7F, 0x20, 0x6F, 0xB3) : 'EF_VBS',
|
||||
(0x7F, 0x20, 0x6F, 0xB4) : 'EF_VBSS',
|
||||
(0x7F, 0x20, 0x6F, 0xB5) : 'EF_eMLPP',
|
||||
(0x7F, 0x20, 0x6F, 0xB6) : 'EF_AAeM',
|
||||
(0x7F, 0x20, 0x6F, 0xB7) : 'EF_ECC',
|
||||
(0x7F, 0x20, 0x6F, 0x50) : 'EF_CBMIR',
|
||||
(0x7F, 0x20, 0x6F, 0x51) : 'EF_NIA',
|
||||
(0x7F, 0x20, 0x6F, 0x52) : 'EF_KcGPRS',
|
||||
(0x7F, 0x20, 0x6F, 0x53) : 'EF_LOCIGPRS',
|
||||
(0x7F, 0x20, 0x6F, 0x54) : 'EF_SUME',
|
||||
(0x7F, 0x20, 0x6F, 0x60) : 'EF_PLMNwAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x61) : 'EF_OPLMNwAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x62) : 'EF_HPLMNAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x63) : 'EF_CPBCCH',
|
||||
(0x7F, 0x20, 0x6F, 0x64) : 'EF_INVSCAN',
|
||||
(0x7F, 0x20, 0x6F, 0xC5) : 'EF_PNN',
|
||||
(0x7F, 0x20, 0x6F, 0xC6) : 'EF_OPL',
|
||||
(0x7F, 0x20, 0x6F, 0xC7) : 'EF_MBDN',
|
||||
(0x7F, 0x20, 0x6F, 0xC8) : 'EF_EXT6',
|
||||
(0x7F, 0x20, 0x6F, 0xC9) : 'EF_MBI',
|
||||
(0x7F, 0x20, 0x6F, 0xCA) : 'EF_MWIS',
|
||||
(0x7F, 0x20, 0x6F, 0xCB) : 'EF_CFIS',
|
||||
(0x7F, 0x20, 0x6F, 0xCC) : 'EF_EXT7',
|
||||
(0x7F, 0x20, 0x6F, 0xCD) : 'EF_SPDI',
|
||||
(0x7F, 0x20, 0x6F, 0xCE) : 'EF_MMSN',
|
||||
(0x7F, 0x20, 0x6F, 0xCF) : 'EF_EXT8',
|
||||
(0x7F, 0x20, 0x6F, 0xD0) : 'EF_MMSICP',
|
||||
(0x7F, 0x20, 0x6F, 0xD1) : 'EF_MMSUP',
|
||||
(0x7F, 0x20, 0x6F, 0xD2) : 'EF_MMSUCP',
|
||||
}
|
||||
|
||||
USIM_FS = {
|
||||
(0x3F, 0x00) : 'MF',
|
||||
(0x2F, 0x00) : 'EF_DIR',
|
||||
(0x2F, 0x05) : 'EF_PL',
|
||||
(0x2F, 0x06) : 'EF_ARR',
|
||||
(0x2F, 0xE2) : 'EF_ICCID',
|
||||
(0x7F, 0x20) : 'DF_GSM',
|
||||
(0x7F, 0x10) : 'DF_TELECOM',
|
||||
(0x7F, 0x10, 0x6F, 0x06) : 'EF_ARR',
|
||||
(0x7F, 0x10, 0x6F, 0x3A) : 'EF_ADN',
|
||||
(0x7F, 0x10, 0x6F, 0x3B) : 'EF_FDN',
|
||||
(0x7F, 0x10, 0x6F, 0x3C) : 'EF_SMS',
|
||||
(0x7F, 0x10, 0x6F, 0x4F) : 'EF_ECCP',
|
||||
(0x7F, 0x10, 0x6F, 0x40) : 'EF_MSISDN',
|
||||
(0x7F, 0x10, 0x6F, 0x42) : 'EF_SMSP',
|
||||
(0x7F, 0x10, 0x6F, 0x43) : 'EF_SMSS',
|
||||
(0x7F, 0x10, 0x6F, 0x44) : 'EF_LND',
|
||||
(0x7F, 0x10, 0x6F, 0x47) : 'EF_SMSR',
|
||||
(0x7F, 0x10, 0x6F, 0x49) : 'EF_SDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4A) : 'EF_EXT1',
|
||||
(0x7F, 0x10, 0x6F, 0x4B) : 'EF_EXT2',
|
||||
(0x7F, 0x10, 0x6F, 0x4C) : 'EF_EXT3',
|
||||
(0x7F, 0x10, 0x6F, 0x4D) : 'EF_BDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4E) : 'EF_EXT4',
|
||||
(0x7F, 0x10, 0x6F, 0x53) : 'EF_RMA',
|
||||
(0x7F, 0x10, 0x6F, 0x54) : 'EF_SUME',
|
||||
(0x7F, 0x10, 0x5F, 0x50) : 'DF_GRAPHICS',
|
||||
(0x7F, 0x10, 0x5F, 0x50, 0x4F, 0x20) : 'EF_IMG',
|
||||
#(0x7F, 0x10, 0x5F, 0x50, 0x4F, 0xXX) : 'EF_IIDFn',
|
||||
(0x5F, 0x3A) : 'DF_PHONEBOOK',
|
||||
(0x5F, 0x3A, 0x4F, 0x30) : 'EF_PBR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_IAP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ADN',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EXT1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_PBC',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GRP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_AAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ANR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_SNE',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_CCP1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_UID',
|
||||
(0x5F, 0x3A, 0x4F, 0x22) : 'EF_PSC',
|
||||
(0x5F, 0x3A, 0x4F, 0x23) : 'EF_CC',
|
||||
(0x5F, 0x3A, 0x4F, 0x24) : 'EF_PUID',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EMAIL',
|
||||
(0x5F, 0x3B) : 'DF_MULTIMEDIA',
|
||||
(0x5F, 0x3B, 0x4F, 0x47) : 'EF_MML',
|
||||
(0x5F, 0x3B, 0x4F, 0x48) : 'EF_MMDF',
|
||||
}
|
||||
|
||||
# this MF_FS is a trick that concatenate SIM_FS and USIM_FS
|
||||
# useful when bruteforcing the FS under MF whatever the type of card selected
|
||||
MF_FS = {
|
||||
(0x3F, 0x00) : 'MF',
|
||||
(0x2F, 0x00) : 'EF_DIR',
|
||||
(0x2F, 0x05) : 'EF_ELP',
|
||||
(0x2F, 0x06) : 'EF_ARR',
|
||||
(0x2F, 0xE2) : 'EF_ICCID',
|
||||
(0x5F, 0x3A) : 'DF_PHONEBOOK',
|
||||
(0x5F, 0x3A, 0x4F, 0x30) : 'EF_PBR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_IAP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ADN',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EXT1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_PBC',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GRP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_AAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ANR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_SNE',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_CCP1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_UID',
|
||||
(0x5F, 0x3A, 0x4F, 0x22) : 'EF_PSC',
|
||||
(0x5F, 0x3A, 0x4F, 0x23) : 'EF_CC',
|
||||
(0x5F, 0x3A, 0x4F, 0x24) : 'EF_PUID',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EMAIL',
|
||||
(0x5F, 0x3B) : 'DF_MULTIMEDIA',
|
||||
(0x5F, 0x3B, 0x4F, 0x47) : 'EF_MML',
|
||||
(0x5F, 0x3B, 0x4F, 0x48) : 'EF_MMDF',
|
||||
(0x7F, 0x10) : 'DF_TELECOM',
|
||||
(0x7F, 0x10, 0x6F, 0x06) : 'EF_ARR',
|
||||
(0x7F, 0x10, 0x6F, 0x3A) : 'EF_ADN',
|
||||
(0x7F, 0x10, 0x6F, 0x3B) : 'EF_FDN',
|
||||
(0x7F, 0x10, 0x6F, 0x3C) : 'EF_SMS',
|
||||
(0x7F, 0x10, 0x6F, 0x3D) : 'EF_CCP',
|
||||
(0x7F, 0x10, 0x6F, 0x40) : 'EF_MSISDN',
|
||||
(0x7F, 0x10, 0x6F, 0x42) : 'EF_SMSP',
|
||||
(0x7F, 0x10, 0x6F, 0x43) : 'EF_SMSS',
|
||||
(0x7F, 0x10, 0x6F, 0x44) : 'EF_LND',
|
||||
(0x7F, 0x10, 0x6F, 0x47) : 'EF_SMSR',
|
||||
(0x7F, 0x10, 0x6F, 0x49) : 'EF_SDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4A) : 'EF_EXT1',
|
||||
(0x7F, 0x10, 0x6F, 0x4B) : 'EF_EXT2',
|
||||
(0x7F, 0x10, 0x6F, 0x4C) : 'EF_EXT3',
|
||||
(0x7F, 0x10, 0x6F, 0x4D) : 'EF_BDN',
|
||||
(0x7F, 0x10, 0x6F, 0x4E) : 'EF_EXT4',
|
||||
(0x7F, 0x10, 0x6F, 0x4F) : 'EF_ECCP',
|
||||
(0x7F, 0x10, 0x5F, 0x50) : 'DF_GRAPHICS',
|
||||
(0x7F, 0x10, 0x5F, 0x50, 0x4F, 0x20) : 'EF_IMG',
|
||||
(0x7F, 0x10, 0x6F, 0x53) : 'EF_RMA',
|
||||
(0x7F, 0x10, 0x6F, 0x54) : 'EF_SUME',
|
||||
(0x7F, 0x10, 0x6F, 0x58) : 'EF_CMI',
|
||||
(0x7F, 0x20) : 'DF_GSM',
|
||||
(0x7F, 0x20, 0x5F, 0x3C) : 'DF_MExE',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x40) : 'EF_MExE-ST',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x41) : 'EF_ORPK',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x42) : 'EF_ARPK',
|
||||
(0x7F, 0x20, 0x5F, 0x3C, 0x4F, 0x43) : 'EF_TPRPK',
|
||||
(0x7F, 0x20, 0x5F, 0x30) : 'DF_IRIDIUM',
|
||||
(0x7F, 0x20, 0x5F, 0x31) : 'DF_GLOBALSTAR',
|
||||
(0x7F, 0x20, 0x5F, 0x32) : 'DF_ICO',
|
||||
(0x7F, 0x20, 0x5F, 0x33) : 'DF_ACeS',
|
||||
(0x7F, 0x20, 0x5F, 0x40) : 'DF_EIA/TIA-553',
|
||||
(0x7F, 0x20, 0x5F, 0x60) : 'DF_CTS',
|
||||
(0x7F, 0x20, 0x5F, 0x70) : 'DF_SoLSA',
|
||||
(0x7F, 0x20, 0x5F, 0x70, 0x4F, 0x30) : 'EF_SAI',
|
||||
(0x7F, 0x20, 0x5F, 0x70, 0x4F, 0x31) : 'EF_SLL',
|
||||
(0x7F, 0x20, 0x6F, 0x05) : 'EF_LP',
|
||||
(0x7F, 0x20, 0x6F, 0x07) : 'EF_IMSI',
|
||||
(0x7F, 0x20, 0x6F, 0x20) : 'EF_Kc',
|
||||
(0x7F, 0x20, 0x6F, 0x2C) : 'EF_DCK',
|
||||
(0x7F, 0x20, 0x6F, 0x30) : 'EF_PLMNsel',
|
||||
(0x7F, 0x20, 0x6F, 0x31) : 'EF_HPLMN',
|
||||
(0x7F, 0x20, 0x6F, 0x32) : 'EF_CNL',
|
||||
(0x7F, 0x20, 0x6F, 0x37) : 'EF_ACMmax',
|
||||
(0x7F, 0x20, 0x6F, 0x38) : 'EF_SST',
|
||||
(0x7F, 0x20, 0x6F, 0x39) : 'EF_ACM',
|
||||
(0x7F, 0x20, 0x6F, 0x3E) : 'EF_GID1',
|
||||
(0x7F, 0x20, 0x6F, 0x3F) : 'EF_GID2',
|
||||
(0x7F, 0x20, 0x6F, 0x41) : 'EF_PUCT',
|
||||
(0x7F, 0x20, 0x6F, 0x45) : 'EF_CBMI',
|
||||
(0x7F, 0x20, 0x6F, 0x46) : 'EF_SPN',
|
||||
(0x7F, 0x20, 0x6F, 0x48) : 'EF_CBMID',
|
||||
(0x7F, 0x20, 0x6F, 0x74) : 'EF_BCCH',
|
||||
(0x7F, 0x20, 0x6F, 0x78) : 'EF_ACC',
|
||||
(0x7F, 0x20, 0x6F, 0x7B) : 'EF_FPLMN',
|
||||
(0x7F, 0x20, 0x6F, 0x7E) : 'EF_LOCI',
|
||||
(0x7F, 0x20, 0x6F, 0xAD) : 'EF_AD',
|
||||
(0x7F, 0x20, 0x6F, 0xAE) : 'EF_PHASE',
|
||||
(0x7F, 0x20, 0x6F, 0xB1) : 'EF_VGCS',
|
||||
(0x7F, 0x20, 0x6F, 0xB2) : 'EF_VGCSS',
|
||||
(0x7F, 0x20, 0x6F, 0xB3) : 'EF_VBS',
|
||||
(0x7F, 0x20, 0x6F, 0xB4) : 'EF_VBSS',
|
||||
(0x7F, 0x20, 0x6F, 0xB5) : 'EF_eMLPP',
|
||||
(0x7F, 0x20, 0x6F, 0xB6) : 'EF_AAeM',
|
||||
(0x7F, 0x20, 0x6F, 0xB7) : 'EF_ECC',
|
||||
(0x7F, 0x20, 0x6F, 0x50) : 'EF_CBMIR',
|
||||
(0x7F, 0x20, 0x6F, 0x51) : 'EF_NIA',
|
||||
(0x7F, 0x20, 0x6F, 0x52) : 'EF_KcGPRS',
|
||||
(0x7F, 0x20, 0x6F, 0x53) : 'EF_LOCIGPRS',
|
||||
(0x7F, 0x20, 0x6F, 0x54) : 'EF_SUME',
|
||||
(0x7F, 0x20, 0x6F, 0x60) : 'EF_PLMNwAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x61) : 'EF_OPLMNwAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x62) : 'EF_HPLMNAcT',
|
||||
(0x7F, 0x20, 0x6F, 0x63) : 'EF_CPBCCH',
|
||||
(0x7F, 0x20, 0x6F, 0x64) : 'EF_INVSCAN',
|
||||
(0x7F, 0x20, 0x6F, 0xC5) : 'EF_PNN',
|
||||
(0x7F, 0x20, 0x6F, 0xC6) : 'EF_OPL',
|
||||
(0x7F, 0x20, 0x6F, 0xC7) : 'EF_MBDN',
|
||||
(0x7F, 0x20, 0x6F, 0xC8) : 'EF_EXT6',
|
||||
(0x7F, 0x20, 0x6F, 0xC9) : 'EF_MBI',
|
||||
(0x7F, 0x20, 0x6F, 0xCA) : 'EF_MWIS',
|
||||
(0x7F, 0x20, 0x6F, 0xCB) : 'EF_CFIS',
|
||||
(0x7F, 0x20, 0x6F, 0xCC) : 'EF_EXT7',
|
||||
(0x7F, 0x20, 0x6F, 0xCD) : 'EF_SPDI',
|
||||
(0x7F, 0x20, 0x6F, 0xCE) : 'EF_MMSN',
|
||||
(0x7F, 0x20, 0x6F, 0xCF) : 'EF_EXT8',
|
||||
(0x7F, 0x20, 0x6F, 0xD0) : 'EF_MMSICP',
|
||||
(0x7F, 0x20, 0x6F, 0xD1) : 'EF_MMSUP',
|
||||
(0x7F, 0x20, 0x6F, 0xD2) : 'EF_MMSUCP',
|
||||
(0x7F, 0x23) : 'DF_FP-CTS',
|
||||
(0x7F, 0x22) : 'DF_IS-41',
|
||||
}
|
||||
|
||||
USIM_app_FS = {
|
||||
(0x5F, 0x40) : 'DF_WLAN',
|
||||
(0x5F, 0x40, 0x4F, 0x41) : 'EF_Pseudo',
|
||||
(0x5F, 0x40, 0x4F, 0x42) : 'EF_UPLMNWLAN',
|
||||
(0x5F, 0x40, 0x4F, 0x43) : 'EF_0PLMNWLAN',
|
||||
(0x5F, 0x40, 0x4F, 0x44) : 'EF_USSIDL',
|
||||
(0x5F, 0x40, 0x4F, 0x45) : 'EF_OSSIDL',
|
||||
(0x5F, 0x40, 0x4F, 0x46) : 'EF_WRI',
|
||||
(0x5F, 0x70) : 'DF_SoLSA',
|
||||
(0x5F, 0x70, 0x4F, 0x30) : 'EF_SAI',
|
||||
(0x5F, 0x70, 0x4F, 0x31) : 'EF_SLL',
|
||||
(0x5F, 0x3C) : 'DF_MExE',
|
||||
(0x5F, 0x3C, 0x4F, 0x40) : 'EF_MExE-ST',
|
||||
(0x5F, 0x3C, 0x4F, 0x41) : 'EF_ORPK',
|
||||
(0x5F, 0x3C, 0x4F, 0x42) : 'EF_ARPK',
|
||||
(0x5F, 0x3C, 0x4F, 0x43) : 'EF_TPRK',
|
||||
#(0x5F, 0x3C, 0x4F, 0xXX) : 'EF_TKCDF',
|
||||
(0x5F, 0x3B) : 'DF_GSM-ACCESS',
|
||||
(0x5F, 0x3B, 0x4F, 0x20) : 'EF_Kc',
|
||||
(0x5F, 0x3B, 0x4F, 0x52) : 'EF_KcGPRS',
|
||||
(0x5F, 0x3B, 0x4F, 0x63) : 'EF_CPBCCH',
|
||||
(0x5F, 0x3B, 0x4F, 0x64) : 'EF_invSCAN',
|
||||
(0x5F, 0x3A) : 'DF_PHONEBOOK',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_UID',
|
||||
(0x5F, 0x3A, 0x4F, 0x22) : 'EF_PSC',
|
||||
(0x5F, 0x3A, 0x4F, 0x23) : 'EF_CC',
|
||||
(0x5F, 0x3A, 0x4F, 0x24) : 'EF_PUID',
|
||||
(0x5F, 0x3A, 0x4F, 0x30) : 'EF_PBR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_CCP1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_IAP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ADN',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EXT1',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_PBC',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GRP',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_AAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_GAS',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_ANR',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_SNE',
|
||||
#(0x5F, 0x3A, 0x4F, 0xXX) : 'EF_EMAIL',
|
||||
(0x6F, 0x05) : 'EF_LI',
|
||||
(0x6F, 0x06) : 'EF_ARR',
|
||||
(0x6F, 0x07) : 'EF_IMSI',
|
||||
(0x6F, 0x08) : 'EF_Keys',
|
||||
(0x6F, 0x09) : 'EF_KeysPS',
|
||||
(0x6F, 0x2C) : 'EF_DCK',
|
||||
(0x6F, 0x31) : 'EF_HPLMN',
|
||||
(0x6F, 0x32) : 'EF_CNL',
|
||||
(0x6F, 0x37) : 'EF_ACMmax',
|
||||
(0x6F, 0x38) : 'EF_UST',
|
||||
(0x6F, 0x39) : 'EF_ACM',
|
||||
(0x6F, 0x3B) : 'EF_FDN',
|
||||
(0x6F, 0x3C) : 'EF_SMS',
|
||||
(0x6F, 0x3E) : 'EF_GID1',
|
||||
(0x6F, 0x3F) : 'EF_GID2',
|
||||
(0x6F, 0x40) : 'EF_MSISDN',
|
||||
(0x6F, 0x41) : 'EF_PUCT',
|
||||
(0x6F, 0x42) : 'EF_SMSP',
|
||||
(0x6F, 0x43) : 'EF_SMSS',
|
||||
(0x6F, 0x45) : 'EF_CBMI',
|
||||
(0x6F, 0x46) : 'EF_SPN',
|
||||
(0x6F, 0x47) : 'EF_SMSR',
|
||||
(0x6F, 0x48) : 'EF_CBMID',
|
||||
(0x6F, 0x49) : 'EF_SDN',
|
||||
(0x6F, 0x4B) : 'EF_EXT2',
|
||||
(0x6F, 0x4C) : 'EF_EXT3',
|
||||
(0x6F, 0x4D) : 'EF_BDN',
|
||||
(0x6F, 0x4E) : 'EF_EXT5',
|
||||
(0x6F, 0x50) : 'EF_CBMIR',
|
||||
(0x6F, 0x55) : 'EF_EXT4',
|
||||
(0x6F, 0x56) : 'EF_EST',
|
||||
(0x6F, 0x57) : 'EF_ACL',
|
||||
(0x6F, 0x58) : 'EF_CMI',
|
||||
(0x6F, 0x5B) : 'EF_START-HFN',
|
||||
(0x6F, 0x5C) : 'EF_THRESHOLD',
|
||||
(0x6F, 0x60) : 'EF_PLMNwAcT',
|
||||
(0x6F, 0x61) : 'EF_OPLMNwAcT',
|
||||
(0x6F, 0x62) : 'EF_HPLMNwAcT',
|
||||
(0x6F, 0xD9) : 'EF_EHPLMN',
|
||||
(0x6F, 0x73) : 'EF_PSLOCI',
|
||||
(0x6F, 0x78) : 'EF_ACC',
|
||||
(0x6F, 0x7B) : 'EF_FPLMN',
|
||||
(0x6F, 0x7E) : 'EF_LOCI',
|
||||
(0x6F, 0x80) : 'EF_ICI',
|
||||
(0x6F, 0x81) : 'EF_OCI',
|
||||
(0x6F, 0x82) : 'EF_ICT',
|
||||
(0x6F, 0x83) : 'EF_OCT',
|
||||
(0x6F, 0xAD) : 'EF_AD',
|
||||
(0x6F, 0xB5) : 'EF_eMLPP',
|
||||
(0x6F, 0xB6) : 'EF_AAeM',
|
||||
(0x6F, 0xB7) : 'EF_ECC',
|
||||
(0x6F, 0xC3) : 'EF_Hiddenkey',
|
||||
(0x6F, 0xC4) : 'EF_NETPAR',
|
||||
(0x6F, 0xC5) : 'EF_PNN',
|
||||
(0x6F, 0xC6) : 'EF_OPL',
|
||||
(0x6F, 0xC7) : 'EF_MBDN',
|
||||
(0x6F, 0xC8) : 'EF_EXT6',
|
||||
(0x6F, 0xC9) : 'EF_MBI',
|
||||
(0x6F, 0xCA) : 'EF_MWIS',
|
||||
(0x6F, 0xCB) : 'EF_CFIS',
|
||||
(0x6F, 0xCC) : 'EF_EXT7',
|
||||
(0x6F, 0xCD) : 'EF_SPDI',
|
||||
(0x6F, 0xCE) : 'EF_MMSN',
|
||||
(0x6F, 0xCF) : 'EF_EXT8',
|
||||
(0x6F, 0xD0) : 'EF_MMSICP',
|
||||
(0x6F, 0xD1) : 'EF_MMSUP',
|
||||
(0x6F, 0xD2) : 'EF_MMSUCP',
|
||||
(0x6F, 0xD3) : 'EF_NIA',
|
||||
(0x6F, 0x4F) : 'EF_CCP2',
|
||||
(0x6F, 0xB1) : 'EF_VGCS',
|
||||
(0x6F, 0xB2) : 'EF_VGCSS',
|
||||
(0x6F, 0xB3) : 'EF_VBS',
|
||||
(0x6F, 0xB4) : 'EF_VBSS',
|
||||
(0x6F, 0xD4) : 'EF_VGCSCA',
|
||||
(0x6F, 0xD5) : 'EF_VBSCA',
|
||||
(0x6F, 0xD6) : 'EF_GBAP',
|
||||
(0x6F, 0xD7) : 'EF_MSK',
|
||||
(0x6F, 0xD8) : 'EF_MUK',
|
||||
(0x6F, 0xDA) : 'EF_GBANL',
|
||||
(0x6F, 0xDB) : 'EF_EHPLMNPI',
|
||||
(0x6F, 0xDC) : 'EF_LRPLMNSI',
|
||||
(0x6F, 0xDD) : 'EF_NAFKCA',
|
||||
(0x6F, 0xDE) : 'EF_SPNI',
|
||||
(0x6F, 0xDF) : 'EF_PNNI',
|
||||
}
|
||||
|
||||
# Actually, those DF can be under MF, ADF_USIM or DF_TELECOM:
|
||||
DF_PHONEBOOK = {
|
||||
(0x5F, 0x3A) : 'DF_PHONEBOOK',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_UID',
|
||||
(0x5F, 0x3A, 0x4F, 0x22) : 'EF_PSC',
|
||||
(0x5F, 0x3A, 0x4F, 0x23) : 'EF_CC',
|
||||
(0x5F, 0x3A, 0x4F, 0x24) : 'EF_PUID',
|
||||
(0x5F, 0x3A, 0x4F, 0x30) : 'EF_PBR',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_CCP1',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_IAP',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_ADN',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_EXT1',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_PBC',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_GRP',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_AAS',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_GAS',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_ANR',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_SNE',
|
||||
(0x5F, 0x3A, 0x4F) : 'EF_EMAIL',
|
||||
}
|
||||
|
||||
DF_GRAPHICS = {
|
||||
(0x5F, 0x50) : 'DF_GRAPHICS',
|
||||
(0x5F, 0x50, 0x4F, 0x20) : 'EF_IMG',
|
||||
}
|
||||
|
||||
DF_MULTIMEDIA = {
|
||||
(0x5F, 0x3B) : 'DF_MULTIMEDIA',
|
||||
(0x5F, 0x3B, 0x4F, 0x47) : 'EF_MML',
|
||||
(0x5F, 0x3B, 0x4F, 0x48) : 'EF_MMDF',
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,456 @@
|
|||
# -*- 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
|
||||
# SIM card
|
||||
# communication based on ISO7816 card
|
||||
#
|
||||
# needs pyscard from:
|
||||
# http://pyscard.sourceforge.net/
|
||||
#################################
|
||||
|
||||
from card.ICC import ISO7816
|
||||
from card.FS import SIM_FS, MF_FS
|
||||
from card.utils import *
|
||||
|
||||
SIM_service_table = {
|
||||
1 : "CHV1 disable function",
|
||||
2 : "Abbreviated Dialling Numbers (ADN)",
|
||||
3 : "Fixed Dialling Numbers (FDN)",
|
||||
4 : "Short Message Storage (SMS)",
|
||||
5 : "Advice of Charge (AoC)",
|
||||
6 : "Capability Configuration Parameters (CCP)",
|
||||
7 : "PLMN selector",
|
||||
8 : "RFU",
|
||||
9 : "MSISDN",
|
||||
10 : "Extension1",
|
||||
11 : "Extension2",
|
||||
12 : "SMS Parameters",
|
||||
13 : "Last Number Dialled (LND)",
|
||||
14 : "Cell Broadcast Message Identifier",
|
||||
15 : "Group Identifier Level 1",
|
||||
16 : "Group Identifier Level 2",
|
||||
17 : "Service Provider Name",
|
||||
18 : "Service Dialling Numbers (SDN)",
|
||||
19 : "Extension3",
|
||||
20 : "RFU",
|
||||
21 : "VGCS Group Identifier List (EFVGCS and EFVGCSS)",
|
||||
22 : "VBS Group Identifier List (EFVBS and EFVBSS)",
|
||||
23 : "enhanced Multi-Level Precedence and Pre-emption Service",
|
||||
24 : "Automatic Answer for eMLPP",
|
||||
25 : "Data download via SMS-CB",
|
||||
26 : "Data download via SMS-PP",
|
||||
27 : "Menu selection",
|
||||
28 : "Call control",
|
||||
29 : "Proactive SIM",
|
||||
30 : "Cell Broadcast Message Identifier Ranges",
|
||||
31 : "Barred Dialling Numbers (BDN)",
|
||||
32 : "Extension4",
|
||||
33 : "De-personalization Control Keys",
|
||||
34 : "Co-operative Network List",
|
||||
35 : "Short Message Status Reports",
|
||||
36 : "Network's indication of alerting in the MS ",
|
||||
37 : "Mobile Originated Short Message control by SIM ",
|
||||
38 : "GPRS",
|
||||
39 : "Image (IMG)",
|
||||
40 : "SoLSA (Support of Local Service Area)",
|
||||
41 : "USSD string data object supported in Call Control",
|
||||
42 : "RUN AT COMMAND command",
|
||||
43 : "User controlled PLMN Selector with Access Technology",
|
||||
44 : "Operator controlled PLMN Selector with Access Technology",
|
||||
45 : "HPLMN Selector with Access Technology",
|
||||
46 : "CPBCCH Information",
|
||||
47 : "Investigation Scan",
|
||||
48 : "Extended Capability Configuration Parameters",
|
||||
49 : "MExE",
|
||||
50 : "RPLMN last used Access Technology",
|
||||
51 : "PLMN Network Name",
|
||||
52 : "Operator PLMN List",
|
||||
53 : "Mailbox Dialling Numbers ",
|
||||
54 : "Message Waiting Indication Status",
|
||||
55 : "Call Forwarding Indication Status",
|
||||
56 : "Service Provider Display Information",
|
||||
}
|
||||
|
||||
class SIM(ISO7816):
|
||||
'''
|
||||
define attributes, methods and facilities for ETSI / 3GPP SIM card
|
||||
check SIM specifications in ETSI TS 102.221 and 3GPP TS 51.011
|
||||
|
||||
inherit methods and objects from ISO7816 class
|
||||
use self.dbg = 1 or more to print live debugging information
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
'''
|
||||
initialize like an ISO7816-4 card with CLA=0xA0
|
||||
can also be used for USIM working in SIM mode,
|
||||
'''
|
||||
ISO7816.__init__(self, CLA=0xA0)
|
||||
|
||||
if self.dbg >= 2:
|
||||
log(3, '(SIM.__init__) type definition: %s' % type(self))
|
||||
log(3, '(SIM.__init__) CLA definition: %s' % hex(self.CLA))
|
||||
|
||||
@staticmethod
|
||||
def sw_status(sw1, sw2):
|
||||
'''
|
||||
sw_status(sw1=int, sw2=int) -> string
|
||||
|
||||
extends SW status bytes interpretation from ISO7816
|
||||
with ETSI / 3GPP SW codes
|
||||
helps to speak with the smartcard!
|
||||
'''
|
||||
status = ISO7816.sw_status(sw1, sw2)
|
||||
if sw1 == 0x91: status = 'normal processing, with extra info ' \
|
||||
'containing a command for the terminal: length of the ' \
|
||||
'response data %d' % sw2
|
||||
elif sw1 == 0x9E: status = 'normal processing, SIM data download ' \
|
||||
'error: length of the response data %d' % sw2
|
||||
elif sw1 == 0x9F: status = 'normal processing: length of the ' \
|
||||
'response data %d' % sw2
|
||||
elif (sw1, sw2) == (0x93, 0x00): status = 'SIM application toolkit ' \
|
||||
'busy, command cannot be executed at present'
|
||||
elif sw1 == 0x92 :
|
||||
status = 'memory management'
|
||||
if sw2 < 16: status += ': command successful but after %d '\
|
||||
'retry routine' % sw2
|
||||
elif sw2 == 0x40: status += ': memory problem'
|
||||
elif sw1 == 0x94:
|
||||
status = 'referencing management'
|
||||
if sw2 == 0x00: status += ': no EF selected'
|
||||
elif sw2 == 0x02: status += ': out of range (invalid address)'
|
||||
elif sw2 == 0x04: status += ': file ID or pattern not found'
|
||||
elif sw2 == 0x08: status += ': file inconsistent with the command'
|
||||
elif sw1 == 0x98:
|
||||
status = 'security management'
|
||||
if sw2 == 0x02: status += ': no CHV initialized'
|
||||
elif sw2 == 0x04: status += ': access condition not fulfilled, ' \
|
||||
'at least 1 attempt left'
|
||||
elif sw2 == 0x08: status += ': in contradiction with CHV status'
|
||||
elif sw2 == 0x10: status += ': in contradiction with ' \
|
||||
'invalidation status'
|
||||
elif sw2 == 0x40: status += ': unsuccessful CHV verification, ' \
|
||||
'no attempt left'
|
||||
elif sw2 == 0x50: status += ': increase cannot be performed, ' \
|
||||
'max value reached'
|
||||
elif sw2 == 0x62: status += ': authentication error, ' \
|
||||
'application specific'
|
||||
elif sw2 == 0x63: status += ': security session expired'
|
||||
return status
|
||||
|
||||
def verify_pin(self, pin='', pin_type=1):
|
||||
'''
|
||||
verify CHV1 (PIN code) or CHV2 with VERIFY APDU command
|
||||
call ISO7816 VERIFY method
|
||||
'''
|
||||
if pin_type in [1, 2] and type(pin) is str and \
|
||||
len(pin) == 4 and 0 <= int(pin) < 10000:
|
||||
PIN = [ord(i) for i in pin] + [0xFF, 0xFF, 0xFF, 0xFF]
|
||||
self.coms.push( self.VERIFY(P2=pin_type, Data=PIN) )
|
||||
else:
|
||||
if self.dbg:
|
||||
log(2, '(verify_pin) bad input parameters')
|
||||
|
||||
def disable_pin(self, pin='', pin_type=1):
|
||||
'''
|
||||
disable CHV1 (PIN code) or CHV2 with DISABLE_CHV APDU command
|
||||
TIP: do it as soon as you can when you are working
|
||||
with a SIM / USIM card for which you know the PIN!
|
||||
call ISO7816 DISABLE method
|
||||
'''
|
||||
if pin_type in [1, 2] and type(pin) is str and \
|
||||
len(pin) == 4 and 0 <= int(pin) < 10000:
|
||||
PIN = [ord(i) for i in pin] + [0xFF, 0xFF, 0xFF, 0xFF]
|
||||
self.coms.push( self.DISABLE_CHV(P2=pin_type, Data=PIN) )
|
||||
else:
|
||||
if self.dbg:
|
||||
log(2, '(disable_pin) bad input parameters')
|
||||
|
||||
def enable_pin(self, pin='', pin_type=1):
|
||||
'''
|
||||
enable CHV1 (PIN code) or CHV2 with ENABLE_CHV APDU command
|
||||
call ISO7816 ENABLE method
|
||||
'''
|
||||
if pin_type in [1, 2] and type(pin) is str and \
|
||||
len(pin) == 4 and 0 <= int(pin) < 10000:
|
||||
PIN = [ord(i) for i in pin] + [0xFF, 0xFF, 0xFF, 0xFF]
|
||||
self.coms.push( self.ENABLE_CHV(P2=pin_type, Data=PIN) )
|
||||
else:
|
||||
if self.dbg:
|
||||
log(2, '(enable_pin) bad input parameters')
|
||||
|
||||
def unblock_pin(self, pin_type=1, unblock_pin=''):
|
||||
'''
|
||||
WARNING: not correctly implemented!!!
|
||||
and PUK are in general 8 nums...
|
||||
TODO: make it correctly!
|
||||
APDU Tx de-activated
|
||||
|
||||
unblock CHV1 (PIN code) or CHV2 with UNBLOCK_CHV APDU command
|
||||
and set 0000 value for new PIN
|
||||
call ISO7816 UNBLOCK_CHV method
|
||||
'''
|
||||
log(1, '(unblock_pin) not implemented: aborting')
|
||||
return
|
||||
#if pin_type == 1:
|
||||
# pin_type = 0
|
||||
if pin_type in [0, 2] and type(unblock_pin) is str and \
|
||||
len(unblock_pin) == 4 and 0 <= int(unblock_pin) < 10000:
|
||||
UNBL_PIN = [ord(i) for i in unblock_pin] + [0xFF, 0xFF, 0xFF, 0xFF]
|
||||
#self.coms.push( self.UNBLOCK_CHV(P2=pin_type, Lc=0x10, \
|
||||
# Data=UNBL_PIN + \
|
||||
# [0x30, 0x30, 0x30, 0x30, 0xFF, 0xFF, 0xFF, 0xFF]) )
|
||||
else:
|
||||
if self.dbg:
|
||||
log(2, '(unblock_pin) bad input parameters')
|
||||
#return self.UNBLOCK_CHV(P2=pin_type)
|
||||
|
||||
def parse_file(self, Data=[]):
|
||||
'''
|
||||
parse_file(Data=[0x12, 0x34, 0x56, 0x89]) -> dict(file)
|
||||
|
||||
parses a list of bytes returned when selecting a file
|
||||
interprets the content of some informative bytes for right accesses,
|
||||
type / format of file... see TS 51.011
|
||||
works over the SIM file structure
|
||||
'''
|
||||
fil = {}
|
||||
fil['Size'] = Data[2]*0x100 + Data[3]
|
||||
fil['File Identifier'] = Data[4:6]
|
||||
fil['Type'] = ('RFU', 'MF', 'DF', '', 'EF')[Data[6]]
|
||||
fil['Length'] = Data[12]
|
||||
if fil['Type'] == 'MF' or fil['Type'] == 'DF':
|
||||
fil['DF_num'] = Data[14]
|
||||
fil['EF_num'] = Data[15]
|
||||
fil['codes_num'] = Data[16]
|
||||
fil['CHV1'] = ('not initialized','initialized')\
|
||||
[(Data[18] & 0x80) / 0x80]\
|
||||
+ ': %d attempts remain' % (Data[18] & 0x0F)
|
||||
fil['unblock_CHV1'] = ('not initialized','initialized')\
|
||||
[(Data[19] & 0x80) / 0x80]\
|
||||
+ ': %d attempts remain' % (Data[19] & 0x0F)
|
||||
fil['CHV2'] = ('not initialized','initialized')\
|
||||
[(Data[20] & 0x80) / 0x80]\
|
||||
+ ': %d attempts remain' % (Data[20] & 0x0F)
|
||||
fil['unblock_CHV2'] = ('not initialized','initialized')\
|
||||
[(Data[21] & 0x80) / 0x80]\
|
||||
+ ': %d attempts remain' % (Data[21] & 0x0F)
|
||||
if len(Data) > 23:
|
||||
fil['Adm'] = Data[23:]
|
||||
elif fil['Type'] == 'EF':
|
||||
cond = ('ALW', 'CHV1', 'CHV2', 'RFU', 'ADM_4', 'ADM_5',
|
||||
'ADM_6', 'ADM_7', 'ADM_8', 'ADM_9', 'ADM_A',
|
||||
'ADM_B', 'ADM_C', 'ADM_D', 'ADM_E', 'NEW')
|
||||
fil['UPDATE'] = cond[Data[8] & 0x0F]
|
||||
fil['READ'] = cond[Data[8] >> 4]
|
||||
fil['INCREASE'] = cond[Data[9] >> 4]
|
||||
fil['INVALIDATE'] = cond[Data[10] & 0x0F]
|
||||
fil['REHABILITATE'] = cond[Data[10] >> 4]
|
||||
fil['Status'] = ('not read/updatable when invalidated',
|
||||
'read/updatable when invalidated')\
|
||||
[byteToBit(Data[11])[5]] \
|
||||
+ (': invalidated',': not invalidated')\
|
||||
[byteToBit(Data[11])[7]]
|
||||
fil['Structure'] = ('transparent', 'linear fixed', '', 'cyclic')\
|
||||
[Data[13]]
|
||||
if fil['Structure'] == 'cyclic':
|
||||
fil['INCREASE'] = byteToBit(Data[7])[1]
|
||||
if len(Data) > 14:
|
||||
fil['Record Length'] = Data[14]
|
||||
return fil
|
||||
|
||||
def run_gsm_alg(self, RAND=16*[0x00]):
|
||||
'''
|
||||
self.run_gsm_alg( RAND ) -> ( SRES, Kc )
|
||||
RAND : list of bytes, length 16
|
||||
SRES : list of bytes, length 4
|
||||
Kc : list of bytes, length 8
|
||||
|
||||
runs GSM authentication algorithm:
|
||||
accepts any kind of RAND (old GSM fashion)
|
||||
feed with RAND 16 bytes value
|
||||
returns a list with SRES and Kc, or None on error
|
||||
'''
|
||||
if len(RAND) != 16:
|
||||
if self.dbg:
|
||||
log(1, '(run_gsm_alg) bad RAND value: aborting')
|
||||
return None
|
||||
# select DF_GSM directory
|
||||
self.select([0x7F, 0x20])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(run_gsm_alg) %s' % self.coms())
|
||||
return None
|
||||
# run authentication
|
||||
self.coms.push(self.INTERNAL_AUTHENTICATE(P1=0x00, P2=0x00, Data=RAND))
|
||||
if self.coms()[2][0] != 0x9F:
|
||||
if self.dbg >= 2:
|
||||
log(3, '(run_gsm_alg) %s' % self.coms())
|
||||
return None
|
||||
# get authentication response
|
||||
self.coms.push(self.GET_RESPONSE(Le=self.coms()[2][1]))
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(run_gsm_alg) %s' % self.coms())
|
||||
return None
|
||||
SRES, Kc = self.coms()[3][0:4], self.coms()[3][4:]
|
||||
return [ SRES, Kc ]
|
||||
|
||||
def get_imsi(self):
|
||||
'''
|
||||
self.get_imsi() -> string(IMSI)
|
||||
|
||||
reads IMSI value at address [0x6F, 0x07]
|
||||
returns IMSI string on success or None on error
|
||||
'''
|
||||
# select DF_GSM for SIM card
|
||||
self.select([0x7F, 0x20])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_imsi) %s' % self.coms())
|
||||
return None
|
||||
|
||||
# select IMSI file
|
||||
imsi = self.select([0x6F, 0x07])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_imsi) %s' % self.coms())
|
||||
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_services(self):
|
||||
'''
|
||||
self.get_services() -> None
|
||||
|
||||
reads SIM Service Table at address [0x6F, 0x38]
|
||||
returns list of services allowed / activated
|
||||
'''
|
||||
# select DF_GSM for SIM card
|
||||
self.select([0x7F, 0x20])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_services) %s' % self.coms())
|
||||
return 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):
|
||||
'''
|
||||
self.read_services() -> None
|
||||
|
||||
reads SIM Service Table at address [0x6F, 0x38]
|
||||
prints services allowed / activated
|
||||
returns None
|
||||
'''
|
||||
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:
|
||||
# 2 bits per service -> 4 services per byte
|
||||
for i in range(0, 7, 2):
|
||||
cnt += 1
|
||||
if B & 2**i:
|
||||
info = 'allocated'
|
||||
if B & (2**i+1):
|
||||
info += ' | activated'
|
||||
if cnt in SIM_service_table:
|
||||
services.append('%i : %s : %s' \
|
||||
% (cnt, SIM_service_table[cnt], info))
|
||||
else:
|
||||
services.append('%i : %s' % (cnt, info))
|
||||
return services
|
||||
|
||||
def explore_fs(self, filename='sim_fs', depth=True, emul=False):
|
||||
'''
|
||||
self.explore_fs(self, filename='sim_fs') -> void
|
||||
filename: file to write in information found
|
||||
depth: depth in recursivity, True=infinite
|
||||
|
||||
brute force all file addresses from MF recursively
|
||||
(until no more DF are found)
|
||||
write information on existing DF and file in the output file
|
||||
'''
|
||||
simfs_entries = MF_FS.keys()
|
||||
if not emul:
|
||||
self.explore_DF([], None, depth)
|
||||
|
||||
fd = open(filename, 'w')
|
||||
fd.write('\n### MF ###\n')
|
||||
f = self.select()
|
||||
write_dict(f, fd)
|
||||
fd.write('\n')
|
||||
#
|
||||
for f in self.FS:
|
||||
path = tuple(f['Absolut Path'])
|
||||
if path in simfs_entries:
|
||||
f['Name'] = MF_FS[path]
|
||||
write_dict(f, fd)
|
||||
fd.write('\n')
|
||||
|
||||
fd.close()
|
||||
|
||||
def get_ICCID(self):
|
||||
# select MF
|
||||
self.select([0x3F, 0x0])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_ICCID) %s' % self.coms())
|
||||
return None
|
||||
|
||||
# select IMSI file
|
||||
iccid = self.select([0x2F, 0xE2])
|
||||
if self.coms()[2] != (0x90, 0x00):
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_ICCID) %s' % self.coms())
|
||||
return None
|
||||
|
||||
# and parse the received data into the IMSI structure
|
||||
if 'Data' in iccid.keys() and len(iccid['Data']) >= 10:
|
||||
return decode_BCD(iccid['Data'])
|
||||
|
||||
# if issue with the content of the ICCID file
|
||||
if self.dbg >= 2:
|
||||
log(3, '(get_ICCID) %s' % self.coms())
|
||||
return None
|
||||
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
# -*- 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',
|
||||
}
|
||||
|
||||
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):
|
||||
'''
|
||||
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, 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')
|
||||
|
||||
@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()
|
||||
#
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# -*- 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.
|
||||
"""
|
||||
|
||||
# smartcard Integrated Circuit Card library
|
||||
# based on Laurent Rousseau pcsclite daemon or Microsoft scard service
|
||||
# and Jean-Daniel Aussel pyscard (magical) python binding
|
||||
# specificities of SIM and USIM card available
|
||||
|
||||
__all__ = ['utils', 'ICC', 'SIM', 'USIM', 'FS']
|
||||
__version__ = '0.2.0'
|
|
@ -0,0 +1,334 @@
|
|||
# -*- 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.
|
||||
"""
|
||||
|
||||
#################################
|
||||
# generic functions #
|
||||
# being used in smartcard specs #
|
||||
#################################
|
||||
|
||||
from collections import deque
|
||||
from smartcard.util import toBytes
|
||||
|
||||
|
||||
###############
|
||||
# log wrapper #
|
||||
###############
|
||||
log_levels = {1:'ERR', 2:'WNG', 3:'DBG'}
|
||||
def log(level, string):
|
||||
# could output to a file
|
||||
# but here, just print()
|
||||
print('[%s] %s' % (log_levels[level], string))
|
||||
|
||||
# from python 2.6, format('b') allows to use 0b10010110 notation:
|
||||
# much convinient
|
||||
def byteToBit(byte):
|
||||
'''
|
||||
byteToBit(0xAB) -> [1, 0, 1, 0, 1, 0, 1, 1]
|
||||
|
||||
converts a byte integer value into a list of bits
|
||||
'''
|
||||
bit = [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
for i in range(8):
|
||||
if byte % pow(2, i+1):
|
||||
bit[7-i] = 1
|
||||
byte = byte - pow(2, i)
|
||||
return bit
|
||||
|
||||
# equivalent to the pyscard function "toASCIIBytes"
|
||||
# new version of python (>2.6) seems to have a built-in "bytes" type
|
||||
def stringToByte(string):
|
||||
'''
|
||||
stringToByte('test') -> [116, 101, 115, 116]
|
||||
|
||||
converts a string into a list of bytes
|
||||
'''
|
||||
bytelist = []
|
||||
for c in string:
|
||||
bytelist.extend( toBytes(c.encode('hex')) )
|
||||
return bytelist
|
||||
|
||||
# equivalent to the pyscard function "toASCIIString"
|
||||
def byteToString(bytelist):
|
||||
'''
|
||||
byteToString([116, 101, 115, 116]) -> 'test'
|
||||
|
||||
converts a list of bytes into a string
|
||||
'''
|
||||
string = ''
|
||||
for b in bytelist:
|
||||
string += chr(b)
|
||||
return string
|
||||
|
||||
def LV_parser(bytelist):
|
||||
'''
|
||||
LV_parser([0x02, 0xAB, 0xCD, 0x01, 0x12, 0x34]) -> [[171, 205], [18], []]
|
||||
|
||||
parses Length-Value records in a list of bytes
|
||||
returns a list of list of bytes
|
||||
length coded on 1 byte
|
||||
'''
|
||||
values = []
|
||||
while len(bytelist) > 0:
|
||||
l = bytelist[0]
|
||||
values.append( bytelist[1:1+l] )
|
||||
bytelist = bytelist[1+l:]
|
||||
return values
|
||||
|
||||
def first_TLV_parser(bytelist):
|
||||
'''
|
||||
first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
|
||||
|
||||
parses first TLV format record in a list of bytelist
|
||||
returns a 3-Tuple: Tag, Length, Value
|
||||
Value is a list of bytes
|
||||
parsing of length is ETSI'style 101.220
|
||||
'''
|
||||
Tag = bytelist[0]
|
||||
if bytelist[1] == 0xFF:
|
||||
Len = bytelist[2]*256 + bytelist[3]
|
||||
Val = bytelist[4:4+Len]
|
||||
else:
|
||||
Len = bytelist[1]
|
||||
Val = bytelist[2:2+Len]
|
||||
return (Tag, Len, Val)
|
||||
|
||||
def TLV_parser(bytelist):
|
||||
'''
|
||||
TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
|
||||
|
||||
loops on the input list of bytes with the "first_TLV_parser()" function
|
||||
returns a list of 3-Tuples
|
||||
'''
|
||||
ret = []
|
||||
while len(bytelist) > 0:
|
||||
T, L, V = first_TLV_parser(bytelist)
|
||||
if T == 0xFF:
|
||||
# padding bytes
|
||||
break
|
||||
ret.append( (T, L, V) )
|
||||
# need to manage length of L
|
||||
if L > 0xFE:
|
||||
bytelist = bytelist[ L+4 : ]
|
||||
else:
|
||||
bytelist = bytelist[ L+2 : ]
|
||||
return ret
|
||||
|
||||
def first_BERTLV_parser(bytelist):
|
||||
'''
|
||||
first_BERTLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00])
|
||||
-> ([1, 'contextual', 'constructed', 10], [1, 2], [171, 205])
|
||||
|
||||
parses first BER-TLV format record in a list of bytes
|
||||
returns a 3-Tuple: Tag, Length, Value
|
||||
Tag: [Tag class, Tag DO, Tag number]
|
||||
Length: [Length of length, Length value]
|
||||
Value: [Value bytes list]
|
||||
parsing of length is ETSI'style 101.220
|
||||
'''
|
||||
# Tag class and DO
|
||||
byte0 = byteToBit(bytelist[0])
|
||||
if byte0[0:2] == [0, 0]:
|
||||
Tag_class = 'universal'
|
||||
elif byte0[0:2] == [0, 1]:
|
||||
Tag_class = 'applicative'
|
||||
elif byte0[0:2] == [1, 0]:
|
||||
Tag_class = 'contextual'
|
||||
elif byte0[0:2] == [1, 1]:
|
||||
Tag_class = 'private'
|
||||
if byte0[2:3] == [0]:
|
||||
Tag_DO = 'primitive'
|
||||
elif byte0[2:3] == [1]:
|
||||
Tag_DO = 'constructed'
|
||||
# Tag coded with more than 1 byte
|
||||
i = 0
|
||||
if byte0[3:8] == [1, 1, 1, 1, 1]:
|
||||
Tag_bits = byteToBit(bytelist[1])[1:8]
|
||||
i += 1
|
||||
while byteToBit(bytelist[i])[0] == 1:
|
||||
i += 1
|
||||
Tag_bits += byteToBit(bytelist[i])[1:8]
|
||||
# Tag coded with 1 byte
|
||||
else:
|
||||
Tag_bits = byte0[3:8]
|
||||
|
||||
# Tag number calculation
|
||||
Tag_num = 0
|
||||
for j in range(len(Tag_bits)):
|
||||
Tag_num += Tag_bits[len(Tag_bits)-j-1] * pow(2, j)
|
||||
|
||||
# Length coded with more than 1 byte (BER long form)
|
||||
if bytelist[i+1] & 0x80:
|
||||
Len_num = bytelist[i+1] - 0x80
|
||||
Len = reduce(lambda x,y: (x<<8)+y, bytelist[i+2:i+2+Len_num])
|
||||
Val = bytelist[i+2+Len_num:i+2+Len_num+Len]
|
||||
# Length coded with 1 byte
|
||||
else:
|
||||
Len_num = 1
|
||||
Len = bytelist[i+1]
|
||||
Val = bytelist[i+2:i+2+Len]
|
||||
|
||||
return ([i+1, Tag_class, Tag_DO, Tag_num], [Len_num, Len], Val)
|
||||
#return ([Tag_class, Tag_DO, Tag_num], Len, Val)
|
||||
|
||||
def BERTLV_parser(bytelist):
|
||||
'''
|
||||
BERTLV_parser([0xAA, ..., 0xFF]) -> [([T], L, [V]), ([T], L, [V]), ...]
|
||||
|
||||
loops on the input bytes with the "first_BERTLV_parser()" function
|
||||
returns a list of 3-Tuples containing BERTLV records
|
||||
'''
|
||||
ret = []
|
||||
while len(bytelist) > 0:
|
||||
T, L, V = first_BERTLV_parser(bytelist)
|
||||
#if T == 0xFF:
|
||||
# break # padding bytes
|
||||
ret.append( (T[1:], L[1], V) )
|
||||
# need to manage lengths of Tag and Length
|
||||
bytelist = bytelist[ T[0] + L[0] + L[1] : ]
|
||||
return ret
|
||||
|
||||
def decode_BCD(data=[]):
|
||||
'''
|
||||
decode_BCD([0x21, 0xFE, 0xA3]) -> '121415310'
|
||||
|
||||
to decode serial number (IMSI, ICCID...) from list of bytes
|
||||
'''
|
||||
string = ''
|
||||
for B in data:
|
||||
# 1st digit (4 LSB), can be padding (e.g. 0xF)
|
||||
if (B&0x0F) < 10: string += str(B&0x0F)
|
||||
# 2nd digit (4 MSB), can be padding (e.g. 0xF)
|
||||
if (B>>4) < 10: string += str(B>>4)
|
||||
return string
|
||||
|
||||
def compute_luhn(digit_str=''):
|
||||
'''
|
||||
compute_luhn('15632458') -> 4
|
||||
|
||||
return the luhn code of the digits provided
|
||||
'''
|
||||
if not digit_str.isdigit():
|
||||
print('you must provide a string of digits')
|
||||
return
|
||||
# append 0
|
||||
d = [int(c) for c in digit_str+'0']
|
||||
# sum of odd digits
|
||||
cs = sum(d[-1::-2])
|
||||
# sum of (sum of digits(even digits * 2))
|
||||
cs += sum([(v*2)%9 if v != 9 else 9 for v in d[-2::-2]])
|
||||
# modulo 10: luhn checksum
|
||||
cs = cs%10
|
||||
# return the luhn code
|
||||
if cs == 0: return cs
|
||||
else: return 10-cs
|
||||
|
||||
def write_dict(dict, fd):
|
||||
'''
|
||||
write a dict() content to a file descriptor
|
||||
'''
|
||||
keys = dict.keys()
|
||||
keys.sort()
|
||||
fd.write('\n')
|
||||
for k in keys:
|
||||
rec = dict[k]
|
||||
if isinstance(rec, list) and \
|
||||
len(rec) == [isinstance(i, int) for i in rec].count(True):
|
||||
rec = ''.join(['[', ', '.join(map(hex, rec)), ']'])
|
||||
fd.write('%s: %s\n' % (k, rec))
|
||||
|
||||
def make_graph(FS, master_name='(0x3F, 0x00)\nMF'):
|
||||
try:
|
||||
import pydot
|
||||
except:
|
||||
log(1, '(make_graph) pydot library not found: aborting')
|
||||
return
|
||||
#return
|
||||
# build a graph using graphviz Dot language, with pydot
|
||||
# create a graph with master MF or AID node
|
||||
nodes={}
|
||||
graph = pydot.Dot(graph_type='digraph', rankdir='LR')
|
||||
nodes['master'] = pydot.Node(master_name, style='filled', fillcolor='green')
|
||||
graph.add_node(nodes['master'])
|
||||
# attach nodes under master
|
||||
for file in FS:
|
||||
abspath = file['Absolut Path']
|
||||
# build file name for the node
|
||||
label = ''.join(( \
|
||||
''.join((file['Name'], '\n')) if 'Name' in file.keys() else '', \
|
||||
'(', hex(abspath[-2]), ' ', hex(abspath[-1]), ')'))
|
||||
# check for EF or DF for node color
|
||||
color='yellow' if file['Type'][:2] == 'EF' else 'blue'
|
||||
# make node
|
||||
nodes['%s'%abspath] = pydot.Node('%s'%abspath, label=label,\
|
||||
style='filled', fillcolor=color)
|
||||
# put it in the graph and append it to the parent
|
||||
graph.add_node(nodes['%s'%abspath])
|
||||
graph.add_edge(pydot.Edge(nodes['%s'%abspath[:-2]] if \
|
||||
len(abspath) >= 4 else nodes['master'], \
|
||||
nodes['%s'%abspath]))
|
||||
# and return graph ...
|
||||
return graph
|
||||
|
||||
|
||||
|
||||
#######################################################
|
||||
# Generic class to keep track of sent / received APDU #
|
||||
#######################################################
|
||||
class apdu_stack:
|
||||
'''
|
||||
input / output wrapping class
|
||||
for APDU communications
|
||||
|
||||
allows to keep track of communications
|
||||
and exchanged commands
|
||||
|
||||
based on the python "deque" fifo-like object
|
||||
'''
|
||||
|
||||
def __init__(self, limit=10):
|
||||
'''
|
||||
initializes apdu_stack with the maximum of IO to keep track of
|
||||
'''
|
||||
self.apdu_stack = deque([], limit)
|
||||
|
||||
def push(self, apdu_response):
|
||||
'''
|
||||
stacks the returned response into the apdu_stack
|
||||
'''
|
||||
self.apdu_stack.append( apdu_response )
|
||||
|
||||
def __repr__(self):
|
||||
'''
|
||||
represents the whole stack of responses pushed on
|
||||
'''
|
||||
s = ''
|
||||
for apdu in self.apdu_stack:
|
||||
s += apdu.__repr__() + '\n'
|
||||
return s
|
||||
|
||||
def __call__(self):
|
||||
'''
|
||||
calling the apdu_stack returns the last response pushed on it
|
||||
'''
|
||||
try:
|
||||
return self.apdu_stack[-1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
Loading…
Reference in New Issue