<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>