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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add support for ADF_USIM/EF_EHPLMN<br><br>If the EF.EHPLMN exists, it contains the "Equivalent Home PLMN List".<br>The odd part of that list is that it is not just a list of additional<br>PLMN identities, but if the first digits of the IMSI are *not* listed<br>in EF.EHPLMN, then the MCC/MNC of the IMSI prefix is suddently no<br>longer considered the home network, but the subscriber is roaming.<br><br>See TS 23.122: "If the HPLMN code derived from the IMSI is not present<br>in the EHPLMN list, then it shall be treated as a Visited PLMN for PLMN<br>selection purposes."<br><br>Change-Id: I22d96ab4a424ec5bc1fb02f5e80165c646a748d3<br>---<br>M pySim-read.py<br>M pySim/cards.py<br>M pySim/commands.py<br>M pySim/utils.py<br>M pysim-testdata/sysmoISIM-SJA2.ok<br>5 files changed, 83 insertions(+), 4 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim-read.py b/pySim-read.py</span><br><span>index 69cab4d..d6674a5 100755</span><br><span>--- a/pySim-read.py</span><br><span>+++ b/pySim-read.py</span><br><span>@@ -241,6 +241,13 @@</span><br><span>         # Check whether we have th AID of USIM, if so select it by its AID</span><br><span>   # EF.UST - File Id in ADF USIM : 6f38</span><br><span>        if '9000' == card.select_adf_by_aid():</span><br><span style="color: hsl(120, 100%, 40%);">+                # EF.EHPLMN</span><br><span style="color: hsl(120, 100%, 40%);">+           if card.file_exists(EF_USIM_ADF_map['EHPLMN']):</span><br><span style="color: hsl(120, 100%, 40%);">+                       (res, sw) = card.read_ehplmn()</span><br><span style="color: hsl(120, 100%, 40%);">+                        if sw == '9000':</span><br><span style="color: hsl(120, 100%, 40%);">+                              print("EHPLMN:\n%s" % (res))</span><br><span style="color: hsl(120, 100%, 40%);">+                        else:</span><br><span style="color: hsl(120, 100%, 40%);">+                         print("EHPLMN: Can't read, response code = %s" % (sw,))</span><br><span>                # EF.UST</span><br><span>             (res, sw) = card.read_binary(EF_USIM_ADF_map['UST'])</span><br><span>                 if sw == '9000':</span><br><span>diff --git a/pySim/cards.py b/pySim/cards.py</span><br><span>index 808fde1..2d779b3 100644</span><br><span>--- a/pySim/cards.py</span><br><span>+++ b/pySim/cards.py</span><br><span>@@ -24,6 +24,7 @@</span><br><span> #</span><br><span> </span><br><span> from pySim.ts_51_011 import EF, DF</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_31_102 import EF_USIM_ADF_map</span><br><span> from pySim.utils import *</span><br><span> from smartcard.util import toBytes</span><br><span> </span><br><span>@@ -41,6 +42,13 @@</span><br><span>            print("warning: erasing is not supported for specified card type!")</span><br><span>                return</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def file_exists(self, fid):</span><br><span style="color: hsl(120, 100%, 40%);">+           res_arr = self._scc.try_select_file(fid)</span><br><span style="color: hsl(120, 100%, 40%);">+              for res in res_arr:</span><br><span style="color: hsl(120, 100%, 40%);">+               if res[1] != '9000':</span><br><span style="color: hsl(120, 100%, 40%);">+                      return False</span><br><span style="color: hsl(120, 100%, 40%);">+          return True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        def verify_adm(self, key):</span><br><span>           '''</span><br><span>              Authenticate with ADM key</span><br><span>@@ -262,6 +270,25 @@</span><br><span>             len = self._scc.record_size(ef)</span><br><span>              self._scc.update_record(ef, rec_no, "ff" * len, force_len=False, verify=True)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class UsimCard(Card):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, ssc):</span><br><span style="color: hsl(120, 100%, 40%);">+              super(UsimCard, self).__init__(ssc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def read_ehplmn(self):</span><br><span style="color: hsl(120, 100%, 40%);">+                (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])</span><br><span style="color: hsl(120, 100%, 40%);">+          if sw == '9000':</span><br><span style="color: hsl(120, 100%, 40%);">+                      return (format_xplmn(res), sw)</span><br><span style="color: hsl(120, 100%, 40%);">+                else:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return (None, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def update_ehplmn(self, mcc, mnc):</span><br><span style="color: hsl(120, 100%, 40%);">+            data = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)</span><br><span style="color: hsl(120, 100%, 40%);">+                size = len(data[0]) // 2</span><br><span style="color: hsl(120, 100%, 40%);">+              ehplmn = enc_plmn(mcc, mnc)</span><br><span style="color: hsl(120, 100%, 40%);">+           data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)</span><br><span style="color: hsl(120, 100%, 40%);">+         return sw</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> class _MagicSimBase(Card):</span><br><span>      """</span><br><span>@@ -552,7 +579,7 @@</span><br><span>                     return None</span><br><span>          return None</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class SysmoUSIMgr1(Card):</span><br><span style="color: hsl(120, 100%, 40%);">+class SysmoUSIMgr1(UsimCard):</span><br><span>      """</span><br><span>   sysmocom sysmoUSIM-GR1</span><br><span>       """</span><br><span>@@ -653,7 +680,7 @@</span><br><span>                     data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class SysmoUSIMSJS1(Card):</span><br><span style="color: hsl(120, 100%, 40%);">+class SysmoUSIMSJS1(UsimCard):</span><br><span>         """</span><br><span>   sysmocom sysmoUSIM-SJS1</span><br><span>      """</span><br><span>@@ -1037,7 +1064,7 @@</span><br><span>           return None</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class SysmoISIMSJA2(Card):</span><br><span style="color: hsl(120, 100%, 40%);">+class SysmoISIMSJA2(UsimCard):</span><br><span>        """</span><br><span>   sysmocom sysmoISIM-SJA2</span><br><span>      """</span><br><span>@@ -1144,16 +1171,22 @@</span><br><span>                         if p.get('opc'):</span><br><span>                             self._scc.update_binary('af20', p['opc'], 17)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-               # update EF-USIM_AUTH_KEY in ADF.USIM</span><br><span>                self._scc.select_file(['3f00'])</span><br><span>              aid = self.read_aid()</span><br><span>                if (aid):</span><br><span style="color: hsl(120, 100%, 40%);">+                     # update EF-USIM_AUTH_KEY in ADF.USIM</span><br><span>                        self._scc.select_adf(aid)</span><br><span>                    if p.get('ki'):</span><br><span>                              self._scc.update_binary('af20', p['ki'], 1)</span><br><span>                  if p.get('opc'):</span><br><span>                             self._scc.update_binary('af20', p['opc'], 17)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+                     # update EF.EHPLMN in ADF.USIM</span><br><span style="color: hsl(120, 100%, 40%);">+                        if self.file_exists(EF_USIM_ADF_map['EHPLMN']):</span><br><span style="color: hsl(120, 100%, 40%);">+                               if p.get('mcc') and p.get('mnc'):</span><br><span style="color: hsl(120, 100%, 40%);">+                                     sw = self.update_ehplmn(p['mcc'], p['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if sw != '9000':</span><br><span style="color: hsl(120, 100%, 40%);">+                                              print("Programming EHPLMN failed with code %s"%sw)</span><br><span>                 return</span><br><span> </span><br><span> </span><br><span>diff --git a/pySim/commands.py b/pySim/commands.py</span><br><span>index 7288b19..c260a97 100644</span><br><span>--- a/pySim/commands.py</span><br><span>+++ b/pySim/commands.py</span><br><span>@@ -100,6 +100,17 @@</span><br><span>       def sel_ctrl(self, value):</span><br><span>           self._sel_ctrl = value</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def try_select_file(self, dir_list):</span><br><span style="color: hsl(120, 100%, 40%);">+          rv = []</span><br><span style="color: hsl(120, 100%, 40%);">+               if type(dir_list) is not list:</span><br><span style="color: hsl(120, 100%, 40%);">+                        dir_list = [dir_list]</span><br><span style="color: hsl(120, 100%, 40%);">+         for i in dir_list:</span><br><span style="color: hsl(120, 100%, 40%);">+                    data, sw = self._tp.send_apdu(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)</span><br><span style="color: hsl(120, 100%, 40%);">+                    rv.append((data, sw))</span><br><span style="color: hsl(120, 100%, 40%);">+                 if sw != '9000':</span><br><span style="color: hsl(120, 100%, 40%);">+                              return rv</span><br><span style="color: hsl(120, 100%, 40%);">+             return rv</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  def select_file(self, dir_list):</span><br><span>             rv = []</span><br><span>              if type(dir_list) is not list:</span><br><span>diff --git a/pySim/utils.py b/pySim/utils.py</span><br><span>index ac82774..dc14d58 100644</span><br><span>--- a/pySim/utils.py</span><br><span>+++ b/pySim/utils.py</span><br><span>@@ -125,6 +125,9 @@</span><br><span> def hexstr_to_fivebytearr(s):</span><br><span>   return [s[i:i+10] for i in range(0, len(s), 10) ]</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def hexstr_to_threebytearr(s):</span><br><span style="color: hsl(120, 100%, 40%);">+ return [s[i:i+6] for i in range(0, len(s), 6) ]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Accepts hex string representing three bytes</span><br><span> def dec_mcc_from_plmn(plmn):</span><br><span>   ia = h2i(plmn)</span><br><span>@@ -213,6 +216,25 @@</span><br><span>        res['status'] = h2i(hexstr[34:36])</span><br><span>   return res</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def dec_xplmn(threehexbytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ res = {'mcc': 0, 'mnc': 0, 'act': []}</span><br><span style="color: hsl(120, 100%, 40%);">+ plmn_chars = 6</span><br><span style="color: hsl(120, 100%, 40%);">+        plmn_str = threehexbytes[:plmn_chars]                           # first three bytes (six ascii hex chars)</span><br><span style="color: hsl(120, 100%, 40%);">+     res['mcc'] = dec_mcc_from_plmn(plmn_str)</span><br><span style="color: hsl(120, 100%, 40%);">+      res['mnc'] = dec_mnc_from_plmn(plmn_str)</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%);">+def format_xplmn(hexstr):</span><br><span style="color: hsl(120, 100%, 40%);">+       s = ""</span><br><span style="color: hsl(120, 100%, 40%);">+      for rec_data in hexstr_to_threebytearr(hexstr):</span><br><span style="color: hsl(120, 100%, 40%);">+               rec_info = dec_xplmn(rec_data)</span><br><span style="color: hsl(120, 100%, 40%);">+                if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:</span><br><span style="color: hsl(120, 100%, 40%);">+                     rec_str = "unused"</span><br><span style="color: hsl(120, 100%, 40%);">+          else:</span><br><span style="color: hsl(120, 100%, 40%);">+                 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+                s += "\t%s # %s\n" % (rec_data, rec_str)</span><br><span style="color: hsl(120, 100%, 40%);">+    return s</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def derive_milenage_opc(ki_hex, op_hex):</span><br><span>       """</span><br><span>   Run the milenage algorithm to calculate OPC from Ki and OP</span><br><span>diff --git a/pysim-testdata/sysmoISIM-SJA2.ok b/pysim-testdata/sysmoISIM-SJA2.ok</span><br><span>index fd3f252..6054840 100644</span><br><span>--- a/pysim-testdata/sysmoISIM-SJA2.ok</span><br><span>+++ b/pysim-testdata/sysmoISIM-SJA2.ok</span><br><span>@@ -100,6 +100,12 @@</span><br><span>       Service 58 - Extension 8</span><br><span>     Service 59 - MMS User Connectivity Parameters</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+EHPLMN:</span><br><span style="color: hsl(120, 100%, 40%);">+        00f110 # MCC: 001 MNC: 001</span><br><span style="color: hsl(120, 100%, 40%);">+        ffffff # unused</span><br><span style="color: hsl(120, 100%, 40%);">+        ffffff # unused</span><br><span style="color: hsl(120, 100%, 40%);">+        ffffff # unused</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> USIM Service Table: beff9f9de73e0408400170730000002e00000000</span><br><span>  Service 2 - Fixed Dialling Numbers (FDN)</span><br><span>     Service 3 - Extension 2</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/18649">change 18649</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/+/18649"/><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: I22d96ab4a424ec5bc1fb02f5e80165c646a748d3 </div>
<div style="display:none"> Gerrit-Change-Number: 18649 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </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: guilly@gmail.com <guilly@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>