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