<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/23564">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add decoder/encoder for EF.SUCI_Calc_Info<br><br>Change-Id: I848a766e6d00be497c7db905475e0681cce197ac<br>---<br>M pySim/ts_31_102.py<br>M tests/test_utils.py<br>2 files changed, 171 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py</span><br><span>index 02b0aea..54fd95d 100644</span><br><span>--- a/pySim/ts_31_102.py</span><br><span>+++ b/pySim/ts_31_102.py</span><br><span>@@ -274,6 +274,126 @@</span><br><span> </span><br><span> import pySim.ts_102_221</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+# TS 31.102 4.4.11.8</span><br><span style="color: hsl(120, 100%, 40%);">+class EF_SUCI_Calc_Info(TransparentEF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid="4f07", sfid=0x07, name='EF.SUCI_Calc_Info', size={2, None},</span><br><span style="color: hsl(120, 100%, 40%);">+        desc='SUCI Calc Info'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_prot_scheme_id_list(self, in_list):</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes = [0xa0]</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes.append(len(in_list)*2) # two byte per entry</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # position in list determines priority; high-priority items (low index) come first</span><br><span style="color: hsl(120, 100%, 40%);">+        for scheme in sorted(in_list, key=lambda item: item["priority"]):</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(scheme["identifier"])</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(scheme["key_index"])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return out_bytes</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_hnet_pubkey_list(self, hnet_pubkey_list):</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes = [0xa1] # pubkey list tag</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes.append(0x00) # length filled later</span><br><span style="color: hsl(120, 100%, 40%);">+        length = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for key in hnet_pubkey_list:</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(0x80) # identifier tag</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(0x01) # TODO size, fixed to 1 byte</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(key["hnet_pubkey_identifier"])</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(0x81) # key tag</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes.append(len(key["hnet_pubkey"])//2)</span><br><span style="color: hsl(120, 100%, 40%);">+            length += 5+len(key["hnet_pubkey"])//2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            pubkey_bytes = h2b(key["hnet_pubkey"])</span><br><span style="color: hsl(120, 100%, 40%);">+            out_bytes += pubkey_bytes</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # fill length</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes[1] = length</span><br><span style="color: hsl(120, 100%, 40%);">+        return out_bytes</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _encode_hex(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes = self._encode_prot_scheme_id_list(in_json['prot_scheme_id_list'])</span><br><span style="color: hsl(120, 100%, 40%);">+        out_bytes += self._encode_hnet_pubkey_list(in_json['hnet_pubkey_list'])</span><br><span style="color: hsl(120, 100%, 40%);">+        return "".join(["%02X" % i for i in out_bytes])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_prot_scheme_id_list(self, in_bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        prot_scheme_id_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+        pos = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        # two bytes per entry</span><br><span style="color: hsl(120, 100%, 40%);">+        while pos < len(in_bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+            prot_scheme = {</span><br><span style="color: hsl(120, 100%, 40%);">+                'priority':   pos//2, # first in list: high priority</span><br><span style="color: hsl(120, 100%, 40%);">+                'identifier': in_bytes[pos],</span><br><span style="color: hsl(120, 100%, 40%);">+                'key_index':  in_bytes[pos+1]</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += 2</span><br><span style="color: hsl(120, 100%, 40%);">+            prot_scheme_id_list.append(prot_scheme)</span><br><span style="color: hsl(120, 100%, 40%);">+        return prot_scheme_id_list</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_hnet_pubkey_list(self, in_bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        hnet_pubkey_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+        pos = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_bytes[pos] != 0xa1:</span><br><span style="color: hsl(120, 100%, 40%);">+            print("missing Home Network Public Key List data object")</span><br><span style="color: hsl(120, 100%, 40%);">+            return {}</span><br><span style="color: hsl(120, 100%, 40%);">+        pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        hnet_pubkey_list_len = in_bytes[pos]</span><br><span style="color: hsl(120, 100%, 40%);">+        pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        while pos < hnet_pubkey_list_len:</span><br><span style="color: hsl(120, 100%, 40%);">+            if in_bytes[pos] != 0x80:</span><br><span style="color: hsl(120, 100%, 40%);">+                print("missing Home Network Public Key Identifier tag")</span><br><span style="color: hsl(120, 100%, 40%);">+                return {}</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+            hnet_pubkey_id_len = in_bytes[pos] # TODO might be more than 1 byte?</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+            hnet_pubkey_id = in_bytes[pos:pos+hnet_pubkey_id_len][0]</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += hnet_pubkey_id_len</span><br><span style="color: hsl(120, 100%, 40%);">+            if in_bytes[pos] != 0x81:</span><br><span style="color: hsl(120, 100%, 40%);">+                print("missing Home Network Public Key tag")</span><br><span style="color: hsl(120, 100%, 40%);">+                return {}</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+            hnet_pubkey_len = in_bytes[pos]</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+            hnet_pubkey = in_bytes[pos:pos+hnet_pubkey_len]</span><br><span style="color: hsl(120, 100%, 40%);">+            pos += hnet_pubkey_len</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            hnet_pubkey_list.append({</span><br><span style="color: hsl(120, 100%, 40%);">+                'hnet_pubkey_identifier': hnet_pubkey_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                'hnet_pubkey':            b2h(hnet_pubkey)</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%);">+        return hnet_pubkey_list</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_bin(self, in_bin):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self._decode_hex(b2h(in_hex))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _decode_hex(self, in_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        in_bytes = h2b(in_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+        pos = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if in_bytes[pos] != 0xa0:</span><br><span style="color: hsl(120, 100%, 40%);">+            print("missing Protection Scheme Identifier List data object tag")</span><br><span style="color: hsl(120, 100%, 40%);">+            return {}</span><br><span style="color: hsl(120, 100%, 40%);">+        pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        prot_scheme_id_list_len = in_bytes[pos] # TODO maybe more than 1 byte</span><br><span style="color: hsl(120, 100%, 40%);">+        pos += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        # decode Protection Scheme Identifier List data object</span><br><span style="color: hsl(120, 100%, 40%);">+        prot_scheme_id_list = self._decode_prot_scheme_id_list(in_bytes[pos:pos+prot_scheme_id_list_len])</span><br><span style="color: hsl(120, 100%, 40%);">+        pos += prot_scheme_id_list_len</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # remaining data holds Home Network Public Key Data Object</span><br><span style="color: hsl(120, 100%, 40%);">+        hnet_pubkey_list = self._decode_hnet_pubkey_list(in_bytes[pos:])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return {</span><br><span style="color: hsl(120, 100%, 40%);">+            'prot_scheme_id_list': prot_scheme_id_list,</span><br><span style="color: hsl(120, 100%, 40%);">+            'hnet_pubkey_list':    hnet_pubkey_list</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%);">+    def _encode_bin(self, in_json):</span><br><span style="color: hsl(120, 100%, 40%);">+        return h2b(self._encode_hex(in_json))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> class EF_LI(TransRecEF):</span><br><span>     def __init__(self, fid='6f05', sfid=None, name='EF.LI', size={2,None}, rec_len=2,</span><br><span>                  desc='Language Indication'):</span><br><span>@@ -340,6 +460,27 @@</span><br><span>             """Deactivate a service within EF.UST"""</span><br><span>             self._cmd.card.update_ust(int(arg), 0)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class DF_USIM_5GS(CardDF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, fid='5FC0', name='DF.5GS', desc='5GS related files'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(fid=fid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+        files = [</span><br><span style="color: hsl(120, 100%, 40%);">+          # I'm looking at 31.102 R15.9</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F01', None, 'EF.5GS3GPPLOCI', '5GS 3GPP location information', size={20,20}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F02', None, 'EF.5GSN3GPPLOCI', '5GS non-3GPP location information', size={20,20}),</span><br><span style="color: hsl(120, 100%, 40%);">+          #LinFixedEF('4F03', None, 'EF.5GS3GPPNSC', '5GS 3GPP Access NAS Security Context'),</span><br><span style="color: hsl(120, 100%, 40%);">+          #LinFixedEF('4F04', None, 'EF.5GSN3GPPNSC', '5GS non-3GPP Access NAS Security Context'),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F05', None, 'EF.5GAUTHKEYS', '5G authentication keys', size={68, None}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F06', None, 'EF.UAC_AIC', 'UAC Access Identities Configuration', size={4, 4}),</span><br><span style="color: hsl(120, 100%, 40%);">+          EF_SUCI_Calc_Info(), #TransparentEF('4F07', None, 'EF.SUCI_Calc_Info', 'SUCI Calculation Information', size={2, None}),</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F08', None, 'EF.OPL5G', '5GS Operator PLMN List', size={10, None}),</span><br><span style="color: hsl(120, 100%, 40%);">+          # TransparentEF('4F09', None, 'EF.NSI', 'Network Specific Identifier'), # FFS</span><br><span style="color: hsl(120, 100%, 40%);">+          TransparentEF('4F0A', None, 'EF.Routing_Indicator', 'Routing Indicator', size={4,4}),</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%);">+        self.add_files(files)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_select_response(self, data_hex):</span><br><span style="color: hsl(120, 100%, 40%);">+        return data_hex</span><br><span> </span><br><span> class ADF_USIM(CardADF):</span><br><span>     def __init__(self, aid='a0000000871002', name='ADF.USIM', fid=None, sfid=None,</span><br><span>@@ -370,6 +511,7 @@</span><br><span>           EF_CBMID(sfid=0x0e),</span><br><span>           EF_ECC(sfid=0x01),</span><br><span>           EF_CBMIR(),</span><br><span style="color: hsl(120, 100%, 40%);">+          DF_USIM_5GS(),</span><br><span>           ]</span><br><span>         self.add_files(files)</span><br><span> </span><br><span>diff --git a/tests/test_utils.py b/tests/test_utils.py</span><br><span>old mode 100644</span><br><span>new mode 100755</span><br><span>index bcf6140..badde55</span><br><span>--- a/tests/test_utils.py</span><br><span>+++ b/tests/test_utils.py</span><br><span>@@ -1,9 +1,26 @@</span><br><span> #!/usr/bin/env python3</span><br><span> </span><br><span> import unittest</span><br><span style="color: hsl(0, 100%, 40%);">-import pySim.utils as utils</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim import utils</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_102 import EF_SUCI_Calc_Info</span><br><span> </span><br><span> class DecTestCase(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+      # TS33.501 Annex C.4 test keys</span><br><span style="color: hsl(120, 100%, 40%);">+        hnet_pubkey_profile_b = "0272DA71976234CE833A6907425867B82E074D44EF907DFB4B3E21C1C2256EBCD1" # ID 27 in test file</span><br><span style="color: hsl(120, 100%, 40%);">+   hnet_pubkey_profile_a = "5A8D38864820197C3394B92613B20B91633CBD897119273BF8E4A6F4EEC0A650" # ID 30 in test file  </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TS31.121 4.9.4 EF_SUCI_Calc_Info test file</span><br><span style="color: hsl(120, 100%, 40%);">+  testfile_suci_calc_info = "A006020101020000A14B80011B8121" +hnet_pubkey_profile_b +"80011E8120" +hnet_pubkey_profile_a</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  decoded_testfile_suci = {</span><br><span style="color: hsl(120, 100%, 40%);">+             'prot_scheme_id_list': [</span><br><span style="color: hsl(120, 100%, 40%);">+                      {'priority': 0, 'identifier': 2, 'key_index': 1},</span><br><span style="color: hsl(120, 100%, 40%);">+                     {'priority': 1, 'identifier': 1, 'key_index': 2},</span><br><span style="color: hsl(120, 100%, 40%);">+                     {'priority': 2, 'identifier': 0, 'key_index': 0}],</span><br><span style="color: hsl(120, 100%, 40%);">+            'hnet_pubkey_list': [</span><br><span style="color: hsl(120, 100%, 40%);">+                 {'hnet_pubkey_identifier': 27, 'hnet_pubkey': hnet_pubkey_profile_b.lower()}, # because h2b/b2h returns all lower-case</span><br><span style="color: hsl(120, 100%, 40%);">+                        {'hnet_pubkey_identifier': 30, 'hnet_pubkey': hnet_pubkey_profile_a.lower()}]</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span>        def testSplitHexStringToListOf5ByteEntries(self):</span><br><span>            input_str = "ffffff0003ffffff0002ffffff0001"</span><br><span>@@ -81,5 +98,16 @@</span><br><span>          expected += "\tffffff0000 # unused\n"</span><br><span>              self.assertEqual(utils.format_xplmn_w_act(input_str), expected)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def testDecodeSuciCalcInfo(self):</span><br><span style="color: hsl(120, 100%, 40%);">+             suci_calc_info = EF_SUCI_Calc_Info()</span><br><span style="color: hsl(120, 100%, 40%);">+          decoded = suci_calc_info._decode_hex(self.testfile_suci_calc_info)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.assertDictEqual(self.decoded_testfile_suci, decoded)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def testEncodeSuciCalcInfo(self):</span><br><span style="color: hsl(120, 100%, 40%);">+             suci_calc_info = EF_SUCI_Calc_Info()</span><br><span style="color: hsl(120, 100%, 40%);">+          encoded = suci_calc_info._encode_hex(self.decoded_testfile_suci)</span><br><span style="color: hsl(120, 100%, 40%);">+              self.assertEqual(encoded.lower(), self.testfile_suci_calc_info.lower())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if __name__ == "__main__":</span><br><span>    unittest.main()</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/23564">change 23564</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/+/23564"/><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: I848a766e6d00be497c7db905475e0681cce197ac </div>
<div style="display:none"> Gerrit-Change-Number: 23564 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: merlinchlosta <merlin.chlosta@rub.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>