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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">ARA-M related command support<br><br>This introduces support for talking to the ARA-M application on a card,<br>as specified in the GlobalPlatform "Secure Element Access Control"<br>specification v1.1.<br><br>Change-Id: Ia9107a4629c3d68320f32bbd4dd26e1f430717da<br>---<br>M docs/shell.rst<br>M pySim-shell.py<br>A pySim/ara_m.py<br>3 files changed, 454 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/docs/shell.rst b/docs/shell.rst</span><br><span>index e5d70a0..3ab1113 100644</span><br><span>--- a/docs/shell.rst</span><br><span>+++ b/docs/shell.rst</span><br><span>@@ -443,6 +443,94 @@</span><br><span>    :func: ADF_USIM.AddlShellCommands.authenticate_parser</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ARA-M commands</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%);">+The ARA-M commands exist to manage the access rules stored in an ARA-M applet on the card.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ARA-M in the context of SIM cards is primarily used to enable Android UICC Carrier Privileges,</span><br><span style="color: hsl(120, 100%, 40%);">+please see https://source.android.com/devices/tech/config/uicc for more details on the background.</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%);">+aram_get_all</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%);">+Obtain and decode all access rules from the ARA-M applet on the card.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+NOTE: if the total size of the access rules exceeds 255 bytes, this command will fail, as</span><br><span style="color: hsl(120, 100%, 40%);">+it doesn't yet implement fragmentation/reassembly on rule retrieval. YMMV</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  pySIM-shell (MF/ADF.ARA-M)> aram_get_all</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%);">+          "ResponseAllRefArDO": [</span><br><span style="color: hsl(120, 100%, 40%);">+              {</span><br><span style="color: hsl(120, 100%, 40%);">+                  "RefArDO": [</span><br><span style="color: hsl(120, 100%, 40%);">+                      {</span><br><span style="color: hsl(120, 100%, 40%);">+                          "RefDO": [</span><br><span style="color: hsl(120, 100%, 40%);">+                              {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "AidRefDO": "ffffffffffff"</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%);">+                                  "DevAppIdRefDO": "e46872f28b350b7e1f140de535c2a8d5804f0be3"</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%);">+                      },</span><br><span style="color: hsl(120, 100%, 40%);">+                      {</span><br><span style="color: hsl(120, 100%, 40%);">+                          "ArDO": [</span><br><span style="color: hsl(120, 100%, 40%);">+                              {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "ApduArDO": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      "generic_access_rule": "always"</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%);">+                              {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "PermArDO": {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      "permissions": "0000000000000001"</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%);">+                          ]</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%);">+              }</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%);">+  ]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+aram_get_config</span><br><span style="color: hsl(120, 100%, 40%);">+~~~~~~~~~~~~~~~</span><br><span style="color: hsl(120, 100%, 40%);">+Perform Config handshake with ARA-M applet: Tell it our version and retrieve its version.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+NOTE: Not supported in all ARA-M implementations.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.. argparse::</span><br><span style="color: hsl(120, 100%, 40%);">+   :module: pySim.ara_m</span><br><span style="color: hsl(120, 100%, 40%);">+   :func: ADF_ARAM.AddlShellCommands.get_config_parser</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%);">+aram_store_ref_ar_do</span><br><span style="color: hsl(120, 100%, 40%);">+~~~~~~~~~~~~~~~~~~~~</span><br><span style="color: hsl(120, 100%, 40%);">+Store a [new] access rule on the ARA-M applet.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.. argparse::</span><br><span style="color: hsl(120, 100%, 40%);">+   :module: pySim.ara_m</span><br><span style="color: hsl(120, 100%, 40%);">+   :func: ADF_ARAM.AddlShellCommands.store_ref_ar_do_parse</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For example, to store an Android UICC carrier privilege rule for the SHA1 hash of the certificate used to sign the CoIMS android app of Supreeth Herle (https://github.com/herlesupreeth/CoIMS_Wiki) you can use the following command:</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  pySIM-shell (MF/ADF.ARA-M)> aram_store_ref_ar_do --aid FFFFFFFFFFFF --device-app-id E46872F28B350B7E1F140DE535C2A8D5804F0BE3 --android-permissions 0000000000000001 --apdu-always</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%);">+aram_delete_all</span><br><span style="color: hsl(120, 100%, 40%);">+~~~~~~~~~~~~~~~</span><br><span style="color: hsl(120, 100%, 40%);">+This command will request deletion of all access rules stored within the</span><br><span style="color: hsl(120, 100%, 40%);">+ARA-M applet.  Use it with caution, there is no undo.  Any rules later</span><br><span style="color: hsl(120, 100%, 40%);">+intended must be manually inserted again using `aram_store_ref_ar_do`</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> cmd2 settable parameters</span><br><span> ------------------------</span><br><span>diff --git a/pySim-shell.py b/pySim-shell.py</span><br><span>index 037b843..128c0ea 100755</span><br><span>--- a/pySim-shell.py</span><br><span>+++ b/pySim-shell.py</span><br><span>@@ -49,6 +49,7 @@</span><br><span> from pySim.ts_102_221 import CardProfileUICC</span><br><span> from pySim.ts_31_102 import CardApplicationUSIM</span><br><span> from pySim.ts_31_103 import CardApplicationISIM</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ara_m import CardApplicationARAM</span><br><span> from pySim.gsm_r import DF_EIRENE</span><br><span> </span><br><span> # we need to import this module so that the SysmocomSJA2 sub-class of</span><br><span>@@ -86,6 +87,7 @@</span><br><span>      profile = CardProfileUICC()</span><br><span>  profile.add_application(CardApplicationUSIM())</span><br><span>       profile.add_application(CardApplicationISIM())</span><br><span style="color: hsl(120, 100%, 40%);">+        profile.add_application(CardApplicationARAM())</span><br><span>       rs = RuntimeState(card, profile)</span><br><span> </span><br><span>         # FIXME: do this dynamically</span><br><span>diff --git a/pySim/ara_m.py b/pySim/ara_m.py</span><br><span>new file mode 100644</span><br><span>index 0000000..5dee3e0</span><br><span>--- /dev/null</span><br><span>+++ b/pySim/ara_m.py</span><br><span>@@ -0,0 +1,364 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# -*- coding: utf-8 -*-</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# without this, pylint will fail when inner classes are used</span><br><span style="color: hsl(120, 100%, 40%);">+# within the 'nested' kwarg of our TlvMeta metaclass on python 3.7 :(</span><br><span style="color: hsl(120, 100%, 40%);">+# pylint: disable=undefined-variable</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%);">+Support for the Secure Element Access Control, specifically the ARA-M inside an UICC.</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%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# Copyright (C) 2021 Harald Welte <laforge@osmocom.org></span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is free software: you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+# the Free Software Foundation, either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+# (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+# GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+# along with this program.  If not, see <http://www.gnu.org/licenses/>.</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from construct import *</span><br><span style="color: hsl(120, 100%, 40%);">+from construct import Optional as COptional</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.construct import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.tlv import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# various BER-TLV encoded Data Objects (DOs)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AidRefDO(BER_TLV_IE, tag=0x4f):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-3</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = HexAdapter(GreedyBytes)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AidRefEmptyDO(BER_TLV_IE, tag=0xc0):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-3</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DevAppIdRefDO(BER_TLV_IE, tag=0xc1):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-4</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = HexAdapter(GreedyBytes)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class PkgRefDO(BER_TLV_IE, tag=0xca):</span><br><span style="color: hsl(120, 100%, 40%);">+    # Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('package_name_string'/GreedyString("ascii"))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class RefDO(BER_TLV_IE, tag=0xe1, nested=[AidRefDO,AidRefEmptyDO,DevAppIdRefDO,PkgRefDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-5</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ApduArDO(BER_TLV_IE, tag=0xd0):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-8</span><br><span style="color: hsl(120, 100%, 40%);">+    def _from_bytes(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        if len(do) == 1:</span><br><span style="color: hsl(120, 100%, 40%);">+            if do[0] == 0x00:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.decoded = {'generic_access_rule': 'never'}</span><br><span style="color: hsl(120, 100%, 40%);">+                return self.decoded</span><br><span style="color: hsl(120, 100%, 40%);">+            elif do[0] == 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.decoded = {'generic_access_rule': 'always'}</span><br><span style="color: hsl(120, 100%, 40%);">+                return self.decoded</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                return ValueError('Invalid 1-byte generic APDU access rule')</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            if len(do) % 8:</span><br><span style="color: hsl(120, 100%, 40%);">+                return ValueError('Invalid non-modulo-8 length of APDU filter: %d' % len(do))</span><br><span style="color: hsl(120, 100%, 40%);">+            self.decoded['apdu_filter'] = []</span><br><span style="color: hsl(120, 100%, 40%);">+            offset = 0</span><br><span style="color: hsl(120, 100%, 40%);">+            while offset < len(do):</span><br><span style="color: hsl(120, 100%, 40%);">+                self.decoded['apdu_filter'] += {'header': b2h(do[offset:offset+4]),</span><br><span style="color: hsl(120, 100%, 40%);">+                                                'mask': b2h(do[offset+4:offset+8])}</span><br><span style="color: hsl(120, 100%, 40%);">+            self.decoded = res</span><br><span style="color: hsl(120, 100%, 40%);">+            return res</span><br><span style="color: hsl(120, 100%, 40%);">+    def _to_bytes(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        if 'generic_access_rule' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+            if self.decoded['generic_access_rule'] == 'never':</span><br><span style="color: hsl(120, 100%, 40%);">+                return b'\x00'</span><br><span style="color: hsl(120, 100%, 40%);">+            elif self.decoded['generic_access_rule'] == 'always':</span><br><span style="color: hsl(120, 100%, 40%);">+                return b'\x01'</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                return ValueError('Invalid 1-byte generic APDU access rule')</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            if not 'apdu_filter' in self.decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+                return ValueError('Invalid APDU AR DO')</span><br><span style="color: hsl(120, 100%, 40%);">+            filters = self.decoded['apdu_filter']</span><br><span style="color: hsl(120, 100%, 40%);">+            res = b''</span><br><span style="color: hsl(120, 100%, 40%);">+            for f in filters:</span><br><span style="color: hsl(120, 100%, 40%);">+                if not 'header' in f or not 'mask' in f:</span><br><span style="color: hsl(120, 100%, 40%);">+                    return ValueError('APDU filter must contain header and mask')</span><br><span style="color: hsl(120, 100%, 40%);">+                header_b = h2b(f['header'])</span><br><span style="color: hsl(120, 100%, 40%);">+                mask_b = h2b(f['mask'])</span><br><span style="color: hsl(120, 100%, 40%);">+                if len(header_b) != 4 or len(mask_b) != 4:</span><br><span style="color: hsl(120, 100%, 40%);">+                    return ValueError('APDU filter header and mask must each be 4 bytes')</span><br><span style="color: hsl(120, 100%, 40%);">+                res += header_b + mask_b</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%);">+class NfcArDO(BER_TLV_IE, tag=0xd1):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-9</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('nfc_event_access_rule'/Enum(Int8ub, never=0, always=1))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class PermArDO(BER_TLV_IE, tag=0xdb):</span><br><span style="color: hsl(120, 100%, 40%);">+    # Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('permissions'/HexAdapter(Bytes(8)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ArDO(BER_TLV_IE, tag=0xe3, nested=[ApduArDO, NfcArDO, PermArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-7</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class RefArDO(BER_TLV_IE, tag=0xe2, nested=[RefDO, ArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-6</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseAllRefArDO(BER_TLV_IE, tag=0xff40, nested=[RefArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 4-2</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseArDO(BER_TLV_IE, tag=0xff50, nested=[ArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 4-3</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseRefreshTagDO(BER_TLV_IE, tag=0xdf20):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 4-4</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('refresh_tag'/HexAdapter(Bytes(8)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DeviceInterfaceVersionDO(BER_TLV_IE, tag=0xe6):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-12</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('major'/Int8ub, 'minor'/Int8ub, 'patch'/Int8ub)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DeviceConfigDO(BER_TLV_IE, tag=0xe4, nested=[DeviceInterfaceVersionDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-10</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseDeviceConfigDO(BER_TLV_IE, tag=0xff7f, nested=[DeviceConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-14</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AramConfigDO(BER_TLV_IE, tag=0xe5, nested=[DeviceInterfaceVersionDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-11</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseAramConfigDO(BER_TLV_IE, tag=0xdf21, nested=[AramConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 4-5</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandStoreRefArDO(BER_TLV_IE, tag=0xf0, nested=[RefArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-2</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandDelete(BER_TLV_IE, tag=0xf1, nested=[AidRefDO, AidRefEmptyDO, RefDO, RefArDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-4</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandUpdateRefreshTagDO(BER_TLV_IE, tag=0xf2):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID V1.1 Table 5-6</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandRegisterClientAidsDO(BER_TLV_IE, tag=0xf7, nested=[AidRefDO, AidRefEmptyDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-7</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandGet(BER_TLV_IE, tag=0xf3, nested=[AidRefDO, AidRefEmptyDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-8</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandGetAll(BER_TLV_IE, tag=0xf4):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-9</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandGetClientAidsDO(BER_TLV_IE, tag=0xf6):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-10</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandGetNext(BER_TLV_IE, tag=0xf5):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-11</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CommandGetDeviceConfigDO(BER_TLV_IE, tag=0xf8):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-12</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ResponseAracAidDO(BER_TLV_IE, tag=0xff70, nested=[AidRefDO, AidRefEmptyDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 5-13</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class BlockDO(BER_TLV_IE, tag=0xe7):</span><br><span style="color: hsl(120, 100%, 40%);">+    # SEID v1.1 Table 6-13</span><br><span style="color: hsl(120, 100%, 40%);">+    _construct = Struct('offset'/Int16ub, 'length'/Int8ub)</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%);">+# SEID v1.1 Table 4-1</span><br><span style="color: hsl(120, 100%, 40%);">+class GetCommandDoCollection(TLV_IE_Collection, nested=[RefDO, DeviceConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# SEID v1.1 Table 4-2</span><br><span style="color: hsl(120, 100%, 40%);">+class GetResponseDoCollection(TLV_IE_Collection, nested=[ResponseAllRefArDO, ResponseArDO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                         ResponseRefreshTagDO, ResponseAramConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# SEID v1.1 Table 5-1</span><br><span style="color: hsl(120, 100%, 40%);">+class StoreCommandDoCollection(TLV_IE_Collection,</span><br><span style="color: hsl(120, 100%, 40%);">+                               nested=[BlockDO, CommandStoreRefArDO, CommandDelete,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       CommandUpdateRefreshTagDO, CommandRegisterClientAidsDO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       CommandGet, CommandGetAll, CommandGetClientAidsDO,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       CommandGetNext, CommandGetDeviceConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</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%);">+# SEID v1.1 Section 5.1.2</span><br><span style="color: hsl(120, 100%, 40%);">+class StoreResponseDoCollection(TLV_IE_Collection,</span><br><span style="color: hsl(120, 100%, 40%);">+                                nested=[ResponseAllRefArDO, ResponseAracAidDO, ResponseDeviceConfigDO]):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ADF_ARAM(CardADF):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, aid='a00000015141434c00', name='ADF.ARA-M', fid=None, sfid=None,</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc='ARA-M Application'):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.shell_commands += [self.AddlShellCommands()]</span><br><span style="color: hsl(120, 100%, 40%);">+        files = []</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%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def xceive_apdu_tlv(tp, hdr:Hexstr, cmd_do, resp_cls, exp_sw='9000'):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Transceive an APDU with the card, transparently encoding the command data from TLV</span><br><span style="color: hsl(120, 100%, 40%);">+        and decoding the response data tlv."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if cmd_do:</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd_do_enc = cmd_do.to_ie()</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd_do_len = len(cmd_do_enc)</span><br><span style="color: hsl(120, 100%, 40%);">+            if cmd_do_len > 255:</span><br><span style="color: hsl(120, 100%, 40%);">+                return ValueError('DO > 255 bytes not supported yet')</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd_do_enc = b''</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd_do_len = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        c_apdu = hdr + ('%02x' % cmd_do_len) + b2h(cmd_do_enc)</span><br><span style="color: hsl(120, 100%, 40%);">+        (data, sw) = tp.send_apdu_checksw(c_apdu, exp_sw)</span><br><span style="color: hsl(120, 100%, 40%);">+        if data:</span><br><span style="color: hsl(120, 100%, 40%);">+            if resp_cls:</span><br><span style="color: hsl(120, 100%, 40%);">+                resp_do = resp_cls()</span><br><span style="color: hsl(120, 100%, 40%);">+                resp_do.from_tlv(h2b(data))</span><br><span style="color: hsl(120, 100%, 40%);">+                return resp_do</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                return data</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</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 store_data(tp, do) -> bytes:</span><br><span style="color: hsl(120, 100%, 40%);">+        """Build the Command APDU for STORE DATA."""</span><br><span style="color: hsl(120, 100%, 40%);">+        return ADF_ARAM.xceive_apdu_tlv(tp, '80e29000', do, StoreResponseDoCollection)</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 get_all(tp):</span><br><span style="color: hsl(120, 100%, 40%);">+        return ADF_ARAM.xceive_apdu_tlv(tp, '80caff40', None, GetResponseDoCollection)</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 get_config(tp, v_major=0, v_minor=0, v_patch=1):</span><br><span style="color: hsl(120, 100%, 40%);">+        cmd_do = DeviceConfigDO()</span><br><span style="color: hsl(120, 100%, 40%);">+        cmd_do.from_dict([{'DeviceInterfaceVersionDO': {'major': v_major, 'minor': v_minor, 'patch': v_patch }}])</span><br><span style="color: hsl(120, 100%, 40%);">+        return ADF_ARAM.xceive_apdu_tlv(tp, '80cadf21', cmd_do, ResponseAramConfigDO)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @with_default_category('Application-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%);">+        def do_aram_get_all(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """GET DATA [All] on the ARA-M Applet"""</span><br><span style="color: hsl(120, 100%, 40%);">+            res_do = ADF_ARAM.get_all(self._cmd.card._scc._tp)</span><br><span style="color: hsl(120, 100%, 40%);">+            if res_do:</span><br><span style="color: hsl(120, 100%, 40%);">+                self._cmd.poutput_json(res_do.to_dict())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_aram_get_config(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """GET DATA [Config] on the ARA-M Applet"""</span><br><span style="color: hsl(120, 100%, 40%);">+            res_do = ADF_ARAM.get_config(self._cmd.card._scc._tp)</span><br><span style="color: hsl(120, 100%, 40%);">+            if res_do:</span><br><span style="color: hsl(120, 100%, 40%);">+                self._cmd.poutput_json(res_do.to_dict())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        store_ref_ar_do_parse = argparse.ArgumentParser()</span><br><span style="color: hsl(120, 100%, 40%);">+        # REF-DO</span><br><span style="color: hsl(120, 100%, 40%);">+        store_ref_ar_do_parse.add_argument('--device-app-id', required=True, help='Identifies the specific device application that the rule appplies to. Hash of Certificate of Application Provider, or UUID. (20/32 hex bytes)')</span><br><span style="color: hsl(120, 100%, 40%);">+        aid_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()</span><br><span style="color: hsl(120, 100%, 40%);">+        aid_grp.add_argument('--aid', help='Identifies the specific SE application for which rules are to be stored. Can be a partial AID, containing for example only the RID.  (5-16 hex bytes)')</span><br><span style="color: hsl(120, 100%, 40%);">+        aid_grp.add_argument('--aid-empty', action='store_true', help='No specific SE application, applies to all applications')</span><br><span style="color: hsl(120, 100%, 40%);">+        store_ref_ar_do_parse.add_argument('--pkg-ref', help='Full Android Java package name (up to 127 chars ASCII)')</span><br><span style="color: hsl(120, 100%, 40%);">+        # AR-DO</span><br><span style="color: hsl(120, 100%, 40%);">+        apdu_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()</span><br><span style="color: hsl(120, 100%, 40%);">+        apdu_grp.add_argument('--apdu-never', action='store_true', help='APDU access is not allowed')</span><br><span style="color: hsl(120, 100%, 40%);">+        apdu_grp.add_argument('--apdu-always', action='store_true', help='APDU access is allowed')</span><br><span style="color: hsl(120, 100%, 40%);">+        apdu_grp.add_argument('--apdu-filter', help='APDU filter: 4 byte CLA/INS/P1/P2 followed by 4 byte mask (8 hex bytes)')</span><br><span style="color: hsl(120, 100%, 40%);">+        nfc_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()</span><br><span style="color: hsl(120, 100%, 40%);">+        nfc_grp.add_argument('--nfc-always', action='store_true', help='NFC event access is allowed')</span><br><span style="color: hsl(120, 100%, 40%);">+        nfc_grp.add_argument('--nfc-never', action='store_true', help='NFC event access is not allowed')</span><br><span style="color: hsl(120, 100%, 40%);">+        store_ref_ar_do_parse.add_argument('--android-permissions', help='Android UICC Carrier Privilege Permissions (8 hex bytes)')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        @cmd2.with_argparser(store_ref_ar_do_parse)</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_aram_store_ref_ar_do(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Perform STORE DATA [Command-Store-REF-AR-DO] to store a new access rule."""</span><br><span style="color: hsl(120, 100%, 40%);">+            # REF</span><br><span style="color: hsl(120, 100%, 40%);">+            ref_do_content = []</span><br><span style="color: hsl(120, 100%, 40%);">+            if opts.aid:</span><br><span style="color: hsl(120, 100%, 40%);">+                ref_do_content += [{'AidRefDO': opts.aid}]</span><br><span style="color: hsl(120, 100%, 40%);">+            elif opts.aid_empty:</span><br><span style="color: hsl(120, 100%, 40%);">+                ref_do_content += [{'AidRefEmptyDO': None}]</span><br><span style="color: hsl(120, 100%, 40%);">+            ref_do_content += [{'DevAppIdRefDO': opts.device_app_id}]</span><br><span style="color: hsl(120, 100%, 40%);">+            if opts.pkg_ref:</span><br><span style="color: hsl(120, 100%, 40%);">+                ref_do_content += [{'PkgRefDO': opts.pkg_ref}]</span><br><span style="color: hsl(120, 100%, 40%);">+            # AR</span><br><span style="color: hsl(120, 100%, 40%);">+            ar_do_content = []</span><br><span style="color: hsl(120, 100%, 40%);">+            if opts.apdu_never:</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'ApduArDO': {'generic_access_rule': 'never'}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            elif opts.apdu_always:</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'ApduArDO': {'generic_access_rule': 'always'}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            elif opts.apdu_filter:</span><br><span style="color: hsl(120, 100%, 40%);">+                # TODO: multiple filters</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'ApduArDO': {'apdu_filter': [opts.apdu_filter]}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            if opts.nfc_always:</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'NfcArDO': {'nfc_event_access_rule': 'always'}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            elif opts.nfc_never:</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'NfcArDO': {'nfc_event_access_rule': 'never'}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            if opts.android_permissions:</span><br><span style="color: hsl(120, 100%, 40%);">+                ar_do_content += [{'PermArDO': {'permissions': opts.android_permissions}}]</span><br><span style="color: hsl(120, 100%, 40%);">+            d = [{'RefArDO': [{ 'RefDO': ref_do_content}, {'ArDO': ar_do_content }]}]</span><br><span style="color: hsl(120, 100%, 40%);">+            csrado = CommandStoreRefArDO()</span><br><span style="color: hsl(120, 100%, 40%);">+            csrado.from_dict(d)</span><br><span style="color: hsl(120, 100%, 40%);">+            res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, csrado)</span><br><span style="color: hsl(120, 100%, 40%);">+            if res_do:</span><br><span style="color: hsl(120, 100%, 40%);">+                self._cmd.poutput_json(res_do.to_dict())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def do_aram_delete_all(self, opts):</span><br><span style="color: hsl(120, 100%, 40%);">+            """Perform STORE DATA [Command-Delete[all]] to delete all access rules."""</span><br><span style="color: hsl(120, 100%, 40%);">+            deldo = CommandDelete()</span><br><span style="color: hsl(120, 100%, 40%);">+            res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, deldo)</span><br><span style="color: hsl(120, 100%, 40%);">+            if res_do:</span><br><span style="color: hsl(120, 100%, 40%);">+                self._cmd.poutput_json(res_do.to_dict())</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%);">+# SEAC v1.1 Section 4.1.2.2 + 5.1.2.2</span><br><span style="color: hsl(120, 100%, 40%);">+sw_aram = {</span><br><span style="color: hsl(120, 100%, 40%);">+    'ARA-M': {</span><br><span style="color: hsl(120, 100%, 40%);">+        '6381': 'Rule successfully stored but an access rule already exists',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6382': 'Rule successfully stored bu contained at least one unknown (discarded) BER-TLV',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6581': 'Memory Problem',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6700': 'Wrong Length in Lc',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6981': 'DO is not supported by the ARA-M/ARA-C',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6982': 'Security status not satisfied',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6984': 'Rules have been updated and must be read again / logical channels in use',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6985': 'Conditions not satisfied',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6a80': 'Incorrect values in the command data',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6a84': 'Rules have been updated and must be read again',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6a86': 'Incorrect P1 P2',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6a88': 'Referenced data not found',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6a89': 'Conflicting access rule already exists in the Secure Element',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6d00': 'Invalid instruction',</span><br><span style="color: hsl(120, 100%, 40%);">+        '6e00': 'Invalid class',</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardApplicationARAM(CardApplication):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+      super().__init__('ARA-M', adf=ADF_ARAM(), sw=sw_aram)</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/25885">change 25885</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/+/25885"/><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: Ia9107a4629c3d68320f32bbd4dd26e1f430717da </div>
<div style="display:none"> Gerrit-Change-Number: 25885 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </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: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: herlesupreeth <herlesupreeth@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>