<p>laforge has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/24037">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Implement EF.ARR (Access Rule Reference) decoding<br><br>The Access Mode (AM) and Security Condition (SC) DOs are incredibly<br>convoluted, so we need a lot of code to properly decode them.<br><br>Change-Id: If4f0725a849d41fd93de327ed00996d8179f2b0e<br>---<br>M README.md<br>M pySim/ts_102_221.py<br>M requirements.txt<br>M setup.py<br>4 files changed, 385 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/37/24037/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/README.md b/README.md</span><br><span>index db294c6..274ab11 100644</span><br><span>--- a/README.md</span><br><span>+++ b/README.md</span><br><span>@@ -39,11 +39,12 @@</span><br><span> - pytlv</span><br><span> - cmd2</span><br><span> - jsonpath-ng</span><br><span style="color: hsl(120, 100%, 40%);">+- bidict</span><br><span> </span><br><span> Example for Debian:</span><br><span> </span><br><span> apt-get install python3-pyscard python3-serial python3-cmd2 python3-pip python3-yaml</span><br><span style="color: hsl(0, 100%, 40%);">- pip3 install pytlv</span><br><span style="color: hsl(120, 100%, 40%);">+ pip3 install pytlv bidict</span><br><span> </span><br><span> Alternatively, everything can be installed using pip:</span><br><span> </span><br><span>diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py</span><br><span>index 88a36a1..82818b7 100644</span><br><span>--- a/pySim/ts_102_221.py</span><br><span>+++ b/pySim/ts_102_221.py</span><br><span>@@ -21,6 +21,52 @@</span><br><span> from struct import pack, unpack</span><br><span> from pySim.utils import *</span><br><span> from pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+from bidict import bidict</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [</span><br><span style="color: hsl(120, 100%, 40%);">+ # TS 102 221 Section 10.1.2 Table 10.5 "Coding of Instruction Byte"</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('SELECT', 0xA4, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('STATUS', 0xF2, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('READ BINARY', 0xB0, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('UPDATE BINARY', 0xD6, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('READ RECORD', 0xB2, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('UPDATE RECORD', 0xDC, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('SEARCH RECORD', 0xA2, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('INCREASE', 0x32, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('RETRIEVE DATA', 0xCB, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('SET DATA', 0xDB, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('VERIFY PIN', 0x20, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('CHANGE PIN', 0x24, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('DISABLE PIN', 0x26, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('ENABLE PIN', 0x28, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('UNBLOCK PIN', 0x2C, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('DEACTIVATE FILE', 0x04, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('ACTIVATE FILE', 0x44, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('AUTHENTICATE', 0x88, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('AUTHENTICATE', 0x89, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('GET CHALLENGE', 0x84, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINAL CAPABILITY', 0xAA, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINAL PROFILE', 0x10, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('ENVELOPE', 0xC2, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('FETCH', 0x12, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINAL RESPONSE', 0x14, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('MANAGE CHANNEL', 0x70, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('MANAGE SECURE CHANNEL', 0x73, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TRANSACT DATA', 0x75, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('SUSPEND UICC', 0x76, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('GET IDENTITY', 0x78, ['8X', 'CX', 'EX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('EXCHANGE CAPABILITIES', 0x7A, ['80']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('GET RESPONSE', 0xC0, ['0X', '4X', '6X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ # TS 102 222 Section 6.1 Table 1 "Coding of the commands"</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('CREATE FILE', 0xE0, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('DELETE FILE', 0xE4, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('DEACTIVATE FILE', 0x04, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('ACTIVATE FILE', 0x44, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINATE DF', 0xE6, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINATE EF', 0xE8, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('TERMINATE CARD USAGE', 0xFE, ['0X', '4X']),</span><br><span style="color: hsl(120, 100%, 40%);">+ CardCommand('RESIZE FILE', 0xD4, ['8X', 'CX']),</span><br><span style="color: hsl(120, 100%, 40%);">+ ])</span><br><span> </span><br><span> </span><br><span> FCP_TLV_MAP = {</span><br><span>@@ -150,6 +196,269 @@</span><br><span> return val</span><br><span> return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()}</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class _AM_DO_DF(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__('access_mode', 'Access Mode', tag=0x80)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def from_value(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ res = []</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(do) != 1:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError("We only support single-byte AMF inside AM-DO")</span><br><span style="color: hsl(120, 100%, 40%);">+ amf = do[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ # tables 17..29 and 41..44 of 7816-4</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x80 == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x40:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('delete_file')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x20:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('terminate_df')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x10:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('activate_file')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x08:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('deactivate_file')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x04:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('create_file_df')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x02:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('create_file_ef')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('delete_file_child')</span><br><span style="color: hsl(120, 100%, 40%);">+ self.decoded = res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ val = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'delete_file' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x40</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'terminate_df' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x20</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'activate_file' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x10</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'deactivate_file' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x08</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'create_file_df' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x04</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'create_file_ef' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x02</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'delete_file_child' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x01</span><br><span style="color: hsl(120, 100%, 40%);">+ return val.to_bytes(1, 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class _AM_DO_EF(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+ """ISO7816-4 9.3.2 Table 18 + 9.3.3.1 Table 31"""</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__('access_mode', 'Access Mode', tag=0x80)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def from_value(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ res = []</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(do) != 1:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError("We only support single-byte AMF inside AM-DO")</span><br><span style="color: hsl(120, 100%, 40%);">+ amf = do[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ # tables 17..29 and 41..44 of 7816-4</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x80 == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x40:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('delete_file')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x20:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('terminate_ef')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x10:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('activate_file_or_record')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x08:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('deactivate_file_or_record')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x04:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('write_append')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x02:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('update_erase')</span><br><span style="color: hsl(120, 100%, 40%);">+ if amf & 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('read_search_compare')</span><br><span style="color: hsl(120, 100%, 40%);">+ self.decoded = res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ val = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'delete_file' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x40</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'terminate_ef' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x20</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'activate_file_or_record' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x10</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'deactivate_file_or_record' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x08</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'write_append' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x04</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'update_erase' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x02</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'read_search_compare' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ val |= 0x01</span><br><span style="color: hsl(120, 100%, 40%);">+ return val.to_bytes(1, 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class _AM_DO_CHDR(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Command Header Access Mode DO according to ISO 7816-4 Table 32."""</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, tag):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__('command_header', 'Command Header Description', tag=tag)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def from_value(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ res = {}</span><br><span style="color: hsl(120, 100%, 40%);">+ i = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tag & 0x08:</span><br><span style="color: hsl(120, 100%, 40%);">+ res['CLA'] = do[i]</span><br><span style="color: hsl(120, 100%, 40%);">+ i += 1</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tag & 0x04:</span><br><span style="color: hsl(120, 100%, 40%);">+ res['INS'] = do[i]</span><br><span style="color: hsl(120, 100%, 40%);">+ i += 1</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tag & 0x02:</span><br><span style="color: hsl(120, 100%, 40%);">+ res['P1'] = do[i]</span><br><span style="color: hsl(120, 100%, 40%);">+ i += 1</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tag & 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ res['P2'] = do[i]</span><br><span style="color: hsl(120, 100%, 40%);">+ i += 1</span><br><span style="color: hsl(120, 100%, 40%);">+ self.decoded = res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def _compute_tag(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Override to encode the tag, as it depends on the value."""</span><br><span style="color: hsl(120, 100%, 40%);">+ tag = 0x80</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'CLA' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ tag |= 0x08</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'INS' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ tag |= 0x04</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'P1' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ tag |= 0x02</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'P2' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ tag |= 0x01</span><br><span style="color: hsl(120, 100%, 40%);">+ return tag</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ res = bytearray()</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'CLA' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append(self.decoded['CLA'])</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'INS' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append(self.decoded['INS'])</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'P1' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append(self.decoded['P1'])</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'P2' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append(self.decoded['P2'])</span><br><span style="color: hsl(120, 100%, 40%);">+ return res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_DO_CHDR = DataObjectChoice('am_do_chdr', members=[</span><br><span style="color: hsl(120, 100%, 40%);">+ _AM_DO_CHDR(0x81), _AM_DO_CHDR(0x82), _AM_DO_CHDR(0x83), _AM_DO_CHDR(0x84),</span><br><span style="color: hsl(120, 100%, 40%);">+ _AM_DO_CHDR(0x85), _AM_DO_CHDR(0x86), _AM_DO_CHDR(0x87), _AM_DO_CHDR(0x88),</span><br><span style="color: hsl(120, 100%, 40%);">+ _AM_DO_CHDR(0x89), _AM_DO_CHDR(0x8a), _AM_DO_CHDR(0x8b), _AM_DO_CHDR(0x8c),</span><br><span style="color: hsl(120, 100%, 40%);">+ _AM_DO_CHDR(0x8d), _AM_DO_CHDR(0x8e), _AM_DO_CHDR(0x8f)])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AM_DO_DF = AM_DO_CHDR | _AM_DO_DF()</span><br><span style="color: hsl(120, 100%, 40%);">+AM_DO_EF = AM_DO_CHDR | _AM_DO_EF()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# TS 102 221 Section 9.5.1 / Table 9.3</span><br><span style="color: hsl(120, 100%, 40%);">+pin_names = bidict({</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x01: 'PIN1',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x02: 'PIN2',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x03: 'PIN3',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x04: 'PIN4',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x05: 'PIN5',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x06: 'PIN6',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x07: 'PIN7',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x08: 'PIN8',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x0a: 'ADM1',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x0b: 'ADM2',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x0c: 'ADM3',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x0d: 'ADM4',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x0e: 'ADM5',</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x11: 'UNIVERSAL_PIN',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x81: '2PIN1',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x82: '2PIN2',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x83: '2PIN3',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x84: '2PIN4',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x85: '2PIN5',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x86: '2PIN6',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x87: '2PIN7',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x88: '2PIN8',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x8a: 'ADM6',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x8b: 'ADM7',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x8c: 'ADM8',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x8d: 'ADM9',</span><br><span style="color: hsl(120, 100%, 40%);">+ 0x8e: 'ADM10',</span><br><span style="color: hsl(120, 100%, 40%);">+ })</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CRT_DO(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Control Reference Template as per TS 102 221 9.5.1"""</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__('control_reference_template', 'Control Reference Template', tag=0xA4)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def from_value(self, do: bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Decode a Control Reference Template DO."""</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(do) != 6:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError('Unsupported CRT DO length: %s', do)</span><br><span style="color: hsl(120, 100%, 40%);">+ if do[0] != 0x83 or do[1] != 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError('Unsupported Key Ref Tag or Len in CRT DO %s', do)</span><br><span style="color: hsl(120, 100%, 40%);">+ if do[3:] != b'\x95\x01\x08':</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError('Unsupported Usage Qualifier Tag or Len in CRT DO %s', do)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.encoded = do[0:6]</span><br><span style="color: hsl(120, 100%, 40%);">+ self.decoded = pin_names[do[2]]</span><br><span style="color: hsl(120, 100%, 40%);">+ return do[6:]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ pin = pin_names.inverse[self.decoded]</span><br><span style="color: hsl(120, 100%, 40%);">+ return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# ISO7816-4 9.3.3 Table 33</span><br><span style="color: hsl(120, 100%, 40%);">+class SecCondByte_DO(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, tag=0x9d):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__('security_condition_byte', tag=tag)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def from_value(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(binary) != 1:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError</span><br><span style="color: hsl(120, 100%, 40%);">+ inb = binary[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ cond = 'always'</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb == 0xff:</span><br><span style="color: hsl(120, 100%, 40%);">+ cond = 'never'</span><br><span style="color: hsl(120, 100%, 40%);">+ res = []</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb & 0x80:</span><br><span style="color: hsl(120, 100%, 40%);">+ cond = 'and'</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ cond = 'or'</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb & 0x40:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('secure_messaging')</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb & 0x20:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('external_auth')</span><br><span style="color: hsl(120, 100%, 40%);">+ if inb & 0x10:</span><br><span style="color: hsl(120, 100%, 40%);">+ res.append('user_auth')</span><br><span style="color: hsl(120, 100%, 40%);">+ rd = {'mode': cond }</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(res):</span><br><span style="color: hsl(120, 100%, 40%);">+ rd['conditions'] = res</span><br><span style="color: hsl(120, 100%, 40%);">+ self.decoded = rd</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ mode = self.decoded['mode']</span><br><span style="color: hsl(120, 100%, 40%);">+ if mode == 'always':</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ elif mode == 'never':</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0xff</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ if mode == 'and':</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= 0x80</span><br><span style="color: hsl(120, 100%, 40%);">+ elif mode == 'or':</span><br><span style="color: hsl(120, 100%, 40%);">+ pass</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError('Unknown mode %s' % mode)</span><br><span style="color: hsl(120, 100%, 40%);">+ for c in self.decoded['conditions']:</span><br><span style="color: hsl(120, 100%, 40%);">+ if c == 'secure_messaging':</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= 0x40</span><br><span style="color: hsl(120, 100%, 40%);">+ elif c == 'external_auth':</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= 0x20</span><br><span style="color: hsl(120, 100%, 40%);">+ elif c == 'user_auth':</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= 0x10</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError('Unknown condition %s' % c)</span><br><span style="color: hsl(120, 100%, 40%);">+ return res.to_bytes(1, 'big')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Always_DO = TL0_DataObject('always', 'Always', 0x90)</span><br><span style="color: hsl(120, 100%, 40%);">+Never_DO = TL0_DataObject('never', 'Never', 0x97)</span><br><span style="color: hsl(120, 100%, 40%);">+SC_DO = DataObjectChoice('security_condition', 'Security Condition',</span><br><span style="color: hsl(120, 100%, 40%);">+ members=[Always_DO, Never_DO, SecCondByte_DO(), SecCondByte_DO(0x9e), CRT_DO()])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> # ETSI TS 102 221 Section 11.1.1.3</span><br><span> def decode_select_response(resp_hex):</span><br><span>@@ -207,6 +516,78 @@</span><br><span> class EF_ARR(LinFixedEF):</span><br><span> def __init__(self, fid='2f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'):</span><br><span> super().__init__(fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+ # add those commands to the general commands of a TransparentEF</span><br><span style="color: hsl(120, 100%, 40%);">+ self.shell_commands += [self.AddlShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+ def flatten(inp:list):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Flatten the somewhat deep/complex/nested data returned from decoder."""</span><br><span style="color: hsl(120, 100%, 40%);">+ def sc_abbreviate(sc):</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'always' in sc:</span><br><span style="color: hsl(120, 100%, 40%);">+ return 'always'</span><br><span style="color: hsl(120, 100%, 40%);">+ elif 'never' in sc:</span><br><span style="color: hsl(120, 100%, 40%);">+ return 'never'</span><br><span style="color: hsl(120, 100%, 40%);">+ elif 'control_reference_template' in sc:</span><br><span style="color: hsl(120, 100%, 40%);">+ return sc['control_reference_template']</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ return sc</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ by_mode = {}</span><br><span style="color: hsl(120, 100%, 40%);">+ for t in inp:</span><br><span style="color: hsl(120, 100%, 40%);">+ am = t[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ sc = t[1]</span><br><span style="color: hsl(120, 100%, 40%);">+ sc_abbr = sc_abbreviate(sc)</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'access_mode' in am:</span><br><span style="color: hsl(120, 100%, 40%);">+ for m in am['access_mode']:</span><br><span style="color: hsl(120, 100%, 40%);">+ by_mode[m] = sc_abbr</span><br><span style="color: hsl(120, 100%, 40%);">+ elif 'command_header' in am:</span><br><span style="color: hsl(120, 100%, 40%);">+ ins = am['command_header']['INS']</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'CLA' in am['command_header']:</span><br><span style="color: hsl(120, 100%, 40%);">+ cla = am['command_header']['CLA']</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ cla = None</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd = ts_102_22x_cmdset.lookup(ins, cla)</span><br><span style="color: hsl(120, 100%, 40%);">+ if cmd:</span><br><span style="color: hsl(120, 100%, 40%);">+ name = cmd.name.lower().replace(' ','_')</span><br><span style="color: hsl(120, 100%, 40%);">+ by_mode[name] = sc_abbr</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError</span><br><span style="color: hsl(120, 100%, 40%);">+ return by_mode</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def _decode_record_bin(self, raw_bin_data):</span><br><span style="color: hsl(120, 100%, 40%);">+ # we can only guess if we should decode for EF or DF here :(</span><br><span style="color: hsl(120, 100%, 40%);">+ arr_seq = DataObjectSequence('arr', sequence = [AM_DO_EF, SC_DO])</span><br><span style="color: hsl(120, 100%, 40%);">+ dec = arr_seq.decode_multi(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+ # we cannot pass the result through flatten() here, as we don't have a related</span><br><span style="color: hsl(120, 100%, 40%);">+ # 'un-flattening' decoder, and hence would be unable to encode :(</span><br><span style="color: hsl(120, 100%, 40%);">+ return dec[0]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @with_default_category('File-Specific Commands')</span><br><span style="color: hsl(120, 100%, 40%);">+ class AddlShellCommands(CommandSet):</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @cmd2.with_argparser(LinFixedEF.ShellCommands.read_rec_dec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+ def do_read_arr_record(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Read one EF.ARR record in flattened, human-friendly form."""</span><br><span style="color: hsl(120, 100%, 40%);">+ (data, sw) = self._cmd.rs.read_record_dec(opts.record_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+ data = self._cmd.rs.selected_file.flatten(data)</span><br><span style="color: hsl(120, 100%, 40%);">+ self._cmd.poutput_json(data, opts.oneline)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ @cmd2.with_argparser(LinFixedEF.ShellCommands.read_recs_dec_parser)</span><br><span style="color: hsl(120, 100%, 40%);">+ def do_read_arr_records(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Read + decode all EF.ARR records in flattened, human-friendly form."""</span><br><span style="color: hsl(120, 100%, 40%);">+ num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']</span><br><span style="color: hsl(120, 100%, 40%);">+ # collect all results in list so they are rendered as JSON list when printing</span><br><span style="color: hsl(120, 100%, 40%);">+ data_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+ for recnr in range(1, 1 + num_of_rec):</span><br><span style="color: hsl(120, 100%, 40%);">+ (data, sw) = self._cmd.rs.read_record_dec(recnr)</span><br><span style="color: hsl(120, 100%, 40%);">+ data = self._cmd.rs.selected_file.flatten(data)</span><br><span style="color: hsl(120, 100%, 40%);">+ data_list.append(data)</span><br><span style="color: hsl(120, 100%, 40%);">+ self._cmd.poutput_json(data_list, opts.oneline)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> # TS 102 221 Section 13.6</span><br><span> class EF_UMPC(TransparentEF):</span><br><span>diff --git a/requirements.txt b/requirements.txt</span><br><span>index 6da27cc..6e8336c 100644</span><br><span>--- a/requirements.txt</span><br><span>+++ b/requirements.txt</span><br><span>@@ -4,3 +4,4 @@</span><br><span> cmd2</span><br><span> jsonpath-ng</span><br><span> construct</span><br><span style="color: hsl(120, 100%, 40%);">+bidict</span><br><span>diff --git a/setup.py b/setup.py</span><br><span>index 75d7d20..08608dc 100644</span><br><span>--- a/setup.py</span><br><span>+++ b/setup.py</span><br><span>@@ -15,6 +15,7 @@</span><br><span> "cmd2 >= 1.3.0",</span><br><span> "jsonpath-ng",</span><br><span> "construct >= 2.9",</span><br><span style="color: hsl(120, 100%, 40%);">+ "bidict",</span><br><span> ],</span><br><span> scripts=[</span><br><span> 'pySim-prog.py',</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/24037">change 24037</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/pysim/+/24037"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: pysim </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If4f0725a849d41fd93de327ed00996d8179f2b0e </div>
<div style="display:none"> Gerrit-Change-Number: 24037 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>