aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2020-08-31 14:34:05 +0200
committerHarald Welte <laforge@osmocom.org>2020-08-31 14:36:42 +0200
commitde31d9f88b6817eedb071caee7ce5357b2e62c95 (patch)
tree4e7c2227a49f2a3afbb93c11b7549fcc1ad63283
parent76c74c93d52d2a6881613fdc022a18443820f0cd (diff)
downloadsysmo-usim-tool-de31d9f88b6817eedb071caee7ce5357b2e62c95.tar.gz
sysmo-usim-tool-de31d9f88b6817eedb071caee7ce5357b2e62c95.tar.bz2
sysmo-usim-tool-de31d9f88b6817eedb071caee7ce5357b2e62c95.tar.xz
sysmo-usim-tool-de31d9f88b6817eedb071caee7ce5357b2e62c95.zip
Sync 'card' library with upstream
This reflects 2a81963790e27eb6b188359af169c45afb6d3aaf of https://github.com/mitshell/card.git
-rw-r--r--card/ICC.py305
-rw-r--r--card/SIM.py65
-rw-r--r--card/USIM.py86
-rw-r--r--card/utils.py136
-rw-r--r--simcard.py2
5 files changed, 386 insertions, 208 deletions
diff --git a/card/ICC.py b/card/ICC.py
index f55bfcc..9a0d7ec 100644
--- a/card/ICC.py
+++ b/card/ICC.py
@@ -35,10 +35,8 @@ import re
# smartcard python modules from pyscard
from smartcard.CardType import AnyCardType
-from smartcard.CardType import ATRCardType
from smartcard.CardRequest import CardRequest
from smartcard.CardConnection import CardConnection
-from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver
from smartcard.ATR import ATR
from smartcard.Exceptions import CardConnectionException
from smartcard.util import toHexString
@@ -51,13 +49,13 @@ from card.utils import *
###########################################################
class ISO7816(object):
- '''
+ """
define attributes, methods and facilities for ISO-7816-4 standard smartcard
use self.dbg = 1 or more to print live debugging information
standard instructions codes available in "INS_dic" class dictionnary
standard file tags available in "file_tags" class dictionnary
- '''
+ """
dbg = 0
@@ -103,7 +101,7 @@ class ISO7816(object):
0xC0 : 'GET RESPONSE',
0xC2 : 'ENVELOPE',
0xC3 : 'ENVELOPE',
- 0xCA : 'RETRIEVE DATA',
+ 0xCA : 'GET DATA',
0xCB : 'RETRIEVE DATA',
0xD2 : 'WRITE RECORD',
0xD6 : 'UPDATE BINARY',
@@ -143,54 +141,46 @@ class ISO7816(object):
0xAB : 'Security Attribute expanded',
}
- def __init__(self, atr=None, CLA=0x00):
- '''
+ def __init__(self, CLA=0x00):
+ """
connect smartcard and defines class CLA code for communication
uses "pyscard" library services
creates self.CLA attribute with CLA code
and self.coms attribute with associated "apdu_stack" instance
- '''
-
- if (atr):
- cardtype = ATRCardType(atr)
- else:
- cardtype = AnyCardType()
-
+ """
+ cardtype = AnyCardType()
cardrequest = CardRequest(timeout=1, cardType=cardtype)
self.cardservice = cardrequest.waitforcard()
self.cardservice.connection.connect()
self.reader = self.cardservice.connection.getReader()
self.ATR = self.cardservice.connection.getATR()
-
- #observer = ConsoleCardConnectionObserver()
- #self.cardservice.connection.addObserver(observer)
-
+
self.CLA = CLA
self.coms = apdu_stack()
def disconnect(self):
- '''
+ """
disconnect smartcard: stops the session
uses "pyscard" library service
- '''
+ """
self.cardservice.connection.disconnect()
def define_class(self, CLA=0x00):
- '''
+ """
define smartcard class attribute for APDU command
override CLA value defined in class initialization
- '''
+ """
self.CLA = CLA
def ATR_scan(self, smlist_file="/usr/share/pcsc/smartcard_list.txt"):
- '''
+ """
print smartcard info retrieved from AnswerToReset
thanks to pyscard routine
if pcsc_scan is installed,
use the signature file passed as argument for guessing the card
- '''
+ """
print('\nsmartcard reader: %s' % self.reader)
if self.ATR != None:
print('\nsmart card ATR is: %s' % toHexString(self.ATR))
@@ -225,12 +215,12 @@ class ISO7816(object):
@staticmethod
def sw_status(sw1, sw2):
- '''
+ """
sw_status(sw1=int, sw2=int) -> string
SW status bytes interpretation as defined in ISO-7816 part 4 standard
helps to speak and understand with the smartcard!
- '''
+ """
status = 'undefined status'
if sw1 == 0x90 and sw2 == 0x00: status = 'normal processing: ' \
'command accepted: no further qualification'
@@ -326,7 +316,7 @@ class ISO7816(object):
return status
def sr_apdu(self, apdu, force=False):
- '''
+ """
sr_apdu(apdu=[0x.., 0x.., ...]) ->
list [ string(apdu sent information),
string(SW codes interpretation),
@@ -335,7 +325,7 @@ class ISO7816(object):
generic function to send apdu, receive and interpret response
force: force card reconnection if pyscard transmission fails
- '''
+ """
if force:
try:
data, sw1, sw2 = self.cardservice.connection.transmit(apdu)
@@ -356,7 +346,7 @@ class ISO7816(object):
data ]
def bf_cla(self, start=0, param=[0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00]):
- '''
+ """
bf_cla( start=int(starting CLA),
param=list(bytes for selecting file 0x3F, 0x00) ) ->
list( CLA which could be supported )
@@ -368,7 +358,7 @@ class ISO7816(object):
WARNING:
can block the card definitively
Do not do it with your own VISA / MASTERCARD
- '''
+ """
clist = []
for i in range(start, 256):
ret = self.sr_apdu([i] + param)
@@ -379,7 +369,7 @@ class ISO7816(object):
return clist
def bf_ins(self, start=0):
- '''
+ """
bf_cla( start=int(starting INS) )
-> list( INS which could be supported )
@@ -390,7 +380,7 @@ class ISO7816(object):
WARNING:
can block the card definitively
Do not do it with your own VISA / MASTERCARD
- '''
+ """
ilist = []
for i in range(start, 256):
if self.dbg >= 3:
@@ -409,43 +399,43 @@ class ISO7816(object):
# ISO 7816 and described further in ETSI 101.221
###
def READ_BINARY(self, P1=0x00, P2=0x00, Le=0x01):
- '''
+ """
APDU command to read the content of EF file with transparent structure
Le: length of data bytes to be read
call sr_apdu method
- '''
+ """
READ_BINARY = [self.CLA, 0xB0, P1, P2, Le]
return self.sr_apdu(READ_BINARY)
def WRITE_BINARY(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to write the content of EF file with transparent structure
Data: list of data bytes to be written
call sr_apdu method
- '''
+ """
WRITE_BINARY = [self.CLA, 0xD0, P1, P2, len(Data)] + Data
return self.sr_apdu(WRITE_BINARY)
def UPDATE_BINARY(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to update the content of EF file with transparent structure
Data: list of data bytes to be written
call sr_apdu method
- '''
+ """
UPDATE_BINARY = [self.CLA, 0xD6, P1, P2, len(Data)] + Data
return self.sr_apdu(UPDATE_BINARY)
def ERASE_BINARY(self, P1=0x00, P2=0x00, Lc=None, Data=[]):
- '''
+ """
APDU command to erase the content of EF file with transparent structure
Lc: 'None' or '0x02'
Data: list of data bytes to be written
call sr_apdu method
- '''
+ """
if Lc is None:
ERASE_BINARY = [self.CLA, 0x0E, P1, P2]
else:
@@ -453,71 +443,71 @@ class ISO7816(object):
return self.sr_apdu(ERASE_BINARY)
def READ_RECORD(self, P1=0x00, P2=0x00, Le=0x00):
- '''
+ """
APDU command to read the content of EF file with record structure
P1: record number
P2: reference control
Le: length of data bytes to be read
call sr_apdu method
- '''
+ """
READ_RECORD = [self.CLA, 0xB2, P1, P2, Le]
return self.sr_apdu(READ_RECORD)
def WRITE_RECORD(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to write the content of EF file with record structure
P1: record number
P2: reference control
Data: list of data bytes to be written in the record
call sr_apdu method
- '''
+ """
WRITE_RECORD = [self.CLA, 0xD2, P1, P2, len(Data)] + Data
return self.sr_apdu(WRITE_RECORD)
def APPEND_RECORD(self, P2=0x00, Data=[]):
- '''
+ """
APDU command to append a record on EF file with record structure
P2: reference control
Data: list of data bytes to be appended on the record
call sr_apdu method
- '''
+ """
APPEND_RECORD = [self.CLA, 0xE2, 0x00, P2, len(Data)] + Data
return self.sr_apdu(APPEND_RECORD)
def UPDATE_RECORD(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to update the content of EF file with record structure
P1: record number
P2: reference control
Data: list of data bytes to update the record
call sr_apdu method
- '''
+ """
APPEND_RECORD = [self.CLA, 0xDC, P1, P2, len(Data)] + Data
return self.sr_apdu(APPEND_RECORD)
def GET_DATA(self, P1=0x00, P2=0x00, Le=0x01):
- '''
+ """
APDU command to retrieve data object
P1 and P2: reference control for data object description
Le: number of bytes expected in the response
call sr_apdu method
- '''
+ """
GET_DATA = [self.CLA, 0xCA, P1, P2, Le]
return self.sr_apdu(GET_DATA)
def PUT_DATA(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to store data object
P1 and P2: reference control for data object description
Data: list of data bytes to put in the data object structure
call sr_apdu method
- '''
+ """
if len(Data) == 0:
PUT_DATA = [self.CLA, 0xDA, P1, P2]
elif 1 <= len(Data) <= 255:
@@ -529,26 +519,26 @@ class ISO7816(object):
def SELECT_FILE(self, P1=0x00, P2=0x00, Data=[0x3F, 0x00], \
with_length=True):
- '''
+ """
APDU command to select file
P1 and P2: selection control
Data: list of bytes describing the file identifier or address
call sr_apdu method
- '''
+ """
if with_length:
Data = [min(len(Data), 255)] + Data
SELECT_FILE = [self.CLA, 0xA4, P1, P2] + Data
return self.sr_apdu(SELECT_FILE)
def VERIFY(self, P2=0x00, Data=[]):
- '''
+ """
APDU command to verify user PIN, password or security codes
P2: reference control
Data: list of bytes to be verified by the card
call sr_apdu method
- '''
+ """
if len(Data) == 0:
VERIFY = [self.CLA, 0x20, 0x00, P2]
elif 1 <= len(Data) <= 255:
@@ -559,25 +549,25 @@ class ISO7816(object):
return self.sr_apdu(VERIFY)
def INTERNAL_AUTHENTICATE(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to run internal authentication algorithm
P1 and P2: reference control (algo, secret key selection...)
Data: list of bytes containing the authentication challenge
call sr_apdu method
- '''
+ """
INTERNAL_AUTHENTICATE = [self.CLA, 0x88, P1, P2, len(Data)] + Data
return self.sr_apdu(INTERNAL_AUTHENTICATE)
def EXTERNAL_AUTHENTICATE(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to conditionally update the security status of the card
after getting a challenge from it
P1 and P2: reference control (algo, secret key selection...)
Data: list of bytes containing the challenge response
call sr_apdu method
- '''
+ """
if len(Data) == 0:
EXTERNAL_AUTHENTICATE = [self.CLA, 0x82, P1, P2]
elif 1 <= len(Data) <= 255:
@@ -588,23 +578,23 @@ class ISO7816(object):
return self.sr_apdu(EXTERNAL_AUTHENTICATE)
def GET_CHALLENGE(self):
- '''
+ """
APDU command to get a challenge for external entity authentication
to the card
call sr_apdu method
- '''
+ """
GET_CHALLENGE = [self.CLA, 0x84, 0x00, 0x00]
return self.sr_apdu(GET_CHALLENGE)
def MANAGE_CHANNEL(self, P1=0x00, P2=0x00):
- '''
+ """
APDU to open and close supplementary logical channels
P1=0x00 to open, 0x80 to close
P2=0x00, 1, 2 or 3 to ask for logical channel number
call sr_apdu method
- '''
+ """
if (P1, P2) == (0x00, 0x00):
MANAGE_CHANNEL = [self.CLA, 0x70, P1, P2, 0x01]
else:
@@ -612,24 +602,24 @@ class ISO7816(object):
return self.sr_apdu(MANAGE_CHANNEL)
def GET_RESPONSE(self, Le=0x01):
- '''
+ """
APDU command to retrieve data after selection
or other kind of request that should get an extensive reply
Le: expected length of data
call sr_apdu method
- '''
+ """
GET_RESPONSE = [self.CLA, 0xC0, 0x00, 0x00, Le]
return self.sr_apdu(GET_RESPONSE)
def ENVELOPE(self, Data=[]):
- '''
+ """
APDU command to encapsulate data (APDU or other...)
check ETSI TS 102.221 for some examples...
Data: list of bytes
call sr_apdu method
- '''
+ """
if len(Data) == 0:
ENVELOPE = [self.CLA, 0xC2, 0x00, 0x00]
elif 1 <= len(Data) <= 255:
@@ -637,7 +627,7 @@ class ISO7816(object):
return self.sr_apdu(ENVELOPE)
def SEARCH_RECORD(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to seach pattern in the current EF file
with record structure
@@ -645,36 +635,36 @@ class ISO7816(object):
P2: type of search
Data: list of bytes describing a pattern to search for
call sr_apdu method
- '''
+ """
SEARCH_RECORD = [self.CLA, 0xA2, P1, P2, len(Data)] + Data
return self.sr_apdu(SEARCH_RECORD)
def DISABLE_CHV(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to disable CHV verification (such as PIN or password...)
P1: let to 0x00... or read ISO and ETSI specifications
P2: type of CHV to disable
Data: list of bytes for CHV value
call sr_apdu method
- '''
+ """
DISABLE_CHV = [self.CLA, 0x26, P1, P2, len(Data)] + Data
return self.sr_apdu(DISABLE_CHV)
def ENABLE_CHV(self, P1=0x00, P2=0x00, Data=[]):
- '''
+ """
APDU command to enable CHV verification (such as PIN or password...)
P1: let to 0x00... or read ISO and ETSI specifications
P2: type of CHV to enable
Data: list of bytes for CHV value
call sr_apdu method
- '''
+ """
ENABLE_CHV = [self.CLA, 0x28, P1, P2, len(Data)] + Data
return self.sr_apdu(ENABLE_CHV)
def UNBLOCK_CHV(self, P2=0x00, Data=[]):
- '''
+ """
APDU command to unblock CHV code (e.g. with PUK for deblocking PIN)
P2: type of CHV to unblock
@@ -682,25 +672,45 @@ class ISO7816(object):
Data: if Lc=0x10, UNBLOCK_CHV (PUK) value and new CHV (PIN) values,
each are 8 digits
call sr_apdu method
- '''
+ """
if len(Data) != 16:
UNBLOCK_CHV = [self.CLA, 0x2C, 0x00, P2]
else:
UNBLOCK_CHV = [self.CLA, 0x2C, 0x00, P2, 0x10] + Data
return self.sr_apdu(UNBLOCK_CHV)
+ def FETCH(self, Le=0x01):
+ """
+ APDU command to receive an ICC proactive command
+ that will need to be responded by a TERMINAL RESPONSE
+
+ Le: expected length of data
+ call sr_apdu method
+ """
+ FETCH = [self.CLA, 0x12, 0x00, 0x00, Le]
+ return self.sr_apdu(FETCH)
+
+ def TERMINAL_RESPONSE(self, Data=[]):
+ """
+ APDU command to provide a response to an ICC proactive command
+
+ Data: list of bytes for the response to be provided to the ICC
+ """
+ TERMINAL_RESP = [self.CLA, 0x14, 0x00, 0x00, len(Data)] + Data
+ return self.sr_apdu(TERMINAL_RESP)
+
##########################
# evolved "macro" method for ISO7816 card
# need the "coms" attribute being an apdu_stack()
##########################
def parse_file(self, Data=[]):
- '''
+ """
parse_file(self, Data) -> Dict()
parses a list of bytes returned when selecting a file
interprets the content of some informative bytes
for file structure and parsing method...
- '''
+ """
ber = BERTLV_parser( Data )
if self.dbg >= 3:
log(3, 'BER structure:\n%s' % ber)
@@ -744,13 +754,13 @@ class ISO7816(object):
return fil
def parse_FCP(self, Data=[]):
- '''
+ """
parse_FCP(Data) -> Dict()
parses a list of bytes returned when selecting a file
interprets the content of some informative bytes
for file structure and parsing method...
- '''
+ """
fil = {}
# loop on the Data bytes to parse TLV'style attributes
toProcess = Data
@@ -817,11 +827,11 @@ class ISO7816(object):
@staticmethod
def parse_life_cycle(Data, fil):
- '''
+ """
parses a list of bytes provided in Data
interprets the content as the life cycle
and enriches the file dictionnary passed as argument
- '''
+ """
if Data[0] == 1: fil['Life Cycle Status'] = 'creation state'
elif Data[0] == 3: fil['Life Cycle Status'] = 'initialization state'
elif Data[0] in (5, 7): fil['Life Cycle Status'] = 'operational state' \
@@ -836,11 +846,11 @@ class ISO7816(object):
@staticmethod
def parse_file_descriptor(Data, fil):
- '''
+ """
parses a list of bytes provided in Data
interprets the content as the file descriptor
and enriches the file dictionnary passed as argument
- '''
+ """
# parse the File Descriptor Byte
fd = Data[0]
fd_type = (fd >> 3) & 0b00111
@@ -881,11 +891,11 @@ class ISO7816(object):
@staticmethod
def parse_proprietary(Data, fil):
- '''
+ """
parses a list of bytes provided in Data
interprets the content as the proprietary parameters
and enriches the file dictionnary passed as argument
- '''
+ """
propr_tags = {
0x80:"UICC characteristics",
0x81:"Application power consumption",
@@ -904,13 +914,12 @@ class ISO7816(object):
Data = Data[L+2:]
return fil
-
def parse_compact_security_attribute(self, Data, fil):
- '''
+ """
parses a list of bytes provided in Data
interprets the content as the compact form for security parameters
and enriches the file dictionnary passed as argument
- '''
+ """
# See ISO-IEC 7816-4 section 5.4.3, with compact and expanded format
AM = Data[0]
SC = Data[1:]
@@ -997,10 +1006,10 @@ class ISO7816(object):
@staticmethod
def parse_expanded_security_attribute(Data, fil):
- '''
+ """
check references to EF_ARR file containing access conditions
see ISO 7816-4
- '''
+ """
# self.ARR = {ARR_id:[ARR_content],...}
return fil
file_length = len(Data)
@@ -1023,23 +1032,23 @@ class ISO7816(object):
@staticmethod
def parse_security_attribute(Data, fil):
- '''
+ """
TODO: to implement...
need to work further on how to do it (with ref to EF_ARR)
- '''
+ """
# See ISO-IEC 7816-4 section 5.4.3, with compact and expanded format
# not implemented yet (looks like useless for (U)SIM card ?)
return fil
def parse_FCI(self, Data=[]):
- '''
+ """
parse_FCI(Data) -> Dict()
parses a list of bytes returned when selecting a file
interprets the content of some informative bytes
for file structure and parsing method...
- '''
+ """
fil = {}
# loop on the Data bytes to parse TLV'style attributes
toProcess = Data
@@ -1112,13 +1121,13 @@ class ISO7816(object):
def read_EF(self, fil):
- '''
+ """
interprets the content of file parameters (Structure, Size, Length...)
and enriches the file dictionnary passed as argument
with "Data" key and corresponding
- list of bytes for EF transparent
- list of list of bytes for cyclic or linear EF
- '''
+ """
# read EF transparent data
if fil['Structure'] == 'transparent':
self.coms.push( self.READ_BINARY(Le=fil['Size']) )
@@ -1133,7 +1142,7 @@ class ISO7816(object):
fil['Data'] = []
# for record data: need to check the number of recordings
# stored in the file, and iterate for each
- for i in range( (fil['Size'] / fil['Record Length']) ):
+ for i in range( (fil['Size'] // fil['Record Length']) ):
self.coms.push( self.READ_RECORD(P1=i+1, P2=0x04, \
Le=fil['Record Length']) )
if self.coms()[2] != (0x90, 0x00):
@@ -1154,7 +1163,7 @@ class ISO7816(object):
return fil
def select(self, addr=[0x3F, 0x00], type="fid", with_length=True):
- '''
+ """
self.select(addr=[0x.., 0x..], type="fid", with_length=True)
-> dict() on success, None on error
@@ -1181,7 +1190,7 @@ class ISO7816(object):
in the SELECT_FILE APDU
APDUs exchanged available thanks to the attribute `self`.coms
- '''
+ """
# get the UICC trigger
is_UICC = isinstance(self, UICC)
@@ -1228,7 +1237,7 @@ class ISO7816(object):
# take the parse_file() method from the instance:
# ISO7816, UICC (for USIM) or SIM
file = self.parse_file(data)
- if file['Type'][0:2] == 'EF':
+ if 'Type' in file.keys() and file['Type'][0:2] == 'EF':
file = self.read_EF(file)
# finally returns the whole file dictionnary,
@@ -1240,14 +1249,14 @@ class ISO7816(object):
###############
def go_to_path(self, path=[], under_AID=None):
- '''
+ """
self.go_to_path(path=[0x.., 0x.., 0x.., 0x.., ..], under_AID=None)
-> void
selects all DF addresses successively from the path given
uses the .select() method with "fid" as selection type
works with AID number too
- '''
+ """
# check path length
if len(path) % 2:
log(1, '(go_to_path) path length not correct: %s' % path)
@@ -1273,13 +1282,13 @@ class ISO7816(object):
# this helps to build the blacklist:
def make_blacklist(self, DF_path=[], under_AID=None):
- '''
+ """
self.make_blacklist(DF_path=[0x.., 0x.., 0x.., 0x..], under_AID=None)
-> list( DFs )
check dictionnaries describing MF or AID directory structure
and return DF not to select when scanning for file ID under a DF
- '''
+ """
# IC card Master File, never reselect it...
MF = [0x3F, 0x00]
# you should also avoid to reselect it
@@ -1326,7 +1335,7 @@ class ISO7816(object):
def scan_DF(self, dir_path=[], under_AID=None, \
hi_addr=(0, 0xff), lo_addr=(0, 0xff)):
- '''
+ """
self.scan_DF(dir_path=[0x.., 0x.., 0x.., 0x..], under_AID=None)
-> list(filesystem), list(child_DF)
@@ -1335,7 +1344,7 @@ class ISO7816(object):
lo_addr: 8 LSB of the file address to brute force
avoid selecting blacklisted files (MF, parent_DF, brother_DF, current_DF)
return list of all found files (EF, DF) and list of child DF
- '''
+ """
# build blacklist of addresses from the current directory structure
# and selected path, in order to select only child file ID:
BL = self.make_blacklist(dir_path, under_AID)
@@ -1387,7 +1396,7 @@ class ISO7816(object):
return FS, child_DF
def explore_DF(self, DF_path=[], under_AID=None, recursive=True):
- '''
+ """
self.explore_DF(dir_path=[0x.., 0x.., 0x.., 0x..], under_AID=None, \
recursive=True)
-> None
@@ -1397,7 +1406,7 @@ class ISO7816(object):
a certain level)
fill in self.FS dictionnary with found DF and files
and self._MF_struct or self._AID`num`_struct with directory structure
- '''
+ """
# init by scanning the given DF_path (MF or AID)
FS, child_DF = self.scan_DF(DF_path, under_AID)
# then init or extend self._MF_struct or
@@ -1439,19 +1448,22 @@ class ISO7816(object):
##############################################
class UICC(ISO7816):
- '''
+ """
define attributes, methods and facilities for ETSI UICC card
check UICC specifications mainly in ETSI TS 102.221
inherits (eventually overrides) methods and objects from ISO7816 class
use self.dbg = 1 or more to print live debugging information
- '''
+ """
AID_RID = {
(0xA0, 0x00, 0x00, 0x00, 0x09): 'ETSI',
(0xA0, 0x00, 0x00, 0x00, 0x87): '3GPP',
(0xA0, 0x00, 0x00, 0x03, 0x43): '3GPP2',
+ (0xA0, 0x00, 0x00, 0x06, 0x45): 'OneM2M',
(0xA0, 0x00, 0x00, 0x04, 0x12): 'OMA',
(0xA0, 0x00, 0x00, 0x04, 0x24): 'WiMAX',
+ (0xA0, 0x00, 0x00, 0x00, 0x03): 'GlobalPlatform',
+ (0xA0, 0x00, 0x00, 0x01, 0x51): 'GlobalPlatform'
}
AID_ETSI_app_code = {
(0x00, 0x00): 'Reserved',
@@ -1469,16 +1481,32 @@ class UICC(ISO7816):
(0x10, 0x04): 'ISIM',
(0x10, 0x05): 'USIM API for JavaCard',
(0x10, 0x06): 'ISIM API for JavaCard',
- (0x10, 0x05): 'Contact Manager API for JavaCard',
+ (0x10, 0x07): 'Contact Manager API for JavaCard',
+ (0x10, 0x08): '3GPP USIM-INI',
+ (0x10, 0x09): '3GPP USIM-RN',
+ (0x10, 0x0A): '3GPP HPSIM',
}
+ # TODO: check USIM specific AID as defined in TS 31.130, annex C
AID_3GPP2_app_code = {
(0x10, 0x02): 'CSIM',
}
+ AID_OneM2M_app_code = {
+ (0x10, 0x01): 'oneM2M UICC',
+ (0x10, 0x02): 'oneM2M 1M2MSM',
+ }
AID_country_code = {
(0xFF, 0x33): 'France',
(0xFF, 0x44): 'United Kingdom',
(0xFF, 0x49): 'Germany',
}
+ AID_GP_code = {
+ (0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00): 'GlobalPlatform card manager (before v211)',
+ (0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00): 'GlobalPlatform card manager (before v211)',
+ (0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00): 'GlobalPlatform card manager (v211 and after)',
+ (0xA0, 0x00, 0x00, 0x00, 0x18, 0x43, 0x4D, 0x00): 'GlobalPlatform card manager (GemXpresso Pro)'
+ }
+ # TODO: check UICC access control AID as defined in the Android API
+ #https://source.android.com/devices/tech/config/uicc
pin_status = {
0x01 : "PIN Appl 1",
@@ -1535,19 +1563,20 @@ class UICC(ISO7816):
]
def __init__(self):
- '''
+ """
initializes like an ISO7816-4 card with CLA=0x00
initialized on the MF
- '''
+ """
ISO7816.__init__(self, CLA=0x00)
self.AID = []
+ self.AID_GP = {}
if self.dbg >= 2:
log(3, '(UICC.__init__) type definition: %s' % type(self))
log(3, '(UICC.__init__) CLA definition: %s' % hex(self.CLA))
def parse_file(self, Data=[]):
- '''
+ """
parse_file(Data=[0x12, 0x34, 0x56, 0x89]) -> dict(file)
mainly based on the ISO7816 parsing style
@@ -1555,7 +1584,7 @@ class UICC(ISO7816):
interprets the content of some informative bytes for right accesses,
type / format of file... see TS 102.221
works over the UICC file structure (quite different from e.g. SIM card)
- '''
+ """
# First ISO7816 parsing
fil = ISO7816.parse_file(self, Data)
@@ -1574,11 +1603,11 @@ class UICC(ISO7816):
@staticmethod
def parse_pin_status(Data, fil):
- '''
+ """
parses a list of bytes provided in Data
interprets the content as the UICC pin status
and enriches the file dictionnary passed as argument
- '''
+ """
PS_DO = Data[2:2+Data[1]]
Data = Data[2+len(PS_DO):]
PIN_status = ''
@@ -1620,13 +1649,12 @@ class UICC(ISO7816):
return fil
def get_AID(self):
- '''
+ """
checks EF_DIR at the MF level,
and available AID (Application ID) referenced
puts it into self.AID
- interprets and print the content of the self.AID list
- '''
+ """
#go back to MF and select EF_DIR
#self.select(addr=[])
@@ -1649,9 +1677,9 @@ class UICC(ISO7816):
@staticmethod
def interpret_AID(aid=[]):
- '''
- interprets and prints the aid provided
- '''
+ """
+ returns a string with the interpretation of the AID provided
+ """
if len(aid) < 11:
return
# check AID format
@@ -1670,6 +1698,9 @@ class UICC(ISO7816):
if aid_rid == (0xA0, 0x00, 0x00, 0x03, 0x43) \
and aid_app in UICC.AID_3GPP2_app_code.keys():
aid_app = UICC.AID_3GPP2_app_code[aid_app]
+ if aid_rid == (0xA0, 0x00, 0x00, 0x06, 0x45) \
+ and aid_app in UICC.AID_OneM2M_app_code.keys():
+ aid_app = UICC.AID_OneM2M_app_code[aid_app]
# get AID responsible SDO and country
if aid_rid in UICC.AID_RID.keys():
aid_rid = UICC.AID_RID[aid_rid]
@@ -1679,11 +1710,25 @@ class UICC(ISO7816):
return('%s || %s || %s || %s || %s' \
% (aid_rid, aid_app, aid_country, aid_provider, tuple(aid[11:])))
+ def get_AID_GP(self):
+ """
+ tries to select all AID addresses from AID_GP_app_code at the MF level
+
+ puts those to which there is a positive SW response into self.AID_GP
+ """
+ for aid in self.AID_GP_code.keys():
+ aid = list(aid)
+ self.select_by_name(aid)
+ if self.coms()[2] == (0x90, 0x00):
+ # positive response, where we could read the data returned by
+ # the application
+ self.AID_GP[tuple(aid)] = BERTLV_extract(self.coms()[3])
+
def get_ICCID(self):
- '''
+ """
check EF_ICCID at the MF level,
and returnq the ASCII value of the ICCID
- '''
+ """
#go back to MF and select EF_ICCID
#self.select(addr=[])
@@ -1696,15 +1741,15 @@ class UICC(ISO7816):
return decode_BCD( EF_ICCID['Data'] )
def select_by_name(self, name=[]):
- '''
+ """
AID selection by name: should be AID bytes
- '''
+ """
return self.select(name, 'aid')
def select_by_aid(self, aid_num=1):
- '''
+ """
AID selection by index
- '''
+ """
if hasattr(self, 'AID') and aid_num <= len(self.AID)+1:
return self.select(self.AID[aid_num-1], 'aid')
diff --git a/card/SIM.py b/card/SIM.py
index 4065167..2e6c9c7 100644
--- a/card/SIM.py
+++ b/card/SIM.py
@@ -90,21 +90,22 @@ SIM_service_table = {
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, atr = None):
- '''
+ 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, atr, CLA=0xA0)
+ """
+ ISO7816.__init__(self, CLA=0xA0)
if self.dbg >= 2:
log(3, '(SIM.__init__) type definition: %s' % type(self))
@@ -112,13 +113,13 @@ class SIM(ISO7816):
@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 ' \
@@ -158,10 +159,10 @@ class SIM(ISO7816):
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]
@@ -171,12 +172,12 @@ class SIM(ISO7816):
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]
@@ -186,10 +187,10 @@ class SIM(ISO7816):
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]
@@ -199,7 +200,7 @@ class SIM(ISO7816):
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!
@@ -208,7 +209,7 @@ class SIM(ISO7816):
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:
@@ -225,14 +226,14 @@ class SIM(ISO7816):
#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]
@@ -243,16 +244,16 @@ class SIM(ISO7816):
fil['EF_num'] = Data[15]
fil['codes_num'] = Data[16]
fil['CHV1'] = ('not initialized','initialized')\
- [(Data[18] & 0x80) / 0x80]\
+ [Data[18] >> 7]\
+ ': %d attempts remain' % (Data[18] & 0x0F)
fil['unblock_CHV1'] = ('not initialized','initialized')\
- [(Data[19] & 0x80) / 0x80]\
+ [Data[19] >> 7]\
+ ': %d attempts remain' % (Data[19] & 0x0F)
fil['CHV2'] = ('not initialized','initialized')\
- [(Data[20] & 0x80) / 0x80]\
+ [Data[20] >> 7]\
+ ': %d attempts remain' % (Data[20] & 0x0F)
fil['unblock_CHV2'] = ('not initialized','initialized')\
- [(Data[21] & 0x80) / 0x80]\
+ [Data[21] >> 7]\
+ ': %d attempts remain' % (Data[21] & 0x0F)
if len(Data) > 23:
fil['Adm'] = Data[23:]
@@ -279,7 +280,7 @@ class SIM(ISO7816):
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
@@ -289,7 +290,7 @@ class SIM(ISO7816):
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')
@@ -316,12 +317,12 @@ class SIM(ISO7816):
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):
@@ -346,12 +347,12 @@ class SIM(ISO7816):
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):
@@ -371,13 +372,13 @@ class SIM(ISO7816):
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)
@@ -401,7 +402,7 @@ class SIM(ISO7816):
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
@@ -409,7 +410,7 @@ class SIM(ISO7816):
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)
diff --git a/card/USIM.py b/card/USIM.py
index 930d761..ce1a62e 100644
--- a/card/USIM.py
+++ b/card/USIM.py
@@ -132,26 +132,60 @@ USIM_service_table = {
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):
- '''
+ 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, atr, CLA=0x00)
+ ISO7816.__init__(self, CLA=0x00)
self.AID = []
if self.dbg >= 2:
@@ -209,12 +243,12 @@ class USIM(UICC):
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:
@@ -229,14 +263,14 @@ class USIM(UICC):
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:
@@ -251,14 +285,14 @@ class USIM(UICC):
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:
@@ -273,7 +307,7 @@ class USIM(UICC):
return None
def get_GBA_BP(self):
- '''
+ """
get_GBA_BP() -> [[RAND, B-TID, KeyLifetime], ...],
Length-Value parsing style
@@ -282,7 +316,7 @@ class USIM(UICC):
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:
@@ -296,7 +330,7 @@ class USIM(UICC):
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)
@@ -305,7 +339,7 @@ class USIM(UICC):
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:
@@ -328,14 +362,14 @@ class USIM(UICC):
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:
@@ -366,7 +400,7 @@ class USIM(UICC):
return None
def authenticate(self, RAND=[], AUTN=[], ctx='3G'):
- '''
+ """
self.authenticate(RAND, AUTN, ctx='3G') -> [key1, key2...],
LV parsing style
@@ -380,7 +414,7 @@ class USIM(UICC):
[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:
@@ -446,7 +480,7 @@ class USIM(UICC):
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
@@ -464,7 +498,7 @@ class USIM(UICC):
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
@@ -486,13 +520,13 @@ class USIM(UICC):
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):
@@ -525,7 +559,7 @@ class USIM(UICC):
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
@@ -533,7 +567,7 @@ class USIM(UICC):
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)
diff --git a/card/utils.py b/card/utils.py
index 4f52ea4..028e918 100644
--- a/card/utils.py
+++ b/card/utils.py
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
# being used in smartcard specs #
#################################
+import sys
+
from collections import deque
from smartcard.util import toBytes
@@ -35,9 +37,39 @@ def log(level, string):
# could output to a file
# but here, just print()
print('[%s] %s' % (log_levels[level], string))
-
+
+
+BER_TAG = {
+ 1 : 'BOOLEAN',
+ 2 : 'INTEGER',
+ 3 : 'BIT STRING',
+ 4 : 'OCTET STRING',
+ 5 : 'NULL',
+ 6 : 'OID',
+ 7 : 'ObkectDescriptor',
+ 8 : 'EXTERNAL',
+ 9 : 'REAL',
+ 10: 'ENUMERATED',
+ 11: 'EMBEDDED-PDV',
+ 12: 'UTF8String',
+ 13: 'RELATIVE-OID',
+ 16: 'SEQUENCE',
+ 17: 'SET',
+ 19: 'PrintableString',
+ 22: 'IA5String',
+ 23: 'UTCTime',
+ 24: 'GeneralizedTime',
+ 26: 'VisibleString',
+ 31: 'DATE',
+ 32: 'TIME-OF-DAY',
+ 33: 'DATE-TIME',
+ 34: 'DURATION',
+ 35: 'OID-IRI',
+ 36: 'RELATIVE-OID-IRI'
+ }
+
# from python 2.6, format('b') allows to use 0b10010110 notation:
-# much convinient
+# much convenient
def byteToBit(byte):
'''
byteToBit(0xAB) -> [1, 0, 1, 0, 1, 0, 1, 1]
@@ -59,10 +91,16 @@ def stringToByte(string):
converts a string into a list of bytes
'''
- bytelist = []
- for c in string:
- bytelist.extend( toBytes(c.encode('hex')) )
- return bytelist
+ if sys.version_info[0] < 3:
+ bytelist = []
+ for c in string:
+ bytelist.extend( toBytes(c.encode('hex')) )
+ return bytelist
+ else:
+ if isinstance(string, str):
+ return list(string.encode('ascii'))
+ else:
+ return list(string)
# equivalent to the pyscard function "toASCIIString"
def byteToString(bytelist):
@@ -71,10 +109,13 @@ def byteToString(bytelist):
converts a list of bytes into a string
'''
- string = ''
- for b in bytelist:
- string += chr(b)
- return string
+ if sys.version_info[0] < 3:
+ string = ''
+ for b in bytelist:
+ string += chr(b)
+ return string
+ else:
+ return bytes(bytelist)
def LV_parser(bytelist):
'''
@@ -168,7 +209,7 @@ def first_BERTLV_parser(bytelist):
else:
Tag_bits = byte0[3:8]
- # Tag number calculation
+ # 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)
@@ -178,7 +219,8 @@ def first_BERTLV_parser(bytelist):
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
+
+ # Length coded with 1 byte (BER short form)
else:
Len_num = 1
Len = bytelist[i+1]
@@ -204,6 +246,66 @@ def BERTLV_parser(bytelist):
bytelist = bytelist[ T[0] + L[0] + L[1] : ]
return ret
+def BERTLV_extract(bytelist):
+ '''
+ BERTLV_extract([]) -> {}
+
+ parse the input bytes as BERTLV structure recursively until no more
+ constructed object are present, and returns a corresponding dict of
+ {tag_value: (tag_complete, data_value)}
+ '''
+ ret = []
+ comps = BERTLV_parser(bytelist)
+ for comp in comps:
+ if comp[0][1] == 'primitive':
+ if comp[0][0] == 'universal' and comp[0][2] in BER_TAG:
+ ret.append( [[comp[0][0], comp[0][2], BER_TAG[comp[0][2]]],
+ comp[2]] )
+ #if comp[0][2] == 6:
+ # # decode OID
+ # ret[-1].append( decode_OID(ret[-1][1]) )
+ else:
+ ret.append( [[comp[0][0], comp[0][2]], comp[2]] )
+ else:
+ if comp[0][0] == 'universal' and comp[0][2] in BER_TAG:
+ ret.append( [[comp[0][0], comp[0][2], BER_TAG[comp[0][2]]],
+ BERTLV_extract(comp[2])] )
+ else:
+ ret.append( [[comp[0][0], comp[0][2]],
+ BERTLV_extract(comp[2])] )
+ return ret
+
+def decode_OID(data=[]):
+ '''
+ decode a BER-encoded ASN.1 OID into a string representing the ASN.1 OID
+ abstract value
+ '''
+ if not data:
+ return ''
+ arcs = []
+ # decode OID arc values
+ v = 0
+ for b in data:
+ v <<= 7
+ if b&0x80:
+ v += (b&0x7f)
+ else:
+ v += b
+ arcs.append(v)
+ v = 0
+ if v != 0:
+ # invalid or incomplete OID
+ return ''
+ #
+ if arcs[0] < 40:
+ return '0 ' + ' '.join(['%i' % v for v in arcs])
+ elif 40 <= arcs[0] < 80:
+ arcs[0] = arcs[0]-40
+ return '1 ' + ' '.join(['%i' % v for v in arcs])
+ else:
+ arcs[0] = arcs[0]-80
+ return '2 ' + ' '.join(['%i' % v for v in data])
+
def decode_BCD(data=[]):
'''
decode_BCD([0x21, 0xFE, 0xA3]) -> '121415310'
@@ -216,10 +318,7 @@ def decode_BCD(data=[]):
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)
- if len(string) <= 0:
- return None
- else:
- return string
+ return string
def compute_luhn(digit_str=''):
'''
@@ -231,7 +330,7 @@ def compute_luhn(digit_str=''):
print('you must provide a string of digits')
return
# append 0
- d = [int(c) for c in digit_str+'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))
@@ -246,8 +345,7 @@ def write_dict(dict, fd):
'''
write a dict() content to a file descriptor
'''
- keys = dict.keys()
- keys.sort()
+ keys = sorted(dict.keys())
fd.write('\n')
for k in keys:
rec = dict[k]
diff --git a/simcard.py b/simcard.py
index 4d8b7fe..6f83c44 100644
--- a/simcard.py
+++ b/simcard.py
@@ -85,7 +85,7 @@ class Simcard():
# Constructor: Create a new simcard object
def __init__(self, cardtype = GSM_USIM, atr = None):
if cardtype == GSM_USIM:
- self.card = USIM(atr)
+ self.card = USIM()
self.usim = True
# Detect ISIM / USIM applications