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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">WIP: Add option to set UE operation mode in EF_AD (Administrative Data)<br><br>Use ``--opmode=OPMODE`` in cmdline mode or column ``OPMODE`` in csv mode<br>to specify OPMODE as listed below.<br><br>Details:<br>The ``EF_AD`` field contains administrative data (AD).<br>It consists of four bytes ``B1``, ``B2``, ``B3``, ``B4``,<br>and optionally further bytes for future use.<br><br>Previous implementation only sets the MNC field appropriately<br>(located in `B4`) and sets all other bits/bytes to 0.<br><br>However, `B1` also defines the *UE operation mode* (see below).<br>For type approval operations, such as testing with a test uSIM,<br>this value could be set to `0x80` rather than `0x00`(= normal operation).<br>This may unlock some UE capabilities that are restricted in<br>normal operation mode.<br><br>Excerpt from [ETSI TS 131 102, 4.2.18](https://www.etsi.org/deliver/etsi_ts/131100_131199/131102/04.15.00_60/ts_131102v041500p.pdf):<br><br>```<br>B1 - UE operation mode:<br>    Coding:<br>       Initial value<br> - '00' normal operation.<br>      - '80' type approval operations.<br>      - '01' normal operation + specific facilities.<br>        - '81' type approval operations + specific facilities.<br>        - '02' maintenance (off line).<br>        - '04' cell test operation.<br><br>B2 - Additional information:<br>   Coding:<br>       Reserved for future use<br><br>B3 - Additional information:<br>       Coding:<br>       - B3.b1: OFM setting (Ciphering Indicator)<br>    - B3.others: Reserved for future use<br><br>B4 - Length of MNC in the IMSI:<br>       Coding:<br>       - B4.b4..B4.b1: length:  '0010' (= 2) or '0011' (=3)<br>  - B4.others: Reserved for future use<br>```<br><br>**Legend:** Byte X, bit Y: BX.bY<br><br>Further reading: https://nickvsnetworking.com/usim-basics/<br><br>Change-Id: Ie9040c6b127c268878a0845ed73d0918ec6bbb08<br>---<br>M pySim-prog.py<br>M pySim/cards.py<br>2 files changed, 71 insertions(+), 21 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim-prog.py b/pySim-prog.py</span><br><span>index 7b1283b..0abd190 100755</span><br><span>--- a/pySim-prog.py</span><br><span>+++ b/pySim-prog.py</span><br><span>@@ -37,7 +37,7 @@</span><br><span> from pySim.transport import init_reader</span><br><span> from pySim.cards import _cards_classes, card_detect</span><br><span> from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn, dec_iccid</span><br><span style="color: hsl(0, 100%, 40%);">-from pySim.ts_51_011 import EF</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF, EF_AD</span><br><span> from pySim.card_handler import *</span><br><span> from pySim.utils import *</span><br><span> </span><br><span>@@ -143,6 +143,11 @@</span><br><span>    parser.add_option("--acc", dest="acc",</span><br><span>                   help="Set ACC bits (Access Control Code). not all card types are supported",</span><br><span>               )</span><br><span style="color: hsl(120, 100%, 40%);">+     parser.add_option("--opmode", dest="opmode", type="choice",</span><br><span style="color: hsl(120, 100%, 40%);">+                     help="Set UE Operation Mode in EF.AD (Administrative Data)",</span><br><span style="color: hsl(120, 100%, 40%);">+                        default=None,</span><br><span style="color: hsl(120, 100%, 40%);">+                 choices=['{:02X}'.format(m) for m in list(EF_AD.OP_MODE.keys())],</span><br><span style="color: hsl(120, 100%, 40%);">+             )</span><br><span>    parser.add_option("--epdgid", dest="epdgid",</span><br><span>                     help="Set Home Evolved Packet Data Gateway (ePDG) Identifier. (Only FQDN format supported)",</span><br><span>               )</span><br><span>@@ -472,6 +477,7 @@</span><br><span>              'ims_hdomain': opts.ims_hdomain,</span><br><span>             'impi' : opts.impi,</span><br><span>          'impu' : opts.impu,</span><br><span style="color: hsl(120, 100%, 40%);">+           'opmode': opts.opmode,</span><br><span>       }</span><br><span> </span><br><span> </span><br><span>@@ -490,6 +496,8 @@</span><br><span>      if 'acc' in params:</span><br><span>          s.append(" > ACC      : %(acc)s")</span><br><span>       s.append(" > ADM1(hex): %(pin_adm)s")</span><br><span style="color: hsl(120, 100%, 40%);">+    if 'opmode' in params:</span><br><span style="color: hsl(120, 100%, 40%);">+                s.append(" > OPMODE   : %(opmode)s")</span><br><span>    print("\n".join(s) % params)</span><br><span> </span><br><span> </span><br><span>diff --git a/pySim/cards.py b/pySim/cards.py</span><br><span>index 8ac80bf..8489c97 100644</span><br><span>--- a/pySim/cards.py</span><br><span>+++ b/pySim/cards.py</span><br><span>@@ -22,7 +22,7 @@</span><br><span> # along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span> #</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from pySim.ts_51_011 import EF, DF</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import EF, DF, EF_AD</span><br><span> from pySim.ts_31_102 import EF_USIM_ADF_map</span><br><span> from pySim.ts_31_103 import EF_ISIM_ADF_map</span><br><span> from pySim.utils import *</span><br><span>@@ -153,22 +153,52 @@</span><br><span>            data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))</span><br><span>            return sw</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   def update_ad(self, mnc):</span><br><span style="color: hsl(0, 100%, 40%);">-               #See also: 3GPP TS 31.102, chapter 4.2.18</span><br><span style="color: hsl(0, 100%, 40%);">-               mnclen = len(str(mnc))</span><br><span style="color: hsl(0, 100%, 40%);">-          if mnclen == 1:</span><br><span style="color: hsl(0, 100%, 40%);">-                 mnclen = 2</span><br><span style="color: hsl(0, 100%, 40%);">-              if mnclen > 3:</span><br><span style="color: hsl(0, 100%, 40%);">-                       raise RuntimeError('unable to calculate proper mnclen')</span><br><span style="color: hsl(120, 100%, 40%);">+       def update_ad(self, mnc=None, opmode=None, ofm=None):</span><br><span style="color: hsl(120, 100%, 40%);">+         """</span><br><span style="color: hsl(120, 100%, 40%);">+            Update Administrative Data (AD)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-             data, sw = self._scc.read_binary(EF['AD'], length=None, offset=0)</span><br><span style="color: hsl(120, 100%, 40%);">+             See Sec. "4.2.18 EF_AD (Administrative Data)"</span><br><span style="color: hsl(120, 100%, 40%);">+               in 3GPP TS 31.102 for the details of the EF_AD contents.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            # Reset contents to EF.AD in case the file is uninintalized</span><br><span style="color: hsl(0, 100%, 40%);">-             if data.lower() == "ffffffff":</span><br><span style="color: hsl(0, 100%, 40%);">-                        data = "00000000"</span><br><span style="color: hsl(120, 100%, 40%);">+           Set any parameter to None to keep old value(s) on card.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-             content = data[0:6] + "%02X" % mnclen</span><br><span style="color: hsl(0, 100%, 40%);">-         data, sw = self._scc.update_binary(EF['AD'], content)</span><br><span style="color: hsl(120, 100%, 40%);">+         Parameters:</span><br><span style="color: hsl(120, 100%, 40%);">+                   mnc (str): MNC of IMSI</span><br><span style="color: hsl(120, 100%, 40%);">+                        opmode (Hex-str, 1 Byte): MS Operation Mode</span><br><span style="color: hsl(120, 100%, 40%);">+                   ofm (Hex-str, 1 Byte): Operational Feature Monitor (OFM) aka Ciphering Indicator</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+                      str: Return code of write operation</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%);">+          ad = EF_AD()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                # read from card</span><br><span style="color: hsl(120, 100%, 40%);">+              raw_hex_data, sw = self._scc.read_binary(EF['AD'], length=None, offset=0)</span><br><span style="color: hsl(120, 100%, 40%);">+             raw_bin_data = h2b(raw_hex_data)</span><br><span style="color: hsl(120, 100%, 40%);">+              abstract_data = ad.decode_bin(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         # perform updates</span><br><span style="color: hsl(120, 100%, 40%);">+             if mnc:</span><br><span style="color: hsl(120, 100%, 40%);">+                       mnclen = len(str(mnc))</span><br><span style="color: hsl(120, 100%, 40%);">+                        if mnclen == 1:</span><br><span style="color: hsl(120, 100%, 40%);">+                               mnclen = 2</span><br><span style="color: hsl(120, 100%, 40%);">+                    if mnclen > 3:</span><br><span style="color: hsl(120, 100%, 40%);">+                             raise RuntimeError('invalid length of mnc "{}"'.format(mnc))</span><br><span style="color: hsl(120, 100%, 40%);">+                        abstract_data['len_of_mnc_in_imsi'] = mnclen</span><br><span style="color: hsl(120, 100%, 40%);">+          if opmode:</span><br><span style="color: hsl(120, 100%, 40%);">+                    opmode_symb = ad.OP_MODE.get(int(opmode, 16))</span><br><span style="color: hsl(120, 100%, 40%);">+                 if opmode_symb:</span><br><span style="color: hsl(120, 100%, 40%);">+                               abstract_data['ms_operation_mode'] = opmode_symb</span><br><span style="color: hsl(120, 100%, 40%);">+                      else:</span><br><span style="color: hsl(120, 100%, 40%);">+                         raise RuntimeError('invalid opmode "{}"'.format(opmode))</span><br><span style="color: hsl(120, 100%, 40%);">+            if ofm:</span><br><span style="color: hsl(120, 100%, 40%);">+                       abstract_data['specific_facilities']['ofm'] = bool(int(ofm, 16))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            # write to card</span><br><span style="color: hsl(120, 100%, 40%);">+               raw_bin_data = ad.encode_bin(abstract_data)</span><br><span style="color: hsl(120, 100%, 40%);">+           raw_hex_data = b2h(raw_bin_data)</span><br><span style="color: hsl(120, 100%, 40%);">+              data, sw = self._scc.update_binary(EF['AD'], raw_hex_data)</span><br><span>           return sw</span><br><span> </span><br><span>        def read_spn(self):</span><br><span>@@ -915,8 +945,12 @@</span><br><span>                           print("Programming HPLMNwAcT failed with code %s"%sw)</span><br><span> </span><br><span>          # EF.AD</span><br><span style="color: hsl(0, 100%, 40%);">-         if p.get('mcc') and p.get('mnc'):</span><br><span style="color: hsl(0, 100%, 40%);">-                       sw = self.update_ad(p['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+         if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):</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%);">+                             mnc = p['mnc']</span><br><span style="color: hsl(120, 100%, 40%);">+                        else:</span><br><span style="color: hsl(120, 100%, 40%);">+                         mnc = None</span><br><span style="color: hsl(120, 100%, 40%);">+                    sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))</span><br><span>                         if sw != '9000':</span><br><span>                             print("Programming AD failed with code %s"%sw)</span><br><span> </span><br><span>@@ -1205,8 +1239,12 @@</span><br><span>                                print("Programming OPLMNwAcT failed with code %s"%sw)</span><br><span> </span><br><span>          # EF.AD</span><br><span style="color: hsl(0, 100%, 40%);">-         if p.get('mcc') and p.get('mnc'):</span><br><span style="color: hsl(0, 100%, 40%);">-                       sw = self.update_ad(p['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+         if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):</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%);">+                             mnc = p['mnc']</span><br><span style="color: hsl(120, 100%, 40%);">+                        else:</span><br><span style="color: hsl(120, 100%, 40%);">+                         mnc = None</span><br><span style="color: hsl(120, 100%, 40%);">+                    sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))</span><br><span>                         if sw != '9000':</span><br><span>                             print("Programming AD failed with code %s"%sw)</span><br><span> </span><br><span>@@ -1300,8 +1338,12 @@</span><br><span>                                print("Programming HPLMNwAcT failed with code %s"%sw)</span><br><span> </span><br><span>          # EF.AD</span><br><span style="color: hsl(0, 100%, 40%);">-         if p.get('mcc') and p.get('mnc'):</span><br><span style="color: hsl(0, 100%, 40%);">-                       sw = self.update_ad(p['mnc'])</span><br><span style="color: hsl(120, 100%, 40%);">+         if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):</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%);">+                             mnc = p['mnc']</span><br><span style="color: hsl(120, 100%, 40%);">+                        else:</span><br><span style="color: hsl(120, 100%, 40%);">+                         mnc = None</span><br><span style="color: hsl(120, 100%, 40%);">+                    sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))</span><br><span>                         if sw != '9000':</span><br><span>                             print("Programming AD failed with code %s"%sw)</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/23632">change 23632</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/+/23632"/><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: Ie9040c6b127c268878a0845ed73d0918ec6bbb08 </div>
<div style="display:none"> Gerrit-Change-Number: 23632 </div>
<div style="display:none"> Gerrit-PatchSet: 12 </div>
<div style="display:none"> Gerrit-Owner: Falkenber9 <robert.falkenberg@tu-dortmund.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>