<p>Ludovic Rousseau has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-sim-auth/+/20909">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pycodestyle: remove extra space characters<br><br>ICC.py:707:1: W293 blank line contains whitespace<br>ICC.py:715:57: W291 trailing whitespace<br>etc.<br><br>Change-Id: Ie8a5fc47775fe7d7fe0e19f7378ffda104fa6112<br>---<br>M card/ICC.py<br>M card/SIM.py<br>M card/USIM.py<br>M card/utils.py<br>4 files changed, 390 insertions(+), 395 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-sim-auth refs/changes/09/20909/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/card/ICC.py b/card/ICC.py</span><br><span>index 4a2ba0e..729dbc0 100644</span><br><span>--- a/card/ICC.py</span><br><span>+++ b/card/ICC.py</span><br><span>@@ -41,23 +41,23 @@</span><br><span> from smartcard.util import toHexString</span><br><span> </span><br><span> from card.utils import *</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ###########################################################</span><br><span style="color: hsl(0, 100%, 40%);">-# ISO7816 class with attributes and methods as defined </span><br><span style="color: hsl(0, 100%, 40%);">-# by ISO-7816 part 4 standard for smartcard </span><br><span style="color: hsl(120, 100%, 40%);">+# ISO7816 class with attributes and methods as defined</span><br><span style="color: hsl(120, 100%, 40%);">+# by ISO-7816 part 4 standard for smartcard</span><br><span> ###########################################################</span><br><span> </span><br><span> class ISO7816(object):</span><br><span> '''</span><br><span> define attributes, methods and facilities for ISO-7816-4 standard smartcard</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> use self.dbg = 1 or more to print live debugging information</span><br><span> standard instructions codes available in "INS_dic" class dictionnary</span><br><span> standard file tags available in "file_tags" class dictionnary</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> dbg = 0</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> INS_dic = {</span><br><span> 0x04 : 'DEACTIVATE FILE',</span><br><span> 0x0C : 'ERASE RECORD(S)',</span><br><span>@@ -114,7 +114,7 @@</span><br><span> 0xF2 : 'STATUS',</span><br><span> 0xFE : 'TERMINATE CARD USAGE',</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> file_tags = {</span><br><span> 0x80 : 'Size',</span><br><span> 0x81 : 'Length',</span><br><span>@@ -135,13 +135,13 @@</span><br><span> 0xA2 : 'DO Pairs',</span><br><span> 0xA5 : 'Proprietary BERTLV',</span><br><span> 0xAB : 'Security Attribute expanded',</span><br><span style="color: hsl(0, 100%, 40%);">- } </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __init__(self, CLA=0x00):</span><br><span> '''</span><br><span> connect smartcard and defines class CLA code for communication</span><br><span> uses "pyscard" library services</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> creates self.CLA attribute with CLA code</span><br><span> and self.coms attribute with associated "apdu_stack" instance</span><br><span> '''</span><br><span>@@ -151,32 +151,32 @@</span><br><span> self.cardservice.connection.connect()</span><br><span> self.reader = self.cardservice.connection.getReader()</span><br><span> self.ATR = self.cardservice.connection.getATR()</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> self.CLA = CLA</span><br><span> self.coms = apdu_stack()</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def disconnect(self):</span><br><span> '''</span><br><span> disconnect smartcard: stops the session</span><br><span> uses "pyscard" library service</span><br><span> '''</span><br><span> self.cardservice.connection.disconnect()</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def define_class(self, CLA=0x00):</span><br><span> '''</span><br><span> define smartcard class attribute for APDU command</span><br><span> override CLA value defined in class initialization</span><br><span> '''</span><br><span> self.CLA = CLA</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def ATR_scan(self, smlist_file="/usr/local/share/pcsc/smartcard_list.txt"):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- print smartcard info retrieved from AnswerToReset </span><br><span style="color: hsl(120, 100%, 40%);">+ print smartcard info retrieved from AnswerToReset</span><br><span> thanks to pyscard routine</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if pcsc_scan is installed,</span><br><span> use the signature file passed as argument for guessing the card</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> check also the more complete "parseATR" tool</span><br><span> '''</span><br><span> print('\nsmartcard reader: ', self.reader)</span><br><span>@@ -198,7 +198,7 @@</span><br><span> ATRfinger = ''</span><br><span> j = 1</span><br><span> for i in range(len(smlist)):</span><br><span style="color: hsl(0, 100%, 40%);">- if ATRre.match(smlist[i]): </span><br><span style="color: hsl(120, 100%, 40%);">+ if ATRre.match(smlist[i]):</span><br><span> if re.compile(smlist[i][:len(smlist[i])-1]).\</span><br><span> match(toHexString(self.ATR)):</span><br><span> while re.compile('\t.{1,}').match(smlist[i+j]):</span><br><span>@@ -210,17 +210,17 @@</span><br><span> print("smartcard ATR fingerprint:\n%s" % ATRfinger)</span><br><span> else:</span><br><span> print("%s file not found" % smlist_file)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def sw_status(self, sw1, sw2):</span><br><span> '''</span><br><span> sw_status(sw1=int, sw2=int) -> string</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> SW status bytes interpretation as defined in ISO-7816 part 4 standard</span><br><span> helps to speak and understand with the smartcard!</span><br><span> '''</span><br><span> status = 'undefined status'</span><br><span> if sw1 == 0x90 and sw2 == 0x00: status = 'normal processing: ' \</span><br><span style="color: hsl(0, 100%, 40%);">- 'command accepted: no further qualification' </span><br><span style="color: hsl(120, 100%, 40%);">+ 'command accepted: no further qualification'</span><br><span> elif sw1 == 0x61: status = 'normal processing: %i bytes ' \</span><br><span> 'still available' % sw2</span><br><span> elif sw1 == 0x62:</span><br><span>@@ -230,7 +230,7 @@</span><br><span> elif sw2 == 0x81: status += ': part of returned data may' \</span><br><span> 'be corrupted'</span><br><span> elif sw2 == 0x82: status += ': end of file/record reached ' \</span><br><span style="color: hsl(0, 100%, 40%);">- 'before reading Le bytes' </span><br><span style="color: hsl(120, 100%, 40%);">+ 'before reading Le bytes'</span><br><span> elif sw2 == 0x83: status += ': selected file invalidated'</span><br><span> elif sw2 == 0x84: status += ': FCI not formatted'</span><br><span> elif sw2 == 0x85: status += ': selected file in termination state'</span><br><span>@@ -303,7 +303,7 @@</span><br><span> elif sw1 == 0x6B and sw2 == 0x00: status = 'checking error: '\</span><br><span> 'wrong parameter(s) P1-P2'</span><br><span> elif sw1 == 0x6C: status = 'checking error: wrong length Le: ' \</span><br><span style="color: hsl(0, 100%, 40%);">- 'exact length is %s' % toHexString([sw2]) </span><br><span style="color: hsl(120, 100%, 40%);">+ 'exact length is %s' % toHexString([sw2])</span><br><span> elif sw1 == 0x6D and sw2 == 0x00: status = 'checking error: ' \</span><br><span> 'instruction code not supported or invalid'</span><br><span> elif sw1 == 0x6E and sw2 == 0x00: status = 'checking error: ' \</span><br><span>@@ -311,20 +311,20 @@</span><br><span> elif sw1 == 0x6F and sw2 == 0x00: status = 'checking error: ' \</span><br><span> 'no precise diagnosis'</span><br><span> return status</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def sr_apdu(self, apdu, force=False):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- sr_apdu(apdu=[0x.., 0x.., ...]) -> </span><br><span style="color: hsl(120, 100%, 40%);">+ sr_apdu(apdu=[0x.., 0x.., ...]) -></span><br><span> list [ string(apdu sent information),</span><br><span> string(SW codes interpretation),</span><br><span> 2-tuple(sw1, sw2),</span><br><span> list(response bytes) ]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> generic function to send apdu, receive and interpret response</span><br><span> force: force card reconnection if pyscard transmission fails</span><br><span> '''</span><br><span> if force:</span><br><span style="color: hsl(0, 100%, 40%);">- try: </span><br><span style="color: hsl(120, 100%, 40%);">+ try:</span><br><span> data, sw1, sw2 = self.cardservice.connection.transmit(apdu)</span><br><span> except CardConnectionException:</span><br><span> ISO7816.__init__(self, CLA = self.CLA)</span><br><span>@@ -332,115 +332,115 @@</span><br><span> else:</span><br><span> data, sw1, sw2 = self.cardservice.connection.transmit(apdu)</span><br><span> # replaces INS code by strings when available</span><br><span style="color: hsl(0, 100%, 40%);">- if apdu[1] in list(self.INS_dic.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ if apdu[1] in list(self.INS_dic.keys()):</span><br><span> apdu_name = self.INS_dic[apdu[1]] + ' '</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> apdu_name = ''</span><br><span> sw_stat = self.sw_status(sw1, sw2)</span><br><span> return ['%sapdu: %s' % (apdu_name, toHexString(apdu)),</span><br><span> 'sw1, sw2: %s - %s' % ( toHexString([sw1, sw2]), sw_stat ),</span><br><span> (sw1, sw2),</span><br><span> data ]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def bf_cla(self, start=0, param=[0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00]):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- bf_cla( start=int(starting CLA), </span><br><span style="color: hsl(120, 100%, 40%);">+ bf_cla( start=int(starting CLA),</span><br><span> param=list(bytes for selecting file 0x3F, 0x00) ) -></span><br><span> list( CLA which could be supported )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> tries all classes CLA codes to check the possibly supported ones</span><br><span> prints CLA suspected to be supported</span><br><span> returns the list of those CLA codes</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- WARNING: </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ WARNING:</span><br><span> can block the card definitively</span><br><span> Do not do it with your own VISA / MASTERCARD</span><br><span> '''</span><br><span> clist = []</span><br><span> for i in range(start, 256):</span><br><span> ret = self.sr_apdu([i] + param)</span><br><span style="color: hsl(0, 100%, 40%);">- if ret[2] != (0x6E, 0x00): </span><br><span style="color: hsl(120, 100%, 40%);">+ if ret[2] != (0x6E, 0x00):</span><br><span> print(ret)</span><br><span> clist.append(i)</span><br><span> return clist</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def bf_ins(self, start=0):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- bf_cla( start=int(starting INS) ) </span><br><span style="color: hsl(120, 100%, 40%);">+ bf_cla( start=int(starting INS) )</span><br><span> -> list( INS which could be supported )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> tries all instructions INS codes to check the supported ones</span><br><span> prints INS suspected to be supported</span><br><span> returns the list of those INS codes</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- WARNING: </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ WARNING:</span><br><span> can block the card definitively</span><br><span> Do not do it with your own VISA / MASTERCARD</span><br><span> '''</span><br><span> ilist = []</span><br><span> for i in range(start, 256):</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 1:</span><br><span> print('DEBUG: testing %d for INS code with %d CLA code' \</span><br><span> % (i, self.CLA))</span><br><span> ret = self.sr_apdu([self.CLA, i, 0x00, 0x00])</span><br><span style="color: hsl(0, 100%, 40%);">- if ret[2] != (0x6D, 0x00): </span><br><span style="color: hsl(120, 100%, 40%);">+ if ret[2] != (0x6D, 0x00):</span><br><span> print(ret)</span><br><span> ilist.append(i)</span><br><span> return ilist</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ###</span><br><span> # Below is defined a list of standard commands to be used with (U)SIM cards</span><br><span style="color: hsl(0, 100%, 40%);">- # They are mainly defined and described in </span><br><span style="color: hsl(120, 100%, 40%);">+ # They are mainly defined and described in</span><br><span> # ISO 7816 and described further in ETSI 101.221</span><br><span> ###</span><br><span> def READ_BINARY(self, P1=0x00, P2=0x00, Le=0x01):</span><br><span> '''</span><br><span> APDU command to read the content of EF file with transparent structure</span><br><span> Le: length of data bytes to be read</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> READ_BINARY = [self.CLA, 0xB0, P1, P2, Le]</span><br><span> return self.sr_apdu(READ_BINARY)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def WRITE_BINARY(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to write the content of EF file with transparent structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Data: list of data bytes to be written</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> WRITE_BINARY = [self.CLA, 0xD0, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(WRITE_BINARY)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def UPDATE_BINARY(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to update the content of EF file with transparent structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Data: list of data bytes to be written</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> UPDATE_BINARY = [self.CLA, 0xD6, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(UPDATE_BINARY)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def ERASE_BINARY(self, P1=0x00, P2=0x00, Lc=None, Data=[]):</span><br><span> '''</span><br><span> APDU command to erase the content of EF file with transparent structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Lc: 'None' or '0x02'</span><br><span> Data: list of data bytes to be written</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if Lc is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if Lc is None:</span><br><span> ERASE_BINARY = [self.CLA, 0x0E, P1, P2]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> ERASE_BINARY = [self.CLA, 0x0E, P1, P2, 0x02] + Data</span><br><span style="color: hsl(0, 100%, 40%);">- return self.sr_apdu(ERASE_BINARY) </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ return self.sr_apdu(ERASE_BINARY)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def READ_RECORD(self, P1=0x00, P2=0x00, Le=0x00):</span><br><span> '''</span><br><span> APDU command to read the content of EF file with record structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1: record number</span><br><span> P2: reference control</span><br><span> Le: length of data bytes to be read</span><br><span>@@ -448,11 +448,11 @@</span><br><span> '''</span><br><span> READ_RECORD = [self.CLA, 0xB2, P1, P2, Le]</span><br><span> return self.sr_apdu(READ_RECORD)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def WRITE_RECORD(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to write the content of EF file with record structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1: record number</span><br><span> P2: reference control</span><br><span> Data: list of data bytes to be written in the record</span><br><span>@@ -460,22 +460,22 @@</span><br><span> '''</span><br><span> WRITE_RECORD = [self.CLA, 0xD2, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(WRITE_RECORD)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def APPEND_RECORD(self, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to append a record on EF file with record structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P2: reference control</span><br><span> Data: list of data bytes to be appended on the record</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> APPEND_RECORD = [self.CLA, 0xE2, 0x00, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(APPEND_RECORD)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def UPDATE_RECORD(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to update the content of EF file with record structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1: record number</span><br><span> P2: reference control</span><br><span> Data: list of data bytes to update the record</span><br><span>@@ -483,40 +483,40 @@</span><br><span> '''</span><br><span> APPEND_RECORD = [self.CLA, 0xDC, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(APPEND_RECORD)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def GET_DATA(self, P1=0x00, P2=0x00, Le=0x01):</span><br><span> '''</span><br><span> APDU command to retrieve data object</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1 and P2: reference control for data object description</span><br><span> Le: number of bytes expected in the response</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> GET_DATA = [self.CLA, 0xCA, P1, P2, Le]</span><br><span> return self.sr_apdu(GET_DATA)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def PUT_DATA(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to store data object</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1 and P2: reference control for data object description</span><br><span> Data: list of data bytes to put in the data object structure</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 0:</span><br><span> PUT_DATA = [self.CLA, 0xDA, P1, P2]</span><br><span style="color: hsl(0, 100%, 40%);">- elif 1 <= len(Data) <= 255: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif 1 <= len(Data) <= 255:</span><br><span> PUT_DATA = [self.CLA, 0xDA, P1, P2, len(Data)] + Data</span><br><span> # should never be the case, however... who wants to try</span><br><span> else:</span><br><span> PUT_DATA = [self.CLA, 0xDA, P1, P2, 0xFF] + Data[0:255]</span><br><span style="color: hsl(0, 100%, 40%);">- return self.sr_apdu(PUT_DATA) </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ return self.sr_apdu(PUT_DATA)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def SELECT_FILE(self, P1=0x00, P2=0x00, Data=[0x3F, 0x00], \</span><br><span> with_length=True):</span><br><span> '''</span><br><span> APDU command to select file</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1 and P2: selection control</span><br><span> Data: list of bytes describing the file identifier or address</span><br><span> call sr_apdu method</span><br><span>@@ -525,107 +525,107 @@</span><br><span> Data = [min(len(Data), 255)] + Data</span><br><span> SELECT_FILE = [self.CLA, 0xA4, P1, P2] + Data</span><br><span> return self.sr_apdu(SELECT_FILE)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def VERIFY(self, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to verify user PIN, password or security codes</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P2: reference control</span><br><span> Data: list of bytes to be verified by the card</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 0:</span><br><span> VERIFY = [self.CLA, 0x20, 0x00, P2]</span><br><span style="color: hsl(0, 100%, 40%);">- elif 1 <= len(Data) <= 255: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif 1 <= len(Data) <= 255:</span><br><span> VERIFY = [self.CLA, 0x20, 0x00, P2, len(Data)] + Data</span><br><span> # should never be the case, however... who wants to try</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> VERIFY = [self.CLA, 0x20, 0x00, P2, 0xFF] + Data[0:255]</span><br><span> return self.sr_apdu(VERIFY)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def INTERNAL_AUTHENTICATE(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to run internal authentication algorithm</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1 and P2: reference control (algo, secret key selection...)</span><br><span> Data: list of bytes containing the authentication challenge</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> INTERNAL_AUTHENTICATE = [self.CLA, 0x88, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(INTERNAL_AUTHENTICATE)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def EXTERNAL_AUTHENTICATE(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- APDU command to conditionally update the security status of the card </span><br><span style="color: hsl(120, 100%, 40%);">+ APDU command to conditionally update the security status of the card</span><br><span> after getting a challenge from it</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1 and P2: reference control (algo, secret key selection...)</span><br><span> Data: list of bytes containing the challenge response</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 0:</span><br><span> EXTERNAL_AUTHENTICATE = [self.CLA, 0x82, P1, P2]</span><br><span style="color: hsl(0, 100%, 40%);">- elif 1 <= len(Data) <= 255: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif 1 <= len(Data) <= 255:</span><br><span> EXTERNAL_AUTHENTICATE = [self.CLA, 0x82, P1, P2, len(Data)] + Data</span><br><span> # should never be the case, however... who wants to try</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> EXTERNAL_AUTHENTICATE = [self.CLA, 0x82, P1, P2, 0xFF] + Data[0:255]</span><br><span style="color: hsl(0, 100%, 40%);">- return self.sr_apdu(EXTERNAL_AUTHENTICATE) </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ return self.sr_apdu(EXTERNAL_AUTHENTICATE)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def GET_CHALLENGE(self):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- APDU command to get a challenge for external entity authentication </span><br><span style="color: hsl(120, 100%, 40%);">+ APDU command to get a challenge for external entity authentication</span><br><span> to the card</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> GET_CHALLENGE = [self.CLA, 0x84, 0x00, 0x00]</span><br><span> return self.sr_apdu(GET_CHALLENGE)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def MANAGE_CHANNEL(self, P1=0x00, P2=0x00):</span><br><span> '''</span><br><span> APDU to open and close supplementary logical channels</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1=0x00 to open, 0x80 to close</span><br><span> P2=0x00, 1, 2 or 3 to ask for logical channel number</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if (P1, P2) == (0x00, 0x00): </span><br><span style="color: hsl(120, 100%, 40%);">+ if (P1, P2) == (0x00, 0x00):</span><br><span> MANAGE_CHANNEL = [self.CLA, 0x70, P1, P2, 0x01]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> MANAGE_CHANNEL = [self.CLA, 0x70, P1, P2]</span><br><span> return self.sr_apdu(MANAGE_CHANNEL)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def GET_RESPONSE(self, Le=0x01):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- APDU command to retrieve data after selection </span><br><span style="color: hsl(120, 100%, 40%);">+ APDU command to retrieve data after selection</span><br><span> or other kind of request that should get an extensive reply</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Le: expected length of data</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span> GET_RESPONSE = [self.CLA, 0xC0, 0x00, 0x00, Le]</span><br><span> return self.sr_apdu(GET_RESPONSE)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def ENVELOPPE(self, Data=[]):</span><br><span> '''</span><br><span> APDU command to encapsulate data (APDU or other...)</span><br><span> check ETSI TS 102.221 for some examples...</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Data: list of bytes</span><br><span> call sr_apdu method</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 0:</span><br><span> ENVELOPPE = [self.CLA, 0xC2, 0x00, 0x00]</span><br><span style="color: hsl(0, 100%, 40%);">- elif 1 <= len(Data) <= 255: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif 1 <= len(Data) <= 255:</span><br><span> ENVELOPPE = [self.CLA, 0xC2, 0x00, 0x00, len(Data)] + Data</span><br><span> return self.sr_apdu(ENVELOPPE)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def SEARCH_RECORD(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- APDU command to seach pattern in the current EF file </span><br><span style="color: hsl(120, 100%, 40%);">+ APDU command to seach pattern in the current EF file</span><br><span> with record structure</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1: record number</span><br><span> P2: type of search</span><br><span> Data: list of bytes describing a pattern to search for</span><br><span>@@ -633,11 +633,11 @@</span><br><span> '''</span><br><span> SEARCH_RECORD = [self.CLA, 0xA2, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(SEARCH_RECORD)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def DISABLE_CHV(self, P1=0x00, P2=0x00, Data=[]):</span><br><span> '''</span><br><span> APDU command to disable CHV verification (such as PIN or password...)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P1: let to 0x00... or read ISO and ETSI specifications</span><br><span> P2: type of CHV to disable</span><br><span> Data: list of bytes for CHV value</span><br><span>@@ -645,24 +645,24 @@</span><br><span> '''</span><br><span> DISABLE_CHV = [self.CLA, 0x26, P1, P2, len(Data)] + Data</span><br><span> return self.sr_apdu(DISABLE_CHV)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def UNBLOCK_CHV(self, P2=0x00, Lc=None, Data=[]):</span><br><span> '''</span><br><span> APDU command to unblock CHV code (e.g. with PUK for deblocking PIN)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> P2: type of CHV to unblock</span><br><span> Lc: Empty or 0x10</span><br><span> Data: if Lc=0x10, UNBLOCK_CHV value and new CHV value to set</span><br><span> call sr_apdu method</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> TODO: check the exact coding for the Data</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- if Lc is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if Lc is None:</span><br><span> UNBLOCK_CHV = [self.CLA, 0x2C, 0x00, P2]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> UNBLOCK_CHV = [self.CLA, 0x2C, 0x00, P2, 0x10] + Data</span><br><span style="color: hsl(0, 100%, 40%);">- return self.sr_apdu(UNBLOCK_CHV) </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ return self.sr_apdu(UNBLOCK_CHV)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ##########################</span><br><span> # evolved "macro" method for ISO7816 card</span><br><span> # need the "coms" attribute being an apdu_stack()</span><br><span>@@ -670,9 +670,9 @@</span><br><span> def parse_file(self, Data=[]):</span><br><span> '''</span><br><span> parse_file(self, Data) -> Dict()</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses a list of bytes returned when selecting a file</span><br><span style="color: hsl(0, 100%, 40%);">- interprets the content of some informative bytes </span><br><span style="color: hsl(120, 100%, 40%);">+ interprets the content of some informative bytes</span><br><span> for file structure and parsing method...</span><br><span> '''</span><br><span> ber = BERTLV_parser( Data )</span><br><span>@@ -681,54 +681,54 @@</span><br><span> if len(ber) > 1:</span><br><span> # TODO: implements recursive BER object parsing</span><br><span> print('[WNG] more than 1 BER object: %s' % ber)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # for FCP control structure, precise parsing is done</span><br><span> # this structure seems to be the most used for (U)SIM cards</span><br><span style="color: hsl(0, 100%, 40%);">- if ber[0][0][2] == 0x2: </span><br><span style="color: hsl(120, 100%, 40%);">+ if ber[0][0][2] == 0x2:</span><br><span> fil = self.parse_FCP( ber[0][2] )</span><br><span> fil['Control'] = 'FCP'</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # for other control structure, DIY</span><br><span> fil = {}</span><br><span style="color: hsl(0, 100%, 40%);">- if ber[0][0][2] == 0x4: </span><br><span style="color: hsl(120, 100%, 40%);">+ if ber[0][0][2] == 0x4:</span><br><span> fil['Control'] = 'FMD'</span><br><span> if self.dbg:</span><br><span> print('[WNG] FMD file structure parsing not implemented')</span><br><span style="color: hsl(0, 100%, 40%);">- elif ber[0][0][2] == 0xF: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif ber[0][0][2] == 0xF:</span><br><span> fil['Control'] = 'FCI'</span><br><span> if self.dbg:</span><br><span> print('[WNG] FCI file structure parsing not implemented')</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> fil['Control'] = ber[0][0]</span><br><span> if self.dbg:</span><br><span> print('[WNG] unknown file structure')</span><br><span> fil['Data'] = ber[0][2]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def parse_FCP(self, Data=[]):</span><br><span> '''</span><br><span> parse_FCP(Data) -> Dict()</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses a list of bytes returned when selecting a file</span><br><span style="color: hsl(0, 100%, 40%);">- interprets the content of some informative bytes </span><br><span style="color: hsl(120, 100%, 40%);">+ interprets the content of some informative bytes</span><br><span> for file structure and parsing method...</span><br><span> '''</span><br><span> fil = {}</span><br><span> # loop on the Data bytes to parse TLV'style attributes</span><br><span> toProcess = Data</span><br><span> while len(toProcess) > 0:</span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: seemd full compliancy </span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: seemd full compliancy</span><br><span> # would require to work with the BERTLV parser...</span><br><span> [T, L, V] = first_TLV_parser(toProcess)</span><br><span> if self.dbg > 2:</span><br><span style="color: hsl(0, 100%, 40%);">- if T in list(self.file_tags.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ if T in list(self.file_tags.keys()):</span><br><span> Tag = self.file_tags[T]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> Tag = T</span><br><span> print('[DBG] %s / %s: %s' % (T, Tag, V))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # do extra processing here</span><br><span> # File ID, DF name, Short file id</span><br><span> if T in (0x83, 0x84, 0x88):</span><br><span>@@ -739,8 +739,8 @@</span><br><span> fil = self.parse_security_attribute_compact(V, fil)</span><br><span> # Security Attributes</span><br><span> elif T in (0x86, 0x8B, 0x8E, 0xA0, 0xA1, 0xAB):</span><br><span style="color: hsl(0, 100%, 40%);">- fil[self.file_tags[T]] = V </span><br><span style="color: hsl(0, 100%, 40%);">- # TODO: no concrete parsing at this time... </span><br><span style="color: hsl(120, 100%, 40%);">+ fil[self.file_tags[T]] = V</span><br><span style="color: hsl(120, 100%, 40%);">+ # TODO: no concrete parsing at this time...</span><br><span> fil = self.parse_security_attribute(V, fil)</span><br><span> # file size or length</span><br><span> elif T in (0x80, 0x81):</span><br><span>@@ -762,16 +762,16 @@</span><br><span> fil[self.file_tags[T]] = V</span><br><span> else:</span><br><span> fil[T] = V</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # truncate the data to process and loop</span><br><span> if L < 256:</span><br><span> toProcess = toProcess[L+2:]</span><br><span> else:</span><br><span> toProcess = toProcess[L+4:]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # and return the file </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # and return the file</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_life_cycle(Data, fil):</span><br><span> '''</span><br><span>@@ -790,7 +790,7 @@</span><br><span> elif Data[0] >= 16: fil['Life Cycle Status'] = 'proprietary'</span><br><span> else: fil['Life Cycle Status'] = 'RFU'</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_file_descriptor(Data, fil):</span><br><span> '''</span><br><span>@@ -821,21 +821,21 @@</span><br><span> # type bits b4 to b6</span><br><span> if fd_type == 0: fil['Type'] = 'EF working'</span><br><span> elif fd_type == 1: fil['Type'] = 'EF internal'</span><br><span style="color: hsl(0, 100%, 40%);">- elif fd_type == 7: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif fd_type == 7:</span><br><span> fil['Type'] = 'DF'</span><br><span> if fd_struct == 1: fil['Structure'] = 'BER-TLV'</span><br><span> elif fd_struct == 2: fil['Structure'] = 'TLV'</span><br><span> else: fil['Type'] = 'EF proprietary'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # for linear and cyclic EF: </span><br><span style="color: hsl(0, 100%, 40%);">- # the following is convenient for UICC, </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # for linear and cyclic EF:</span><br><span style="color: hsl(120, 100%, 40%);">+ # the following is convenient for UICC,</span><br><span> # but looks not fully conform to ISO standard</span><br><span> # see coding convention in ISO 7816-4 Table 87</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 5: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 5:</span><br><span> fil['Record Length'], fil['Record Number'] = Data[3], Data[4]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_proprietary(Data, fil):</span><br><span> '''</span><br><span>@@ -856,11 +856,11 @@</span><br><span> }</span><br><span> while len(Data) > 0:</span><br><span> [T, L, V] = first_TLV_parser( Data )</span><br><span style="color: hsl(0, 100%, 40%);">- if T in list(propr_tags.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ if T in list(propr_tags.keys()):</span><br><span> fil[propr_tags[T]] = V</span><br><span> Data = Data[L+2:]</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_security_attribute_compact(Data, fil):</span><br><span> '''</span><br><span>@@ -872,7 +872,7 @@</span><br><span> AM = Data[0]</span><br><span> SC = Data[1:]</span><br><span> sec = '#'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if 'Type' in list(fil.keys()):</span><br><span> # DF parsing</span><br><span> if fil['Type'] == 'DF':</span><br><span>@@ -894,7 +894,7 @@</span><br><span> if AM & 0b00000100: sec += ' WRITE / APPEND #'</span><br><span> if AM & 0b00000010: sec += ' UPDATE / ERASE #'</span><br><span> if AM & 0b00000001: sec += ' READ / SEARCH #'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # loop on SC:</span><br><span> for cond in SC:</span><br><span> if cond == 0 : sec += ' Always #'</span><br><span>@@ -906,28 +906,28 @@</span><br><span> if cond & 0b01000000: sec += ' secure messaging #'</span><br><span> if cond & 0b00100000: sec += ' external authentication #'</span><br><span> if cond & 0b00010000: sec += ' user authentication #'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #file['Security Attributes raw'] = Data</span><br><span> fil['Security Attributes'] = sec</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_security_attribute(Data, fil):</span><br><span> '''</span><br><span> TODO: to implement...</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> need to work further on how to do it (with ref to EF_ARR)</span><br><span> '''</span><br><span> # See ISO-IEC 7816-4 section 5.4.3, with compact and expanded format</span><br><span> #if self.dbg:</span><br><span> # print '[DBG] parse_security_attribute() not implemented'</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def read_EF(self, fil):</span><br><span> '''</span><br><span> interprets the content of file parameters (Structure, Size, Length...)</span><br><span> and enriches the file dictionnary passed as argument</span><br><span style="color: hsl(0, 100%, 40%);">- with "Data" key and corresponding </span><br><span style="color: hsl(120, 100%, 40%);">+ with "Data" key and corresponding</span><br><span> - list of bytes for EF transparent</span><br><span> - list of list of bytes for cyclic or linear EF</span><br><span> '''</span><br><span>@@ -935,11 +935,11 @@</span><br><span> if fil['Structure'] == 'transparent':</span><br><span> self.coms.push( self.READ_BINARY(Le=fil['Size']) )</span><br><span> if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 1:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return fil</span><br><span> fil['Data'] = self.coms()[3]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # read EF cyclic / linear all records data</span><br><span> elif fil['Structure'] != 'transparent':</span><br><span> fil['Data'] = []</span><br><span>@@ -949,7 +949,7 @@</span><br><span> self.coms.push( self.READ_RECORD(P1=i+1, P2=0x04, \</span><br><span> Le=fil['Record Length']) )</span><br><span> if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(0, 100%, 40%);">- # should mean there is an issue </span><br><span style="color: hsl(120, 100%, 40%);">+ # should mean there is an issue</span><br><span> # somewhere in the file parsing process</span><br><span> if self.dbg:</span><br><span> print('[WNG] error in iterating the RECORD parsing at' \</span><br><span>@@ -958,18 +958,18 @@</span><br><span> if self.coms()[3][1:] == len(self.coms()[3][1:]) * [255]:</span><br><span> # record is empty, contains padding only</span><br><span> pass</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> fil['Data'].append(self.coms()[3])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # return the [Data] for transparent or </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # return the [Data] for transparent or</span><br><span> # [[Record1],[Record2]...] for cyclic / linear</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def select(self, Data=[0x3F, 0x00], typ="fid", with_length=True):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- self.select(Data=[0x.., 0x..], typ="fid", with_length=True) </span><br><span style="color: hsl(120, 100%, 40%);">+ self.select(Data=[0x.., 0x..], typ="fid", with_length=True)</span><br><span> -> dict(file) on success, None on error</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> selects the file</span><br><span> if error, returns None</span><br><span> if processing correct: gets response with info on the file</span><br><span>@@ -977,89 +977,89 @@</span><br><span> works in USIM fashion</span><br><span> else returns the data dictionnary: check parse_file_(U)SIM methods</span><br><span> last apdu available from the attribute self.coms</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> different types of file selection are possible:</span><br><span style="color: hsl(0, 100%, 40%);">- "fid": select by file id, only the direct child or </span><br><span style="color: hsl(120, 100%, 40%);">+ "fid": select by file id, only the direct child or</span><br><span> parent files of the last selected MF / DF / ADF</span><br><span> "pmf": select by path from MF</span><br><span style="color: hsl(0, 100%, 40%);">- "pdf": select by path from last selected MF / DF / ADF </span><br><span style="color: hsl(120, 100%, 40%);">+ "pdf": select by path from last selected MF / DF / ADF</span><br><span> (or relative path)</span><br><span> "aid": select by ADF (Application) name</span><br><span> '''</span><br><span> # get the UICC trigger</span><br><span> is_UICC = isinstance(self, UICC)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # handle type of selection:</span><br><span> if typ == "pmf": P1 = 0x08</span><br><span> elif typ == "pdf": P1 = 0x09</span><br><span> elif typ == "aid": P1 = 0x04</span><br><span> # the case of selection by "fid":</span><br><span style="color: hsl(0, 100%, 40%);">- else: P1 = 0x00 </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ else: P1 = 0x00</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # for UICC instance</span><br><span> # ask the return of the FCP template for the selected file:</span><br><span> if is_UICC:</span><br><span> P2 = 0x04</span><br><span> else:</span><br><span> P2 = 0x00</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # used to get back to MF without getting MF attributes:</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) == 0:</span><br><span> P1, P2 = 0x00, 0x0C</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # select file and check SW; if error, returns None, else get response</span><br><span> self.coms.push(self.SELECT_FILE(P1=P1, P2=P2, Data=Data, \</span><br><span> with_length=with_length))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # different SW codes for UICC and old ISO card (e.g. SIM)</span><br><span> if is_UICC and self.coms()[2][0] != 0x61 \</span><br><span> or not is_UICC and self.coms()[2][0] != 0x9F:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 1:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # get response and check SW: </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # get response and check SW:</span><br><span> # if error, return None, else parse file info</span><br><span> self.coms.push(self.GET_RESPONSE(Le=self.coms()[2][1]))</span><br><span> if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 1:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> data = self.coms()[3]</span><br><span> # take the `parse_file()' method from the instance:</span><br><span> # ISO7816, UICC or SIM</span><br><span> fil = self.parse_file(data)</span><br><span> if fil['Type'][0:2] == 'EF':</span><br><span> fil = self.read_EF(fil)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # finally returns the whole file dictionnary, </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # finally returns the whole file dictionnary,</span><br><span> # containing the ['Data'] key for EF file</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #</span><br><span> ###############</span><br><span> # TODO:</span><br><span> # improve all of the following...</span><br><span> ###############</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def flat_files_bf(self, path=[], under_AID=0, \</span><br><span> hi_addr=(0, 0xff), lo_addr=(0, 0xff)):</span><br><span> '''</span><br><span> flat_files_bf(self, path=[], under_AID=0, \</span><br><span> hi_addr=(0, 0xff), lo_addr=(0, 0xff))</span><br><span> -> list(files), list(DF_to_explore)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> path: path of the DF under MF or AID to brute force</span><br><span> under_AID: if > 0, select the AID number to init the brute force</span><br><span> only available for UICC instance</span><br><span> hi_addr: 8 MSB of the file address to brute force</span><br><span> lo_addr: 8 LSB of the file address to brute force</span><br><span> with_select_length: use the length parameter with SELECT instruction</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> brute force file addresses of direct child under a given DF</span><br><span> get information on existing files, and discovered DF</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> WARNING: not very tested yet...</span><br><span> '''</span><br><span> # init return variables</span><br><span>@@ -1068,7 +1068,7 @@</span><br><span> MF, sel_type = [0x3F, 0x00], 'fid'</span><br><span> if isinstance(self, UICC):</span><br><span> sel_type = 'pdf'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # start by selecting MF</span><br><span> try:</span><br><span> r = self.select(MF)</span><br><span>@@ -1078,7 +1078,7 @@</span><br><span> if r == None:</span><br><span> print('[ERR] MF not found!')</span><br><span> return</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #if needed, select AID</span><br><span> if isinstance(self, UICC) and under_AID:</span><br><span> try:</span><br><span>@@ -1090,7 +1090,7 @@</span><br><span> if r == None:</span><br><span> print('[ERR] AID not found')</span><br><span> return</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # place on the DF path to bf</span><br><span> # select it by 'path from last selected DF'</span><br><span> if len(path) > 0:</span><br><span>@@ -1102,7 +1102,7 @@</span><br><span> if path_init == None:</span><br><span> print('[ERR] path not found: %s' % path)</span><br><span> return</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Dany'style programming</span><br><span> def reinit():</span><br><span> self.select(MF)</span><br><span>@@ -1110,7 +1110,7 @@</span><br><span> self.select_by_aid(under_AID)</span><br><span> if len(path) > 0:</span><br><span> self.select(path, sel_type)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # loop over the address space to brute force files</span><br><span> i, j = 0, 0</span><br><span> for i in range(hi_addr[0], hi_addr[1]):</span><br><span>@@ -1135,19 +1135,19 @@</span><br><span> reinit()</span><br><span> if 'Absolut Path' in list(fil.keys()):</span><br><span> DF_to_explore.append(fil['Absolut Path'])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # re-initialize at MF and return</span><br><span> self.select(MF)</span><br><span> return FS, DF_to_explore</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def init_FS(self):</span><br><span> self.FS = []</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def recu_files_bf(self, path=[], under_AID=0):</span><br><span> '''</span><br><span> recu_files_bf(self, path=[], under_AID=0)</span><br><span> -> void</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> fills self.FS attribute with all files and DF discovered</span><br><span> recursively</span><br><span> '''</span><br><span>@@ -1159,13 +1159,13 @@</span><br><span> '[ERR] FS not initialized: %s' % type(self.FS)</span><br><span> return</span><br><span> DF = ret[1]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # recursive method call</span><br><span> # DF contains absolut path</span><br><span> for addr in DF:</span><br><span> print('[DBG] path: %s' % addr)</span><br><span> self.recu_files_bf(path=addr, under_AID=under_AID)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def __write_dict(dict, fd):</span><br><span> keys = list(dict.keys())</span><br><span>@@ -1173,31 +1173,31 @@</span><br><span> fd.write('\n')</span><br><span> for k in keys:</span><br><span> fd.write('%s: %s\n' % (k, dict[k]))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def scan_fs(self, filename='card_fs', stdout=False):</span><br><span> '''</span><br><span> bf_files_under_MF(self, output='card_fs', stdout=True)</span><br><span> -> void</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> filename: file to write found information in</span><br><span> stdout: print information on stdout too</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> brute force all file addresses from MF and found AID</span><br><span> recursively (until no more DF are found)</span><br><span style="color: hsl(0, 100%, 40%);">- write information on existing file on the output, </span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+ write information on existing file on the output,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> WARNING: not very tested either...</span><br><span> '''</span><br><span> fd = open(filename, 'w')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> self.init_FS()</span><br><span> self.recu_files_bf()</span><br><span> fd.write('\n### MF ###\n')</span><br><span> for f in self.FS:</span><br><span> self.__write_dict(f, fd)</span><br><span> fd.write('\n')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # TODO: loop that</span><br><span> #self.init_FS()</span><br><span> #self.recu_files_bf(under_AID=1)</span><br><span>@@ -1205,7 +1205,7 @@</span><br><span> #for f in self.FS:</span><br><span> # self.__write_dict(f, fd)</span><br><span> # fd.write('\n')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> fd.close()</span><br><span> #</span><br><span> </span><br><span>@@ -1217,7 +1217,7 @@</span><br><span> '''</span><br><span> define attributes, methods and facilities for ETSI UICC card</span><br><span> check UICC specifications mainly in ETSI TS 102.221</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> inherits (eventually overrides) methods and objects from ISO7816 class</span><br><span> use self.dbg = 1 or more to print live debugging information</span><br><span> '''</span><br><span>@@ -1254,7 +1254,7 @@</span><br><span> (0xFF, 0x44): 'United Kingdom',</span><br><span> (0xFF, 0x49): 'Germany',</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> pin_status = {</span><br><span> 0x01 : "PIN Appl 1",</span><br><span> 0x02 : "PIN Appl 2",</span><br><span>@@ -1286,7 +1286,7 @@</span><br><span> 0x8D : "ADM9",</span><br><span> 0x8E : "ADM10",</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> files = [</span><br><span> ([0x3F, 0x00], 'MF', 'MF'),</span><br><span> ([0x2F, 0x00], 'EF', 'EF_DIR'),</span><br><span>@@ -1308,48 +1308,48 @@</span><br><span> ([0x7F, 0x90], 'DF', 'DF_TETRA'),</span><br><span> ([0x7F, 0x31], 'DF', 'DF_iDEN'),</span><br><span> ]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __init__(self):</span><br><span> '''</span><br><span> initializes like an ISO7816-4 card with CLA=0x00</span><br><span> and check available AID (Application ID) read from EF_DIR</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> initializes on the MF</span><br><span> '''</span><br><span> ISO7816.__init__(self, CLA=0x00)</span><br><span> self.AID = []</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if self.dbg:</span><br><span> print('[DBG] type definition: %s' % type(self))</span><br><span> print('[DBG] CLA definition: %s' % hex(self.CLA))</span><br><span> #print '[DBG] EF_DIR file selection and reading...'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def parse_file(self, Data=[]):</span><br><span> '''</span><br><span> parse_file(Data=[0x12, 0x34, 0x56, 0x89]) -> dict(file)</span><br><span> mainly based on the ISO7816 parsing style</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses a list of bytes returned when selecting a file</span><br><span style="color: hsl(0, 100%, 40%);">- interprets the content of some informative bytes for right accesses, </span><br><span style="color: hsl(120, 100%, 40%);">+ interprets the content of some informative bytes for right accesses,</span><br><span> type / format of file... see TS 102.221</span><br><span> works over the UICC file structure (quite different from e.g. SIM card)</span><br><span> '''</span><br><span> # First ISO7816 parsing</span><br><span> fil = ISO7816.parse_file(self, Data)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Then UICC extra attributes parsing</span><br><span> if 0xC6 in list(fil.keys()):</span><br><span> fil = self.parse_pin_status(fil[0xC6], fil)</span><br><span> del fil[0xC6]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if 'File Identifier' in list(fil.keys()):</span><br><span> for ref in self.files:</span><br><span> if fil['File Identifier'] == ref[0]:</span><br><span> fil['Name'] = ref[2]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # return the enriched file </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # return the enriched file</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> @staticmethod</span><br><span> def parse_pin_status(Data, fil):</span><br><span> '''</span><br><span>@@ -1364,106 +1364,106 @@</span><br><span> [T, L, V] = first_TLV_parser(Data)</span><br><span> assert( T in (0x83, 0x95) )</span><br><span> if T == 0x95: # PIN usage</span><br><span style="color: hsl(0, 100%, 40%);">- if (V[0] << 7) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if (V[0] << 7) & 1:</span><br><span> PIN_status += '#use verification / encipherment ' \</span><br><span> '/ external authentication: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif (V[0] << 6) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif (V[0] << 6) & 1:</span><br><span> PIN_status += '#use computation / decipherment ' \</span><br><span> '/ internal authentication: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif (V[0] << 5) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif (V[0] << 5) & 1:</span><br><span> PIN_status += '#use SM response: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif (V[0] << 4) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif (V[0] << 4) & 1:</span><br><span> PIN_status += '#use SM command: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif (V[0] << 3) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif (V[0] << 3) & 1:</span><br><span> PIN_status += '#use PIN verification: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif (V[0] << 3) & 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif (V[0] << 3) & 1:</span><br><span> PIN_status += '#use biometric user verification: '</span><br><span style="color: hsl(0, 100%, 40%);">- elif V[0] == 0: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif V[0] == 0:</span><br><span> PIN_status += '#verification not required: '</span><br><span> elif T == 0x83: # PIN status</span><br><span> if len(PIN_status) == 0: PIN_status = '#'</span><br><span style="color: hsl(0, 100%, 40%);">- if 0x00 < V[0] < 0x12 or 0x81 <= V[0] < 0x90: </span><br><span style="color: hsl(120, 100%, 40%);">+ if 0x00 < V[0] < 0x12 or 0x81 <= V[0] < 0x90:</span><br><span> PIN_status += UICC.pin_status[V[0]] + '#'</span><br><span> elif 0x12 <= V[0] < 0x1E:</span><br><span> PIN_status += 'RFU (Global)#'</span><br><span> elif 0x90 <= V[0] < 0x9F:</span><br><span> PIN_status += 'RFU (Local)#'</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> PIN_status += '#'</span><br><span style="color: hsl(0, 100%, 40%);">- #if self.dbg >= 2: </span><br><span style="color: hsl(120, 100%, 40%);">+ #if self.dbg >= 2:</span><br><span> # print '[DBG] %s: %s; PIN status: %s' % (T, V, PIN_status)</span><br><span> Data = Data[L+2:]</span><br><span> fil['PIN Status'] = PIN_status</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_AID(self):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- checks EF_DIR at the MF level, </span><br><span style="color: hsl(120, 100%, 40%);">+ checks EF_DIR at the MF level,</span><br><span> and available AID (Application ID) referenced</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> puts it into self.AID</span><br><span> interprets and print the content of the self.AID list</span><br><span> '''</span><br><span> #go back to MF and select EF_DIR</span><br><span> #self.select(Data=[])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # EF_DIR is at the MF level and contains Application ID:</span><br><span> EF_DIR = self.select([0x2F, 0x00], typ='pmf')</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] EF_DIR: %s' % EF_DIR)</span><br><span style="color: hsl(0, 100%, 40%);">- if EF_DIR is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if EF_DIR is None:</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # EF_DIR is an EF with linear fixed structure: contains records:</span><br><span> for rec in EF_DIR['Data']:</span><br><span> # check for a (new) AID:</span><br><span> if (rec[0], rec[2]) == (0x61, 0x4F) and len(rec) > 6 \</span><br><span> and rec[4:4+rec[3]] not in self.AID:</span><br><span> self.AID.append( rec[4:4+rec[3]] )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> i = 1</span><br><span> for aid in self.AID:</span><br><span> aid_rid = tuple(aid[0:5])</span><br><span> aid_app = tuple(aid[5:7])</span><br><span> aid_country = tuple(aid[7:9])</span><br><span> aid_provider = tuple(aid[9:11])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # get AID application code, depending on SDO...</span><br><span> if aid_rid == (0xA0, 0x00, 0x00, 0x00, 0x09) \</span><br><span style="color: hsl(0, 100%, 40%);">- and aid_app in list(self.ETSI_AID_app_code.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ and aid_app in list(self.ETSI_AID_app_code.keys()):</span><br><span> aid_app = self.ETSI_AID_app_code[aid_app]</span><br><span> if aid_rid == (0xA0, 0x00, 0x00, 0x00, 0x87) \</span><br><span style="color: hsl(0, 100%, 40%);">- and aid_app in list(self.GPP_AID_app_code.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ and aid_app in list(self.GPP_AID_app_code.keys()):</span><br><span> aid_app = self.GPP_AID_app_code[aid_app]</span><br><span> if aid_rid == (0xA0, 0x00, 0x00, 0x03, 0x43) \</span><br><span style="color: hsl(0, 100%, 40%);">- and aid_app in list(self.GPP2_AID_app_code.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ and aid_app in list(self.GPP2_AID_app_code.keys()):</span><br><span> aid_app = self.GPP2_AID_app_code[aid_app]</span><br><span> # get AID responsible SDO and country</span><br><span> if aid_rid in list(self.AID_RID.keys()): aid_rid = self.AID_RID[aid_rid]</span><br><span style="color: hsl(0, 100%, 40%);">- if aid_country in list(self.AID_country_code.keys()): </span><br><span style="color: hsl(120, 100%, 40%);">+ if aid_country in list(self.AID_country_code.keys()):</span><br><span> aid_country = self.AID_country_code[aid_country]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> print('found [AID %s] %s || %s || %s || %s || %s' \</span><br><span> % (i, aid_rid, aid_app, aid_country, \</span><br><span> aid_provider, tuple(aid[11:]) ))</span><br><span> i += 1</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_ICCID(self):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- check EF_ICCID at the MF level, </span><br><span style="color: hsl(0, 100%, 40%);">- and returnq the ASCII value of the ICCID </span><br><span style="color: hsl(120, 100%, 40%);">+ check EF_ICCID at the MF level,</span><br><span style="color: hsl(120, 100%, 40%);">+ and returnq the ASCII value of the ICCID</span><br><span> '''</span><br><span> #go back to MF and select EF_ICCID</span><br><span> #self.select(Data=[])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # EF_ICCID is at the MF level and contains Application ID:</span><br><span> EF_ICCID = self.select([0x2F, 0xE2], typ='pmf')</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] EF_ICCID: %s' % EF_ICCID)</span><br><span style="color: hsl(0, 100%, 40%);">- if EF_ICCID is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if EF_ICCID is None:</span><br><span> return None</span><br><span> return decode_BCD( EF_ICCID['Data'] )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def select_by_name(self, name=''):</span><br><span> '''</span><br><span> AID selection by name taken from UICC.files</span><br><span>@@ -1471,13 +1471,10 @@</span><br><span> for i in range(len(self.files)):</span><br><span> if name == self.files[i][2]:</span><br><span> return self.select( self.files[i][0], 'pmf' )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def select_by_aid(self, aid_num=1):</span><br><span> '''</span><br><span> AID selection by index</span><br><span> '''</span><br><span> if len(self.AID) != 0:</span><br><span> return self.select(self.AID[aid_num-1], 'aid')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>diff --git a/card/SIM.py b/card/SIM.py</span><br><span>index 78864a7..555f0df 100644</span><br><span>--- a/card/SIM.py</span><br><span>+++ b/card/SIM.py</span><br><span>@@ -36,11 +36,11 @@</span><br><span> '''</span><br><span> define attributes, methods and facilities for ETSI / 3GPP SIM card</span><br><span> check SIM specifications in ETSI TS 102.221 and 3GPP TS 51.011</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> inherit methods and objects from ISO7816 class</span><br><span> use self.dbg = 1 or more to print live debugging information</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __init__(self):</span><br><span> '''</span><br><span> initialize like an ISO7816-4 card with CLA=0xA0</span><br><span>@@ -50,7 +50,7 @@</span><br><span> if self.dbg:</span><br><span> print('[DBG] type definition: %s' % type(self))</span><br><span> print('[DBG] CLA definition: %s' % hex(self.CLA))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> self.caller = {</span><br><span> 'KC' : self.get_Kc,</span><br><span> 'IMSI' : self.get_imsi,</span><br><span>@@ -63,12 +63,12 @@</span><br><span> 'MSISDN' : self.get_msisdn,</span><br><span> 'SMSP' : self.get_smsp,</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def sw_status(self, sw1, sw2):</span><br><span> '''</span><br><span> sw_status(sw1=int, sw2=int) -> string</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- extends SW status bytes interpretation from ISO7816 </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ extends SW status bytes interpretation from ISO7816</span><br><span> with ETSI / 3GPP SW codes</span><br><span> helps to speak with the smartcard!</span><br><span> '''</span><br><span>@@ -109,7 +109,7 @@</span><br><span> 'application specific'</span><br><span> elif sw2 == 0x63: status += ': security session expired'</span><br><span> return status</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def verify_pin(self, pin='', pin_type=1):</span><br><span> '''</span><br><span> verify CHV1 (PIN code) or CHV2 with VERIFY APDU command</span><br><span>@@ -119,14 +119,14 @@</span><br><span> len(pin) == 4 and 0 <= int(pin) < 10000:</span><br><span> PIN = [ord(i) for i in pin] + [0xFF, 0xFF, 0xFF, 0xFF]</span><br><span> self.coms.push( self.VERIFY(P2=pin_type, Data=PIN) )</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[WNG] bad parameters')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def disable_pin(self, pin='', pin_type=1):</span><br><span> '''</span><br><span> disable CHV1 (PIN code) or CHV2 with DISABLE_CHV APDU command</span><br><span style="color: hsl(0, 100%, 40%);">- TIP: do it as soon as you can when you are working </span><br><span style="color: hsl(120, 100%, 40%);">+ TIP: do it as soon as you can when you are working</span><br><span> with a SIM / USIM card for which you know the PIN!</span><br><span> call ISO7816 DISABLE method</span><br><span> '''</span><br><span>@@ -135,22 +135,22 @@</span><br><span> PIN = [ord(i) for i in pin] + [0xFF, 0xFF, 0xFF, 0xFF]</span><br><span> self.coms.push( self.DISABLE_CHV(P2=pin_type, Data=PIN) )</span><br><span> else:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[WNG] bad parameters')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def unblock_pin(self, pin_type=1, unblock_pin=''):</span><br><span> '''</span><br><span> WARNING: not correctly implemented!!!</span><br><span> and PUK are in general 8 nums...</span><br><span> TODO: make it correctly!</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- unblock CHV1 (PIN code) or CHV2 with UNBLOCK_CHV APDU command </span><br><span style="color: hsl(120, 100%, 40%);">+ unblock CHV1 (PIN code) or CHV2 with UNBLOCK_CHV APDU command</span><br><span> and set 0000 value for new PIN</span><br><span> call ISO7816 UNBLOCK_CHV method</span><br><span> '''</span><br><span> print('not correctly implemented')</span><br><span> return</span><br><span style="color: hsl(0, 100%, 40%);">- #if pin_type == 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ #if pin_type == 1:</span><br><span> # pin_type = 0</span><br><span> if pin_type in [0, 2] and type(unblock_pin) is str and \</span><br><span> len(unblock_pin) == 4 and 0 <= int(unblock_pin) < 10000:</span><br><span>@@ -159,16 +159,16 @@</span><br><span> Data=UNBL_PIN + \</span><br><span> [0x30, 0x30, 0x30, 0x30, 0xFF, 0xFF, 0xFF, 0xFF]) )</span><br><span> else:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[WNG] bad parameters')</span><br><span> #return self.UNBLOCK_CHV(P2=pin_type)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def parse_file(self, Data=[]):</span><br><span> '''</span><br><span> parse_file(Data=[0x12, 0x34, 0x56, 0x89]) -> dict(file)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses a list of bytes returned when selecting a file</span><br><span style="color: hsl(0, 100%, 40%);">- interprets the content of some informative bytes for right accesses, </span><br><span style="color: hsl(120, 100%, 40%);">+ interprets the content of some informative bytes for right accesses,</span><br><span> type / format of file... see TS 51.011</span><br><span> works over the SIM file structure</span><br><span> '''</span><br><span>@@ -193,10 +193,10 @@</span><br><span> fil['unblock_CHV2'] = ('not initialized','initialized')\</span><br><span> [(Data[21] & 0x80) // 0x80]\</span><br><span> + ': %d attempts remain' % (Data[21] & 0x0F)</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) > 23: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) > 23:</span><br><span> fil['Adm'] = Data[23:]</span><br><span> elif fil['Type'] == 'EF':</span><br><span style="color: hsl(0, 100%, 40%);">- cond = ('ALW', 'CHV1', 'CHV2', 'RFU', 'ADM_4', 'ADM_5', </span><br><span style="color: hsl(120, 100%, 40%);">+ cond = ('ALW', 'CHV1', 'CHV2', 'RFU', 'ADM_4', 'ADM_5',</span><br><span> 'ADM_6', 'ADM_7', 'ADM_8', 'ADM_9', 'ADM_A',</span><br><span> 'ADM_B', 'ADM_C', 'ADM_D', 'ADM_E', 'NEW')</span><br><span> fil['UPDATE'] = cond[Data[8] & 0x0F]</span><br><span>@@ -204,77 +204,77 @@</span><br><span> fil['INCREASE'] = cond[Data[9] >> 4]</span><br><span> fil['INVALIDATE'] = cond[Data[10] & 0x0F]</span><br><span> fil['REHABILITATE'] = cond[Data[10] >> 4]</span><br><span style="color: hsl(0, 100%, 40%);">- fil['Status'] = ('not read/updatable when invalidated', </span><br><span style="color: hsl(120, 100%, 40%);">+ fil['Status'] = ('not read/updatable when invalidated',</span><br><span> 'read/updatable when invalidated')\</span><br><span> [byteToBit(Data[11])[5]] \</span><br><span> + (': invalidated',': not invalidated')\</span><br><span> [byteToBit(Data[11])[7]]</span><br><span> fil['Structure'] = ('transparent', 'linear fixed', '', 'cyclic')\</span><br><span> [Data[13]]</span><br><span style="color: hsl(0, 100%, 40%);">- if fil['Structure'] == 'cyclic': </span><br><span style="color: hsl(120, 100%, 40%);">+ if fil['Structure'] == 'cyclic':</span><br><span> fil['INCREASE'] = byteToBit(Data[7])[1]</span><br><span style="color: hsl(0, 100%, 40%);">- if len(Data) > 14: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(Data) > 14:</span><br><span> fil['Record Length'] = Data[14]</span><br><span> return fil</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def run_gsm_alg(self, RAND=16*[0x00]):</span><br><span> '''</span><br><span> self.run_gsm_alg( RAND ) -> ( SRES, Kc )</span><br><span> RAND : list of bytes, length 16</span><br><span> SRES : list of bytes, length 4</span><br><span> Kc : list of bytes, length 8</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- run GSM authentication algorithm: </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ run GSM authentication algorithm:</span><br><span> accepts any kind of RAND (old GSM fashion)</span><br><span> feed with RAND 16 bytes value</span><br><span> return a list with SRES and Kc, or None on error</span><br><span> '''</span><br><span> if len(RAND) != 16:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[WNG] needs a 16 bytes input RAND value')</span><br><span> return None</span><br><span> # select DF_GSM directory</span><br><span> self.select([0x7F, 0x20])</span><br><span style="color: hsl(0, 100%, 40%);">- if self.coms()[2] != (0x90, 0x00): </span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span> # run authentication</span><br><span> self.coms.push(self.INTERNAL_AUTHENTICATE(P1=0x00, P2=0x00, Data=RAND))</span><br><span> if self.coms()[2][0] != 0x9F:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span> # get authentication response</span><br><span> self.coms.push(self.GET_RESPONSE(Le=self.coms()[2][1]))</span><br><span> if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span> SRES, Kc = self.coms()[3][0:4], self.coms()[3][4:]</span><br><span> return [ SRES, Kc ]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_imsi(self):</span><br><span> '''</span><br><span> self.get_imsi() -> string(IMSI)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads IMSI value at address [0x6F, 0x07]</span><br><span> returns IMSI string on success or None on error</span><br><span> '''</span><br><span> # select DF_GSM for SIM card</span><br><span> self.select([0x7F, 0x20])</span><br><span style="color: hsl(0, 100%, 40%);">- if self.coms()[2] != (0x90, 0x00): </span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # select IMSI file</span><br><span> imsi = self.select([0x6F, 0x07])</span><br><span style="color: hsl(0, 100%, 40%);">- if self.coms()[2] != (0x90, 0x00): </span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.coms()[2] != (0x90, 0x00):</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # and parse the received data into the IMSI structure</span><br><span> if 'Data' in list(imsi.keys()) and len(imsi['Data']) == 9:</span><br><span> </span><br><span>@@ -282,9 +282,9 @@</span><br><span> print("[DBG] International Mobile Subscriber Identity (IMSI): %s " % decode_BCD(imsi['Data'])[3:])</span><br><span> </span><br><span> return decode_BCD(imsi['Data'])[3:]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # if issue with the content of the DF_IMSI file</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span> </span><br><span>@@ -313,7 +313,7 @@</span><br><span> return Kc['Data']</span><br><span> else:</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # EF loci contains location information</span><br><span> # This conatins TMSI, LAI, TMSI TIME, and Location update status</span><br><span> # and prints the information</span><br><span>@@ -590,4 +590,3 @@</span><br><span> return iccid</span><br><span> else:</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>diff --git a/card/USIM.py b/card/USIM.py</span><br><span>index fa20d1a..45c2a3e 100644</span><br><span>--- a/card/USIM.py</span><br><span>+++ b/card/USIM.py</span><br><span>@@ -37,16 +37,16 @@</span><br><span> '''</span><br><span> defines attributes, methods and facilities for ETSI / 3GPP USIM card</span><br><span> check USIM specifications in 3GPP TS 31.102</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> inherits (eventually overrides) methods and objects from UICC class</span><br><span> use self.dbg = 1 or more to print live debugging information</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __init__(self):</span><br><span> '''</span><br><span> initializes like an ISO7816-4 card with CLA=0x00</span><br><span> and checks available AID (Application ID) read from EF_DIR</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> initializes on the MF</span><br><span> '''</span><br><span> # initialize like a UICC</span><br><span>@@ -55,7 +55,7 @@</span><br><span> if self.dbg:</span><br><span> print('[DBG] type definition: %s' % type(self))</span><br><span> print('[DBG] CLA definition: %s' % hex(self.CLA))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # USIM selection from AID</span><br><span> print('[+] UICC AID found:')</span><br><span> self.get_AID()</span><br><span>@@ -63,37 +63,37 @@</span><br><span> if tuple(aid[0:5]) == (0xA0, 0x00, 0x00, 0x00, 0x87) \</span><br><span> and tuple(aid[5:7]) == (0x10, 0x02) :</span><br><span> usim = self.select( Data=aid, typ='aid')</span><br><span style="color: hsl(0, 100%, 40%);">- if usim is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if usim is None:</span><br><span> print('[+] USIM AID selection failed')</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> print('[+] USIM AID selection succeeded\n')</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_imsi(self):</span><br><span> '''</span><br><span> get_imsi() -> string(IMSI)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads IMSI value at address [0x6F, 0x07]</span><br><span> returns IMSI string on success or None on error</span><br><span> '''</span><br><span> # select IMSI file</span><br><span> imsi = self.select([0x6F, 0x07])</span><br><span style="color: hsl(0, 100%, 40%);">- if imsi is None: </span><br><span style="color: hsl(120, 100%, 40%);">+ if imsi is None:</span><br><span> return None</span><br><span> # and parse the received data into the IMSI structure</span><br><span> if 'Data' in list(imsi.keys()) and len(imsi['Data']) == 9:</span><br><span> return decode_BCD(imsi['Data'])[3:]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # if issue with the content of the DF_IMSI file</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_CS_keys(self):</span><br><span> '''</span><br><span> get_CS_keys() -> [KSI, CK, IK]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads CS UMTS keys at address [0x6F, 0x08]</span><br><span style="color: hsl(0, 100%, 40%);">- returns list of 3 keys, each are list of bytes, on success </span><br><span style="color: hsl(120, 100%, 40%);">+ returns list of 3 keys, each are list of bytes, on success</span><br><span> (or eventually the whole file dict if the format is strange)</span><br><span> or None on error</span><br><span> '''</span><br><span>@@ -105,39 +105,39 @@</span><br><span> EF_KEYS['Data'][17:33])</span><br><span> print('[+] Successful CS keys selection: Get [KSI, CK, IK]')</span><br><span> return [KSI, CK, IK]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> return EF_KEYS</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_PS_keys(self):</span><br><span> '''</span><br><span> get_PS_keys() -> [KSI, CK_PS, IK_PS]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads PS UMTS keys at address [0x6F, 0x09]</span><br><span style="color: hsl(0, 100%, 40%);">- returns list of 3 keys, each are list of bytes, on success </span><br><span style="color: hsl(120, 100%, 40%);">+ returns list of 3 keys, each are list of bytes, on success</span><br><span> (or eventually the whole file dict if the format is strange)</span><br><span> or None on error</span><br><span> '''</span><br><span> EF_KEYSPS = self.select( [0x6F, 0x09] )</span><br><span> if self.coms()[2] == (0x90, 0x00):</span><br><span> if len(EF_KEYSPS['Data']) == 33:</span><br><span style="color: hsl(0, 100%, 40%);">- KSI, CK, IK = ( EF_KEYSPS['Data'][0:1], </span><br><span style="color: hsl(0, 100%, 40%);">- EF_KEYSPS['Data'][1:17], </span><br><span style="color: hsl(120, 100%, 40%);">+ KSI, CK, IK = ( EF_KEYSPS['Data'][0:1],</span><br><span style="color: hsl(120, 100%, 40%);">+ EF_KEYSPS['Data'][1:17],</span><br><span> EF_KEYSPS['Data'][17:33] )</span><br><span> print('[+] Successful PS keys selection: Get [KSI, CK, IK]')</span><br><span> return [KSI, CK, IK]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> return EF_KEYSPS</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_GBA_BP(self):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- get_GBA_BP() -> [[RAND, B-TID, KeyLifetime], ...], </span><br><span style="color: hsl(120, 100%, 40%);">+ get_GBA_BP() -> [[RAND, B-TID, KeyLifetime], ...],</span><br><span> Length-Value parsing style</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- reads EF_GBABP file at address [0x6F, 0xD6], </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ reads EF_GBABP file at address [0x6F, 0xD6],</span><br><span> containing RAND and associated B-TID and KeyLifetime</span><br><span style="color: hsl(0, 100%, 40%);">- returns list of list of bytes on success </span><br><span style="color: hsl(120, 100%, 40%);">+ returns list of list of bytes on success</span><br><span> (or eventually the whole file dict if the format is strange)</span><br><span> or None on error</span><br><span> '''</span><br><span>@@ -149,17 +149,17 @@</span><br><span> '[RAND, B-TID, KeyLifetime]')</span><br><span> #return (RAND, B_TID, Lifetime)</span><br><span> return LV_parser( EF_GBABP['Data'] )</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> return EF_GBABP</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def update_GBA_BP(self, RAND, B_TID, key_lifetime):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- update_GBA_BP([RAND], [B_TID], [key_lifetime]) </span><br><span style="color: hsl(120, 100%, 40%);">+ update_GBA_BP([RAND], [B_TID], [key_lifetime])</span><br><span> -> void (or EF_GBABP file dict if RAND not found)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads EF_GBABP file at address [0x6F, 0xD6],</span><br><span style="color: hsl(0, 100%, 40%);">- checks if RAND provided is referenced, </span><br><span style="color: hsl(120, 100%, 40%);">+ checks if RAND provided is referenced,</span><br><span> and updates the file structure with provided B-TID and KeyLifetime</span><br><span> returns nothing (or eventually the whole file dict</span><br><span> if the RAND is not found)</span><br><span>@@ -172,35 +172,35 @@</span><br><span> self.coms.push( self.UPDATE_BINARY( P2=len(RAND)+1,</span><br><span> Data=[len(B_TID)] + B_TID + \</span><br><span> [len(key_lifetime)] + key_lifetime ))</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 1: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 1:</span><br><span> print('[DBG] %s' % self.coms())</span><br><span> if self.coms()[2] == 0x90 and self.dbg:</span><br><span> print('[+] Successful GBA_BP update with B-TID ' \</span><br><span> 'and key lifetime')</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg > 2: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg > 2:</span><br><span> print('[DBG] new value of EF_GBA_BP:\n%s' \</span><br><span> % self.get_GBA_BP())</span><br><span> else:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[+] RAND not found in GBA_BP')</span><br><span> return GBA_BP</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_GBA_NL(self):</span><br><span> '''</span><br><span> get_GBA_NL() -> [[NAF_ID, B-TID], ...] , TLV parsing style</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reads EF_GBANL file at address [0x6F, 0xDA], containing NAF_ID and B-TID</span><br><span style="color: hsl(0, 100%, 40%);">- returns list of list of bytes vector on success </span><br><span style="color: hsl(120, 100%, 40%);">+ returns list of list of bytes vector on success</span><br><span> (or eventually the whole file dict if the format is strange)</span><br><span> or None on error</span><br><span> '''</span><br><span> EF_GBANL = self.select( [0x6F, 0xDA] )</span><br><span> if self.coms()[2] == (0x90, 0x00):</span><br><span> if len(EF_GBANL['Data'][0]) > 2:</span><br><span style="color: hsl(0, 100%, 40%);">- # This is Tag-Length-Value parsing, </span><br><span style="color: hsl(120, 100%, 40%);">+ # This is Tag-Length-Value parsing,</span><br><span> # with 0x80 for NAF_ID and 0x81 for B-TID</span><br><span> values = []</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> for rec in EF_GBANL['Data']:</span><br><span> NAF_ID, B_TID = [], []</span><br><span> while len(rec) > 0:</span><br><span>@@ -209,26 +209,26 @@</span><br><span> rec = rec[ tlv[1]+4 : ]</span><br><span> else:</span><br><span> rec = rec[ tlv[1]+2 : ]</span><br><span style="color: hsl(0, 100%, 40%);">- if tlv[0] == 0x80: </span><br><span style="color: hsl(120, 100%, 40%);">+ if tlv[0] == 0x80:</span><br><span> NAF_ID = tlv[2]</span><br><span style="color: hsl(0, 100%, 40%);">- elif tlv[0] == 0x81: </span><br><span style="color: hsl(120, 100%, 40%);">+ elif tlv[0] == 0x81:</span><br><span> B_TID = tlv[2]</span><br><span> values.append( [NAF_ID, B_TID] )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> print('[+] Successful GBA_NL selection: ' \</span><br><span> 'Get list of [NAF_ID, B-TID]')</span><br><span> #return (NAF_ID, B_TID)</span><br><span> return values</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> return EF_GBANL</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def authenticate(self, RAND=[], AUTN=[], ctx='3G'):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- self.authenticate(RAND, AUTN, ctx='3G') -> [key1, key2...], </span><br><span style="color: hsl(120, 100%, 40%);">+ self.authenticate(RAND, AUTN, ctx='3G') -> [key1, key2...],</span><br><span> LV parsing style</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- runs the INTERNAL AUTHENTICATE command in the USIM </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ runs the INTERNAL AUTHENTICATE command in the USIM</span><br><span> with the right context:</span><br><span> ctx = '2G', '3G', 'GBA' ('MBMS' or other not supported at this time)</span><br><span> RAND and AUTN are list of bytes; for '2G' context, AUTN is not used</span><br><span>@@ -241,10 +241,10 @@</span><br><span> '''</span><br><span> # prepare input data for authentication</span><br><span> if ctx in ('3G', 'VGCS', 'GBA', 'MBMS') and len(RAND) != 16 \</span><br><span style="color: hsl(0, 100%, 40%);">- and len(AUTN) != 16: </span><br><span style="color: hsl(120, 100%, 40%);">+ and len(AUTN) != 16:</span><br><span> print('[ERR] missing parameters or wrong length')</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> inp = []</span><br><span> if ctx == '3G':</span><br><span> P2 = 0x81</span><br><span>@@ -255,27 +255,27 @@</span><br><span> elif ctx == 'MBMS':</span><br><span> print('[+] Not implemented. Exit.')</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- elif ctx == 'GBA': </span><br><span style="color: hsl(120, 100%, 40%);">+ elif ctx == 'GBA':</span><br><span> P2 = 0x84</span><br><span> inp = [0xDD]</span><br><span> inp.extend( [len(RAND)] + RAND + [len(AUTN)] + AUTN )</span><br><span style="color: hsl(0, 100%, 40%);">- if ctx not in ['3G', 'VGCS', 'MBMS', 'GBA']: </span><br><span style="color: hsl(0, 100%, 40%);">- # and also, if ctx == '2G'... the safe way </span><br><span style="color: hsl(120, 100%, 40%);">+ if ctx not in ['3G', 'VGCS', 'MBMS', 'GBA']:</span><br><span style="color: hsl(120, 100%, 40%);">+ # and also, if ctx == '2G'... the safe way</span><br><span> # to avoid desynchronizing our USIM counter</span><br><span> P2 = 0x80</span><br><span style="color: hsl(0, 100%, 40%);">- if len(RAND) != 16: </span><br><span style="color: hsl(120, 100%, 40%);">+ if len(RAND) != 16:</span><br><span> print('[ERR] RAND has wrong length (%d, should be 16)' % len(RAND))</span><br><span> return None</span><br><span> # override input value for 2G authent</span><br><span> inp = [len(RAND)] + RAND</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> self.coms.push( self.INTERNAL_AUTHENTICATE(P2=P2, Data=inp) )</span><br><span> if self.coms()[2][0] in (0x9F, 0x61):</span><br><span> self.coms.push( self.GET_RESPONSE(Le=self.coms()[2][1]) )</span><br><span> if self.coms()[2] == (0x90, 0x00):</span><br><span> val = self.coms()[3]</span><br><span> if P2 == 0x80:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[+] Successful 2G authentication. Get [RES, Kc]')</span><br><span> values = LV_parser(val)</span><br><span> # returned values are (RES, Kc)</span><br><span>@@ -294,65 +294,65 @@</span><br><span> # returned values can be (RES, CK, IK) or (RES, CK, IK, Kc)</span><br><span> return values</span><br><span> elif val[0] == 0xDC:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[+] Synchronization failure. Get [AUTS]')</span><br><span> values = LV_parser(val[1:])</span><br><span> return values</span><br><span> elif self.dbg:</span><br><span> print('[+] response: %s' % hex(val))</span><br><span> #else:</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[+] authentication error: %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def GBA_derivation(self, NAF_ID=[], IMPI=[]):</span><br><span> '''</span><br><span> self.GBA_derivation(NAF_ID, IMPI) -> [Ks_ext_naf]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- runs the INTERNAL AUTHENTICATE command in the USIM </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ runs the INTERNAL AUTHENTICATE command in the USIM</span><br><span> with the GBA derivation context:</span><br><span> NAF_ID is a list of bytes (use stringToByte())</span><br><span style="color: hsl(0, 100%, 40%);">- "NAF domain name"||"security protocol id", </span><br><span style="color: hsl(120, 100%, 40%);">+ "NAF domain name"||"security protocol id",</span><br><span> eg: "application.org"||"0x010001000a" (> TLS with RSA and SHA)</span><br><span> IMPI is a list of bytes</span><br><span> "IMSI@ims.mncXXX.mccYYY.3gppnetwork.org" if no IMS IMPI</span><br><span style="color: hsl(0, 100%, 40%);">- is specifically defined in the USIM </span><br><span style="color: hsl(120, 100%, 40%);">+ is specifically defined in the USIM</span><br><span> returns a list with GBA ext key (list of bytes) computed in the USIM:</span><br><span> [Ks_ext_naf]</span><br><span style="color: hsl(0, 100%, 40%);">- Ks_int_naf remains available in the USIM </span><br><span style="color: hsl(120, 100%, 40%);">+ Ks_int_naf remains available in the USIM</span><br><span> for further GBA_U key derivation</span><br><span> or None on error</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> see TS 33.220 for GBA specific formats</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- # need to run 1st an authenicate command with 'GBA' context, </span><br><span style="color: hsl(120, 100%, 40%);">+ # need to run 1st an authenicate command with 'GBA' context,</span><br><span> # so to have the required keys in the USIM</span><br><span> P2 = 0x84</span><br><span> inp = [0xDE] + [len(NAF_ID)] + NAF_ID + [len(IMPI)] + IMPI</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> self.coms.push( self.INTERNAL_AUTHENTICATE(P2=P2, Data=inp) )</span><br><span> if self.coms()[2][0] in (0x9F, 0x61):</span><br><span> self.coms.push( self.GET_RESPONSE(Le=self.coms()[2][1]) )</span><br><span> if self.coms()[2] == (0x90, 0x00):</span><br><span> val = self.coms()[3]</span><br><span> if val[0] == 0xDB: # not adapted to 2G context with Kc, RES</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[+] Successful GBA derivation. Get [Ks_EXT_NAF]')</span><br><span> values = LV_parser(val[1:])</span><br><span> return values</span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg: </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg:</span><br><span> print('[DBG] authentication failure: %s' % self.coms())</span><br><span> return None</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- def bf_FS_from_init( self, filename='bf_USIM', file_dict=USIM_app_FS, </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def bf_FS_from_init( self, filename='bf_USIM', file_dict=USIM_app_FS,</span><br><span> init_method='select_by_aid', init_args=[1] ):</span><br><span> '''</span><br><span> bruteforces the USIM filesystem at the application initialization level:</span><br><span> thanks to UICC.select_by_aid(1)</span><br><span> could be used another way...</span><br><span style="color: hsl(0, 100%, 40%);">- stores the result in the file passed in argument </span><br><span style="color: hsl(120, 100%, 40%);">+ stores the result in the file passed in argument</span><br><span> (file will be overwritten)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> TODO: does not manage file recursivity (when entering DF)</span><br><span> only scan 1st level files</span><br><span> '''</span><br><span>@@ -360,7 +360,7 @@</span><br><span> # loop on all possible addresses</span><br><span> for i in range(0x00, 0xff):</span><br><span> for j in range(0x00, 0xff):</span><br><span style="color: hsl(0, 100%, 40%);">- # here "ret" is useless, </span><br><span style="color: hsl(120, 100%, 40%);">+ # here "ret" is useless,</span><br><span> # but calling the method places the smartcard</span><br><span> # at the right address (by default: 1st AID)</span><br><span> ret = getattr(self, init_method)(*init_args)</span><br><span>@@ -387,10 +387,10 @@</span><br><span> fd.write('file exists at address: %s %s\n' \</span><br><span> % (hex(i)[2:], hex(j)[2:]))</span><br><span> fd.write('%s\n' % self.coms()[1])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- if self.dbg and (i % 0x10 == 0 and j % 0x40 == 0): </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.dbg and (i % 0x10 == 0 and j % 0x40 == 0):</span><br><span> print('[-] going over address %s %s' % (i, j))</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> fd.write('\n')</span><br><span> fd.close()</span><br><span> </span><br><span>diff --git a/card/utils.py b/card/utils.py</span><br><span>index 9a04dcf..56d70fc 100644</span><br><span>--- a/card/utils.py</span><br><span>+++ b/card/utils.py</span><br><span>@@ -26,12 +26,12 @@</span><br><span> from smartcard.util import toBytes, toHexString, PACK</span><br><span> import sys</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# from python 2.6, format('b') allows to use 0b10010110 notation: </span><br><span style="color: hsl(120, 100%, 40%);">+# from python 2.6, format('b') allows to use 0b10010110 notation:</span><br><span> # much convinient</span><br><span> def byteToBit(byte):</span><br><span> '''</span><br><span> byteToBit(0xAB) -> [1, 0, 1, 0, 1, 0, 1, 1]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> converts a byte integer value into a list of bits</span><br><span> '''</span><br><span> bit = [0, 0, 0, 0, 0, 0, 0, 0]</span><br><span>@@ -46,7 +46,7 @@</span><br><span> def stringToByte(string):</span><br><span> '''</span><br><span> stringToByte('test') -> [116, 101, 115, 116]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> converts a string into a list of bytes</span><br><span> '''</span><br><span> bytelist = []</span><br><span>@@ -62,7 +62,7 @@</span><br><span> def byteToString(bytelist):</span><br><span> '''</span><br><span> byteToString([116, 101, 115, 116]) -> 'test'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> converts a list of bytes into a string</span><br><span> '''</span><br><span> string = ''</span><br><span>@@ -82,7 +82,7 @@</span><br><span> def LV_parser(bytelist):</span><br><span> '''</span><br><span> LV_parser([0x02, 0xAB, 0xCD, 0x01, 0x12, 0x34]) -> [[171, 205], [18], []]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses Length-Value records in a list of bytes</span><br><span> returns a list of list of bytes</span><br><span> length coded on 1 byte</span><br><span>@@ -97,8 +97,8 @@</span><br><span> def first_TLV_parser(bytelist):</span><br><span> '''</span><br><span> first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- parses first TLV format record in a list of bytelist </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ parses first TLV format record in a list of bytelist</span><br><span> returns a 3-Tuple: Tag, Length, Value</span><br><span> Value is a list of bytes</span><br><span> parsing of length is ETSI'style 101.220</span><br><span>@@ -115,7 +115,7 @@</span><br><span> def TLV_parser(bytelist):</span><br><span> '''</span><br><span> TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> loops on the input list of bytes with the "first_TLV_parser()" function</span><br><span> returns a list of 3-Tuples</span><br><span> '''</span><br><span>@@ -127,17 +127,17 @@</span><br><span> break</span><br><span> ret.append( (T, L, V) )</span><br><span> # need to manage length of L</span><br><span style="color: hsl(0, 100%, 40%);">- if L > 0xFE: </span><br><span style="color: hsl(120, 100%, 40%);">+ if L > 0xFE:</span><br><span> bytelist = bytelist[ L+4 : ]</span><br><span style="color: hsl(0, 100%, 40%);">- else: </span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span> bytelist = bytelist[ L+2 : ]</span><br><span> return ret</span><br><span> </span><br><span> def first_BERTLV_parser(bytelist):</span><br><span> '''</span><br><span style="color: hsl(0, 100%, 40%);">- first_BERTLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) </span><br><span style="color: hsl(120, 100%, 40%);">+ first_BERTLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00])</span><br><span> -> ([1, 'contextual', 'constructed', 10], [1, 2], [171, 205])</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> parses first BER-TLV format record in a list of bytes</span><br><span> returns a 3-Tuple: Tag, Length, Value</span><br><span> Tag: [Tag class, Tag DO, Tag number]</span><br><span>@@ -170,12 +170,12 @@</span><br><span> # Tag coded with 1 byte</span><br><span> else:</span><br><span> Tag_bits = byte0[3:8]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(0, 100%, 40%);">- # Tag number calculation </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Tag number calculation</span><br><span> Tag_num = 0</span><br><span> for j in range(len(Tag_bits)):</span><br><span> Tag_num += Tag_bits[len(Tag_bits)-j-1] * pow(2, j)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Length coded with more than 1 byte</span><br><span> if bytelist[i+1] & 0x80 > 0:</span><br><span> Len_num = bytelist[i+1] - 0x80</span><br><span>@@ -196,14 +196,14 @@</span><br><span> def BERTLV_parser(bytelist):</span><br><span> '''</span><br><span> BERTLV_parser([0xAA, ..., 0xFF]) -> [([T], L, [V]), ([T], L, [V]), ...]</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> loops on the input bytes with the "first_BERTLV_parser()" function</span><br><span> returns a list of 3-Tuples containing BERTLV records</span><br><span> '''</span><br><span> ret = []</span><br><span> while len(bytelist) > 0:</span><br><span> T, L, V = first_BERTLV_parser(bytelist)</span><br><span style="color: hsl(0, 100%, 40%);">- #if T == 0xFF: </span><br><span style="color: hsl(120, 100%, 40%);">+ #if T == 0xFF:</span><br><span> # break # padding bytes</span><br><span> ret.append( (T[1:], L[1], V) )</span><br><span> # need to manage lengths of Tag and Length</span><br><span>@@ -213,14 +213,14 @@</span><br><span> def decode_BCD(data=[]):</span><br><span> '''</span><br><span> decode_BCD([0x21, 0xFE, 0xA3]) -> '121415310'</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> to decode serial number (IMSI, ICCID...) from list of bytes</span><br><span> '''</span><br><span> string = ''</span><br><span> for B in data:</span><br><span> string += str( B & 0x0F )</span><br><span> string += str( B >> 4 )</span><br><span style="color: hsl(0, 100%, 40%);">- return string </span><br><span style="color: hsl(120, 100%, 40%);">+ return string</span><br><span> </span><br><span> </span><br><span> #######################################################</span><br><span>@@ -230,10 +230,10 @@</span><br><span> '''</span><br><span> input / output wrapping class</span><br><span> for APDU communications</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> allows to keep track of communications</span><br><span> and exchanged commands</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> based on the python "deque" fifo-like object</span><br><span> '''</span><br><span> </span><br><span>@@ -242,13 +242,13 @@</span><br><span> initializes apdu_stack with the maximum of IO to keep track of</span><br><span> '''</span><br><span> self.apdu_stack = deque([], limit)</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def push(self, apdu_response):</span><br><span> '''</span><br><span> stacks the returned response into the apdu_stack</span><br><span> '''</span><br><span> self.apdu_stack.append( apdu_response )</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __repr__(self):</span><br><span> '''</span><br><span> represents the whole stack of responses pushed on</span><br><span>@@ -257,10 +257,9 @@</span><br><span> for apdu in self.apdu_stack:</span><br><span> s += apdu.__repr__() + '\n'</span><br><span> return s</span><br><span style="color: hsl(0, 100%, 40%);">- </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def __call__(self):</span><br><span> '''</span><br><span> calling the apdu_stack returns the last response pushed on it</span><br><span> '''</span><br><span> return self.apdu_stack[-1]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-sim-auth/+/20909">change 20909</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-sim-auth/+/20909"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmo-sim-auth </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie8a5fc47775fe7d7fe0e19f7378ffda104fa6112 </div>
<div style="display:none"> Gerrit-Change-Number: 20909 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Ludovic Rousseau <ludovic.rousseau+osmocom@free.fr> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>